1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.util.Map;
19import java.util.HashMap;
20import java.io.IOException;
21import java.io.DataInputStream;
22import java.io.ByteArrayOutputStream;
23import javassist.bytecode.annotation.*;
24
25/**
26 * A class representing
27 * <code>RuntimeVisibleAnnotations_attribute</code> and
28 * <code>RuntimeInvisibleAnnotations_attribute</code>.
29 *
30 * <p>To obtain an AnnotationAttribute object, invoke
31 * <code>getAttribute(AnnotationsAttribute.visibleTag)</code>
32 * in <code>ClassFile</code>, <code>MethodInfo</code>,
33 * or <code>FieldInfo</code>.  The obtained attribute is a
34 * runtime visible annotations attribute.
35 * If the parameter is
36 * <code>AnnotationAttribute.invisibleTag</code>, then the obtained
37 * attribute is a runtime invisible one.
38 *
39 * <p>For example,
40 *
41 * <ul><pre>
42 * import javassist.bytecode.annotation.Annotation;
43 *    :
44 * CtMethod m = ... ;
45 * MethodInfo minfo = m.getMethodInfo();
46 * AnnotationsAttribute attr = (AnnotationsAttribute)
47 *         minfo.getAttribute(AnnotationsAttribute.invisibleTag);
48 * Annotation an = attr.getAnnotation("Author");
49 * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
50 * System.out.println("@Author(name=" + s + ")");
51 * </pre></ul>
52 *
53 * <p>This code snippet retrieves an annotation of the type <code>Author</code>
54 * from the <code>MethodInfo</code> object specified by <code>minfo</code>.
55 * Then, it prints the value of <code>name</code> in <code>Author</code>.
56 *
57 * <p>If the annotation type <code>Author</code> is annotated by a meta annotation:
58 *
59 * <ul><pre>
60 * &#64;Retention(RetentionPolicy.RUNTIME)
61 * </pre></ul>
62 *
63 * <p>Then <code>Author</code> is visible at runtime.  Therefore, the third
64 * statement of the code snippet above must be changed into:
65 *
66 * <ul><pre>
67 * AnnotationsAttribute attr = (AnnotationsAttribute)
68 *         minfo.getAttribute(AnnotationsAttribute.visibleTag);
69 * </pre></ul>
70 *
71 * <p>The attribute tag must be <code>visibleTag</code> instead of
72 * <code>invisibleTag</code>.
73 *
74 * <p>If the member value of an annotation is not specified, the default value
75 * is used as that member value.  If so, <code>getMemberValue()</code> in
76 * <code>Annotation</code> returns <code>null</code>
77 * since the default value is not included in the
78 * <code>AnnotationsAttribute</code>.  It is included in the
79 * <code>AnnotationDefaultAttribute</code> of the method declared in the
80 * annotation type.
81 *
82 * <p>If you want to record a new AnnotationAttribute object, execute the
83 * following snippet:
84 *
85 * <ul><pre>
86 * ClassFile cf = ... ;
87 * ConstPool cp = cf.getConstPool();
88 * AnnotationsAttribute attr
89 *     = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
90 * Annotation a = new Annotation("Author", cp);
91 * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
92 * attr.setAnnotation(a);
93 * cf.addAttribute(attr);
94 * cf.setVersionToJava5();
95 * </pre></ul>
96 *
97 * <p>The last statement is necessary if the class file was produced by
98 * Javassist or JDK 1.4.  Otherwise, it is not necessary.
99 *
100 * @see AnnotationDefaultAttribute
101 * @see javassist.bytecode.annotation.Annotation
102 */
103public class AnnotationsAttribute extends AttributeInfo {
104    /**
105     * The name of the <code>RuntimeVisibleAnnotations</code> attribute.
106     */
107    public static final String visibleTag = "RuntimeVisibleAnnotations";
108
109    /**
110     * The name of the <code>RuntimeInvisibleAnnotations</code> attribute.
111     */
112    public static final String invisibleTag = "RuntimeInvisibleAnnotations";
113
114    /**
115     * Constructs a <code>Runtime(In)VisibleAnnotations_attribute</code>.
116     *
117     * @param cp            constant pool
118     * @param attrname      attribute name (<code>visibleTag</code> or
119     *                      <code>invisibleTag</code>).
120     * @param info          the contents of this attribute.  It does not
121     *                      include <code>attribute_name_index</code> or
122     *                      <code>attribute_length</code>.
123     */
124    public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) {
125        super(cp, attrname, info);
126    }
127
128    /**
129     * Constructs an empty
130     * <code>Runtime(In)VisibleAnnotations_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    {
149        super(cp, n, in);
150    }
151
152    /**
153     * Returns <code>num_annotations</code>.
154     */
155    public int numAnnotations() {
156        return ByteArray.readU16bit(info, 0);
157    }
158
159    /**
160     * Copies this attribute and returns a new copy.
161     */
162    public AttributeInfo copy(ConstPool newCp, Map classnames) {
163        Copier copier = new Copier(info, constPool, newCp, classnames);
164        try {
165            copier.annotationArray();
166            return new AnnotationsAttribute(newCp, getName(), copier.close());
167        }
168        catch (Exception e) {
169            throw new RuntimeException(e);
170        }
171    }
172
173    /**
174     * Parses the annotations and returns a data structure representing
175     * the annotation with the specified type.  See also
176     * <code>getAnnotations()</code> as to the returned data structure.
177     *
178     * @param type      the annotation type.
179     * @return null if the specified annotation type is not included.
180     * @see #getAnnotations()
181     */
182    public Annotation getAnnotation(String type) {
183        Annotation[] annotations = getAnnotations();
184        for (int i = 0; i < annotations.length; i++) {
185            if (annotations[i].getTypeName().equals(type))
186                return annotations[i];
187        }
188
189        return null;
190    }
191
192    /**
193     * Adds an annotation.  If there is an annotation with the same type,
194     * it is removed before the new annotation is added.
195     *
196     * @param annotation        the added annotation.
197     */
198    public void addAnnotation(Annotation annotation) {
199        String type = annotation.getTypeName();
200        Annotation[] annotations = getAnnotations();
201        for (int i = 0; i < annotations.length; i++) {
202            if (annotations[i].getTypeName().equals(type)) {
203                annotations[i] = annotation;
204                setAnnotations(annotations);
205                return;
206            }
207        }
208
209        Annotation[] newlist = new Annotation[annotations.length + 1];
210        System.arraycopy(annotations, 0, newlist, 0, annotations.length);
211        newlist[annotations.length] = annotation;
212        setAnnotations(newlist);
213    }
214
215    /**
216     * Parses the annotations and returns a data structure representing
217     * that parsed annotations.  Note that changes of the node values of the
218     * returned tree are not reflected on the annotations represented by
219     * this object unless the tree is copied back to this object by
220     * <code>setAnnotations()</code>.
221     *
222     * @see #setAnnotations(Annotation[])
223     */
224    public Annotation[] getAnnotations() {
225        try {
226            return new Parser(info, constPool).parseAnnotations();
227        }
228        catch (Exception e) {
229            throw new RuntimeException(e);
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, constPool);
243        try {
244            int n = annotations.length;
245            writer.numAnnotations(n);
246            for (int i = 0; i < n; ++i)
247                annotations[i].write(writer);
248
249            writer.close();
250        }
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     * @param oldname       a JVM class name.
271     * @param newname       a JVM class name.
272     */
273    void renameClass(String oldname, String newname) {
274        HashMap map = new HashMap();
275        map.put(oldname, newname);
276        renameClass(map);
277    }
278
279    void renameClass(Map classnames) {
280        Renamer renamer = new Renamer(info, getConstPool(), classnames);
281        try {
282            renamer.annotationArray();
283        } catch (Exception e) {
284            throw new RuntimeException(e);
285        }
286    }
287
288    void getRefClasses(Map classnames) { renameClass(classnames); }
289
290    /**
291     * Returns a string representation of this object.
292     */
293    public String toString() {
294        Annotation[] a = getAnnotations();
295        StringBuilder sbuf = new StringBuilder();
296        int i = 0;
297        while (i < a.length) {
298            sbuf.append(a[i++].toString());
299            if (i != a.length)
300                sbuf.append(", ");
301        }
302
303        return sbuf.toString();
304    }
305
306    static class Walker {
307        byte[] info;
308
309        Walker(byte[] attrInfo) {
310            info = attrInfo;
311        }
312
313        final void parameters() throws Exception {
314            int numParam = info[0] & 0xff;
315            parameters(numParam, 1);
316        }
317
318        void parameters(int numParam, int pos) throws Exception {
319            for (int i = 0; i < numParam; ++i)
320                pos = annotationArray(pos);
321        }
322
323        final void annotationArray() throws Exception {
324            annotationArray(0);
325        }
326
327        final int annotationArray(int pos) throws Exception {
328            int num = ByteArray.readU16bit(info, pos);
329            return annotationArray(pos + 2, num);
330        }
331
332        int annotationArray(int pos, int num) throws Exception {
333            for (int i = 0; i < num; ++i)
334                pos = annotation(pos);
335
336            return pos;
337        }
338
339        final int annotation(int pos) throws Exception {
340            int type = ByteArray.readU16bit(info, pos);
341            int numPairs = ByteArray.readU16bit(info, pos + 2);
342            return annotation(pos + 4, type, numPairs);
343        }
344
345        int annotation(int pos, int type, int numPairs) throws Exception {
346            for (int j = 0; j < numPairs; ++j)
347                pos = memberValuePair(pos);
348
349            return pos;
350        }
351
352        final int memberValuePair(int pos) throws Exception {
353            int nameIndex = ByteArray.readU16bit(info, pos);
354            return memberValuePair(pos + 2, nameIndex);
355        }
356
357        int memberValuePair(int pos, int nameIndex) throws Exception {
358            return memberValue(pos);
359        }
360
361        final int memberValue(int pos) throws Exception {
362            int tag = info[pos] & 0xff;
363            if (tag == 'e') {
364                int typeNameIndex = ByteArray.readU16bit(info, pos + 1);
365                int constNameIndex = ByteArray.readU16bit(info, pos + 3);
366                enumMemberValue(pos, typeNameIndex, constNameIndex);
367                return pos + 5;
368            }
369            else if (tag == 'c') {
370                int index = ByteArray.readU16bit(info, pos + 1);
371                classMemberValue(pos, index);
372                return pos + 3;
373            }
374            else if (tag == '@')
375                return annotationMemberValue(pos + 1);
376            else if (tag == '[') {
377                int num = ByteArray.readU16bit(info, pos + 1);
378                return arrayMemberValue(pos + 3, num);
379            }
380            else { // primitive types or String.
381                int index = ByteArray.readU16bit(info, pos + 1);
382                constValueMember(tag, index);
383                return pos + 3;
384            }
385        }
386
387        void constValueMember(int tag, int index) throws Exception {}
388
389        void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
390            throws Exception {
391        }
392
393        void classMemberValue(int pos, int index) throws Exception {}
394
395        int annotationMemberValue(int pos) throws Exception {
396            return annotation(pos);
397        }
398
399        int arrayMemberValue(int pos, int num) throws Exception {
400            for (int i = 0; i < num; ++i) {
401                pos = memberValue(pos);
402            }
403
404            return pos;
405        }
406    }
407
408    static class Renamer extends Walker {
409        ConstPool cpool;
410        Map classnames;
411
412        /**
413         * Constructs a renamer.  It renames some class names
414         * into the new names specified by <code>map</code>.
415         *
416         * @param info      the annotations attribute.
417         * @param cp        the constant pool.
418         * @param map       pairs of replaced and substituted class names.
419         *                  It can be null.
420         */
421        Renamer(byte[] info, ConstPool cp, Map map) {
422            super(info);
423            cpool = cp;
424            classnames = map;
425        }
426
427        int annotation(int pos, int type, int numPairs) throws Exception {
428            renameType(pos - 4, type);
429            return super.annotation(pos, type, numPairs);
430        }
431
432        void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
433            throws Exception
434        {
435            renameType(pos + 1, typeNameIndex);
436            super.enumMemberValue(pos, typeNameIndex, constNameIndex);
437        }
438
439        void classMemberValue(int pos, int index) throws Exception {
440            renameType(pos + 1, index);
441            super.classMemberValue(pos, index);
442        }
443
444        private void renameType(int pos, int index) {
445            String name = cpool.getUtf8Info(index);
446            String newName = Descriptor.rename(name, classnames);
447            if (!name.equals(newName)) {
448                int index2 = cpool.addUtf8Info(newName);
449                ByteArray.write16bit(index2, info, pos);
450            }
451        }
452    }
453
454    static class Copier extends Walker {
455        ByteArrayOutputStream output;
456        AnnotationsWriter writer;
457        ConstPool srcPool, destPool;
458        Map classnames;
459
460        /**
461         * Constructs a copier.  This copier renames some class names
462         * into the new names specified by <code>map</code> when it copies
463         * an annotation attribute.
464         *
465         * @param info      the source attribute.
466         * @param src       the constant pool of the source class.
467         * @param dest      the constant pool of the destination class.
468         * @param map       pairs of replaced and substituted class names.
469         *                  It can be null.
470         */
471        Copier(byte[] info, ConstPool src, ConstPool dest, Map map) {
472            super(info);
473            output = new ByteArrayOutputStream();
474            writer = new AnnotationsWriter(output, dest);
475            srcPool = src;
476            destPool = dest;
477            classnames = map;
478        }
479
480        byte[] close() throws IOException {
481            writer.close();
482            return output.toByteArray();
483        }
484
485        void parameters(int numParam, int pos) throws Exception {
486            writer.numParameters(numParam);
487            super.parameters(numParam, pos);
488        }
489
490        int annotationArray(int pos, int num) throws Exception {
491            writer.numAnnotations(num);
492            return super.annotationArray(pos, num);
493        }
494
495        int annotation(int pos, int type, int numPairs) throws Exception {
496            writer.annotation(copyType(type), numPairs);
497            return super.annotation(pos, type, numPairs);
498        }
499
500        int memberValuePair(int pos, int nameIndex) throws Exception {
501            writer.memberValuePair(copy(nameIndex));
502            return super.memberValuePair(pos, nameIndex);
503        }
504
505        void constValueMember(int tag, int index) throws Exception {
506            writer.constValueIndex(tag, copy(index));
507            super.constValueMember(tag, index);
508        }
509
510        void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
511            throws Exception
512        {
513            writer.enumConstValue(copyType(typeNameIndex), copy(constNameIndex));
514            super.enumMemberValue(pos, typeNameIndex, constNameIndex);
515        }
516
517        void classMemberValue(int pos, int index) throws Exception {
518            writer.classInfoIndex(copyType(index));
519            super.classMemberValue(pos, index);
520        }
521
522        int annotationMemberValue(int pos) throws Exception {
523            writer.annotationValue();
524            return super.annotationMemberValue(pos);
525        }
526
527        int arrayMemberValue(int pos, int num) throws Exception {
528            writer.arrayValue(num);
529            return super.arrayMemberValue(pos, num);
530        }
531
532        /**
533         * Copies a constant pool entry into the destination constant pool
534         * and returns the index of the copied entry.
535         *
536         * @param srcIndex      the index of the copied entry into the source
537         *                      constant pool.
538         * @return the index of the copied item into the destination
539         *         constant pool.
540         */
541        int copy(int srcIndex) {
542            return srcPool.copy(srcIndex, destPool, classnames);
543        }
544
545        /**
546         * Copies a constant pool entry into the destination constant pool
547         * and returns the index of the copied entry.  That entry must be
548         * a Utf8Info representing a class name in the L<class name>; form.
549         *
550         * @param srcIndex  the index of the copied entry into the source
551         *                  constant pool.
552         * @return          the index of the copied item into the destination
553         *                  constant pool.
554         */
555        int copyType(int srcIndex) {
556            String name = srcPool.getUtf8Info(srcIndex);
557            String newName = Descriptor.rename(name, classnames);
558            return destPool.addUtf8Info(newName);
559        }
560    }
561
562    static class Parser extends Walker {
563        ConstPool pool;
564        Annotation[][] allParams;   // all parameters
565        Annotation[] allAnno;       // all annotations
566        Annotation currentAnno;     // current annotation
567        MemberValue currentMember;  // current member
568
569        /**
570         * Constructs a parser.  This parser constructs a parse tree of
571         * the annotations.
572         *
573         * @param info      the attribute.
574         * @param src       the constant pool.
575         */
576        Parser(byte[] info, ConstPool cp) {
577            super(info);
578            pool = cp;
579        }
580
581        Annotation[][] parseParameters() throws Exception {
582            parameters();
583            return allParams;
584        }
585
586        Annotation[] parseAnnotations() throws Exception {
587            annotationArray();
588            return allAnno;
589        }
590
591        MemberValue parseMemberValue() throws Exception {
592            memberValue(0);
593            return currentMember;
594        }
595
596        void parameters(int numParam, int pos) throws Exception {
597            Annotation[][] params = new Annotation[numParam][];
598            for (int i = 0; i < numParam; ++i) {
599                pos = annotationArray(pos);
600                params[i] = allAnno;
601            }
602
603            allParams = params;
604        }
605
606        int annotationArray(int pos, int num) throws Exception {
607            Annotation[] array = new Annotation[num];
608            for (int i = 0; i < num; ++i) {
609                pos = annotation(pos);
610                array[i] = currentAnno;
611            }
612
613            allAnno = array;
614            return pos;
615        }
616
617        int annotation(int pos, int type, int numPairs) throws Exception {
618            currentAnno = new Annotation(type, pool);
619            return super.annotation(pos, type, numPairs);
620        }
621
622        int memberValuePair(int pos, int nameIndex) throws Exception {
623            pos = super.memberValuePair(pos, nameIndex);
624            currentAnno.addMemberValue(nameIndex, currentMember);
625            return pos;
626        }
627
628        void constValueMember(int tag, int index) throws Exception {
629            MemberValue m;
630            ConstPool cp = pool;
631            switch (tag) {
632            case 'B' :
633                m = new ByteMemberValue(index, cp);
634                break;
635            case 'C' :
636                m = new CharMemberValue(index, cp);
637                break;
638            case 'D' :
639                m = new DoubleMemberValue(index, cp);
640                break;
641            case 'F' :
642                m = new FloatMemberValue(index, cp);
643                break;
644            case 'I' :
645                m = new IntegerMemberValue(index, cp);
646                break;
647            case 'J' :
648                m = new LongMemberValue(index, cp);
649                break;
650            case 'S' :
651                m = new ShortMemberValue(index, cp);
652                break;
653            case 'Z' :
654                m = new BooleanMemberValue(index, cp);
655                break;
656            case 's' :
657                m = new StringMemberValue(index, cp);
658                break;
659            default :
660                throw new RuntimeException("unknown tag:" + tag);
661            }
662
663            currentMember = m;
664            super.constValueMember(tag, index);
665        }
666
667        void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
668            throws Exception
669        {
670            currentMember = new EnumMemberValue(typeNameIndex,
671                                              constNameIndex, pool);
672            super.enumMemberValue(pos, typeNameIndex, constNameIndex);
673        }
674
675        void classMemberValue(int pos, int index) throws Exception {
676            currentMember = new ClassMemberValue(index, pool);
677            super.classMemberValue(pos, index);
678        }
679
680        int annotationMemberValue(int pos) throws Exception {
681            Annotation anno = currentAnno;
682            pos = super.annotationMemberValue(pos);
683            currentMember = new AnnotationMemberValue(currentAnno, pool);
684            currentAnno = anno;
685            return pos;
686        }
687
688        int arrayMemberValue(int pos, int num) throws Exception {
689            ArrayMemberValue amv = new ArrayMemberValue(pool);
690            MemberValue[] elements = new MemberValue[num];
691            for (int i = 0; i < num; ++i) {
692                pos = memberValue(pos);
693                elements[i] = currentMember;
694            }
695
696            amv.setValue(elements);
697            currentMember = amv;
698            return pos;
699        }
700    }
701}
702