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