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.Annotation;
20import com.android.dx.rop.annotation.AnnotationVisibility;
21import com.android.dx.rop.annotation.NameValuePair;
22import com.android.dx.rop.cst.Constant;
23import com.android.dx.rop.cst.CstAnnotation;
24import com.android.dx.rop.cst.CstArray;
25import com.android.dx.rop.cst.CstUtf8;
26import com.android.dx.util.ByteArrayAnnotatedOutput;
27import com.android.dx.util.AnnotatedOutput;
28
29import java.util.Arrays;
30import java.util.Comparator;
31
32/**
33 * Single annotation, which consists of a type and a set of name-value
34 * element pairs.
35 */
36public 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    /** non-null; unique instance of {@link #TypeIdSorter} */
50    private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
51
52    /** non-null; the annotation to represent */
53    private final Annotation annotation;
54
55    /**
56     * null-ok; type reference for the annotation type; set during
57     * {@link #addContents}
58     */
59    private TypeIdItem type;
60
61    /**
62     * 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 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 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 non-null; where to output to
171     * @param prefix 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