/* * FindBugs - Find bugs in Java programs * Copyright (C) 2003-2005 University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.visitclass; import java.io.DataInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.bcel.classfile.AnnotationEntry; import org.apache.bcel.classfile.Annotations; import org.apache.bcel.classfile.ArrayElementValue; import org.apache.bcel.classfile.Constant; import org.apache.bcel.classfile.ConstantDouble; import org.apache.bcel.classfile.ConstantFloat; import org.apache.bcel.classfile.ConstantInteger; import org.apache.bcel.classfile.ConstantLong; import org.apache.bcel.classfile.ConstantUtf8; import org.apache.bcel.classfile.ElementValue; import org.apache.bcel.classfile.ElementValuePair; import org.apache.bcel.classfile.ParameterAnnotationEntry; import org.apache.bcel.classfile.ParameterAnnotations; import org.apache.bcel.classfile.SimpleElementValue; import edu.umd.cs.findbugs.SystemProperties; import edu.umd.cs.findbugs.internalAnnotations.DottedClassName; import edu.umd.cs.findbugs.util.ClassName; /** * Subclass of PreorderVisitor that visits annotations on classes, fields, * methods, and method parameters. * * @author William Pugh */ public class AnnotationVisitor extends PreorderVisitor { /** * */ private static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; /** * */ private static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; /** * */ private static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; /** * */ private static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; static final boolean DEBUG = SystemProperties.getBoolean("annotation.visitor"); /** * Visit annotation on a class, field or method * * @param annotationClass * class of annotation * @param map * map from names to values * @param runtimeVisible * true if annotation is runtime visible */ public void visitAnnotation(@DottedClassName String annotationClass, Map<String, ElementValue> map, boolean runtimeVisible) { if (DEBUG) { System.out.println("Annotation: " + annotationClass); for (Map.Entry<String, ElementValue> e : map.entrySet()) { System.out.println(" " + e.getKey()); System.out.println(" -> " + e.getValue()); } } } protected static String getAnnotationParameterAsString(Map<String, ElementValue> map, String parameter) { try { ElementValue ev = map.get(parameter); if (ev instanceof SimpleElementValue) return ((SimpleElementValue) ev).getValueString(); return null; } catch (Exception e) { return null; } } protected static String[] getAnnotationParameterAsStringArray(Map<String, ElementValue> map, String parameter) { try { ElementValue e = map.get(parameter); ArrayElementValue a = (ArrayElementValue) e; int size = a.getElementValuesArraySize(); String[] result = new String[size]; for (int i = 0; i < size; i++) result[i] = ((SimpleElementValue) a.getElementValuesArray()[i]).getValueString(); return result; } catch (Exception e) { return null; } } /** * Visit annotation on a method parameter * * @param p * parameter number, starting at zero ("this" parameter is not * counted) * @param annotationClass * class of annotation * @param map * map from names to values * @param runtimeVisible * true if annotation is runtime visible */ public void visitParameterAnnotation(int p, @DottedClassName String annotationClass, Map<String, ElementValue> map, boolean runtimeVisible) { // System.out // .println("Parameter " + p + " Annotation: " + annotationClass); // for (Map.Entry<String, Object> e : map.entrySet()) { // System.out.println(" " + e.getKey()); // System.out.println(" -> " + e.getValue()); // } } public void visitSyntheticParameterAnnotation(int p, boolean runtimeVisible) { } // @Override // public void visit(Unknown obj) { // try { // // String name = obj.getName(); // if (DEBUG) // System.out.println("In " + getDottedClassName() + " found " // + name); // byte[] b = obj.getBytes(); // DataInputStream bytes = new DataInputStream( // new ByteArrayInputStream(b)); // boolean runtimeVisible = // name.equals(RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS); // if (name.equals(RUNTIME_VISIBLE_ANNOTATIONS) // || name.equals(RUNTIME_INVISIBLE_ANNOTATIONS)) { // // int numAnnotations = bytes.readUnsignedShort(); // if (DEBUG) // System.out.println("# of annotations: " // + numAnnotations); // for (int i = 0; i < numAnnotations; i++) { // String annotationName = getAnnotationName(bytes); // int numPairs = bytes.readUnsignedShort(); // Map<String, Object> values = readAnnotationValues( // bytes, numPairs); // visitAnnotation(annotationName, values, name // .equals(RUNTIME_VISIBLE_ANNOTATIONS)); // } // // } else if (runtimeVisible // || name.equals(RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS)) { // int numParameters = bytes.readUnsignedByte(); // if (DEBUG) System.out.println("Number of parameters: " + numParameters); // int numParametersToMethod = getNumberMethodArguments(); // if (DEBUG) System.out.println("Number of parameters to method: " + // numParametersToMethod); // int offset = 0; // if (numParametersToMethod > numParameters) { // offset = 1; // visitSyntheticParameterAnnotation( // 0, // runtimeVisible); // for(int p = numParameters+1; p < numParametersToMethod; p++) { // visitSyntheticParameterAnnotation( // p, // runtimeVisible); // } // } // for (int p = 0; p < numParameters; p++) { // int numAnnotations = bytes.readUnsignedShort(); // if (DEBUG) // System.out.println("# of annotations on parameter " + (offset+p) // + ": " // + numAnnotations); // for (int i = 0; i < numAnnotations; i++) { // String annotationName = getAnnotationName(bytes); // int numPairs = bytes.readUnsignedShort(); // Map<String, Object> values = readAnnotationValues( // bytes, numPairs); // // visitParameterAnnotation( // p+offset, // annotationName, // values, // runtimeVisible); // } // } // // } // // if (DEBUG) { // for (byte aB : b) // System.out.print(Integer.toString((aB & 0xff), 16) // + " "); // System.out.println(); // } // // // } catch (RuntimeException e) { // assert true; // ignore // } catch (IOException e) { // assert true; // ignore // } // } private Map<String, Object> readAnnotationValues(DataInputStream bytes, int numPairs) throws IOException { Map<String, Object> values = new HashMap<String, Object>(); for (int j = 0; j < numPairs; j++) { int memberNameIndex = bytes.readUnsignedShort(); String memberName = ((ConstantUtf8) getConstantPool().getConstant(memberNameIndex)).getBytes(); if (DEBUG) System.out.println("memberName: " + memberName); Object value = readAnnotationValue(bytes); if (DEBUG) System.out.println(memberName + ":" + value); values.put(memberName, value); } return values; } private @DottedClassName String getAnnotationName(DataInputStream bytes) throws IOException { int annotationNameIndex = bytes.readUnsignedShort(); String annotationName = ((ConstantUtf8) getConstantPool().getConstant(annotationNameIndex)).getBytes().replace('/', '.'); annotationName = annotationName.substring(1, annotationName.length() - 1); if (DEBUG) System.out.println("Annotation name: " + annotationName); return annotationName; } private Object readAnnotationValue(DataInputStream bytes) throws IOException { try { char tag = (char) bytes.readUnsignedByte(); if (DEBUG) System.out.println("tag: " + tag); switch (tag) { case '[': { int sz = bytes.readUnsignedShort(); if (DEBUG) System.out.println("Array of " + sz + " entries"); Object[] result = new Object[sz]; for (int i = 0; i < sz; i++) result[i] = readAnnotationValue(bytes); return result; } case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': case 's': case 'c': int cp_index = bytes.readUnsignedShort(); Constant c = getConstantPool().getConstant(cp_index); switch (tag) { case 'B': return (byte) ((ConstantInteger) c).getBytes(); case 'C': return (char) ((ConstantInteger) c).getBytes(); case 'D': return new Double(((ConstantDouble) c).getBytes()); case 'F': return new Float(((ConstantFloat) c).getBytes()); case 'I': return ((ConstantInteger) c).getBytes(); case 'J': return ((ConstantLong) c).getBytes(); case 'S': return (char) ((ConstantInteger) c).getBytes(); case 'Z': return Boolean.valueOf(((ConstantInteger) c).getBytes() != 0); case 's': return ((ConstantUtf8) c).getBytes(); case 'c': String cName = ((ConstantUtf8) c).getBytes().replace('/', '.'); if (cName.startsWith("L") && cName.endsWith(";")) cName = cName.substring(1, cName.length() - 1); if (DEBUG) System.out.println("cName: " + cName); return cName; default: if (DEBUG) System.out.println("Impossible"); throw new IllegalStateException("Impossible"); } case '@': throw new IllegalArgumentException("Not ready to handle annotations as elements of annotations"); case 'e': { int cp1 = bytes.readUnsignedShort(); ConstantUtf8 c1 = (ConstantUtf8) getConstantPool().getConstant(cp1); String cName = c1.getBytes().replace('/', '.'); if (cName.startsWith("L") && cName.endsWith(";")) cName = cName.substring(1, cName.length() - 1); int cp2 = bytes.readUnsignedShort(); ConstantUtf8 c2 = (ConstantUtf8) getConstantPool().getConstant(cp2); String result = cName + "." + c2.getBytes(); // System.out.println(result); return result; } default: if (DEBUG) System.out.println("Unexpected tag of " + tag); throw new IllegalArgumentException("Unexpected tag of " + tag); } } catch (RuntimeException e) { if (DEBUG) { System.out.println("Problem processing annotation " + e.getMessage()); e.printStackTrace(); } throw e; } } @Override public void visitParameterAnnotation(ParameterAnnotations arg0) { ParameterAnnotationEntry[] parameterAnnotationEntries = arg0.getParameterAnnotationEntries(); int numParametersToMethod = getNumberMethodArguments(); int offset = 0; if (numParametersToMethod > parameterAnnotationEntries.length) { offset = 1; System.out.println("Have synthetic parameters"); } for (int i = 0; i < parameterAnnotationEntries.length; i++) { ParameterAnnotationEntry e = parameterAnnotationEntries[i]; for (AnnotationEntry ae : e.getAnnotationEntries()) { boolean runtimeVisible = ae.isRuntimeVisible(); String name = ClassName.fromFieldSignature(ae.getAnnotationType()); if (name == null) continue; name = ClassName.toDottedClassName(name); Map<String, ElementValue> map = new HashMap<String, ElementValue>(); for (ElementValuePair ev : ae.getElementValuePairs()) { map.put(ev.getNameString(), ev.getValue()); } visitParameterAnnotation(offset + i, name, map, runtimeVisible); } } } /* * (non-Javadoc) * * @see * org.apache.bcel.classfile.Visitor#visitAnnotation(org.apache.bcel.classfile * .Annotations) */ @Override public void visitAnnotation(Annotations arg0) { for (AnnotationEntry ae : arg0.getAnnotationEntries()) { boolean runtimeVisible = ae.isRuntimeVisible(); String name = ClassName.fromFieldSignature(ae.getAnnotationType()); if (name == null) continue; name = ClassName.toDottedClassName(name); Map<String, ElementValue> map = new HashMap<String, ElementValue>(); for (ElementValuePair ev : ae.getElementValuePairs()) { map.put(ev.getNameString(), ev.getValue()); } visitAnnotation(name, map, runtimeVisible); } } }