AnnotationsDirectoryItem.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.dex.file;
18
19import com.android.dx.rop.annotation.Annotations;
20import com.android.dx.rop.annotation.AnnotationsList;
21import com.android.dx.rop.cst.CstFieldRef;
22import com.android.dx.rop.cst.CstMethodRef;
23import com.android.dx.util.AnnotatedOutput;
24import com.android.dx.util.Hex;
25
26import java.io.PrintWriter;
27import java.util.ArrayList;
28import java.util.Collections;
29
30/**
31 * Per-class directory of annotations.
32 */
33public final class AnnotationsDirectoryItem extends OffsettedItem {
34    /** the required alignment for instances of this class */
35    private static final int ALIGNMENT = 4;
36
37    /** write size of this class's header, in bytes */
38    private static final int HEADER_SIZE = 16;
39
40    /** write size of a list element, in bytes */
41    private static final int ELEMENT_SIZE = 8;
42
43    /** {@code null-ok;} the class-level annotations, if any */
44    private AnnotationSetItem classAnnotations;
45
46    /** {@code null-ok;} the annotated fields, if any */
47    private ArrayList<FieldAnnotationStruct> fieldAnnotations;
48
49    /** {@code null-ok;} the annotated methods, if any */
50    private ArrayList<MethodAnnotationStruct> methodAnnotations;
51
52    /** {@code null-ok;} the annotated parameters, if any */
53    private ArrayList<ParameterAnnotationStruct> parameterAnnotations;
54
55    /**
56     * Constructs an empty instance.
57     */
58    public AnnotationsDirectoryItem() {
59        super(ALIGNMENT, -1);
60
61        classAnnotations = null;
62        fieldAnnotations = null;
63        methodAnnotations = null;
64        parameterAnnotations = null;
65    }
66
67    /** {@inheritDoc} */
68    @Override
69    public ItemType itemType() {
70        return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
71    }
72
73    /**
74     * Returns whether this item is empty (has no contents).
75     *
76     * @return {@code true} if this item is empty, or {@code false}
77     * if not
78     */
79    public boolean isEmpty() {
80        return (classAnnotations == null) &&
81            (fieldAnnotations == null) &&
82            (methodAnnotations == null) &&
83            (parameterAnnotations == null);
84    }
85
86    /**
87     * Returns whether this item is a candidate for interning. The only
88     * interning candidates are ones that <i>only</i> have a non-null
89     * set of class annotations, with no other lists.
90     *
91     * @return {@code true} if this is an interning candidate, or
92     * {@code false} if not
93     */
94    public boolean isInternable() {
95        return (classAnnotations != null) &&
96            (fieldAnnotations == null) &&
97            (methodAnnotations == null) &&
98            (parameterAnnotations == null);
99    }
100
101    /** {@inheritDoc} */
102    @Override
103    public int hashCode() {
104        if (classAnnotations == null) {
105            return 0;
106        }
107
108        return classAnnotations.hashCode();
109    }
110
111    /**
112     * {@inheritDoc}
113     *
114     * <p><b>Note:</b>: This throws an exception if this item is not
115     * internable.</p>
116     *
117     * @see #isInternable
118     */
119    @Override
120    public int compareTo0(OffsettedItem other) {
121        if (! isInternable()) {
122            throw new UnsupportedOperationException("uninternable instance");
123        }
124
125        AnnotationsDirectoryItem otherDirectory =
126            (AnnotationsDirectoryItem) other;
127        return classAnnotations.compareTo(otherDirectory.classAnnotations);
128    }
129
130    /**
131     * Sets the direct annotations on this instance. These are annotations
132     * made on the class, per se, as opposed to on one of its members.
133     * It is only valid to call this method at most once per instance.
134     *
135     * @param annotations {@code non-null;} annotations to set for this class
136     */
137    public void setClassAnnotations(Annotations annotations) {
138        if (annotations == null) {
139            throw new NullPointerException("annotations == null");
140        }
141
142        if (classAnnotations != null) {
143            throw new UnsupportedOperationException(
144                    "class annotations already set");
145        }
146
147        classAnnotations = new AnnotationSetItem(annotations);
148    }
149
150    /**
151     * Adds a field annotations item to this instance.
152     *
153     * @param field {@code non-null;} field in question
154     * @param annotations {@code non-null;} associated annotations to add
155     */
156    public void addFieldAnnotations(CstFieldRef field,
157            Annotations annotations) {
158        if (fieldAnnotations == null) {
159            fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
160        }
161
162        fieldAnnotations.add(new FieldAnnotationStruct(field,
163                        new AnnotationSetItem(annotations)));
164    }
165
166    /**
167     * Adds a method annotations item to this instance.
168     *
169     * @param method {@code non-null;} method in question
170     * @param annotations {@code non-null;} associated annotations to add
171     */
172    public void addMethodAnnotations(CstMethodRef method,
173            Annotations annotations) {
174        if (methodAnnotations == null) {
175            methodAnnotations = new ArrayList<MethodAnnotationStruct>();
176        }
177
178        methodAnnotations.add(new MethodAnnotationStruct(method,
179                        new AnnotationSetItem(annotations)));
180    }
181
182    /**
183     * Adds a parameter annotations item to this instance.
184     *
185     * @param method {@code non-null;} method in question
186     * @param list {@code non-null;} associated list of annotation sets to add
187     */
188    public void addParameterAnnotations(CstMethodRef method,
189            AnnotationsList list) {
190        if (parameterAnnotations == null) {
191            parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
192        }
193
194        parameterAnnotations.add(new ParameterAnnotationStruct(method, list));
195    }
196
197    /**
198     * Gets the method annotations for a given method, if any. This is
199     * meant for use by debugging / dumping code.
200     *
201     * @param method {@code non-null;} the method
202     * @return {@code null-ok;} the method annotations, if any
203     */
204    public Annotations getMethodAnnotations(CstMethodRef method) {
205        if (methodAnnotations == null) {
206            return null;
207        }
208
209        for (MethodAnnotationStruct item : methodAnnotations) {
210            if (item.getMethod().equals(method)) {
211                return item.getAnnotations();
212            }
213        }
214
215        return null;
216    }
217
218    /**
219     * Gets the parameter annotations for a given method, if any. This is
220     * meant for use by debugging / dumping code.
221     *
222     * @param method {@code non-null;} the method
223     * @return {@code null-ok;} the parameter annotations, if any
224     */
225    public AnnotationsList getParameterAnnotations(CstMethodRef method) {
226        if (parameterAnnotations == null) {
227            return null;
228        }
229
230        for (ParameterAnnotationStruct item : parameterAnnotations) {
231            if (item.getMethod().equals(method)) {
232                return item.getAnnotationsList();
233            }
234        }
235
236        return null;
237    }
238
239    /** {@inheritDoc} */
240    public void addContents(DexFile file) {
241        MixedItemSection wordData = file.getWordData();
242
243        if (classAnnotations != null) {
244            classAnnotations = wordData.intern(classAnnotations);
245        }
246
247        if (fieldAnnotations != null) {
248            for (FieldAnnotationStruct item : fieldAnnotations) {
249                item.addContents(file);
250            }
251        }
252
253        if (methodAnnotations != null) {
254            for (MethodAnnotationStruct item : methodAnnotations) {
255                item.addContents(file);
256            }
257        }
258
259        if (parameterAnnotations != null) {
260            for (ParameterAnnotationStruct item : parameterAnnotations) {
261                item.addContents(file);
262            }
263        }
264    }
265
266    /** {@inheritDoc} */
267    @Override
268    public String toHuman() {
269        throw new RuntimeException("unsupported");
270    }
271
272    /** {@inheritDoc} */
273    @Override
274    protected void place0(Section addedTo, int offset) {
275        // We just need to set the write size here.
276
277        int elementCount = listSize(fieldAnnotations)
278            + listSize(methodAnnotations) + listSize(parameterAnnotations);
279        setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE));
280    }
281
282    /** {@inheritDoc} */
283    @Override
284    protected void writeTo0(DexFile file, AnnotatedOutput out) {
285        boolean annotates = out.annotates();
286        int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations);
287        int fieldsSize = listSize(fieldAnnotations);
288        int methodsSize = listSize(methodAnnotations);
289        int parametersSize = listSize(parameterAnnotations);
290
291        if (annotates) {
292            out.annotate(0, offsetString() + " annotations directory");
293            out.annotate(4, "  class_annotations_off: " + Hex.u4(classOff));
294            out.annotate(4, "  fields_size:           " +
295                    Hex.u4(fieldsSize));
296            out.annotate(4, "  methods_size:          " +
297                    Hex.u4(methodsSize));
298            out.annotate(4, "  parameters_size:       " +
299                    Hex.u4(parametersSize));
300        }
301
302        out.writeInt(classOff);
303        out.writeInt(fieldsSize);
304        out.writeInt(methodsSize);
305        out.writeInt(parametersSize);
306
307        if (fieldsSize != 0) {
308            Collections.sort(fieldAnnotations);
309            if (annotates) {
310                out.annotate(0, "  fields:");
311            }
312            for (FieldAnnotationStruct item : fieldAnnotations) {
313                item.writeTo(file, out);
314            }
315        }
316
317        if (methodsSize != 0) {
318            Collections.sort(methodAnnotations);
319            if (annotates) {
320                out.annotate(0, "  methods:");
321            }
322            for (MethodAnnotationStruct item : methodAnnotations) {
323                item.writeTo(file, out);
324            }
325        }
326
327        if (parametersSize != 0) {
328            Collections.sort(parameterAnnotations);
329            if (annotates) {
330                out.annotate(0, "  parameters:");
331            }
332            for (ParameterAnnotationStruct item : parameterAnnotations) {
333                item.writeTo(file, out);
334            }
335        }
336    }
337
338    /**
339     * Gets the list size of the given list, or {@code 0} if given
340     * {@code null}.
341     *
342     * @param list {@code null-ok;} the list in question
343     * @return {@code >= 0;} its size
344     */
345    private static int listSize(ArrayList<?> list) {
346        if (list == null) {
347            return 0;
348        }
349
350        return list.size();
351    }
352
353    /**
354     * Prints out the contents of this instance, in a debugging-friendly
355     * way. This is meant to be called from {@link ClassDefItem#debugPrint}.
356     *
357     * @param out {@code non-null;} where to output to
358     */
359    /*package*/ void debugPrint(PrintWriter out) {
360        if (classAnnotations != null) {
361            out.println("  class annotations: " + classAnnotations);
362        }
363
364        if (fieldAnnotations != null) {
365            out.println("  field annotations:");
366            for (FieldAnnotationStruct item : fieldAnnotations) {
367                out.println("    " + item.toHuman());
368            }
369        }
370
371        if (methodAnnotations != null) {
372            out.println("  method annotations:");
373            for (MethodAnnotationStruct item : methodAnnotations) {
374                out.println("    " + item.toHuman());
375            }
376        }
377
378        if (parameterAnnotations != null) {
379            out.println("  parameter annotations:");
380            for (ParameterAnnotationStruct item : parameterAnnotations) {
381                out.println("    " + item.toHuman());
382            }
383        }
384    }
385}
386