001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: ClassInterfaceDetector.java 3815 2007-06-25 18:49:41Z gbevin $
007: */
008: package com.uwyn.rife.instrument;
009:
010: import com.uwyn.rife.asm.*;
011:
012: import com.uwyn.rife.instrument.ClassBytesProvider;
013: import com.uwyn.rife.instrument.exceptions.VisitInterruptionException;
014:
015: /**
016: * Detects whether a class implements a particular interface by analyzing the
017: * bytecode instead of loading the class and performing reflection calls.
018: *
019: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
020: * @version $Revision: 3815 $
021: * @since 1.6
022: */
023: public class ClassInterfaceDetector {
024: private final String OBJECT_INTERNAL_NAME = "java/lang/Object";
025: private final String OBJECT_NAME = "java.lang.Object";
026:
027: private ClassBytesProvider mBytesProvider = null;
028: private String mInterfaceNameInternal = null;
029:
030: /**
031: * Creates a new instance of the interface detector.
032: *
033: * @param bytesProvider the bytecode provider that will be used to load the
034: * bytes of the parent classes or interfaces
035: * @param internalInterfaceNameInterned the name of the interface that should
036: * be detected, it should be in the internal bytecode naming format
037: * (/ instead of .) and it should be interned.
038: * @since 1.6
039: */
040: public ClassInterfaceDetector(ClassBytesProvider bytesProvider,
041: String internalInterfaceNameInterned) {
042: mBytesProvider = bytesProvider;
043: mInterfaceNameInternal = internalInterfaceNameInterned;
044: }
045:
046: /**
047: * Perform the detection.
048: *
049: * @param bytes the bytecode of the class that is being analyzed
050: * @param doAutoReload indicator if the class should be automatically
051: * reloaded after a modification to the sources, in case the
052: * {@code ClassBytesProvider} supports this
053: * @return {@code true} if the detection was successful; or
054: * <p>{@code false} otherwise
055: * @throws ClassNotFoundException
056: */
057: public boolean detect(byte[] bytes, boolean doAutoReload)
058: throws ClassNotFoundException {
059: DetectionClassVisitor visitor = new DetectionClassVisitor();
060: ClassReader detection_reader = null;
061:
062: while (!visitor.isClassOrInterface()) {
063: detection_reader = new ClassReader(bytes);
064: try {
065: detection_reader.accept(visitor, true);
066: } catch (VisitInterruptionException e) {
067: // do nothing
068: }
069:
070: if (null == visitor.getSuperName()
071: || OBJECT_NAME == visitor.getSuperName()) {
072: break;
073: }
074:
075: // get the parent's class' bytecode
076: if (!visitor.isClassOrInterface()) {
077: bytes = mBytesProvider.getClassBytes(visitor
078: .getSuperName(), doAutoReload);
079: }
080: }
081:
082: return visitor.isClassOrInterface();
083: }
084:
085: private class DetectionClassVisitor implements ClassVisitor {
086: private boolean mIsClassOrInterface = false;
087: private String mSuperName = null;
088:
089: private DetectionClassVisitor() {
090: }
091:
092: private boolean isClassOrInterface() {
093: return mIsClassOrInterface;
094: }
095:
096: private String getSuperName() {
097: return mSuperName;
098: }
099:
100: public void visit(int version, int access, String name,
101: String signature, String super Name, String[] interfaces) {
102: if (null == super Name) {
103: return;
104: }
105:
106: for (String interface_name : interfaces) {
107: if (mInterfaceNameInternal == interface_name.intern()) {
108: mIsClassOrInterface = true;
109: break;
110: }
111: }
112:
113: if (null == super Name) {
114: mSuperName = null;
115: } else if (OBJECT_INTERNAL_NAME == super Name.intern()) {
116: mSuperName = OBJECT_NAME;
117: } else {
118: mSuperName = super Name.replace('/', '.').intern();
119: }
120:
121: throw new VisitInterruptionException();
122: }
123:
124: public MethodVisitor visitMethod(int access, String name,
125: String desc, String signature, String[] exceptions) {
126: return null;
127: }
128:
129: public void visitInnerClass(String name, String outerName,
130: String innerName, int access) {
131: }
132:
133: public void visitOuterClass(String owner, String name,
134: String desc) {
135: }
136:
137: public FieldVisitor visitField(int access, String name,
138: String desc, String signature, Object value) {
139: return null;
140: }
141:
142: public void visitSource(String source, String debug) {
143: }
144:
145: public AnnotationVisitor visitAnnotation(String desc,
146: boolean visible) {
147: return null;
148: }
149:
150: public void visitAttribute(Attribute attr) {
151: }
152:
153: public void visitEnd() {
154: }
155: }
156: }
|