/* * Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com) * Licensed under the Apache License, Version 2.0 (the "License") * $Id: ConstrainedDetector.java 3918 2008-04-14 17:35:35Z gbevin $ */ package com.uwyn.rife.site.instrument; import com.uwyn.rife.asm.*; import com.uwyn.rife.instrument.exceptions.ClassBytesNotFoundException; import com.uwyn.rife.instrument.exceptions.VisitInterruptionException; import com.uwyn.rife.tools.ClassBytesLoader; /** * Detects whether a class implements the {@code Constrained} interface or not, * by analyzing its bytecode. * * @author Geert Bevin (gbevin[remove] at uwyn dot com) * @version $Revision: 3918 $ * @since 1.6 */ public class ConstrainedDetector { private final static String CONSTRAINED_INTERNAL_NAME = "com/uwyn/rife/site/Constrained"; private final static String CONSTRAINED_NAME = "com.uwyn.rife.site.Constrained"; private final static String OBJECT_INTERNAL_NAME = "java/lang/Object"; private ClassBytesLoader mBytesLoader = null; /** * Creates new instance by providing a loader that is able to retrieve the * bytes of any parent classes that are extended by the class that is * being analyzed. * * @param bytesLoader the loader that will be used to retrieve the bytes * of the additional classes * @since 1.6 */ public ConstrainedDetector(ClassBytesLoader bytesLoader) { mBytesLoader = bytesLoader; } /** * Verifies if the Constrained interface is implemented by the class that * is defined by the bytes that are provided. * * @param internedClassname the class name as the previously interned * string * @param bytes the array of bytes that defines the class that needs to be * analyzed * @return {@code true} if the analyzed class implements the constrained * interface; or * <p>{@code false} otherwise * @exception ClassBytesNotFoundException when the bytes of a parent class * can be found * @since 1.6 */ public boolean isConstrained(String internedClassname, byte[] bytes) throws ClassBytesNotFoundException { if (CONSTRAINED_NAME == internedClassname) { return false; } ConstrainedDetectionClassVisitor visitor = new ConstrainedDetectionClassVisitor(); ClassReader detection_reader = null; while (!visitor.isConstrained()) { detection_reader = new ClassReader(bytes); try { detection_reader.accept(visitor, ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } catch (VisitInterruptionException e) { // do nothing } if (null == visitor.getSuperNameInternal() || OBJECT_INTERNAL_NAME == visitor.getSuperNameInternal()) { break; } // get the parent's class' bytecode if (!visitor.isConstrained()) { String filename = visitor.getSuperNameInternal() + ".class"; try { bytes = mBytesLoader.getClassBytes(filename); } catch (ClassNotFoundException e) { throw new ClassBytesNotFoundException(filename, e); } } } return visitor.isConstrained(); } private class ConstrainedDetectionClassVisitor implements ClassVisitor { private boolean mIsConstrained = false; private String mSuperNameInternal = null; private boolean isConstrained() { return mIsConstrained; } private String getSuperNameInternal() { return mSuperNameInternal; } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { for (String interface_name : interfaces) { if (CONSTRAINED_INTERNAL_NAME == interface_name.intern()) { mIsConstrained = true; break; } } if (null == superName) { mSuperNameInternal = null; } else { mSuperNameInternal = superName.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() { } } }