/* 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;
/**
* Prototype of a method, like described in the proto_id_item structure.
*
* Implements Comparable in order to easily sort the Prototypes. Like requested by the Dex
* documentation, two prototypes are compared by their return types, and then their arguments.
*
* @author Julien Névo.
*/
public class Prototype implements Comparable<Prototype> {
/**
* Full descriptor of the Prototype, in the "TypeDescriptor" format.
*/
final private String descriptor;
/**
* String representing the whole prototype (return type and parameters) in the "ShortyDescriptor"
* format.
*/
private String shortyDescriptor;
/**
* String representing the return type, in the "TypeDescriptor" format.
*/
private String returnType;
/**
* Strings of the parameter types, in the "TypeDescriptor" format.
*/
private TypeList parameterTypes;
/**
* The hashcode of the Prototype, calculated in the Constructor.
*/
final private int hashcode;
/**
* Constructor of a Prototype. Builds the structure from a descriptor in the TypeDescriptor
* format.
* @param descriptor full descriptor in the TypeDescriptor format.
*/
public Prototype(String descriptor) {
this.descriptor = descriptor;
hashcode = calculateHashCode(descriptor);
}
/**
* Fix internal structure if this is the unique witness kept in the constantpool
*/
public void initialize() {
shortyDescriptor = getShortyDescriptorFromTypeDescriptor(descriptor);
returnType = getFirstDescriptor(descriptor);
parameterTypes = getDescriptors(descriptor, true);
}
/**
* Calculates the hashcode of the Prototype from its descriptor in the TypeDescriptor format.
* @return the hashcode of the Prototype.
*/
public static int calculateHashCode(String descriptor) {
return descriptor.hashCode();
}
// -----------------------------------------
// Getters.
// -----------------------------------------
/**
* Returns a String representing the whole prototype (return type and parameters) in the
* "TypeDescriptor" format.
* @return a String representing the whole prototype (return type and parameters) in the
* "TypeDescriptor" format.
*/
public String getDescriptor() {
return descriptor;
}
/**
* Returns a String representing the whole prototype (return type and parameters) in the
* "ShortyDescriptor" format.
* @return a String representing the whole prototype (return type and parameters) in the
* "ShortyDescriptor" format.
*/
public String getShortyDescriptor() {
return shortyDescriptor;
}
/**
* Returns a String representing the return type, in the "TypeDescriptor" format.
* @return a String representing the return type, in the "TypeDescriptor" format.
*/
public String getReturnType() {
return returnType;
}
/**
* Returns a TypeList of the parameter types, in the "TypeDescriptor" format. It may be an empty
* array.
* @return a TypeList of the parameter types, in the "TypeDescriptor" format.
*/
public TypeList getParameterTypes() {
return parameterTypes;
}
/**
* Returns the number of parameters this Prototype contains (doesn't count the return type). May be 0.
* @return the number of parameters this Prototype contains.
*/
public int getNbParameters() {
return parameterTypes.size();
}
// -----------------------------------------
// Static utility methods
// -----------------------------------------
/**
* Returns the first Type found in the descriptor given. Returns Null if the String is empty, Null or
* invalid.
* @param descriptor descriptor.
* @return the first Type found in the descriptor given, or Null if the String is empty, Null or
* invalid.
*/
public static String getFirstDescriptor(String descriptor) {
StringBuilder result = new StringBuilder(10);
if ((descriptor != null) && (descriptor.length() > 0)) {
char c = descriptor.charAt(0);
switch (c) {
case 'V':
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'J':
case 'F':
case 'D':
result.append(c);
break;
case 'L':
int i = descriptor.indexOf(';');
if (i > 0) {
result.append(descriptor.substring(0, i + 1));
}
break;
case '[':
result.append('[');
result.append(getFirstDescriptor(descriptor.substring(1)));
}
}
return result.toString();
}
/**
* Returns the size in bytes of a given type, or 0 for a Void or unknown type.
* @param type type.
* @return the size in bytes of a given type, or 0 for a Void or unknown type.
*/
public static int getSizeOfType(String type) {
int result = 0;
switch (type.charAt(0)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'F':
case 'L':
case '[':
result = 2;
break;
case 'J':
case 'D':
result = 4;
break;
}
return result;
}
/**
* Returns the sum in bytes of the size of every type of the given descriptor, skipping or not
* the return type (the first information of the descriptor).
* @param descriptor the descriptor.
* @param skipReturnType true to skip the first type found.
* @return the sum in bytes of the size of every type of the given descriptor.
*/
public static int getSizeOfDescriptor(String descriptor, boolean skipReturnType) {
int result = 0;
String foundDescriptor;
while ((descriptor != null) && (descriptor.length() > 0)) {
foundDescriptor = getFirstDescriptor(descriptor);
if (foundDescriptor == null) {
descriptor = null;
} else {
int foundResult = getSizeOfType(foundDescriptor);
if (skipReturnType) {
skipReturnType = false;
} else {
result += foundResult;
}
descriptor = descriptor.substring(foundDescriptor.length());
}
}
return result;
}
/**
* Returns a ShortyDescriptor from a given TypeDescriptor.
* @param desc the descriptor.
* @return a ShortyDescriptor.
*/
public static String getShortyDescriptorFromTypeDescriptor(String desc) {
StringBuilder result = new StringBuilder(5);
while (!desc.equals("")) {
String foundDesc = getFirstDescriptor(desc);
desc = desc.substring(foundDesc.length());
// Any reference type would be "Lxxx;" or "[xx", which means the returned String would
// have a length > 1.
if (foundDesc.length() > 1) {
result.append('L');
} else {
result.append(foundDesc);
}
}
return result.toString();
}
/**
* Returns the number of parameters (return parameter not included) in the given descriptor.
* @param desc the descriptor.
* @return the number of parameters (return parameter not included).
*/
public static int getNbParametersFromTypeDescriptor(String desc) {
return getShortyDescriptorFromTypeDescriptor(desc).length() - 1;
}
/**
* Returns a TypeList containing descriptors in the TypeDescriptor format from a descriptor also in the
* TypeDescriptor format, or an empty array if nothing was found.
* @param descriptor descriptors in the TypeDescriptor format.
* @param skipFirst true to skip the first descriptor, usually the return type.
* @return a TypeList of descriptors in the TypeDescriptor format.
*/
public static TypeList getDescriptors(String descriptor, boolean skipFirst) {
ArrayList<String> types = new ArrayList<String>();
String foundDesc;
while (!descriptor.equals("")) {
foundDesc = getFirstDescriptor(descriptor);
descriptor = descriptor.substring(foundDesc.length());
if (skipFirst) {
skipFirst = false;
} else {
types.add(foundDesc);
}
}
return new TypeList(types.toArray(new String[types.size()]));
}
// -----------------------------------------
// Overridden methods
// -----------------------------------------
@Override
public int compareTo(Prototype prototype) {
if (this == prototype) {
return 0;
}
// Tests Return type first, as Prototype are sorted by Return type.
int compare = returnType.compareTo(prototype.returnType);
if (compare != 0) {
return compare;
}
// Tests the arguments.
return parameterTypes.compareTo(prototype.parameterTypes);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof Prototype) {
Prototype secondPrototype = (Prototype)o;
return descriptor.equals(secondPrototype.descriptor);
}
return false;
}
@Override
public int hashCode() {
return hashcode;
}
}