Home | History | Annotate | Download | only in file
      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 
     17 package com.android.dexgen.dex.file;
     18 
     19 import com.android.dexgen.rop.annotation.Annotations;
     20 import com.android.dexgen.rop.annotation.AnnotationsList;
     21 import com.android.dexgen.rop.cst.CstFieldRef;
     22 import com.android.dexgen.rop.cst.CstMethodRef;
     23 import com.android.dexgen.util.AnnotatedOutput;
     24 import com.android.dexgen.util.Hex;
     25 
     26 import java.io.PrintWriter;
     27 import java.util.ArrayList;
     28 import java.util.Collections;
     29 
     30 /**
     31  * Per-class directory of annotations.
     32  */
     33 public 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