--- /dev/null
+/*
+ * 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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotation;
+import com.android.dexgen.rop.annotation.AnnotationVisibility;
+import com.android.dexgen.rop.annotation.NameValuePair;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstAnnotation;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+
+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);
+ }
+ }
+}