/* * 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.CstAnnotation; import com.android.dx.rop.cst.CstArray; import com.android.dx.rop.cst.CstUtf8; 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()) { CstUtf8 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); } } }