/* Software Name : AsmDex
* Version : 1.0
*
* Copyright © 2012 France Télécom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.ow2.asmdex.structureWriter;
import java.util.ArrayList;
import java.util.List;
import org.ow2.asmdex.Opcodes;
import org.ow2.asmdex.encodedValue.EncodedValue;
import org.ow2.asmdex.encodedValue.EncodedValueArray;
import org.ow2.asmdex.encodedValue.EncodedValueFactory;
/**
* Simple class representing one class_def_item, for the writer.
*
* The equals and hashCode methods have been overridden in order to detect easily
* duplicates in Sets. ONLY className is used to differentiate the classes.
*
* Implements Comparable in order to easily sort the Classes. The test is minimalist is is only useful as
* a first-pass sort. A second pass, after the Dex file is parsed, will be done to ensure SuperClass are
* encoded before their children.
*
* It is IMPORTANT to note that the encoded Fields/Methods are NOT sorted according to their ids, but in
* the order of appearance of the fields/methods.
*
* @author Julien Névo
*/
public class ClassDefinitionItem implements Comparable<ClassDefinitionItem>, IAnnotationsHolder {
/**
* Name of the Class.
*/
final private String className;
/**
* Access flags of the Class.
*/
final private int accessFlags;
/**
* Name of the super class if any, or Null.
*/
final private String superClassName;
/**
* TypeList containing the names of the interface if any, or Null.
*/
final private TypeList interfaceNames;
/**
* The Constant Pool of the Application.
*/
final private ConstantPool constantPool;
/**
* The source file that defines the Class.
*/
private String sourceFileName;
/**
* The Signature of this Class, or Null if there is none.
*/
final private String[] signature;
/**
* The hashcode of the Class.
*/
final private int hashCode;
/**
* Annotation_set_item, representing all the annotation_items for this Class.
* It concerns Class Annotations only, not Field, Method or Parameters Annotations.
*/
private AnnotationSetItem annotationSetItem = new AnnotationSetItem();
// Fields of class_data_item.
private ArrayList<Field> staticFields = new ArrayList<Field>();
private ArrayList<Field> instanceFields = new ArrayList<Field>();
// Methods of class_data_item.
private ArrayList<Method> directMethods = new ArrayList<Method>();
private ArrayList<Method> virtualMethods = new ArrayList<Method>();
/**
* This may, if needed, contain an EncodedArray which will contain Encoded Values to Member Classes. It will be
* filled through the visitInnerClass method in ClassWriter. All this will allow the encoding of the
* dalvik.annotation.MemberClasses Annotation.
*/
private EncodedValueArray memberClassArray;
/**
* The annotation_directory_item of the Class. It is only filled after the Class has been fully
* parsed, or stays to Null if no Annotation is present in the Class, or the Methods, Fields,
* Parameters it possesses.
*/
private AnnotationDirectoryItem annotationDirectoryItem;
/**
* List of AnnotationItems which is <i>ONLY</i> used for Default Annotations. We may encounter
* several Default Annotation while parsing the <i>Methods</i>, but they must be encoded in the Class,
* and as <i>one</i> AnnotationElement which contains one Encoded Value of VALUE_ANNOTATION type,
* which contains all the AnnotationElement.
* The list is Null if not used.
*/
private List<AnnotationItem> annotationItemsForDefaultAnnotation;
/**
* Offset in bytes of the access_flags field in the class_def_item structure.
*/
final public static int ACCESS_FLAGS_OFFSET = 4;
/**
* Offset in bytes of the superclass_idx field in the class_def_item structure.
*/
final public static int SUPERCLASS_IDX_OFFSET = 4 * 2;
/**
* Offset in bytes of the interfaces_off field in the class_def_item structure.
*/
final public static int INTERFACES_OFFSET = 4 * 3;
/**
* Offset in bytes of the source_file_idx field in the class_def_item structure.
*/
final public static int SOURCE_FILE_IDX_OFFSET = 4 * 4;
/**
* Offset in bytes of the annotations_off field in the class_def_item structure.
*/
final public static int ANNOTATIONS_OFF_OFFSET = 4 * 5;
/**
* Offset in bytes of the class_data_off field in the class_def_item structure.
*/
final public static int CLASS_DATA_OFF_OFFSET = 4 * 6;
/**
* Constructor of a ClassDefinitionItem.
* @param className the name of the Class, fully qualified.
* @param superClassName the possible name of the super Class, or Null.
* @param accessFlags the access flags.
* @param interfaceNames names of the interface, or Null.
* @param signature the Signature of this Class, or Null if there is none.
* @param constantPool the Constant Pool of this Application.
*/
public ClassDefinitionItem(String className, String superClassName, int accessFlags,
TypeList interfaceNames, String[] signature, ConstantPool constantPool) {
this.className = className;
this.accessFlags = accessFlags;
this.superClassName = superClassName;
this.interfaceNames = interfaceNames;
this.signature = signature;
this.constantPool = constantPool;
hashCode = className.hashCode();
}
// ----------------------------------------------
// Getters and Setters.
// ----------------------------------------------
/**
* Returns the name of the Class.
* @return the name of the Class.
*/
public String getClassName() {
return className;
}
/**
* Returns the Super Class of this Class. May be Null is the Class has no Super Class.
* @return the Super Class of this Class, or Null.
*/
public String getSuperClassName() {
return superClassName;
}
/**
* Returns the Interfaces used by the Class. The list it contains may be empty.
* @return the Interfaces used by the Class.
*/
public TypeList getInterfaces() {
return interfaceNames;
}
/**
* Returns the access flags of the Class.
* @return the access flags of the Class.
*/
public int getAccessFlags() {
return accessFlags;
}
/**
* Returns the source file that defines the Class. May be Null.
* @return the source file that defines the Class. May be Null.
*/
public String getSourceFileName() {
return sourceFileName;
}
/**
* Sets the source file that defines the Class. May be Null.
*
*/
public void setSourceFileName(String sourceFileName) {
this.sourceFileName = sourceFileName;
}
/**
* Returns the Signature of the Class. May be Null.
* @return the Signature of the Class. May be Null.
*/
public String[] getSignature() {
return signature;
}
/**
* Returns the direct methods this class contains.
* @return the direct methods this class contains.
*/
public ArrayList<Method> getDirectMethods() {
return directMethods;
}
/**
* Returns the virtual methods this class contains.
* @return the virtual methods this class contains.
*/
public ArrayList<Method> getVirtualMethods() {
return virtualMethods;
}
/**
* Returns the static fields of this class.
* @return the static fields of this class.
*/
public ArrayList<Field> getStaticFields() {
return staticFields;
}
/**
* Returns the instance fields of this class.
* @return the instance fields of this class.
*/
public ArrayList<Field> getInstanceFields() {
return instanceFields;
}
/**
* Returns the number of direct methods this class currently contains.
* @return the number of direct methods this class currently contains.
*/
public int getNbDirectMethods() {
return directMethods.size();
}
/**
* Returns the number of virtual methods this class currently contains.
* @return the number of virtual methods this class currently contains.
*/
public int getNbVirtualMethods() {
return virtualMethods.size();
}
/**
* Returns the number of static fields this class currently contains.
* @return the number of static fields this class currently contains.
*/
public int getNbStaticFields() {
return staticFields.size();
}
/**
* Returns the number of instance fields this class currently contains.
* @return the number of instance fields this class currently contains.
*/
public int getNbInstanceFields() {
return instanceFields.size();
}
/**
* Returns the annotation_set_item this structure currently contains.
* @return the annotation_set_item this structure currently contains.
*/
@Override
public AnnotationSetItem getAnnotationSetItem() {
return annotationSetItem;
}
/**
* Returns the number of annotation_items this structure currently contains.
* @return the number of annotation_items this structure currently contains.
*/
@Override
public int getNbAnnotations() {
return annotationSetItem.getNbAnnotationItems();
}
/**
* Returns the count of annotated Methods, whether they are direct or virtual. This number is only set when
* parsing the Class is fully done.
* @return the count of annotated Methods.
*/
public int getNbAnnotatedMethods() {
return annotationDirectoryItem == null ? 0 : annotationDirectoryItem.getNbAnnotatedMethods();
}
/**
* Returns the count of Methods that have annotated Parameters. This number is only set when
* parsing the Class is fully done.
* @return the count of Methods that have annotated Parameters.
*/
public int getNbMethodWithAnnotatedParameters() {
return annotationDirectoryItem == null ? 0 : annotationDirectoryItem.getNbAnnotatedParameters();
}
/**
* Returns the count of annotated Fields, whether they are static or instance Fields.
* This number is only set when parsing the Class is fully done.
* @return the count of annotated Fields.
*/
public int getNbAnnotatedFields() {
return annotationDirectoryItem == null ? 0 : annotationDirectoryItem.getNbAnnotatedFields();
}
/**
* Returns the Encoded Array containing the Member Classes, or null if none were added.
* @return the Encoded Array containing the Member Classes, or null if none were added.
*/
public EncodedValueArray getMemberClassArray() {
return memberClassArray;
}
/**
* Returns the annotation_directory_item, filled after the whole Class has been parsed.
* It may be Null if no Annotation is present in the Class, or the Methods, Fields,
* Parameters it possesses.
* @return the annotation_directory_item, or Null.
*/
public AnnotationDirectoryItem getAnnotationDirectoryItem() {
return annotationDirectoryItem;
}
/**
* Return the list of AnnotationItems containing each Default Annotation for this Class. It may be Null
* if none were found.
* @return the list of AnnotationItems, or Null.
*/
public List<AnnotationItem> getAnnotationItemsForDefaultAnnotation() {
return annotationItemsForDefaultAnnotation;
}
// ----------------------------------------------
// Public methods.
// ----------------------------------------------
/**
* Indicates if the Class is an Interface.
* @return true if the Class is an Interface.
*/
public boolean isInterface() {
return (accessFlags & Opcodes.ACC_INTERFACE) != 0;
}
/**
* Adds a Method (direct or virtual) to the method list of the Class.
* @param method method to add.
*/
public void addMethod(Method method) {
if (method.isVirtual()) {
virtualMethods.add(method);
} else {
directMethods.add(method);
}
}
/**
* Adds a Field to the Field list of the Class, whether is it a Static or Instance Field.
* @param field the field to add.
*/
public void addField(Field field) {
if (field.isStatic()) {
staticFields.add(field);
} else {
instanceFields.add(field);
}
}
/**
* Adds an annotation_item to the annotations_set_items.
* @param annotationItem the Annotation Item to add.
*/
public void addAnnotationItem(AnnotationItem annotationItem) {
annotationSetItem.addAnnotationItem(annotationItem);
}
/**
* Adds a Member Class Encoded Value to the Member Classes. They will be encoded as a
* dalvik.annotation.MemberClasses Annotation.
* @param memberClassType the value to add.
* @param constantPool the Constant Pool of the Application.
*/
public void addMemberClassValue(String memberClassType, ConstantPool constantPool) {
if (memberClassArray == null) {
memberClassArray = new EncodedValueArray();
}
constantPool.addTypeToConstantPool(memberClassType);
// Creates an IElementValue and adds it to the Array.
EncodedValue encodedValue = EncodedValueFactory.getEncodedValue(memberClassType, Opcodes.VALUE_TYPE);
memberClassArray.addEncodedValue(encodedValue);
}
/**
* Adds an AnnotationItem to the list of AnnotationItem of Default Annotation. It must only be
* used in that case.
* @param annotationItem the AnnotationItem to add.
*/
public void addAnnotationItemForDefaultAnnotation(AnnotationItem annotationItem) {
if (annotationItemsForDefaultAnnotation == null) {
annotationItemsForDefaultAnnotation = new ArrayList<AnnotationItem>(1);
}
annotationItemsForDefaultAnnotation.add(annotationItem);
}
/**
* Builds the annotation_directory_item. This must be done only after the Class has been fully parsed.
* The structure is kept only if it possesses at least one annotation.
*/
public void buildAnnotationDirectoryItem() {
AnnotationDirectoryItem adi = new AnnotationDirectoryItem();
adi.setClassAnnotationSetItem(annotationSetItem);
boolean foundAnnotation = (annotationSetItem.getNbAnnotationItems() > 0);
foundAnnotation |= buildAnnotationDirectoryItemForMethods(directMethods, adi);
foundAnnotation |= buildAnnotationDirectoryItemForMethods(virtualMethods, adi);
for (Field field : staticFields) {
if (field.getNbAnnotations() > 0) {
adi.addAnnotatedField(field);
foundAnnotation = true;
}
}
// Tiny redundant code, not very important.
for (Field field : instanceFields) {
if (field.getNbAnnotations() > 0) {
adi.addAnnotatedField(field);
foundAnnotation = true;
}
}
if (foundAnnotation) {
annotationDirectoryItem = adi;
constantPool.addAnnotationDirectoryItem(annotationDirectoryItem);
}
}
/**
* Adds the Methods as annotated Methods if they have annotations. Should only been called
* by buildAnnotationDirectoryItem.
* @param methods a List of Methods, whether they are direct or virtual.
* @param adi the AnnotationDirectoryItem to which to add the Methods.
* @return true if at least an annotation was found.
*/
private boolean buildAnnotationDirectoryItemForMethods(List<Method> methods, AnnotationDirectoryItem adi) {
boolean foundAnnotation = false;
for (Method method : methods) {
if (method.getNbAnnotations() > 0) {
adi.addAnnotatedMethods(method);
foundAnnotation = true;
}
if (method.getNbParameterAnnotations() > 0) {
adi.addAnnotatedParameter(method.getAnnotatedParameterSetRefList());
foundAnnotation = true;
}
}
return foundAnnotation;
}
// ----------------------------------------------
// Overridden methods.
// ----------------------------------------------
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ClassDefinitionItem) {
ClassDefinitionItem cdi = (ClassDefinitionItem)obj;
return className.equals(cdi.className);
}
return false;
}
@Override
public int compareTo(ClassDefinitionItem cdi) {
if (this == cdi) {
return 0;
}
// Tests the class name. This test is minimalist is is only useful as a first-pass sort.
// A second pass, after the Dex file is parsed, will be done to ensure SuperClass are encoded
// before their children.
return className.compareTo(cdi.className);
}
}