blob: 1d92247807f845d2037c4a61fa08233d532a77ed [file] [log] [blame]
/*
* 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.CstString;
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()) {
CstString 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);
}
}
}