AnnotationDirectoryItem.java revision 83b80f81d311b233188c281059aad4a9f5e8b4e6
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.EncodedValue.AnnotationEncodedSubValue;
32import org.jf.dexlib.Util.ArrayUtils;
33import org.jf.dexlib.Util.Input;
34import org.jf.dexlib.Util.AnnotatedOutput;
35
36import java.util.ArrayList;
37import java.util.Collections;
38import java.util.List;
39
40public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
41    private AnnotationSetItem classAnnotations;
42
43    private FieldIdItem[] fieldAnnotationFields;
44    private AnnotationSetItem[] fieldAnnotations;
45
46    private MethodIdItem[] methodAnnotationMethods;
47    private AnnotationSetItem[] methodAnnotations;
48
49    private MethodIdItem[] parameterAnnotationMethods;
50    private AnnotationSetRefList[] parameterAnnotations;
51
52    /**
53     * typically each AnnotationDirectoryItem will have a distinct parent. The only case that isn't true is when
54     * the AnnotationDirectoryItem *only* contains class annotations, with no other type of annotation. In that
55     * case, the same AnnotationDirectoryItem could be referenced from multiple classes.
56     * This isn't a problem though, because this field is only used in compareTo to determine the sort order,
57     * which handles it as a special case
58     */
59    private ClassDefItem parent = null;
60
61    /**
62     * Creates a new uninitialized <code>AnnotationDirectoryItem</code>
63     * @param dexFile The <code>DexFile</code> that this item belongs to
64     */
65    protected AnnotationDirectoryItem(DexFile dexFile) {
66        super(dexFile);
67    }
68
69    /**
70     * Creates a new <code>AnnotationDirectoryItem</code> with the given values
71     * @param dexFile The <code>DexFile</code> that this item belongs to
72     * @param classAnnotations The annotations associated with the overall class
73     * @param fieldAnnotationFields An array of <code>FieldIdItem</code> objects that the annotations in
74     * <code>fieldAnnotations</code> are associated with
75     * @param fieldAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
76     * fields in <code>fieldAnnotationFields</code>
77     * @param methodAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
78     * <code>methodAnnotations</code> are associated with
79     * @param methodAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
80     * methods in <code>methodAnnotationMethods</code>
81     * @param parameterAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
82     * <code>parameterAnnotations</code> are associated with
83     * @param parameterAnnotations An array of <code>AnnotationSetRefList</code> objects that contain the parameter
84     * annotations for the methods in <code>parameterAnnotationMethods</code>
85     */
86    private AnnotationDirectoryItem(DexFile dexFile, AnnotationSetItem classAnnotations,
87                                    FieldIdItem[] fieldAnnotationFields, AnnotationSetItem[] fieldAnnotations,
88                                    MethodIdItem[] methodAnnotationMethods, AnnotationSetItem[] methodAnnotations,
89                                    MethodIdItem[] parameterAnnotationMethods,
90                                    AnnotationSetRefList[] parameterAnnotations) {
91        super(dexFile);
92        this.classAnnotations = classAnnotations;
93        this.fieldAnnotationFields = fieldAnnotationFields;
94        this.fieldAnnotations = fieldAnnotations;
95        this.methodAnnotationMethods = methodAnnotationMethods;
96        this.methodAnnotations = methodAnnotations;
97        this.parameterAnnotationMethods = parameterAnnotationMethods;
98        this.parameterAnnotations = parameterAnnotations;
99    }
100
101    /**
102     * Returns an <code>AnnotationDirectoryItem</code> for the given values, and that has been interned into the given
103     * <code>DexFile</code>
104     * @param dexFile The <code>DexFile</code> that this item belongs to
105     * @param classAnnotations The annotations associated with the class
106     * @param fieldAnnotationFields An array of <code>FieldIdItem</code> objects that the annotations in
107     * <code>fieldAnnotations</code> are associated with
108     * @param fieldAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
109     * fields in <code>fieldAnnotationFields</code>
110     * @param methodAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
111     * <code>methodAnnotations</code> are associated with
112     * @param methodAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
113     * methods in <code>methodAnnotationMethods</code>
114     * @param parameterAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
115     * <code>parameterAnnotations</code> are associated with
116     * @param parameterAnnotations An array of <code>AnnotationSetRefList</code> objects that contain the parameter
117     * annotations for the methods in <code>parameterAnnotationMethods</code>
118     * @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
119     * <code>DexFile</code>
120     */
121    public static AnnotationDirectoryItem getInternedAnnotationDirectoryItem(DexFile dexFile,
122                                    AnnotationSetItem classAnnotations,
123                                    FieldIdItem[] fieldAnnotationFields, AnnotationSetItem[] fieldAnnotations,
124                                    MethodIdItem[] methodAnnotationMethods, AnnotationSetItem[] methodAnnotations,
125                                    MethodIdItem[] parameterAnnotationMethods,
126                                    AnnotationSetRefList[] parameterAnnotations) {
127        AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations,
128                fieldAnnotationFields, fieldAnnotations, methodAnnotationMethods, methodAnnotations,
129                parameterAnnotationMethods, parameterAnnotations);
130        return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem);
131    }
132
133    /** {@inheritDoc} */
134    protected void readItem(Input in, ReadContext readContext) {
135        readContext.getOffsettedItemByOffset(ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
136        fieldAnnotationFields = new FieldIdItem[in.readInt()];
137        fieldAnnotations = new AnnotationSetItem[fieldAnnotationFields.length];
138
139        methodAnnotationMethods = new MethodIdItem[in.readInt()];
140        methodAnnotations = new AnnotationSetItem[methodAnnotationMethods.length];
141
142        parameterAnnotationMethods = new MethodIdItem[in.readInt()];
143        parameterAnnotations = new AnnotationSetRefList[parameterAnnotationMethods.length];
144
145        for (int i=0; i<fieldAnnotations.length; i++) {
146            fieldAnnotationFields[i] = dexFile.FieldIdsSection.getItemByIndex(in.readInt());
147            fieldAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
148                    ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
149        }
150
151        for (int i=0; i<methodAnnotations.length; i++) {
152            methodAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
153            methodAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
154                    ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
155        }
156
157        for (int i=0; i<parameterAnnotations.length; i++) {
158            parameterAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
159            parameterAnnotations[i] = (AnnotationSetRefList)readContext.getOffsettedItemByOffset(
160                    ItemType.TYPE_ANNOTATION_SET_REF_LIST, in.readInt());
161        }
162    }
163
164    /** {@inheritDoc} */
165    protected int placeItem(int offset) {
166        if (!dexFile.getInplace()) {
167            ArrayUtils.sortTwoArrays(fieldAnnotationFields, fieldAnnotations);
168            ArrayUtils.sortTwoArrays(methodAnnotationMethods, methodAnnotations);
169            ArrayUtils.sortTwoArrays(parameterAnnotationMethods, parameterAnnotations);
170        }
171
172        return offset + 16 + fieldAnnotations.length * 8 + methodAnnotations.length * 8 +
173                parameterAnnotations.length * 8;
174    }
175
176    /** {@inheritDoc} */
177    protected void writeItem(AnnotatedOutput out) {
178        if (out.annotates()) {
179            out.annotate(4, "class_annotations_off");
180            out.annotate(4, "annotated_fields_size");
181            out.annotate(4, "annotated_methods_size");
182            out.annotate(4, "annotated_parameters_size");
183
184
185            for (int i=0; i<fieldAnnotations.length; i++) {
186                out.annotate(4, "field_idx");
187                out.annotate(4, "annotations_off");
188            }
189
190            for (int i=0; i<methodAnnotations.length; i++) {
191                out.annotate(4, "method_idx");
192                out.annotate(4, "annotations_off");
193            }
194
195            for (int i=0; i<parameterAnnotations.length; i++) {
196                out.annotate(4, "method_idx");
197                out.annotate(4, "annotations_off");
198            }
199        }
200
201        out.writeInt(classAnnotations==null?0:classAnnotations.getOffset());
202
203        for (int i=0; i<fieldAnnotations.length; i++) {
204            out.writeInt(fieldAnnotationFields[i].getIndex());
205            out.writeInt(fieldAnnotations[i].getOffset());
206        }
207
208        for (int i=0; i<methodAnnotations.length; i++) {
209            out.writeInt(methodAnnotationMethods[i].getIndex());
210            out.writeInt(methodAnnotations[i].getOffset());
211        }
212
213        for (int i=0; i<parameterAnnotations.length; i++) {
214            out.writeInt(parameterAnnotationMethods[i].getIndex());
215            out.writeInt(parameterAnnotations[i].getOffset());
216        }
217    }
218
219    /** {@inheritDoc} */public ItemType getItemType() {
220        return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
221    }
222
223    /** {@inheritDoc} */
224    public String getConciseIdentity() {
225        return "annotation_directory_item @0x" + Integer.toHexString(getOffset());
226    }
227
228    /** {@inheritDoc} */
229    public int compareTo(AnnotationDirectoryItem o) {
230        if (!isInternable()) {
231            if (!o.isInternable()) {
232                return parent.compareTo(o.parent);
233            }
234            return -1;
235        }
236
237        if (!o.isInternable()) {
238            return 1;
239        }
240
241        return classAnnotations.compareTo(o.classAnnotations);
242    }
243
244    /**
245     * @return The annotations associated with the class
246     */
247    public AnnotationSetItem getClassAnnotations() {
248        return classAnnotations;
249    }
250
251    /**
252     * Iterates over the field annotations, calling delegate.processFieldAnnotations for each
253     * @param delegate the delegate to call
254     */
255    public void iterateFieldAnnotations(FieldAnnotationIteratorDelegate delegate) {
256        for (int i=0; i<fieldAnnotationFields.length; i++) {
257            delegate.processFieldAnnotations(fieldAnnotationFields[i], fieldAnnotations[i]);
258        }
259    }
260
261    public static interface FieldAnnotationIteratorDelegate {
262        void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations);
263    }
264
265    /**
266     * Iterates over the method annotations, calling delegate.processMethodAnnotations for each
267     * @param delegate the delegate to call
268     */
269    public void iterateMethodAnnotations(MethodAnnotationIteratorDelegate delegate) {
270        for (int i=0; i<methodAnnotationMethods.length; i++) {
271            delegate.processMethodAnnotations(methodAnnotationMethods[i], methodAnnotations[i]);
272        }
273    }
274
275    public static interface MethodAnnotationIteratorDelegate {
276        void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations);
277    }
278
279    /**
280     * Iterates over the parameter annotations, calling delegate.processParameterAnnotations for each
281     * @param delegate the delegate to call
282     */
283    public void iteratParameterAnnotations(ParameterAnnotationIteratorDelegate delegate) {
284        for (int i=0; i<parameterAnnotationMethods.length; i++) {
285            delegate.processParameterAnnotations(parameterAnnotationMethods[i], parameterAnnotations[i]);
286        }
287    }
288
289    public static interface ParameterAnnotationIteratorDelegate {
290        void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations);
291    }
292
293    /**
294     * @return true if this <code>AnnotationDirectoryItem</code> is internable. It is only internable if it has
295     * only class annotations, but no field, method or parameter annotations
296     */
297    private boolean isInternable() {
298        return classAnnotations != null &&
299               fieldAnnotations.length == 0 &&
300               methodAnnotations.length == 0 &&
301               parameterAnnotations.length == 0;
302    }
303
304    /**
305     * Sets the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated with.
306     * This is only applicable if this AnnotationDirectoryItem contains only class annotations, and no field, method
307     * or parameter annotations.
308     * @param classDefItem the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated
309     * with
310     */
311    protected void setParent(ClassDefItem classDefItem) {
312        this.parent = classDefItem;
313    }
314
315    @Override
316    public int hashCode() {
317        //an instance is only internable if it has only class annotations, but
318        //no other type of annotation
319        if (!isInternable()) {
320            return super.hashCode();
321        }
322        return classAnnotations.hashCode();
323    }
324
325    @Override
326    public boolean equals(Object o) {
327        if (this==o) {
328            return true;
329        }
330        if (o==null || !this.getClass().equals(o.getClass())) {
331            return false;
332        }
333
334        AnnotationDirectoryItem other = (AnnotationDirectoryItem)o;
335        return (this.compareTo(other) == 0);
336    }
337}
338