blob: 13f727a189dbf40c194447bef55cec113d820610 [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.jack.dx.dex.file;
import com.android.jack.dx.rop.annotation.Annotations;
import com.android.jack.dx.rop.annotation.AnnotationsList;
import com.android.jack.dx.rop.cst.CstFieldRef;
import com.android.jack.dx.rop.cst.CstMethodRef;
import com.android.jack.dx.util.AnnotatedOutput;
import com.android.jack.dx.util.Hex;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
/**
* Per-class directory of annotations.
*/
public final class AnnotationsDirectoryItem extends OffsettedItem {
/** the required alignment for instances of this class */
private static final int ALIGNMENT = 4;
/** write size of this class's header, in bytes */
private static final int HEADER_SIZE = 16;
/** write size of a list element, in bytes */
private static final int ELEMENT_SIZE = 8;
/** {@code null-ok;} the class-level annotations, if any */
private AnnotationSetItem classAnnotations;
/** {@code null-ok;} the annotated fields, if any */
private ArrayList<FieldAnnotationStruct> fieldAnnotations;
/** {@code null-ok;} the annotated methods, if any */
private ArrayList<MethodAnnotationStruct> methodAnnotations;
/** {@code null-ok;} the annotated parameters, if any */
private ArrayList<ParameterAnnotationStruct> parameterAnnotations;
/**
* Constructs an empty instance.
*/
public AnnotationsDirectoryItem() {
super(ALIGNMENT, -1);
classAnnotations = null;
fieldAnnotations = null;
methodAnnotations = null;
parameterAnnotations = null;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
}
/**
* Returns whether this item is empty (has no contents).
*
* @return {@code true} if this item is empty, or {@code false}
* if not
*/
public boolean isEmpty() {
return (classAnnotations == null) && (fieldAnnotations == null) && (methodAnnotations == null)
&& (parameterAnnotations == null);
}
/**
* Returns whether this item is a candidate for interning. The only
* interning candidates are ones that <i>only</i> have a non-null
* set of class annotations, with no other lists.
*
* @return {@code true} if this is an interning candidate, or
* {@code false} if not
*/
public boolean isInternable() {
return (classAnnotations != null) && (fieldAnnotations == null) && (methodAnnotations == null)
&& (parameterAnnotations == null);
}
/** {@inheritDoc} */
@Override
public int hashCode() {
if (classAnnotations == null) {
return 0;
}
return classAnnotations.hashCode();
}
/**
* {@inheritDoc}
*
* <p><b>Note:</b>: This throws an exception if this item is not
* internable.</p>
*
* @see #isInternable
*/
@Override
public int compareTo0(OffsettedItem other) {
if (!isInternable()) {
throw new UnsupportedOperationException("uninternable instance");
}
AnnotationsDirectoryItem otherDirectory = (AnnotationsDirectoryItem) other;
return classAnnotations.compareTo(otherDirectory.classAnnotations);
}
/**
* Sets the direct annotations on this instance. These are annotations
* made on the class, per se, as opposed to on one of its members.
* It is only valid to call this method at most once per instance.
*
* @param annotations {@code non-null;} annotations to set for this class
*/
public void setClassAnnotations(Annotations annotations) {
if (annotations == null) {
throw new NullPointerException("annotations == null");
}
if (classAnnotations != null) {
throw new UnsupportedOperationException("class annotations already set");
}
classAnnotations = new AnnotationSetItem(annotations);
}
/**
* Adds a field annotations item to this instance.
*
* @param field {@code non-null;} field in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addFieldAnnotations(CstFieldRef field, Annotations annotations) {
if (fieldAnnotations == null) {
fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
}
fieldAnnotations.add(new FieldAnnotationStruct(field, new AnnotationSetItem(annotations)));
}
/**
* Adds a method annotations item to this instance.
*
* @param method {@code non-null;} method in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addMethodAnnotations(CstMethodRef method, Annotations annotations) {
if (methodAnnotations == null) {
methodAnnotations = new ArrayList<MethodAnnotationStruct>();
}
methodAnnotations.add(new MethodAnnotationStruct(method, new AnnotationSetItem(annotations)));
}
/**
* Adds a parameter annotations item to this instance.
*
* @param method {@code non-null;} method in question
* @param list {@code non-null;} associated list of annotation sets to add
*/
public void addParameterAnnotations(CstMethodRef method, AnnotationsList list) {
if (parameterAnnotations == null) {
parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
}
parameterAnnotations.add(new ParameterAnnotationStruct(method, list));
}
/**
* Gets the method annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the method annotations, if any
*/
public Annotations getMethodAnnotations(CstMethodRef method) {
if (methodAnnotations == null) {
return null;
}
for (MethodAnnotationStruct item : methodAnnotations) {
if (item.getMethod().equals(method)) {
return item.getAnnotations();
}
}
return null;
}
/**
* Gets the parameter annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the parameter annotations, if any
*/
public AnnotationsList getParameterAnnotations(CstMethodRef method) {
if (parameterAnnotations == null) {
return null;
}
for (ParameterAnnotationStruct item : parameterAnnotations) {
if (item.getMethod().equals(method)) {
return item.getAnnotationsList();
}
}
return null;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
MixedItemSection wordData = file.getWordData();
if (classAnnotations != null) {
classAnnotations = wordData.intern(classAnnotations);
}
if (fieldAnnotations != null) {
for (FieldAnnotationStruct item : fieldAnnotations) {
item.addContents(file);
}
}
if (methodAnnotations != null) {
for (MethodAnnotationStruct item : methodAnnotations) {
item.addContents(file);
}
}
if (parameterAnnotations != null) {
for (ParameterAnnotationStruct item : parameterAnnotations) {
item.addContents(file);
}
}
}
/** {@inheritDoc} */
@Override
public String toHuman() {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
// We just need to set the write size here.
int elementCount =
listSize(fieldAnnotations) + listSize(methodAnnotations) + listSize(parameterAnnotations);
setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE));
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations);
int fieldsSize = listSize(fieldAnnotations);
int methodsSize = listSize(methodAnnotations);
int parametersSize = listSize(parameterAnnotations);
if (annotates) {
out.annotate(0, offsetString() + " annotations directory");
out.annotate(4, " class_annotations_off: " + Hex.u4(classOff));
out.annotate(4, " fields_size: " + Hex.u4(fieldsSize));
out.annotate(4, " methods_size: " + Hex.u4(methodsSize));
out.annotate(4, " parameters_size: " + Hex.u4(parametersSize));
}
out.writeInt(classOff);
out.writeInt(fieldsSize);
out.writeInt(methodsSize);
out.writeInt(parametersSize);
if (fieldsSize != 0) {
Collections.sort(fieldAnnotations);
if (annotates) {
out.annotate(0, " fields:");
}
for (FieldAnnotationStruct item : fieldAnnotations) {
item.writeTo(file, out);
}
}
if (methodsSize != 0) {
Collections.sort(methodAnnotations);
if (annotates) {
out.annotate(0, " methods:");
}
for (MethodAnnotationStruct item : methodAnnotations) {
item.writeTo(file, out);
}
}
if (parametersSize != 0) {
Collections.sort(parameterAnnotations);
if (annotates) {
out.annotate(0, " parameters:");
}
for (ParameterAnnotationStruct item : parameterAnnotations) {
item.writeTo(file, out);
}
}
}
/**
* Gets the list size of the given list, or {@code 0} if given
* {@code null}.
*
* @param list {@code null-ok;} the list in question
* @return {@code >= 0;} its size
*/
private static int listSize(ArrayList<?> list) {
if (list == null) {
return 0;
}
return list.size();
}
/**
* Prints out the contents of this instance, in a debugging-friendly
* way. This is meant to be called from {@link ClassDefItem#debugPrint}.
*
* @param out {@code non-null;} where to output to
*/
/*package*/void debugPrint(PrintWriter out) {
if (classAnnotations != null) {
out.println(" class annotations: " + classAnnotations);
}
if (fieldAnnotations != null) {
out.println(" field annotations:");
for (FieldAnnotationStruct item : fieldAnnotations) {
out.println(" " + item.toHuman());
}
}
if (methodAnnotations != null) {
out.println(" method annotations:");
for (MethodAnnotationStruct item : methodAnnotations) {
out.println(" " + item.toHuman());
}
}
if (parameterAnnotations != null) {
out.println(" parameter annotations:");
for (ParameterAnnotationStruct item : parameterAnnotations) {
out.println(" " + item.toHuman());
}
}
}
}