001: /*
002: * FindBugs - Find bugs in Java programs
003: * Copyright (C) 2003-2005 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package edu.umd.cs.findbugs.visitclass;
020:
021: import java.io.ByteArrayInputStream;
022: import java.io.DataInputStream;
023: import java.io.IOException;
024: import java.util.HashMap;
025: import java.util.Map;
026:
027: import org.apache.bcel.classfile.Constant;
028: import org.apache.bcel.classfile.ConstantDouble;
029: import org.apache.bcel.classfile.ConstantFloat;
030: import org.apache.bcel.classfile.ConstantInteger;
031: import org.apache.bcel.classfile.ConstantLong;
032: import org.apache.bcel.classfile.ConstantUtf8;
033: import org.apache.bcel.classfile.Unknown;
034:
035: import edu.umd.cs.findbugs.SystemProperties;
036:
037: /**
038: * Subclass of PreorderVisitor that visits annotations
039: * on classes, fields, methods, and method parameters.
040: *
041: * @author William Pugh
042: */
043: public class AnnotationVisitor extends PreorderVisitor {
044:
045: /**
046: *
047: */
048: private static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations";
049: /**
050: *
051: */
052: private static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
053: /**
054: *
055: */
056: private static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
057: /**
058: *
059: */
060: private static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations";
061: static final boolean DEBUG = SystemProperties
062: .getBoolean("annotation.visitor");
063:
064: /**
065: * Visit annotation on a class, field or method
066: * @param annotationClass class of annotation
067: * @param map map from names to values
068: * @param runtimeVisible true if annotation is runtime visible
069: */
070: public void visitAnnotation(String annotationClass,
071: Map<String, Object> map, boolean runtimeVisible) {
072: if (DEBUG) {
073: System.out.println("Annotation: " + annotationClass);
074: for (Map.Entry<String, Object> e : map.entrySet()) {
075: System.out.println(" " + e.getKey());
076: System.out.println(" -> " + e.getValue());
077: }
078: }
079: }
080:
081: /**
082: * Visit annotation on a method parameter
083: * @param p parameter number, starting at zero (this parameter is not counted)
084: * @param annotationClass class of annotation
085: * @param map map from names to values
086: * @param runtimeVisible true if annotation is runtime visible
087: */
088: public void visitParameterAnnotation(int p, String annotationClass,
089: Map<String, Object> map, boolean runtimeVisible) {
090: // System.out
091: // .println("Parameter " + p + " Annotation: " + annotationClass);
092: // for (Map.Entry<String, Object> e : map.entrySet()) {
093: // System.out.println(" " + e.getKey());
094: // System.out.println(" -> " + e.getValue());
095: // }
096: }
097:
098: public void visitSyntheticParameterAnnotation(int p,
099: boolean runtimeVisible) {
100: }
101:
102: @Override
103: public void visit(Unknown obj) {
104: try {
105:
106: String name = obj.getName();
107: if (DEBUG)
108: System.out.println("In " + getDottedClassName()
109: + " found " + name);
110: byte[] b = obj.getBytes();
111: DataInputStream bytes = new DataInputStream(
112: new ByteArrayInputStream(b));
113: boolean runtimeVisible = name
114: .equals(RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
115: if (name.equals(RUNTIME_VISIBLE_ANNOTATIONS)
116: || name.equals(RUNTIME_INVISIBLE_ANNOTATIONS)) {
117:
118: int numAnnotations = bytes.readUnsignedShort();
119: if (DEBUG)
120: System.out.println("# of annotations: "
121: + numAnnotations);
122: for (int i = 0; i < numAnnotations; i++) {
123: String annotationName = getAnnotationName(bytes);
124: int numPairs = bytes.readUnsignedShort();
125: Map<String, Object> values = readAnnotationValues(
126: bytes, numPairs);
127: visitAnnotation(annotationName, values, name
128: .equals(RUNTIME_VISIBLE_ANNOTATIONS));
129: }
130:
131: } else if (runtimeVisible
132: || name
133: .equals(RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS)) {
134: int numParameters = bytes.readUnsignedByte();
135: if (DEBUG)
136: System.out.println("Number of parameters: "
137: + numParameters);
138: int numParametersToMethod = getNumberMethodArguments();
139: if (DEBUG)
140: System.out
141: .println("Number of parameters to method: "
142: + numParametersToMethod);
143: int offset = 0;
144: if (numParametersToMethod > numParameters) {
145: offset = 1;
146: visitSyntheticParameterAnnotation(0, runtimeVisible);
147: for (int p = numParameters + 1; p < numParametersToMethod; p++) {
148: visitSyntheticParameterAnnotation(p,
149: runtimeVisible);
150: }
151: }
152: for (int p = 0; p < numParameters; p++) {
153: int numAnnotations = bytes.readUnsignedShort();
154: if (DEBUG)
155: System.out
156: .println("# of annotations on parameter "
157: + (offset + p)
158: + ": "
159: + numAnnotations);
160: for (int i = 0; i < numAnnotations; i++) {
161: String annotationName = getAnnotationName(bytes);
162: int numPairs = bytes.readUnsignedShort();
163: Map<String, Object> values = readAnnotationValues(
164: bytes, numPairs);
165:
166: visitParameterAnnotation(p + offset,
167: annotationName, values, runtimeVisible);
168: }
169: }
170:
171: }
172:
173: if (DEBUG) {
174: for (byte aB : b)
175: System.out.print(Integer.toString((aB & 0xff), 16)
176: + " ");
177: System.out.println();
178: }
179:
180: } catch (Exception e) {
181: // ignore
182: }
183: }
184:
185: private Map<String, Object> readAnnotationValues(
186: DataInputStream bytes, int numPairs) throws IOException {
187: Map<String, Object> values = new HashMap<String, Object>();
188: for (int j = 0; j < numPairs; j++) {
189: int memberNameIndex = bytes.readUnsignedShort();
190: String memberName = ((ConstantUtf8) getConstantPool()
191: .getConstant(memberNameIndex)).getBytes();
192: if (DEBUG)
193: System.out.println("memberName: " + memberName);
194: Object value = readAnnotationValue(bytes);
195: if (DEBUG)
196: System.out.println(memberName + ":" + value);
197: values.put(memberName, value);
198: }
199: return values;
200: }
201:
202: private String getAnnotationName(DataInputStream bytes)
203: throws IOException {
204: int annotationNameIndex = bytes.readUnsignedShort();
205: String annotationName = ((ConstantUtf8) getConstantPool()
206: .getConstant(annotationNameIndex)).getBytes().replace(
207: '/', '.');
208: annotationName = annotationName.substring(1, annotationName
209: .length() - 1);
210: if (DEBUG)
211: System.out.println("Annotation name: " + annotationName);
212: return annotationName;
213: }
214:
215: private Object readAnnotationValue(DataInputStream bytes)
216: throws IOException {
217: try {
218: char tag = (char) bytes.readUnsignedByte();
219: if (DEBUG)
220: System.out.println("tag: " + tag);
221: switch (tag) {
222: case '[': {
223: int sz = bytes.readUnsignedShort();
224: if (DEBUG)
225: System.out.println("Array of " + sz + " entries");
226: Object[] result = new Object[sz];
227: for (int i = 0; i < sz; i++)
228: result[i] = readAnnotationValue(bytes);
229: return result;
230: }
231: case 'B':
232: case 'C':
233: case 'D':
234: case 'F':
235: case 'I':
236: case 'J':
237: case 'S':
238: case 'Z':
239: case 's':
240: case 'c':
241: int cp_index = bytes.readUnsignedShort();
242: Constant c = getConstantPool().getConstant(cp_index);
243: switch (tag) {
244: case 'B':
245: return (Byte) (byte) ((ConstantInteger) c)
246: .getBytes();
247: case 'C':
248: return (Character) (char) ((ConstantInteger) c)
249: .getBytes();
250: case 'D':
251: return new Double(((ConstantDouble) c).getBytes());
252: case 'F':
253: return new Float(((ConstantFloat) c).getBytes());
254: case 'I':
255: return (Integer) ((ConstantInteger) c).getBytes();
256: case 'J':
257: return (Long) ((ConstantLong) c).getBytes();
258: case 'S':
259: return (Character) (char) ((ConstantInteger) c)
260: .getBytes();
261: case 'Z':
262: return Boolean.valueOf(((ConstantInteger) c)
263: .getBytes() != 0);
264: case 's':
265: return ((ConstantUtf8) c).getBytes();
266: case 'c':
267: String cName = ((ConstantUtf8) c).getBytes()
268: .replace('/', '.');
269: if (cName.startsWith("L") && cName.endsWith(";"))
270: cName = cName.substring(1, cName.length() - 1);
271: if (DEBUG)
272: System.out.println("cName: " + cName);
273: return cName;
274: default:
275: if (DEBUG)
276: System.out.println("Impossible");
277: throw new IllegalStateException("Impossible");
278: }
279: case '@':
280: throw new IllegalArgumentException(
281: "Not ready to handle annotations as elements of annotations");
282: case 'e': {
283: int cp1 = bytes.readUnsignedShort();
284: ConstantUtf8 c1 = (ConstantUtf8) getConstantPool()
285: .getConstant(cp1);
286: String cName = c1.getBytes().replace('/', '.');
287: if (cName.startsWith("L") && cName.endsWith(";"))
288: cName = cName.substring(1, cName.length() - 1);
289: int cp2 = bytes.readUnsignedShort();
290: ConstantUtf8 c2 = (ConstantUtf8) getConstantPool()
291: .getConstant(cp2);
292: String result = cName + "." + c2.getBytes();
293: // System.out.println(result);
294: return result;
295: }
296: default:
297: if (DEBUG)
298: System.out.println("Unexpected tag of " + tag);
299: throw new IllegalArgumentException("Unexpected tag of "
300: + tag);
301: }
302: } catch (RuntimeException e) {
303: if (DEBUG) {
304: System.out.println("Problem processing annotation "
305: + e.getMessage());
306: e.printStackTrace();
307: }
308: throw e;
309: }
310: }
311: }
|