summaryrefslogtreecommitdiffstats
path: root/dx/src/com/android/dx/dex/file/AnnotationItem.java
blob: 1d92247807f845d2037c4a61fa08233d532a77ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.dex.file;

import com.android.dx.rop.annotation.Annotation;
import com.android.dx.rop.annotation.AnnotationVisibility;
import com.android.dx.rop.annotation.NameValuePair;
import com.android.dx.rop.cst.Constant;
import com.android.dx.rop.cst.CstString;
import com.android.dx.util.ByteArrayAnnotatedOutput;
import com.android.dx.util.AnnotatedOutput;

import java.util.Arrays;
import java.util.Comparator;

/**
 * Single annotation, which consists of a type and a set of name-value
 * element pairs.
 */
public final class AnnotationItem extends OffsettedItem {
    /** annotation visibility constant: visible at build time only */
    private static final int VISIBILITY_BUILD = 0;

    /** annotation visibility constant: visible at runtime */
    private static final int VISIBILITY_RUNTIME = 1;

    /** annotation visibility constant: visible at runtime only to system */
    private static final int VISIBILITY_SYSTEM = 2;

    /** the required alignment for instances of this class */
    private static final int ALIGNMENT = 1;

    /** {@code non-null;} unique instance of {@link #TypeIdSorter} */
    private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();

    /** {@code non-null;} the annotation to represent */
    private final Annotation annotation;

    /**
     * {@code null-ok;} type reference for the annotation type; set during
     * {@link #addContents}
     */
    private TypeIdItem type;

    /**
     * {@code null-ok;} encoded form, ready for writing to a file; set during
     * {@link #place0}
     */
    private byte[] encodedForm;

    /**
     * Comparator that sorts (outer) instances by type id index.
     */
    private static class TypeIdSorter implements Comparator<AnnotationItem> {
        /** {@inheritDoc} */
        public int compare(AnnotationItem item1, AnnotationItem item2) {
            int index1 = item1.type.getIndex();
            int index2 = item2.type.getIndex();

            if (index1 < index2) {
                return -1;
            } else if (index1 > index2) {
                return 1;
            }

            return 0;
        }
    }

    /**
     * Sorts an array of instances, in place, by type id index,
     * ignoring all other aspects of the elements. This is only valid
     * to use after type id indices are known.
     *
     * @param array {@code non-null;} array to sort
     */
    public static void sortByTypeIdIndex(AnnotationItem[] array) {
        Arrays.sort(array, TYPE_ID_SORTER);
    }

    /**
     * Constructs an instance.
     *
     * @param annotation {@code non-null;} annotation to represent
     */
    public AnnotationItem(Annotation annotation) {
        /*
         * The write size isn't known up-front because (the variable-lengthed)
         * leb128 type is used to represent some things.
         */
        super(ALIGNMENT, -1);

        if (annotation == null) {
            throw new NullPointerException("annotation == null");
        }

        this.annotation = annotation;
        this.type = null;
        this.encodedForm = null;
    }

    /** {@inheritDoc} */
    @Override
    public ItemType itemType() {
        return ItemType.TYPE_ANNOTATION_ITEM;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        return annotation.hashCode();
    }

    /** {@inheritDoc} */
    @Override
    protected int compareTo0(OffsettedItem other) {
        AnnotationItem otherAnnotation = (AnnotationItem) other;

        return annotation.compareTo(otherAnnotation.annotation);
    }

    /** {@inheritDoc} */
    @Override
    public String toHuman() {
        return annotation.toHuman();
    }

    /** {@inheritDoc} */
    public void addContents(DexFile file) {
        type = file.getTypeIds().intern(annotation.getType());
        ValueEncoder.addContents(file, annotation);
    }

    /** {@inheritDoc} */
    @Override
    protected void place0(Section addedTo, int offset) {
        // Encode the data and note the size.

        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
        ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);

        encoder.writeAnnotation(annotation, false);
        encodedForm = out.toByteArray();

        // Add one for the visibility byte in front of the encoded annotation.
        setWriteSize(encodedForm.length + 1);
    }

    /**
     * Write a (listing file) annotation for this instance to the given
     * output, that consumes no bytes of output. This is for annotating
     * a reference to this instance at the point of the reference.
     *
     * @param out {@code non-null;} where to output to
     * @param prefix {@code non-null;} prefix for each line of output
     */
    public void annotateTo(AnnotatedOutput out, String prefix) {
        out.annotate(0, prefix + "visibility: " +
                annotation.getVisibility().toHuman());
        out.annotate(0, prefix + "type: " + annotation.getType().toHuman());

        for (NameValuePair pair : annotation.getNameValuePairs()) {
            CstString name = pair.getName();
            Constant value = pair.getValue();

            out.annotate(0, prefix + name.toHuman() + ": " +
                    ValueEncoder.constantToHuman(value));
        }
    }

    /** {@inheritDoc} */
    @Override
    protected void writeTo0(DexFile file, AnnotatedOutput out) {
        boolean annotates = out.annotates();
        AnnotationVisibility visibility = annotation.getVisibility();

        if (annotates) {
            out.annotate(0, offsetString() + " annotation");
            out.annotate(1, "  visibility: VISBILITY_" + visibility);
        }

        switch (visibility) {
            case BUILD:   out.writeByte(VISIBILITY_BUILD); break;
            case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
            case SYSTEM:  out.writeByte(VISIBILITY_SYSTEM); break;
            default: {
                // EMBEDDED shouldn't appear at the top level.
                throw new RuntimeException("shouldn't happen");
            }
        }

        if (annotates) {
            /*
             * The output is to be annotated, so redo the work previously
             * done by place0(), except this time annotations will actually
             * get emitted.
             */
            ValueEncoder encoder = new ValueEncoder(file, out);
            encoder.writeAnnotation(annotation, true);
        } else {
            out.write(encodedForm);
        }
    }
}