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.Annotation;
     20 import com.android.dexgen.rop.annotation.AnnotationVisibility;
     21 import com.android.dexgen.rop.annotation.NameValuePair;
     22 import com.android.dexgen.rop.cst.Constant;
     23 import com.android.dexgen.rop.cst.CstAnnotation;
     24 import com.android.dexgen.rop.cst.CstArray;
     25 import com.android.dexgen.rop.cst.CstUtf8;
     26 import com.android.dexgen.util.AnnotatedOutput;
     27 import com.android.dexgen.util.ByteArrayAnnotatedOutput;
     28 
     29 import java.util.Arrays;
     30 import java.util.Comparator;
     31 
     32 /**
     33  * Single annotation, which consists of a type and a set of name-value
     34  * element pairs.
     35  */
     36 public final class AnnotationItem extends OffsettedItem {
     37     /** annotation visibility constant: visible at build time only */
     38     private static final int VISIBILITY_BUILD = 0;
     39 
     40     /** annotation visibility constant: visible at runtime */
     41     private static final int VISIBILITY_RUNTIME = 1;
     42 
     43     /** annotation visibility constant: visible at runtime only to system */
     44     private static final int VISIBILITY_SYSTEM = 2;
     45 
     46     /** the required alignment for instances of this class */
     47     private static final int ALIGNMENT = 1;
     48 
     49     /** {@code non-null;} unique instance of {@link #TypeIdSorter} */
     50     private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
     51 
     52     /** {@code non-null;} the annotation to represent */
     53     private final Annotation annotation;
     54 
     55     /**
     56      * {@code null-ok;} type reference for the annotation type; set during
     57      * {@link #addContents}
     58      */
     59     private TypeIdItem type;
     60 
     61     /**
     62      * {@code null-ok;} encoded form, ready for writing to a file; set during
     63      * {@link #place0}
     64      */
     65     private byte[] encodedForm;
     66 
     67     /**
     68      * Comparator that sorts (outer) instances by type id index.
     69      */
     70     private static class TypeIdSorter implements Comparator<AnnotationItem> {
     71         /** {@inheritDoc} */
     72         public int compare(AnnotationItem item1, AnnotationItem item2) {
     73             int index1 = item1.type.getIndex();
     74             int index2 = item2.type.getIndex();
     75 
     76             if (index1 < index2) {
     77                 return -1;
     78             } else if (index1 > index2) {
     79                 return 1;
     80             }
     81 
     82             return 0;
     83         }
     84     }
     85 
     86     /**
     87      * Sorts an array of instances, in place, by type id index,
     88      * ignoring all other aspects of the elements. This is only valid
     89      * to use after type id indices are known.
     90      *
     91      * @param array {@code non-null;} array to sort
     92      */
     93     public static void sortByTypeIdIndex(AnnotationItem[] array) {
     94         Arrays.sort(array, TYPE_ID_SORTER);
     95     }
     96 
     97     /**
     98      * Constructs an instance.
     99      *
    100      * @param annotation {@code non-null;} annotation to represent
    101      */
    102     public AnnotationItem(Annotation annotation) {
    103         /*
    104          * The write size isn't known up-front because (the variable-lengthed)
    105          * leb128 type is used to represent some things.
    106          */
    107         super(ALIGNMENT, -1);
    108 
    109         if (annotation == null) {
    110             throw new NullPointerException("annotation == null");
    111         }
    112 
    113         this.annotation = annotation;
    114         this.type = null;
    115         this.encodedForm = null;
    116     }
    117 
    118     /** {@inheritDoc} */
    119     @Override
    120     public ItemType itemType() {
    121         return ItemType.TYPE_ANNOTATION_ITEM;
    122     }
    123 
    124     /** {@inheritDoc} */
    125     @Override
    126     public int hashCode() {
    127         return annotation.hashCode();
    128     }
    129 
    130     /** {@inheritDoc} */
    131     @Override
    132     protected int compareTo0(OffsettedItem other) {
    133         AnnotationItem otherAnnotation = (AnnotationItem) other;
    134 
    135         return annotation.compareTo(otherAnnotation.annotation);
    136     }
    137 
    138     /** {@inheritDoc} */
    139     @Override
    140     public String toHuman() {
    141         return annotation.toHuman();
    142     }
    143 
    144     /** {@inheritDoc} */
    145     public void addContents(DexFile file) {
    146         type = file.getTypeIds().intern(annotation.getType());
    147         ValueEncoder.addContents(file, annotation);
    148     }
    149 
    150     /** {@inheritDoc} */
    151     @Override
    152     protected void place0(Section addedTo, int offset) {
    153         // Encode the data and note the size.
    154 
    155         ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
    156         ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
    157 
    158         encoder.writeAnnotation(annotation, false);
    159         encodedForm = out.toByteArray();
    160 
    161         // Add one for the visibility byte in front of the encoded annotation.
    162         setWriteSize(encodedForm.length + 1);
    163     }
    164 
    165     /**
    166      * Write a (listing file) annotation for this instance to the given
    167      * output, that consumes no bytes of output. This is for annotating
    168      * a reference to this instance at the point of the reference.
    169      *
    170      * @param out {@code non-null;} where to output to
    171      * @param prefix {@code non-null;} prefix for each line of output
    172      */
    173     public void annotateTo(AnnotatedOutput out, String prefix) {
    174         out.annotate(0, prefix + "visibility: " +
    175                 annotation.getVisibility().toHuman());
    176         out.annotate(0, prefix + "type: " + annotation.getType().toHuman());
    177 
    178         for (NameValuePair pair : annotation.getNameValuePairs()) {
    179             CstUtf8 name = pair.getName();
    180             Constant value = pair.getValue();
    181 
    182             out.annotate(0, prefix + name.toHuman() + ": " +
    183                     ValueEncoder.constantToHuman(value));
    184         }
    185     }
    186 
    187     /** {@inheritDoc} */
    188     @Override
    189     protected void writeTo0(DexFile file, AnnotatedOutput out) {
    190         boolean annotates = out.annotates();
    191         AnnotationVisibility visibility = annotation.getVisibility();
    192 
    193         if (annotates) {
    194             out.annotate(0, offsetString() + " annotation");
    195             out.annotate(1, "  visibility: VISBILITY_" + visibility);
    196         }
    197 
    198         switch (visibility) {
    199             case BUILD:   out.writeByte(VISIBILITY_BUILD); break;
    200             case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
    201             case SYSTEM:  out.writeByte(VISIBILITY_SYSTEM); break;
    202             default: {
    203                 // EMBEDDED shouldn't appear at the top level.
    204                 throw new RuntimeException("shouldn't happen");
    205             }
    206         }
    207 
    208         if (annotates) {
    209             /*
    210              * The output is to be annotated, so redo the work previously
    211              * done by place0(), except this time annotations will actually
    212              * get emitted.
    213              */
    214             ValueEncoder encoder = new ValueEncoder(file, out);
    215             encoder.writeAnnotation(annotation, true);
    216         } else {
    217             out.write(encodedForm);
    218         }
    219     }
    220 }
    221