001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.bytecode;
017:
018: import java.util.Map;
019: import java.io.IOException;
020: import java.io.DataInputStream;
021: import java.io.ByteArrayOutputStream;
022: import javassist.bytecode.annotation.*;
023:
024: /**
025: * A class representing
026: * <code>RuntimeVisibleAnnotations_attribute</code> and
027: * <code>RuntimeInvisibleAnnotations_attribute</code>.
028: *
029: * <p>To obtain an AnnotationAttribute object, invoke
030: * <code>getAttribute(AnnotationsAttribute.visibleTag)</code>
031: * in <code>ClassFile</code>, <code>MethodInfo</code>,
032: * or <code>FieldInfo</code>. The obtained attribute is a
033: * runtime visible annotations attribute.
034: * If the parameter is
035: * <code>AnnotationAttribute.invisibleTag</code>, then the obtained
036: * attribute is a runtime invisible one.
037: *
038: * <p>For example,
039: *
040: * <ul><pre>
041: * import javassist.bytecode.annotation.Annotation;
042: * :
043: * CtMethod m = ... ;
044: * MethodInfo minfo = m.getMethodInfo();
045: * AnnotationsAttribute attr = (AnnotationsAttribute)
046: * minfo.getAttribute(AnnotationsAttribute.invisibleTag);
047: * Annotation an = attr.getAnnotation("Author");
048: * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
049: * System.out.println("@Author(name=" + s + ")");
050: * </pre></ul>
051: *
052: * <p>This code snippet retrieves an annotation of the type <code>Author</code>
053: * from the <code>MethodInfo</code> object specified by <code>minfo</code>.
054: * Then, it prints the value of <code>name</code> in <code>Author</code>.
055: *
056: * <p>If the annotation type <code>Author</code> is annotated by a meta annotation:
057: *
058: * <ul><pre>
059: * @Retention(RetentionPolicy.RUNTIME)
060: * </pre></ul>
061: *
062: * <p>Then <code>Author</code> is visible at runtime. Therefore, the third
063: * statement of the code snippet above must be changed into:
064: *
065: * <ul><pre>
066: * AnnotationsAttribute attr = (AnnotationsAttribute)
067: * minfo.getAttribute(AnnotationsAttribute.visibleTag);
068: * </pre></ul>
069: *
070: * <p>The attribute tag must be <code>visibleTag</code> instead of
071: * <code>invisibleTag</code>.
072: *
073: * <p>If the member value of an annotation is not specified, the default value
074: * is used as that member value. If so, <code>getMemberValue()</code> in
075: * <code>Annotation</code> returns <code>null</code>
076: * since the default value is not included in the
077: * <code>AnnotationsAttribute</code>. It is included in the
078: * <code>AnnotationDefaultAttribute</code> of the method declared in the
079: * annotation type.
080: *
081: * <p>If you want to record a new AnnotationAttribute object, execute the
082: * following snippet:
083: *
084: * <ul><pre>
085: * ClassFile cf = ... ;
086: * ConstPool cp = cf.getConstPool();
087: * AnnotationsAttribute attr
088: * = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
089: * Annotation a = new Annotation("Author", cp);
090: * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
091: * attr.setAnnotation(a);
092: * cf.addAttribute(attr);
093: * cf.setVersionToJava5();
094: * </pre></ul>
095: *
096: * <p>The last statement is necessary if the class file was produced by
097: * Javassist or JDK 1.4. Otherwise, it is not necessary.
098: *
099: * @see AnnotationDefaultAttribute
100: * @see javassist.bytecode.annotation.Annotation
101: */
102: public class AnnotationsAttribute extends AttributeInfo {
103: /**
104: * The name of the <code>RuntimeVisibleAnnotations</code> attribute.
105: */
106: public static final String visibleTag = "RuntimeVisibleAnnotations";
107:
108: /**
109: * The name of the <code>RuntimeInvisibleAnnotations</code> attribute.
110: */
111: public static final String invisibleTag = "RuntimeInvisibleAnnotations";
112:
113: /**
114: * Constructs a <code>Runtime(In)VisisbleAnnotations_attribute</code>.
115: *
116: * @param cp constant pool
117: * @param attrname attribute name (<code>visibleTag</code> or
118: * <code>invisibleTag</code>).
119: * @param info the contents of this attribute. It does not
120: * include <code>attribute_name_index</code> or
121: * <code>attribute_length</code>.
122: */
123: public AnnotationsAttribute(ConstPool cp, String attrname,
124: byte[] info) {
125: super (cp, attrname, info);
126: }
127:
128: /**
129: * Constructs an empty
130: * <code>Runtime(In)VisisbleAnnotations_attribute</code>.
131: * A new annotation can be later added to the created attribute
132: * by <code>setAnnotations()</code>.
133: *
134: * @param cp constant pool
135: * @param attrname attribute name (<code>visibleTag</code> or
136: * <code>invisibleTag</code>).
137: * @see #setAnnotations(Annotation[])
138: */
139: public AnnotationsAttribute(ConstPool cp, String attrname) {
140: this (cp, attrname, new byte[] { 0, 0 });
141: }
142:
143: /**
144: * @param n the attribute name.
145: */
146: AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
147: throws IOException {
148: super (cp, n, in);
149: }
150:
151: /**
152: * Returns <code>num_annotations</code>.
153: */
154: public int numAnnotations() {
155: return ByteArray.readU16bit(info, 0);
156: }
157:
158: /**
159: * Copies this attribute and returns a new copy.
160: */
161: public AttributeInfo copy(ConstPool newCp, Map classnames) {
162: Copier copier = new Copier(info, constPool, newCp, classnames);
163: try {
164: copier.annotationArray();
165: return new AnnotationsAttribute(newCp, getName(), copier
166: .close());
167: } catch (Exception e) {
168: throw new RuntimeException(e.toString());
169: }
170: }
171:
172: /**
173: * Parses the annotations and returns a data structure representing
174: * the annotation with the specified type. See also
175: * <code>getAnnotations()</code> as to the returned data structure.
176: *
177: * @param type the annotation type.
178: * @return null if the specified annotation type is not included.
179: * @see #getAnnotations()
180: */
181: public Annotation getAnnotation(String type) {
182: Annotation[] annotations = getAnnotations();
183: for (int i = 0; i < annotations.length; i++) {
184: if (annotations[i].getTypeName().equals(type))
185: return annotations[i];
186: }
187:
188: return null;
189: }
190:
191: /**
192: * Adds an annotation. If there is an annotation with the same type,
193: * it is removed before the new annotation is added.
194: *
195: * @param annotation the added annotation.
196: */
197: public void addAnnotation(Annotation annotation) {
198: String type = annotation.getTypeName();
199: Annotation[] annotations = getAnnotations();
200: for (int i = 0; i < annotations.length; i++) {
201: if (annotations[i].getTypeName().equals(type)) {
202: annotations[i] = annotation;
203: setAnnotations(annotations);
204: return;
205: }
206: }
207:
208: Annotation[] newlist = new Annotation[annotations.length + 1];
209: System
210: .arraycopy(annotations, 0, newlist, 0,
211: annotations.length);
212: newlist[annotations.length] = annotation;
213: setAnnotations(newlist);
214: }
215:
216: /**
217: * Parses the annotations and returns a data structure representing
218: * that parsed annotations. Note that changes of the node values of the
219: * returned tree are not reflected on the annotations represented by
220: * this object unless the tree is copied back to this object by
221: * <code>setAnnotations()</code>.
222: *
223: * @see #setAnnotations(Annotation[])
224: */
225: public Annotation[] getAnnotations() {
226: try {
227: return new Parser(info, constPool).parseAnnotations();
228: } catch (Exception e) {
229: throw new RuntimeException(e.toString());
230: }
231: }
232:
233: /**
234: * Changes the annotations represented by this object according to
235: * the given array of <code>Annotation</code> objects.
236: *
237: * @param annotations the data structure representing the
238: * new annotations.
239: */
240: public void setAnnotations(Annotation[] annotations) {
241: ByteArrayOutputStream output = new ByteArrayOutputStream();
242: AnnotationsWriter writer = new AnnotationsWriter(output,
243: constPool);
244: try {
245: int n = annotations.length;
246: writer.numAnnotations(n);
247: for (int i = 0; i < n; ++i)
248: annotations[i].write(writer);
249:
250: writer.close();
251: } catch (IOException e) {
252: throw new RuntimeException(e); // should never reach here.
253: }
254:
255: set(output.toByteArray());
256: }
257:
258: /**
259: * Changes the annotations. A call to this method is equivalent to:
260: * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul>
261: *
262: * @param annotation the data structure representing
263: * the new annotation.
264: */
265: public void setAnnotation(Annotation annotation) {
266: setAnnotations(new Annotation[] { annotation });
267: }
268:
269: /**
270: * Returns a string representation of this object.
271: */
272: public String toString() {
273: Annotation[] a = getAnnotations();
274: StringBuffer sbuf = new StringBuffer();
275: int i = 0;
276: while (i < a.length) {
277: sbuf.append(a[i++].toString());
278: if (i != a.length)
279: sbuf.append(", ");
280: }
281:
282: return sbuf.toString();
283: }
284:
285: static class Walker {
286: byte[] info;
287:
288: Walker(byte[] attrInfo) {
289: info = attrInfo;
290: }
291:
292: final void parameters() throws Exception {
293: int numParam = info[0] & 0xff;
294: parameters(numParam, 1);
295: }
296:
297: void parameters(int numParam, int pos) throws Exception {
298: for (int i = 0; i < numParam; ++i)
299: pos = annotationArray(pos);
300: }
301:
302: final void annotationArray() throws Exception {
303: annotationArray(0);
304: }
305:
306: final int annotationArray(int pos) throws Exception {
307: int num = ByteArray.readU16bit(info, pos);
308: return annotationArray(pos + 2, num);
309: }
310:
311: int annotationArray(int pos, int num) throws Exception {
312: for (int i = 0; i < num; ++i)
313: pos = annotation(pos);
314:
315: return pos;
316: }
317:
318: final int annotation(int pos) throws Exception {
319: int type = ByteArray.readU16bit(info, pos);
320: int numPairs = ByteArray.readU16bit(info, pos + 2);
321: return annotation(pos + 4, type, numPairs);
322: }
323:
324: int annotation(int pos, int type, int numPairs)
325: throws Exception {
326: for (int j = 0; j < numPairs; ++j)
327: pos = memberValuePair(pos);
328:
329: return pos;
330: }
331:
332: final int memberValuePair(int pos) throws Exception {
333: int nameIndex = ByteArray.readU16bit(info, pos);
334: return memberValuePair(pos + 2, nameIndex);
335: }
336:
337: int memberValuePair(int pos, int nameIndex) throws Exception {
338: return memberValue(pos);
339: }
340:
341: final int memberValue(int pos) throws Exception {
342: int tag = info[pos] & 0xff;
343: if (tag == 'e') {
344: int typeNameIndex = ByteArray.readU16bit(info, pos + 1);
345: int constNameIndex = ByteArray
346: .readU16bit(info, pos + 3);
347: enumMemberValue(typeNameIndex, constNameIndex);
348: return pos + 5;
349: } else if (tag == 'c') {
350: int index = ByteArray.readU16bit(info, pos + 1);
351: classMemberValue(index);
352: return pos + 3;
353: } else if (tag == '@')
354: return annotationMemberValue(pos + 1);
355: else if (tag == '[') {
356: int num = ByteArray.readU16bit(info, pos + 1);
357: return arrayMemberValue(pos + 3, num);
358: } else { // primitive types or String.
359: int index = ByteArray.readU16bit(info, pos + 1);
360: constValueMember(tag, index);
361: return pos + 3;
362: }
363: }
364:
365: void constValueMember(int tag, int index) throws Exception {
366: }
367:
368: void enumMemberValue(int typeNameIndex, int constNameIndex)
369: throws Exception {
370: }
371:
372: void classMemberValue(int index) throws Exception {
373: }
374:
375: int annotationMemberValue(int pos) throws Exception {
376: return annotation(pos);
377: }
378:
379: int arrayMemberValue(int pos, int num) throws Exception {
380: for (int i = 0; i < num; ++i) {
381: pos = memberValue(pos);
382: }
383:
384: return pos;
385: }
386: }
387:
388: static class Copier extends Walker {
389: ByteArrayOutputStream output;
390: AnnotationsWriter writer;
391: ConstPool srcPool, destPool;
392: Map classnames;
393:
394: /**
395: * Constructs a copier. This copier renames some class names
396: * into the new names specified by <code>map</code> when it copies
397: * an annotation attribute.
398: *
399: * @param info the source attribute.
400: * @param src the constant pool of the source class.
401: * @param dest the constant pool of the destination class.
402: * @param map pairs of replaced and substituted class names.
403: * It can be null.
404: */
405: Copier(byte[] info, ConstPool src, ConstPool dest, Map map) {
406: super (info);
407: output = new ByteArrayOutputStream();
408: writer = new AnnotationsWriter(output, dest);
409: srcPool = src;
410: destPool = dest;
411: classnames = map;
412: }
413:
414: byte[] close() throws IOException {
415: writer.close();
416: return output.toByteArray();
417: }
418:
419: void parameters(int numParam, int pos) throws Exception {
420: writer.numParameters(numParam);
421: super .parameters(numParam, pos);
422: }
423:
424: int annotationArray(int pos, int num) throws Exception {
425: writer.numAnnotations(num);
426: return super .annotationArray(pos, num);
427: }
428:
429: int annotation(int pos, int type, int numPairs)
430: throws Exception {
431: writer.annotation(copy(type), numPairs);
432: return super .annotation(pos, type, numPairs);
433: }
434:
435: int memberValuePair(int pos, int nameIndex) throws Exception {
436: writer.memberValuePair(copy(nameIndex));
437: return super .memberValuePair(pos, nameIndex);
438: }
439:
440: void constValueMember(int tag, int index) throws Exception {
441: writer.constValueIndex(tag, copy(index));
442: super .constValueMember(tag, index);
443: }
444:
445: void enumMemberValue(int typeNameIndex, int constNameIndex)
446: throws Exception {
447: writer.enumConstValue(copy(typeNameIndex),
448: copy(constNameIndex));
449: super .enumMemberValue(typeNameIndex, constNameIndex);
450: }
451:
452: void classMemberValue(int index) throws Exception {
453: writer.classInfoIndex(copy(index));
454: super .classMemberValue(index);
455: }
456:
457: int annotationMemberValue(int pos) throws Exception {
458: writer.annotationValue();
459: return super .annotationMemberValue(pos);
460: }
461:
462: int arrayMemberValue(int pos, int num) throws Exception {
463: writer.arrayValue(num);
464: return super .arrayMemberValue(pos, num);
465: }
466:
467: /**
468: * Copies a constant pool entry into the destination constant pool
469: * and returns the index of the copied entry.
470: *
471: * @param srcIndex the index of the copied entry into the source
472: * constant pool.
473: * @return the index of the copied item into the destination
474: * constant pool.
475: */
476: int copy(int srcIndex) {
477: return srcPool.copy(srcIndex, destPool, classnames);
478: }
479: }
480:
481: static class Parser extends Walker {
482: ConstPool pool;
483: Annotation[][] allParams; // all parameters
484: Annotation[] allAnno; // all annotations
485: Annotation currentAnno; // current annotation
486: MemberValue currentMember; // current member
487:
488: /**
489: * Constructs a parser. This parser constructs a parse tree of
490: * the annotations.
491: *
492: * @param info the attribute.
493: * @param src the constant pool.
494: */
495: Parser(byte[] info, ConstPool cp) {
496: super (info);
497: pool = cp;
498: }
499:
500: Annotation[][] parseParameters() throws Exception {
501: parameters();
502: return allParams;
503: }
504:
505: Annotation[] parseAnnotations() throws Exception {
506: annotationArray();
507: return allAnno;
508: }
509:
510: MemberValue parseMemberValue() throws Exception {
511: memberValue(0);
512: return currentMember;
513: }
514:
515: void parameters(int numParam, int pos) throws Exception {
516: Annotation[][] params = new Annotation[numParam][];
517: for (int i = 0; i < numParam; ++i) {
518: pos = annotationArray(pos);
519: params[i] = allAnno;
520: }
521:
522: allParams = params;
523: }
524:
525: int annotationArray(int pos, int num) throws Exception {
526: Annotation[] array = new Annotation[num];
527: for (int i = 0; i < num; ++i) {
528: pos = annotation(pos);
529: array[i] = currentAnno;
530: }
531:
532: allAnno = array;
533: return pos;
534: }
535:
536: int annotation(int pos, int type, int numPairs)
537: throws Exception {
538: currentAnno = new Annotation(type, pool);
539: return super .annotation(pos, type, numPairs);
540: }
541:
542: int memberValuePair(int pos, int nameIndex) throws Exception {
543: pos = super .memberValuePair(pos, nameIndex);
544: currentAnno.addMemberValue(nameIndex, currentMember);
545: return pos;
546: }
547:
548: void constValueMember(int tag, int index) throws Exception {
549: MemberValue m;
550: ConstPool cp = pool;
551: switch (tag) {
552: case 'B':
553: m = new ByteMemberValue(index, cp);
554: break;
555: case 'C':
556: m = new CharMemberValue(index, cp);
557: break;
558: case 'D':
559: m = new DoubleMemberValue(index, cp);
560: break;
561: case 'F':
562: m = new FloatMemberValue(index, cp);
563: break;
564: case 'I':
565: m = new IntegerMemberValue(index, cp);
566: break;
567: case 'J':
568: m = new LongMemberValue(index, cp);
569: break;
570: case 'S':
571: m = new ShortMemberValue(index, cp);
572: break;
573: case 'Z':
574: m = new BooleanMemberValue(index, cp);
575: break;
576: case 's':
577: m = new StringMemberValue(index, cp);
578: break;
579: default:
580: throw new RuntimeException("unknown tag:" + tag);
581: }
582:
583: currentMember = m;
584: super .constValueMember(tag, index);
585: }
586:
587: void enumMemberValue(int typeNameIndex, int constNameIndex)
588: throws Exception {
589: currentMember = new EnumMemberValue(typeNameIndex,
590: constNameIndex, pool);
591: super .enumMemberValue(typeNameIndex, constNameIndex);
592: }
593:
594: void classMemberValue(int index) throws Exception {
595: currentMember = new ClassMemberValue(index, pool);
596: super .classMemberValue(index);
597: }
598:
599: int annotationMemberValue(int pos) throws Exception {
600: Annotation anno = currentAnno;
601: pos = super .annotationMemberValue(pos);
602: currentMember = new AnnotationMemberValue(currentAnno, pool);
603: currentAnno = anno;
604: return pos;
605: }
606:
607: int arrayMemberValue(int pos, int num) throws Exception {
608: ArrayMemberValue amv = new ArrayMemberValue(pool);
609: MemberValue[] elements = new MemberValue[num];
610: for (int i = 0; i < num; ++i) {
611: pos = memberValue(pos);
612: elements[i] = currentMember;
613: }
614:
615: amv.setValue(elements);
616: currentMember = amv;
617: return pos;
618: }
619: }
620: }
|