AnnotationDirectoryItem.java revision 9ab2b45ec8531658e3acf0b96b11a214ce8d3b60
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib;
30
31import org.jf.dexlib.Util.Input;
32import org.jf.dexlib.Util.AnnotatedOutput;
33
34import java.util.Collections;
35import java.util.List;
36
37public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
38    private AnnotationSetItem classAnnotations;
39
40    private FieldIdItem[] fieldAnnotationFields;
41    private AnnotationSetItem[] fieldAnnotations;
42
43    private MethodIdItem[] methodAnnotationMethods;
44    private AnnotationSetItem[] methodAnnotations;
45
46    private MethodIdItem[] parameterAnnotationMethods;
47    private AnnotationSetRefList[] parameterAnnotations;
48
49    /**
50     * typically each AnnotationDirectoryItem will have a distinct parent. The only case that isn't true is when
51     * the AnnotationDirectoryItem *only* contains class annotations, with no other type of annotation. In that
52     * case, the same AnnotationDirectoryItem could be referenced from multiple classes.
53     * This isn't a problem though, because this field is only used in compareTo to determine the sort order,
54     * which handles it as a special case
55     */
56    private ClassDefItem parent = null;
57
58    /**
59     * Creates a new uninitialized <code>AnnotationDirectoryItem</code>
60     * @param dexFile The <code>DexFile</code> that this item belongs to
61     */
62    protected AnnotationDirectoryItem(DexFile dexFile) {
63        super(dexFile);
64    }
65
66    /**
67     * Creates a new <code>AnnotationDirectoryItem</code> with the given values
68     * @param dexFile The <code>DexFile</code> that this item belongs to
69     * @param classAnnotations The annotations associated with the overall class
70     * @param fieldAnnotationFields An array of <code>FieldIdItem</code> objects that the annotations in
71     * <code>fieldAnnotations</code> are associated with
72     * @param fieldAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
73     * fields in <code>fieldAnnotationFields</code>
74     * @param methodAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
75     * <code>methodAnnotations</code> are associated with
76     * @param methodAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
77     * methods in <code>methodAnnotationMethods</code>
78     * @param parameterAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
79     * <code>parameterAnnotations</code> are associated with
80     * @param parameterAnnotations An array of <code>AnnotationSetRefList</code> objects that contain the parameter
81     * annotations for the methods in <code>parameterAnnotationMethods</code>
82     */
83    private AnnotationDirectoryItem(DexFile dexFile, AnnotationSetItem classAnnotations,
84                                    FieldIdItem[] fieldAnnotationFields, AnnotationSetItem[] fieldAnnotations,
85                                    MethodIdItem[] methodAnnotationMethods, AnnotationSetItem[] methodAnnotations,
86                                    MethodIdItem[] parameterAnnotationMethods,
87                                    AnnotationSetRefList[] parameterAnnotations) {
88        super(dexFile);
89        this.classAnnotations = classAnnotations;
90        this.fieldAnnotationFields = fieldAnnotationFields;
91        this.fieldAnnotations = fieldAnnotations;
92        this.methodAnnotationMethods = methodAnnotationMethods;
93        this.methodAnnotations = methodAnnotations;
94        this.parameterAnnotationMethods = parameterAnnotationMethods;
95        this.parameterAnnotations = parameterAnnotations;
96    }
97
98    /**
99     * Returns an <code>AnnotationDirectoryItem</code> for the given values, and that has been interned into the given
100     * <code>DexFile</code>
101     * @param dexFile The <code>DexFile</code> that this item belongs to
102     * @param classAnnotations The annotations associated with the class
103     * @param fieldAnnotations A list of <code>FieldAnnotation</code> objects containing the field annotations
104     * @param methodAnnotations A list of <code>MethodAnnotation</code> objects containing the method annotations
105     * @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects containin the parameter
106     * annotations
107     * @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
108     * <code>DexFile</code>
109     */
110    public static AnnotationDirectoryItem getInternedAnnotationDirectoryItem(DexFile dexFile,
111                                    AnnotationSetItem classAnnotations,
112                                    List<FieldAnnotation> fieldAnnotations,
113                                    List<MethodAnnotation> methodAnnotations,
114                                    List<ParameterAnnotation> parameterAnnotations) {
115        FieldIdItem[] fieldAnnotationFields = null;
116        AnnotationSetItem[] fieldAnnotationsArray = null;
117        MethodIdItem[] methodAnnotationMethods = null;
118        AnnotationSetItem[] methodAnnotationsArray = null;
119        MethodIdItem[] parameterAnnotationMethods = null;
120        AnnotationSetRefList[] parameterAnnotationsArray = null;
121
122        if (fieldAnnotations != null && fieldAnnotations.size() > 0) {
123            fieldAnnotationFields = new FieldIdItem[fieldAnnotations.size()];
124            fieldAnnotationsArray = new AnnotationSetItem[fieldAnnotations.size()];
125
126            Collections.sort(fieldAnnotations);
127
128            int index = 0;
129            for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
130                fieldAnnotationFields[index] = fieldAnnotation.field;
131                fieldAnnotationsArray[index++] = fieldAnnotation.annotationSet;
132            }
133        }
134
135        if (methodAnnotations != null && methodAnnotations.size() > 0) {
136            methodAnnotationMethods = new MethodIdItem[methodAnnotations.size()];
137            methodAnnotationsArray = new AnnotationSetItem[methodAnnotations.size()];
138
139            Collections.sort(methodAnnotations);
140
141            int index = 0;
142            for (MethodAnnotation methodAnnotation: methodAnnotations) {
143                methodAnnotationMethods[index] = methodAnnotation.method;
144                methodAnnotationsArray[index++] = methodAnnotation.annotationSet;
145            }
146        }
147
148        if (parameterAnnotations != null && parameterAnnotations.size() > 0) {
149            parameterAnnotationMethods = new MethodIdItem[parameterAnnotations.size()];
150            parameterAnnotationsArray = new AnnotationSetRefList[parameterAnnotations.size()];
151
152            Collections.sort(parameterAnnotations);
153
154            int index = 0;
155            for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
156                parameterAnnotationMethods[index] = parameterAnnotation.method;
157                parameterAnnotationsArray[index++] = parameterAnnotation.annotationSet;
158            }
159        }
160
161        AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations,
162                fieldAnnotationFields, fieldAnnotationsArray, methodAnnotationMethods, methodAnnotationsArray,
163                parameterAnnotationMethods, parameterAnnotationsArray);
164        return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem);
165    }
166
167    /** {@inheritDoc} */
168    protected void readItem(Input in, ReadContext readContext) {
169        classAnnotations = (AnnotationSetItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_ANNOTATION_SET_ITEM,
170                in.readInt());
171        fieldAnnotationFields = new FieldIdItem[in.readInt()];
172        fieldAnnotations = new AnnotationSetItem[fieldAnnotationFields.length];
173
174        methodAnnotationMethods = new MethodIdItem[in.readInt()];
175        methodAnnotations = new AnnotationSetItem[methodAnnotationMethods.length];
176
177        parameterAnnotationMethods = new MethodIdItem[in.readInt()];
178        parameterAnnotations = new AnnotationSetRefList[parameterAnnotationMethods.length];
179
180        for (int i=0; i<fieldAnnotations.length; i++) {
181            fieldAnnotationFields[i] = dexFile.FieldIdsSection.getItemByIndex(in.readInt());
182            fieldAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
183                    ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
184        }
185
186        for (int i=0; i<methodAnnotations.length; i++) {
187            methodAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
188            methodAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
189                    ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
190        }
191
192        for (int i=0; i<parameterAnnotations.length; i++) {
193            parameterAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
194            parameterAnnotations[i] = (AnnotationSetRefList)readContext.getOffsettedItemByOffset(
195                    ItemType.TYPE_ANNOTATION_SET_REF_LIST, in.readInt());
196        }
197    }
198
199    /** {@inheritDoc} */
200    protected int placeItem(int offset) {
201        return offset + 16 + (
202                (fieldAnnotations==null?0:fieldAnnotations.length) +
203                (methodAnnotations==null?0:methodAnnotations.length) +
204                (parameterAnnotations==null?0:parameterAnnotations.length)) * 8;
205    }
206
207    /** {@inheritDoc} */
208    protected void writeItem(AnnotatedOutput out) {
209        if (out.annotates()) {
210            if (!isInternable() && parent != null) {
211                out.annotate(0, parent.getClassType().getTypeDescriptor());
212            }
213            if (classAnnotations != null) {
214                out.annotate(4, "class_annotations_off: 0x" + Integer.toHexString(classAnnotations.getOffset()));
215            } else {
216                out.annotate(4, "class_annotations_off:");
217            }
218
219            int length = fieldAnnotations==null?0:fieldAnnotations.length;
220            out.annotate(4, "annotated_fields_size: 0x" + Integer.toHexString(length) + " (" +
221                    length + ")");
222            length = methodAnnotations==null?0:methodAnnotations.length;
223            out.annotate(4, "annotated_methods_size: 0x" + Integer.toHexString(length) + " (" +
224                    length + ")");
225            length = parameterAnnotations==null?0:parameterAnnotations.length;
226            out.annotate(4, "annotated_parameters_size: 0x" + Integer.toHexString(length) + " (" +
227                    length + ")");
228
229            int index;
230            if (fieldAnnotations != null) {
231               index = 0;
232                for (int i=0; i<fieldAnnotations.length; i++) {
233                    out.annotate(0, "[" + index++ + "] field_annotation");
234
235                    out.indent();
236                    out.annotate(4, "field: " + fieldAnnotationFields[i].getFieldName().getStringValue() + ":" +
237                            fieldAnnotationFields[i].getFieldType().getTypeDescriptor());
238                    out.annotate(4, "annotations_off: 0x" + Integer.toHexString(fieldAnnotations[i].getOffset()));
239                    out.deindent();
240                }
241            }
242
243            if (methodAnnotations != null) {
244                index = 0;
245                for (int i=0; i<methodAnnotations.length; i++) {
246                    out.annotate(0, "[" + index++ + "] method_annotation");
247                    out.indent();
248                    out.annotate(4, "method: " + methodAnnotationMethods[i].getMethodString());
249                    out.annotate(4, "annotations_off: 0x" + Integer.toHexString(methodAnnotations[i].getOffset()));
250                    out.deindent();
251                }
252            }
253
254            if (parameterAnnotations != null) {
255                index = 0;
256                for (int i=0; i<parameterAnnotations.length; i++) {
257                    out.annotate(0, "[" + index++ + "] parameter_annotation");
258                    out.indent();
259                    out.annotate(4, "method: " + parameterAnnotationMethods[i].getMethodString());
260                    out.annotate(4, "annotations_off: 0x" + Integer.toHexString(parameterAnnotations[i].getOffset()));
261                }
262            }
263        }
264
265        out.writeInt(classAnnotations==null?0:classAnnotations.getOffset());
266        out.writeInt(fieldAnnotations==null?0:fieldAnnotations.length);
267        out.writeInt(methodAnnotations==null?0:methodAnnotations.length);
268        out.writeInt(parameterAnnotations==null?0:parameterAnnotations.length);
269
270        if (fieldAnnotations != null) {
271            for (int i=0; i<fieldAnnotations.length; i++) {
272                out.writeInt(fieldAnnotationFields[i].getIndex());
273                out.writeInt(fieldAnnotations[i].getOffset());
274            }
275        }
276
277        if (methodAnnotations != null) {
278            for (int i=0; i<methodAnnotations.length; i++) {
279                out.writeInt(methodAnnotationMethods[i].getIndex());
280                out.writeInt(methodAnnotations[i].getOffset());
281            }
282        }
283
284        if (parameterAnnotations != null) {
285            for (int i=0; i<parameterAnnotations.length; i++) {
286                out.writeInt(parameterAnnotationMethods[i].getIndex());
287                out.writeInt(parameterAnnotations[i].getOffset());
288            }
289        }
290    }
291
292    /** {@inheritDoc} */public ItemType getItemType() {
293        return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
294    }
295
296    /** {@inheritDoc} */
297    public String getConciseIdentity() {
298        return "annotation_directory_item @0x" + Integer.toHexString(getOffset());
299    }
300
301    /** {@inheritDoc} */
302    public int compareTo(AnnotationDirectoryItem o) {
303        if (!isInternable()) {
304            if (!o.isInternable()) {
305                return parent.compareTo(o.parent);
306            }
307            return -1;
308        }
309
310        if (!o.isInternable()) {
311            return 1;
312        }
313
314        return classAnnotations.compareTo(o.classAnnotations);
315    }
316
317    /**
318     * @return The annotations associated with the class
319     */
320    public AnnotationSetItem getClassAnnotations() {
321        return classAnnotations;
322    }
323
324    /**
325     * Iterates over the field annotations, calling delegate.processFieldAnnotations for each
326     * @param delegate the delegate to call
327     */
328    public void iterateFieldAnnotations(FieldAnnotationIteratorDelegate delegate) {
329        for (int i=0; i<fieldAnnotationFields.length; i++) {
330            delegate.processFieldAnnotations(fieldAnnotationFields[i], fieldAnnotations[i]);
331        }
332    }
333
334    public static interface FieldAnnotationIteratorDelegate {
335        void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations);
336    }
337
338    /**
339     * @return the number of field annotations in this <code>AnnotationDirectoryItem</code>
340     */
341    public int getFieldAnnotationCount() {
342        return fieldAnnotationFields.length;
343    }
344
345    /**
346     * Iterates over the method annotations, calling delegate.processMethodAnnotations for each
347     * @param delegate the delegate to call
348     */
349    public void iterateMethodAnnotations(MethodAnnotationIteratorDelegate delegate) {
350        for (int i=0; i<methodAnnotationMethods.length; i++) {
351            delegate.processMethodAnnotations(methodAnnotationMethods[i], methodAnnotations[i]);
352        }
353    }
354
355    public static interface MethodAnnotationIteratorDelegate {
356        void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations);
357    }
358
359    /**
360     * @return the number of method annotations in this <code>AnnotationDirectoryItem</code>
361     */
362    public int getMethodAnnotationCount() {
363        return methodAnnotationMethods.length;
364    }
365
366    /**
367     * Iterates over the parameter annotations, calling delegate.processParameterAnnotations for each
368     * @param delegate the delegate to call
369     */
370    public void iterateParameterAnnotations(ParameterAnnotationIteratorDelegate delegate) {
371        for (int i=0; i<parameterAnnotationMethods.length; i++) {
372            delegate.processParameterAnnotations(parameterAnnotationMethods[i], parameterAnnotations[i]);
373        }
374    }
375
376    public static interface ParameterAnnotationIteratorDelegate {
377        void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations);
378    }
379
380    /**
381     * @return the number of parameter annotations in this <code>AnnotationDirectoryItem</code>
382     */
383    public int getParameterAnnotationCount() {
384        return parameterAnnotationMethods.length;
385    }
386
387    /**
388     * @return true if this <code>AnnotationDirectoryItem</code> is internable. It is only internable if it has
389     * only class annotations, but no field, method or parameter annotations
390     */
391    private boolean isInternable() {
392        return classAnnotations != null &&
393               (fieldAnnotations == null || fieldAnnotations.length == 0) &&
394               (methodAnnotations == null || methodAnnotations.length == 0) &&
395               (parameterAnnotations == null || parameterAnnotations.length == 0);
396    }
397
398    /**
399     * Sets the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated with.
400     * This is only applicable if this AnnotationDirectoryItem contains only class annotations, and no field, method
401     * or parameter annotations.
402     * @param classDefItem the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated
403     * with
404     */
405    protected void setParent(ClassDefItem classDefItem) {
406        this.parent = classDefItem;
407    }
408
409    @Override
410    public int hashCode() {
411        //an instance is only internable if it has only class annotations, but
412        //no other type of annotation
413        if (!isInternable()) {
414            return super.hashCode();
415        }
416        return classAnnotations.hashCode();
417    }
418
419    @Override
420    public boolean equals(Object o) {
421        if (this==o) {
422            return true;
423        }
424        if (o==null || !this.getClass().equals(o.getClass())) {
425            return false;
426        }
427
428        AnnotationDirectoryItem other = (AnnotationDirectoryItem)o;
429        return (this.compareTo(other) == 0);
430    }
431
432    public static class FieldAnnotation implements Comparable<FieldAnnotation> {
433        public final FieldIdItem field;
434        public final AnnotationSetItem annotationSet;
435
436        public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) {
437            this.field = field;
438            this.annotationSet = annotationSet;
439        }
440
441        public int compareTo(FieldAnnotation other) {
442            return field.compareTo(other.field);
443        }
444    }
445
446    public static class MethodAnnotation implements Comparable<MethodAnnotation> {
447        public final MethodIdItem method;
448        public final AnnotationSetItem annotationSet;
449
450        public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) {
451            this.method = method;
452            this.annotationSet = annotationSet;
453        }
454
455        public int compareTo(MethodAnnotation other) {
456            return method.compareTo(other.method);
457        }
458    }
459
460    public static class ParameterAnnotation implements Comparable<ParameterAnnotation> {
461        public final MethodIdItem method;
462        public final AnnotationSetRefList annotationSet;
463
464        public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) {
465            this.method = method;
466            this.annotationSet = annotationSet;
467        }
468
469        public int compareTo(ParameterAnnotation other) {
470            return method.compareTo(other.method);
471        }
472    }
473}
474