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 static com.android.dx.rop.annotation.AnnotationVisibility.SYSTEM;
20
21import com.android.dx.rop.annotation.Annotation;
22import com.android.dx.rop.annotation.NameValuePair;
23import com.android.dx.rop.cst.Constant;
24import com.android.dx.rop.cst.CstAnnotation;
25import com.android.dx.rop.cst.CstArray;
26import com.android.dx.rop.cst.CstInteger;
27import com.android.dx.rop.cst.CstKnownNull;
28import com.android.dx.rop.cst.CstMethodRef;
29import com.android.dx.rop.cst.CstString;
30import com.android.dx.rop.cst.CstType;
31import com.android.dx.rop.type.Type;
32import com.android.dx.rop.type.TypeList;
33
34import java.util.ArrayList;
35
36/**
37 * Utility class for dealing with annotations.
38 */
39public final class AnnotationUtils {
40
41    /** {@code non-null;} type for {@code AnnotationDefault} annotations */
42    private static final CstType ANNOTATION_DEFAULT_TYPE =
43        CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;"));
44
45    /** {@code non-null;} type for {@code EnclosingClass} annotations */
46    private static final CstType ENCLOSING_CLASS_TYPE =
47        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;"));
48
49    /** {@code non-null;} type for {@code EnclosingMethod} annotations */
50    private static final CstType ENCLOSING_METHOD_TYPE =
51        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;"));
52
53    /** {@code non-null;} type for {@code InnerClass} annotations */
54    private static final CstType INNER_CLASS_TYPE =
55        CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;"));
56
57    /** {@code non-null;} type for {@code MemberClasses} annotations */
58    private static final CstType MEMBER_CLASSES_TYPE =
59        CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;"));
60
61    /** {@code non-null;} type for {@code Signature} annotations */
62    private static final CstType SIGNATURE_TYPE =
63        CstType.intern(Type.intern("Ldalvik/annotation/Signature;"));
64
65    /** {@code non-null;} type for {@code Throws} annotations */
66    private static final CstType THROWS_TYPE =
67        CstType.intern(Type.intern("Ldalvik/annotation/Throws;"));
68
69    /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */
70    private static final CstString ACCESS_FLAGS_STRING = new CstString("accessFlags");
71
72    /** {@code non-null;} the UTF-8 constant {@code "name"} */
73    private static final CstString NAME_STRING = new CstString("name");
74
75    /** {@code non-null;} the UTF-8 constant {@code "value"} */
76    private static final CstString VALUE_STRING = new CstString("value");
77
78    /**
79     * This class is uninstantiable.
80     */
81    private AnnotationUtils() {
82        // This space intentionally left blank.
83    }
84
85    /**
86     * Constructs a standard {@code AnnotationDefault} annotation.
87     *
88     * @param defaults {@code non-null;} the defaults, itself as an annotation
89     * @return {@code non-null;} the constructed annotation
90     */
91    public static Annotation makeAnnotationDefault(Annotation defaults) {
92        Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM);
93
94        result.put(new NameValuePair(VALUE_STRING, new CstAnnotation(defaults)));
95        result.setImmutable();
96        return result;
97    }
98
99    /**
100     * Constructs a standard {@code EnclosingClass} annotation.
101     *
102     * @param clazz {@code non-null;} the enclosing class
103     * @return {@code non-null;} the annotation
104     */
105    public static Annotation makeEnclosingClass(CstType clazz) {
106        Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM);
107
108        result.put(new NameValuePair(VALUE_STRING, clazz));
109        result.setImmutable();
110        return result;
111    }
112
113    /**
114     * Constructs a standard {@code EnclosingMethod} annotation.
115     *
116     * @param method {@code non-null;} the enclosing method
117     * @return {@code non-null;} the annotation
118     */
119    public static Annotation makeEnclosingMethod(CstMethodRef method) {
120        Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM);
121
122        result.put(new NameValuePair(VALUE_STRING, method));
123        result.setImmutable();
124        return result;
125    }
126
127    /**
128     * Constructs a standard {@code InnerClass} annotation.
129     *
130     * @param name {@code null-ok;} the original name of the class, or
131     * {@code null} to represent an anonymous class
132     * @param accessFlags the original access flags
133     * @return {@code non-null;} the annotation
134     */
135    public static Annotation makeInnerClass(CstString name, int accessFlags) {
136        Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM);
137        Constant nameCst = (name != null) ? name : CstKnownNull.THE_ONE;
138
139        result.put(new NameValuePair(NAME_STRING, nameCst));
140        result.put(new NameValuePair(ACCESS_FLAGS_STRING,
141                        CstInteger.make(accessFlags)));
142        result.setImmutable();
143        return result;
144    }
145
146    /**
147     * Constructs a standard {@code MemberClasses} annotation.
148     *
149     * @param types {@code non-null;} the list of (the types of) the member classes
150     * @return {@code non-null;} the annotation
151     */
152    public static Annotation makeMemberClasses(TypeList types) {
153        CstArray array = makeCstArray(types);
154        Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM);
155        result.put(new NameValuePair(VALUE_STRING, array));
156        result.setImmutable();
157        return result;
158    }
159
160    /**
161     * Constructs a standard {@code Signature} annotation.
162     *
163     * @param signature {@code non-null;} the signature string
164     * @return {@code non-null;} the annotation
165     */
166    public static Annotation makeSignature(CstString signature) {
167        Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM);
168
169        /*
170         * Split the string into pieces that are likely to be common
171         * across many signatures and the rest of the file.
172         */
173
174        String raw = signature.getString();
175        int rawLength = raw.length();
176        ArrayList<String> pieces = new ArrayList<String>(20);
177
178        for (int at = 0; at < rawLength; /*at*/) {
179            char c = raw.charAt(at);
180            int endAt = at + 1;
181            if (c == 'L') {
182                // Scan to ';' or '<'. Consume ';' but not '<'.
183                while (endAt < rawLength) {
184                    c = raw.charAt(endAt);
185                    if (c == ';') {
186                        endAt++;
187                        break;
188                    } else if (c == '<') {
189                        break;
190                    }
191                    endAt++;
192                }
193            } else {
194                // Scan to 'L' without consuming it.
195                while (endAt < rawLength) {
196                    c = raw.charAt(endAt);
197                    if (c == 'L') {
198                        break;
199                    }
200                    endAt++;
201                }
202            }
203
204            pieces.add(raw.substring(at, endAt));
205            at = endAt;
206        }
207
208        int size = pieces.size();
209        CstArray.List list = new CstArray.List(size);
210
211        for (int i = 0; i < size; i++) {
212            list.set(i, new CstString(pieces.get(i)));
213        }
214
215        list.setImmutable();
216
217        result.put(new NameValuePair(VALUE_STRING, new CstArray(list)));
218        result.setImmutable();
219        return result;
220    }
221
222    /**
223     * Constructs a standard {@code Throws} annotation.
224     *
225     * @param types {@code non-null;} the list of thrown types
226     * @return {@code non-null;} the annotation
227     */
228    public static Annotation makeThrows(TypeList types) {
229        CstArray array = makeCstArray(types);
230        Annotation result = new Annotation(THROWS_TYPE, SYSTEM);
231        result.put(new NameValuePair(VALUE_STRING, array));
232        result.setImmutable();
233        return result;
234    }
235
236    /**
237     * Converts a {@link TypeList} to a {@link CstArray}.
238     *
239     * @param types {@code non-null;} the type list
240     * @return {@code non-null;} the corresponding array constant
241     */
242    private static CstArray makeCstArray(TypeList types) {
243        int size = types.size();
244        CstArray.List list = new CstArray.List(size);
245
246        for (int i = 0; i < size; i++) {
247            list.set(i, CstType.intern(types.getType(i)));
248        }
249
250        list.setImmutable();
251        return new CstArray(list);
252    }
253}
254