/* * 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 static com.android.dx.rop.annotation.AnnotationVisibility.SYSTEM; import com.android.dx.rop.annotation.NameValuePair; import com.android.dx.rop.cst.Constant; import com.android.dx.rop.cst.CstAnnotation; import com.android.dx.rop.cst.CstArray; import com.android.dx.rop.cst.CstInteger; import com.android.dx.rop.cst.CstKnownNull; import com.android.dx.rop.cst.CstMethodRef; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.Type; import com.android.dx.rop.type.TypeList; import java.util.ArrayList; /** * Utility class for dealing with annotations. */ public final class AnnotationUtils { /** {@code non-null;} type for {@code AnnotationDefault} annotations */ private static final CstType ANNOTATION_DEFAULT_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;")); /** {@code non-null;} type for {@code EnclosingClass} annotations */ private static final CstType ENCLOSING_CLASS_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;")); /** {@code non-null;} type for {@code EnclosingMethod} annotations */ private static final CstType ENCLOSING_METHOD_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;")); /** {@code non-null;} type for {@code InnerClass} annotations */ private static final CstType INNER_CLASS_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;")); /** {@code non-null;} type for {@code MemberClasses} annotations */ private static final CstType MEMBER_CLASSES_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;")); /** {@code non-null;} type for {@code Signature} annotations */ private static final CstType SIGNATURE_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/Signature;")); /** {@code non-null;} type for {@code SourceDebugExtension} annotations */ private static final CstType SOURCE_DEBUG_EXTENSION_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/SourceDebugExtension;")); /** {@code non-null;} type for {@code Throws} annotations */ private static final CstType THROWS_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/Throws;")); /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */ private static final CstString ACCESS_FLAGS_STRING = new CstString("accessFlags"); /** {@code non-null;} the UTF-8 constant {@code "name"} */ private static final CstString NAME_STRING = new CstString("name"); /** {@code non-null;} the UTF-8 constant {@code "value"} */ private static final CstString VALUE_STRING = new CstString("value"); /** * This class is uninstantiable. */ private AnnotationUtils() { // This space intentionally left blank. } /** * Constructs a standard {@code AnnotationDefault} annotation. * * @param defaults {@code non-null;} the defaults, itself as an annotation * @return {@code non-null;} the constructed annotation */ public static Annotation makeAnnotationDefault(Annotation defaults) { Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM); result.put(new NameValuePair(VALUE_STRING, new CstAnnotation(defaults))); result.setImmutable(); return result; } /** * Constructs a standard {@code EnclosingClass} annotation. * * @param clazz {@code non-null;} the enclosing class * @return {@code non-null;} the annotation */ public static Annotation makeEnclosingClass(CstType clazz) { Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM); result.put(new NameValuePair(VALUE_STRING, clazz)); result.setImmutable(); return result; } /** * Constructs a standard {@code EnclosingMethod} annotation. * * @param method {@code non-null;} the enclosing method * @return {@code non-null;} the annotation */ public static Annotation makeEnclosingMethod(CstMethodRef method) { Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM); result.put(new NameValuePair(VALUE_STRING, method)); result.setImmutable(); return result; } /** * Constructs a standard {@code InnerClass} annotation. * * @param name {@code null-ok;} the original name of the class, or * {@code null} to represent an anonymous class * @param accessFlags the original access flags * @return {@code non-null;} the annotation */ public static Annotation makeInnerClass(CstString name, int accessFlags) { Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM); Constant nameCst = (name != null) ? name : CstKnownNull.THE_ONE; result.put(new NameValuePair(NAME_STRING, nameCst)); result.put(new NameValuePair(ACCESS_FLAGS_STRING, CstInteger.make(accessFlags))); result.setImmutable(); return result; } /** * Constructs a standard {@code MemberClasses} annotation. * * @param types {@code non-null;} the list of (the types of) the member classes * @return {@code non-null;} the annotation */ public static Annotation makeMemberClasses(TypeList types) { CstArray array = makeCstArray(types); Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM); result.put(new NameValuePair(VALUE_STRING, array)); result.setImmutable(); return result; } /** * Constructs a standard {@code Signature} annotation. * * @param signature {@code non-null;} the signature string * @return {@code non-null;} the annotation */ public static Annotation makeSignature(CstString signature) { Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM); /* * Split the string into pieces that are likely to be common * across many signatures and the rest of the file. */ String raw = signature.getString(); int rawLength = raw.length(); ArrayList pieces = new ArrayList(20); for (int at = 0; at < rawLength; /*at*/) { char c = raw.charAt(at); int endAt = at + 1; if (c == 'L') { // Scan to ';' or '<'. Consume ';' but not '<'. while (endAt < rawLength) { c = raw.charAt(endAt); if (c == ';') { endAt++; break; } else if (c == '<') { break; } endAt++; } } else { // Scan to 'L' without consuming it. while (endAt < rawLength) { c = raw.charAt(endAt); if (c == 'L') { break; } endAt++; } } pieces.add(raw.substring(at, endAt)); at = endAt; } int size = pieces.size(); CstArray.List list = new CstArray.List(size); for (int i = 0; i < size; i++) { list.set(i, new CstString(pieces.get(i))); } list.setImmutable(); result.put(new NameValuePair(VALUE_STRING, new CstArray(list))); result.setImmutable(); return result; } /** * Constructs a standard {@code SourceDebugExtension} annotation. * * @param smapString {@code non-null;} the SMAP string associated with * @return {@code non-null;} the annotation */ public static Annotation makeSourceDebugExtension(CstString smapString) { Annotation result = new Annotation(SOURCE_DEBUG_EXTENSION_TYPE, SYSTEM); result.put(new NameValuePair(VALUE_STRING, smapString)); result.setImmutable(); return result; } /** * Constructs a standard {@code Throws} annotation. * * @param types {@code non-null;} the list of thrown types * @return {@code non-null;} the annotation */ public static Annotation makeThrows(TypeList types) { CstArray array = makeCstArray(types); Annotation result = new Annotation(THROWS_TYPE, SYSTEM); result.put(new NameValuePair(VALUE_STRING, array)); result.setImmutable(); return result; } /** * Converts a {@link TypeList} to a {@link CstArray}. * * @param types {@code non-null;} the type list * @return {@code non-null;} the corresponding array constant */ private static CstArray makeCstArray(TypeList types) { int size = types.size(); CstArray.List list = new CstArray.List(size); for (int i = 0; i < size; i++) { list.set(i, CstType.intern(types.getType(i))); } list.setImmutable(); return new CstArray(list); } }