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.dx.dex.file;
     18 
     19 import com.android.dx.rop.annotation.Annotation;
     20 import com.android.dx.rop.annotation.AnnotationVisibility;
     21 import com.android.dx.rop.annotation.NameValuePair;
     22 import com.android.dx.rop.cst.Constant;
     23 import com.android.dx.rop.cst.CstString;
     24 import com.android.dx.util.AnnotatedOutput;
     25 import com.android.dx.util.ByteArrayAnnotatedOutput;
     26 
     27 import java.util.Arrays;
     28 import java.util.Comparator;
     29 
     30 /**
     31  * Single annotation, which consists of a type and a set of name-value
     32  * element pairs.
     33  */
     34 public final class AnnotationItem extends OffsettedItem {
     35     /** annotation visibility constant: visible at build time only */
     36     private static final int VISIBILITY_BUILD = 0;
     37 
     38     /** annotation visibility constant: visible at runtime */
     39     private static final int VISIBILITY_RUNTIME = 1;
     40 
     41     /** annotation visibility constant: visible at runtime only to system */
     42     private static final int VISIBILITY_SYSTEM = 2;
     43 
     44     /** the required alignment for instances of this class */
     45     private static final int ALIGNMENT = 1;
     46 
     47     /** {@code non-null;} unique instance of {@link #TypeIdSorter} */
     48     private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
     49 
     50     /** {@code non-null;} the annotation to represent */
     51     private final Annotation annotation;
     52 
     53     /**
     54      * {@code null-ok;} type reference for the annotation type; set during
     55      * {@link #addContents}
     56      */
     57     private TypeIdItem type;
     58 
     59     /**
     60      * {@code null-ok;} encoded form, ready for writing to a file; set during
     61      * {@link #place0}
     62      */
     63     private byte[] encodedForm;
     64 
     65     /**
     66      * Comparator that sorts (outer) instances by type id index.
     67      */
     68     private static class TypeIdSorter implements Comparator<AnnotationItem> {
     69         /** {@inheritDoc} */
     70         public int compare(AnnotationItem item1, AnnotationItem item2) {
     71             int index1 = item1.type.getIndex();
     72             int index2 = item2.type.getIndex();
     73 
     74             if (index1 < index2) {
     75                 return -1;
     76             } else if (index1 > index2) {
     77                 return 1;
     78             }
     79 
     80             return 0;
     81         }
     82     }
     83 
     84     /**
     85      * Sorts an array of instances, in place, by type id index,
     86      * ignoring all other aspects of the elements. This is only valid
     87      * to use after type id indices are known.
     88      *
     89      * @param array {@code non-null;} array to sort
     90      */
     91     public static void sortByTypeIdIndex(AnnotationItem[] array) {
     92         Arrays.sort(array, TYPE_ID_SORTER);
     93     }
     94 
     95     /**
     96      * Constructs an instance.
     97      *
     98      * @param annotation {@code non-null;} annotation to represent
     99      * @param dexFile {@code non-null;} dex output
    100      */
    101     public AnnotationItem(Annotation annotation, DexFile dexFile) {
    102         /*
    103          * The write size isn't known up-front because (the variable-lengthed)
    104          * leb128 type is used to represent some things.
    105          */
    106         super(ALIGNMENT, -1);
    107 
    108         if (annotation == null) {
    109             throw new NullPointerException("annotation == null");
    110         }
    111 
    112         this.annotation = annotation;
    113         this.type = null;
    114         this.encodedForm = null;
    115         addContents(dexFile);
    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             CstString 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