/* * Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com> * Licensed under the Apache License, Version 2.0 (the "License") * $Id: ClassInterfaceDetector.java 3918 2008-04-14 17:35:35Z gbevin $ */ package com.uwyn.rife.instrument; import com.uwyn.rife.asm.*; import com.uwyn.rife.instrument.ClassBytesProvider; import com.uwyn.rife.instrument.exceptions.VisitInterruptionException; /** * Detects whether a class implements a particular interface by analyzing the * bytecode instead of loading the class and performing reflection calls. * * @author Geert Bevin (gbevin[remove] at uwyn dot com) * @version $Revision: 3918 $ * @since 1.6 */ public class ClassInterfaceDetector { private final String OBJECT_INTERNAL_NAME = "java/lang/Object"; private final String OBJECT_NAME = "java.lang.Object"; private ClassBytesProvider mBytesProvider = null; private String mInterfaceNameInternal = null; /** * Creates a new instance of the interface detector. * * @param bytesProvider the bytecode provider that will be used to load the * bytes of the parent classes or interfaces * @param internalInterfaceNameInterned the name of the interface that should * be detected, it should be in the internal bytecode naming format * (/ instead of .) and it should be interned. * @since 1.6 */ public ClassInterfaceDetector(ClassBytesProvider bytesProvider, String internalInterfaceNameInterned) { mBytesProvider = bytesProvider; mInterfaceNameInternal = internalInterfaceNameInterned; } /** * Perform the detection. * * @param bytes the bytecode of the class that is being analyzed * @param doAutoReload indicator if the class should be automatically * reloaded after a modification to the sources, in case the * {@code ClassBytesProvider} supports this * @return {@code true} if the detection was successful; or * <p>{@code false} otherwise * @throws ClassNotFoundException */ public boolean detect(byte[] bytes, boolean doAutoReload) throws ClassNotFoundException { DetectionClassVisitor visitor = new DetectionClassVisitor(); ClassReader detection_reader = null; while (!visitor.isClassOrInterface()) { detection_reader = new ClassReader(bytes); try { detection_reader.accept(visitor, ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } catch (VisitInterruptionException e) { // do nothing } if (null == visitor.getSuperName() || OBJECT_NAME == visitor.getSuperName()) { break; } // get the parent's class' bytecode if (!visitor.isClassOrInterface()) { bytes = mBytesProvider.getClassBytes(visitor.getSuperName(), doAutoReload); } } return visitor.isClassOrInterface(); } private class DetectionClassVisitor implements ClassVisitor { private boolean mIsClassOrInterface = false; private String mSuperName = null; private DetectionClassVisitor() { } private boolean isClassOrInterface() { return mIsClassOrInterface; } private String getSuperName() { return mSuperName; } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { if (null == superName) { return; } for (String interface_name : interfaces) { if (mInterfaceNameInternal == interface_name.intern()) { mIsClassOrInterface = true; break; } } if (null == superName) { mSuperName = null; } else if (OBJECT_INTERNAL_NAME == superName.intern()) { mSuperName = OBJECT_NAME; } else { mSuperName = superName.replace('/', '.').intern(); } throw new VisitInterruptionException(); } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return null; } public void visitInnerClass(String name, String outerName, String innerName, int access) { } public void visitOuterClass(String owner, String name, String desc) { } public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; } public void visitSource(String source, String debug) { } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; } public void visitAttribute(Attribute attr) { } public void visitEnd() { } } }