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.CstString;
24import com.android.dx.util.ByteArrayAnnotatedOutput;
25import com.android.dx.util.AnnotatedOutput;
26
27import java.util.Arrays;
28import java.util.Comparator;
29
30/**
31 * Single annotation, which consists of a type and a set of name-value
32 * element pairs.
33 */
34public 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     */
100    public AnnotationItem(Annotation annotation) {
101        /*
102         * The write size isn't known up-front because (the variable-lengthed)
103         * leb128 type is used to represent some things.
104         */
105        super(ALIGNMENT, -1);
106
107        if (annotation == null) {
108            throw new NullPointerException("annotation == null");
109        }
110
111        this.annotation = annotation;
112        this.type = null;
113        this.encodedForm = null;
114    }
115
116    /** {@inheritDoc} */
117    @Override
118    public ItemType itemType() {
119        return ItemType.TYPE_ANNOTATION_ITEM;
120    }
121
122    /** {@inheritDoc} */
123    @Override
124    public int hashCode() {
125        return annotation.hashCode();
126    }
127
128    /** {@inheritDoc} */
129    @Override
130    protected int compareTo0(OffsettedItem other) {
131        AnnotationItem otherAnnotation = (AnnotationItem) other;
132
133        return annotation.compareTo(otherAnnotation.annotation);
134    }
135
136    /** {@inheritDoc} */
137    @Override
138    public String toHuman() {
139        return annotation.toHuman();
140    }
141
142    /** {@inheritDoc} */
143    public void addContents(DexFile file) {
144        type = file.getTypeIds().intern(annotation.getType());
145        ValueEncoder.addContents(file, annotation);
146    }
147
148    /** {@inheritDoc} */
149    @Override
150    protected void place0(Section addedTo, int offset) {
151        // Encode the data and note the size.
152
153        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
154        ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
155
156        encoder.writeAnnotation(annotation, false);
157        encodedForm = out.toByteArray();
158
159        // Add one for the visibility byte in front of the encoded annotation.
160        setWriteSize(encodedForm.length + 1);
161    }
162
163    /**
164     * Write a (listing file) annotation for this instance to the given
165     * output, that consumes no bytes of output. This is for annotating
166     * a reference to this instance at the point of the reference.
167     *
168     * @param out {@code non-null;} where to output to
169     * @param prefix {@code non-null;} prefix for each line of output
170     */
171    public void annotateTo(AnnotatedOutput out, String prefix) {
172        out.annotate(0, prefix + "visibility: " +
173                annotation.getVisibility().toHuman());
174        out.annotate(0, prefix + "type: " + annotation.getType().toHuman());
175
176        for (NameValuePair pair : annotation.getNameValuePairs()) {
177            CstString name = pair.getName();
178            Constant value = pair.getValue();
179
180            out.annotate(0, prefix + name.toHuman() + ": " +
181                    ValueEncoder.constantToHuman(value));
182        }
183    }
184
185    /** {@inheritDoc} */
186    @Override
187    protected void writeTo0(DexFile file, AnnotatedOutput out) {
188        boolean annotates = out.annotates();
189        AnnotationVisibility visibility = annotation.getVisibility();
190
191        if (annotates) {
192            out.annotate(0, offsetString() + " annotation");
193            out.annotate(1, "  visibility: VISBILITY_" + visibility);
194        }
195
196        switch (visibility) {
197            case BUILD:   out.writeByte(VISIBILITY_BUILD); break;
198            case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
199            case SYSTEM:  out.writeByte(VISIBILITY_SYSTEM); break;
200            default: {
201                // EMBEDDED shouldn't appear at the top level.
202                throw new RuntimeException("shouldn't happen");
203            }
204        }
205
206        if (annotates) {
207            /*
208             * The output is to be annotated, so redo the work previously
209             * done by place0(), except this time annotations will actually
210             * get emitted.
211             */
212            ValueEncoder encoder = new ValueEncoder(file, out);
213            encoder.writeAnnotation(annotation, true);
214        } else {
215            out.write(encodedForm);
216        }
217    }
218}
219