blob: 038f14041d69ae0d7175d7dcb97c3d7a7eca8bbc [file] [log] [blame]
package org.eclipse.scout.sdk.util.signature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.scout.commons.CompareUtility;
import org.eclipse.scout.commons.StringUtility;
import org.eclipse.scout.sdk.util.internal.SdkUtilActivator;
import org.eclipse.scout.sdk.util.internal.sigcache.SignatureCache;
import org.eclipse.scout.sdk.util.signature.internal.TypeGenericMapping;
import org.eclipse.scout.sdk.util.type.TypeUtility;
public final class SignatureUtility {
private static final Pattern QUALIFIED_SIG_REGEX = Pattern.compile("^([\\+\\[]*)([^\\<\\(\\;]*)(.*)$");
private static final Pattern SIG_REPLACEMENT_REGEX = Pattern.compile("[\\.\\$]{1}");
private static final Pattern PARAM_SIG_REPLACEMENT_REGEX = Pattern.compile("^([^\\:]*)\\:(.*)$");
private static final Pattern SIG_END = Pattern.compile("(^.*)\\;$");
public static final Pattern DOLLAR_REPLACEMENT_REGEX = Pattern.compile("\\$");
/**
* Character constant indicating an arbitrary array type in a signature.
* Value is <code>'|'</code>.
*/
public static final char C_ARBITRARY_ARRAY = '|';
/**
* Kind constant for a arbitrary array signature.
*
* @see #getTypeSignatureKind(String)
* @since 3.0
*/
public static final int ARBITRARY_ARRAY_SIGNATURE = 29;
private SignatureUtility() {
}
public static String unboxPrimitiveSignature(String signature) {
if (Signature.getTypeSignatureKind(signature) == Signature.BASE_TYPE_SIGNATURE) {
if (Signature.SIG_BOOLEAN.equals(signature)) {
signature = SignatureCache.createTypeSignature(Boolean.class.getName());
}
else if (Signature.SIG_BYTE.equals(signature)) {
signature = SignatureCache.createTypeSignature(Byte.class.getName());
}
else if (Signature.SIG_CHAR.equals(signature)) {
signature = SignatureCache.createTypeSignature(Character.class.getName());
}
else if (Signature.SIG_DOUBLE.equals(signature)) {
signature = SignatureCache.createTypeSignature(Double.class.getName());
}
else if (Signature.SIG_FLOAT.equals(signature)) {
signature = SignatureCache.createTypeSignature(Float.class.getName());
}
else if (Signature.SIG_INT.equals(signature)) {
signature = SignatureCache.createTypeSignature(Integer.class.getName());
}
else if (Signature.SIG_LONG.equals(signature)) {
signature = SignatureCache.createTypeSignature(Long.class.getName());
}
else if (Signature.SIG_SHORT.equals(signature)) {
signature = SignatureCache.createTypeSignature(Short.class.getName());
}
}
return signature;
}
/**
* To get the signature kind of the given signature. If a signature starts with '|' it is a arbitrary array signature
* otherwise see {@link Signature#getTypeSignatureKind(String)}.
*
* @return the signature kind.
* @see Signature#getTypeSignatureKind(String)
*/
public static int getTypeSignatureKind(String signature) {
// need a minimum 1 char
if (signature == null || signature.length() < 1) {
throw new IllegalArgumentException("signature is null or less than 1 char.");
}
char c = signature.charAt(0);
if (c == C_ARBITRARY_ARRAY) {
return ARBITRARY_ARRAY_SIGNATURE;
}
else {
return Signature.getTypeSignatureKind(signature);
}
}
private static String quoteRegexSpecialCharacters(String input) {
input = input.replace("\\", "\\\\");
input = input.replace(".", "\\.");
input = input.replace("+", "\\+");
input = input.replace("?", "\\?");
input = input.replace("^", "\\^");
input = input.replace("$", "\\$");
input = input.replace("[", "\\[");
input = input.replace("]", "\\]");
input = input.replace("(", "\\(");
input = input.replace(")", "\\)");
input = input.replace("{", "\\{");
input = input.replace("}", "\\}");
input = input.replace("*", "\\*");
input = input.replace("|", "\\|");
return input;
}
public static String getResolvedSignature(String signature, IType signatureOwner) throws CoreException {
return getResolvedSignature(signature, signatureOwner, null);
}
private static boolean endsWith(String stringToSearchIn, char charToFind) {
return stringToSearchIn != null && !stringToSearchIn.isEmpty() && stringToSearchIn.charAt(stringToSearchIn.length() - 1) == charToFind;
}
private static boolean startsWith(String stringToSearchIn, char charToFind) {
return stringToSearchIn != null && !stringToSearchIn.isEmpty() && stringToSearchIn.charAt(0) == charToFind;
}
public static String getResolvedSignature(String signature, IType signatureOwner, IType contextType) throws CoreException {
Map<String, String> genericParameters = null;
if (TypeUtility.exists(contextType) && TypeUtility.exists(signatureOwner)) {
LinkedHashMap<String, ITypeGenericMapping> collector = new LinkedHashMap<String, ITypeGenericMapping>();
resolveGenericParametersInSuperHierarchy(contextType, new String[0], contextType.newSupertypeHierarchy(new NullProgressMonitor()), collector);
ITypeGenericMapping mapping = collector.get(signatureOwner.getFullyQualifiedName());
if (mapping != null) {
genericParameters = mapping.getParameters();
}
}
if (genericParameters == null) {
genericParameters = new HashMap<String, String>(0);
}
return getResolvedSignature(signatureOwner, genericParameters, signature);
}
public static boolean isEqualSignature(String signature1, String signature2) {
if (signature1 == null && signature2 == null) {
return true;
}
else if (signature1 == null || signature2 == null) {
return false;
}
signature1 = DOLLAR_REPLACEMENT_REGEX.matcher(signature1).replaceAll(".");
signature2 = DOLLAR_REPLACEMENT_REGEX.matcher(signature2).replaceAll(".");
return signature1.equals(signature2);
}
/**
* To get the simple type reference name within a context represented by the given importValidator. Every fully
* qualified type name will be passed to the importValidator to decide if the import is already in use.
*
* @param fullyQualifiedTypeName
* e.g. java.lang.String (not a signature).
* @param importValidator
* to evaluate all fully qualified names for create an import and use simple names.
* @return the simple reference type name in the given validator scope.
* @throws CoreException
* @see ScoutSdkUtility#getSimpleTypeRefName(String, IImportValidator)
*/
public static String getTypeReferenceFromFqn(String fullyQualifiedTypeName, IImportValidator importValidator) throws CoreException {
return getTypeReference(SignatureCache.createTypeSignature(fullyQualifiedTypeName), importValidator);
}
/**
* @throws CoreException
* @see {@link ScoutSignature#getTypeReference(String, IType, IType, IImportValidator)}
*/
public static String getTypeReference(String signature, IImportValidator importValidator) throws CoreException {
return getTypeReference(signature, null, null, importValidator);
}
/**
* @throws CoreException
* @see {@link ScoutSignature#getTypeReference(String, IType, IType, IImportValidator)}
*/
public static String getTypeReference(String signature, IType signatureOwner, IImportValidator validator) throws CoreException {
return getTypeReference(signature, signatureOwner, null, validator);
}
/**
* <h4>Examples</h4> <xmp>
* getTypeReferenceImpl("Ljava.lang.String;", typeA, typeA, fullyQualifiedImpValidator)
* -> java.lang.String
* getTypeReferenceImpl("QList<?QString>;", typeA, typeA, fullyQualifiedImpValidator)
* -> java.util.List<? extends java.lang.String>
* </xmp>
*
* @param signature
* fully parameterized signature
* @param signatureOwner
* the owner of the signature used to lookup unresolved types.
* @param contextType
* must be a subtype of signature owner or the owner itself. Used to find generic variables as T. If null and
* signature contains generic types the supertype closest to java.lang.Object with the given type parameter
* is calculated.
* @param validator
* an import validator to decide simple name vs. fully qualified name.
* @return the type reference
* @throws CoreException
* @see {@link FullyQualifiedValidator}, {@link IImportValidator}, {@link SimpleImportValidator}
*/
private static String getTypeReference(String signature, IType signatureOwner, IType contextType, IImportValidator validator) throws CoreException {
StringBuilder sigBuilder = new StringBuilder();
int arrayCount = 0;
boolean isArbitraryArray = false;
switch (getTypeSignatureKind(signature)) {
case Signature.WILDCARD_TYPE_SIGNATURE:
sigBuilder.append("?");
if (signature.length() > 1) {
sigBuilder.append(" extends ");
sigBuilder.append(getTypeReference(signature.substring(1), signatureOwner, contextType, validator));
}
break;
case Signature.ARRAY_TYPE_SIGNATURE:
arrayCount = Signature.getArrayCount(signature);
sigBuilder.append(getTypeReference(signature.substring(arrayCount), signatureOwner, contextType, validator));
break;
case ARBITRARY_ARRAY_SIGNATURE:
isArbitraryArray = true;
sigBuilder.append(getTypeReference(signature.substring(1), signatureOwner, contextType, validator));
break;
case Signature.BASE_TYPE_SIGNATURE:
sigBuilder.append(Signature.getSignatureSimpleName(signature));
break;
case Signature.TYPE_VARIABLE_SIGNATURE:
// try to resolve type
String sig = findTypeParameterSignature(signature, signatureOwner, contextType);
if (CompareUtility.equals(sig, signature)) {
sigBuilder.append(sig);
}
else {
sigBuilder.append(getTypeReference(sig, signatureOwner, contextType, validator));
}
break;
default:
String[] typeArguments = Signature.getTypeArguments(signature);
signature = Signature.getTypeErasure(signature);
signature = SIG_REPLACEMENT_REGEX.matcher(signature).replaceAll(".");
if (startsWith(signature, Signature.C_UNRESOLVED)) {
// unresolved
if (signatureOwner != null) {
String simpleName = Signature.getSignatureSimpleName(signature);
String referencedTypeSignature = getReferencedTypeSignature(signatureOwner, simpleName, false);
if (referencedTypeSignature != null) {
sigBuilder.append(validator.getTypeName(referencedTypeSignature));
}
}
else {
sigBuilder.append(Signature.toString(signature));
}
}
else {
// resolved
sigBuilder.append(validator.getTypeName(signature));
}
if (typeArguments != null && typeArguments.length > 0) {
sigBuilder.append(Signature.C_GENERIC_START);
for (int i = 0; i < typeArguments.length; i++) {
if (i > 0) {
sigBuilder.append(", ");
}
sigBuilder.append(getTypeReference(typeArguments[i], signatureOwner, contextType, validator));
}
sigBuilder.append(Signature.C_GENERIC_END);
}
break;
}
for (int i = 0; i < arrayCount; i++) {
sigBuilder.append("[]");
}
if (isArbitraryArray) {
sigBuilder.append("...");
}
return sigBuilder.toString();
}
/**
* To get resolved and substituted generic parameter signatures of the method. The signature starts with
* {@link ScoutSignature#C_ARBITRARY_ARRAY} if the parameter is a arbitrary array.
*
* @param method
* a scout method
* @return an array of the parameter signatures
* @throws CoreException
*/
public static String[] getMethodParameterSignatureResolved(IMethod method) throws CoreException {
return getMethodParameterSignatureResolved(method, method.getDeclaringType());
}
/**
* To get resolved and substituted generic parameter signatures of the method. The signature starts with
* {@link ScoutSignature#C_ARBITRARY_ARRAY} if the parameter is a arbitrary array.
*
* @param jdtMethod
* @param contextType
* the type in what context the method appears, used for generic bindings.
* @return an array of the parameter signatures
* @throws CoreException
*/
public static String[] getMethodParameterSignatureResolved(IMethod jdtMethod, IType contextType) throws CoreException {
LinkedHashMap<String, ITypeGenericMapping> genericMapperCollector = new LinkedHashMap<String, ITypeGenericMapping>();
resolveGenericParametersInSuperHierarchy(contextType, new String[0], contextType.newSupertypeHierarchy(new NullProgressMonitor()), genericMapperCollector);
ITypeGenericMapping mapping = genericMapperCollector.get(jdtMethod.getDeclaringType().getFullyQualifiedName());
Map<String, String> parameters = null;
if (mapping != null) {
parameters = mapping.getParameters();
}
else {
parameters = new HashMap<String, String>(0);
}
return getMethodParameterSignatureResolved(jdtMethod, parameters);
}
public static String[] getMethodParameterSignatureResolved(IMethod jdtMethod, Map<String, String> generics) throws CoreException {
String[] methodParameterSignature = getMethodParameterSignature(jdtMethod);
IType methodOwnerType = jdtMethod.getDeclaringType();
for (int i = 0; i < methodParameterSignature.length; i++) {
methodParameterSignature[i] = getResolvedSignature(methodOwnerType, generics, methodParameterSignature[i]); // TODOmethodParameterSignature[i], methodOwnerType, contextType);
}
return methodParameterSignature;
}
/**
* The get parameter signatures of the given method. The signature starts with
* {@link ScoutSignature#C_ARBITRARY_ARRAY} if the parameter is a arbitrary array. <h5>NOTE:</h5> <b>generic types are
* not resolved use {@link ScoutSignature#getMethodParameterSignatureResolved(IMethod)} to get resolved and
* generic substituted parameter signature</b><br>
* <br>
*
* @param method
* @return
* @throws JavaModelException
*/
public static String[] getMethodParameterSignature(IMethod method) throws JavaModelException {
String[] paramNames = method.getParameterNames();
String[] paramSignatures = Arrays.copyOf(method.getParameterTypes(), method.getParameterTypes().length);
// check for ... array on last parameter
if (paramSignatures != null && paramSignatures.length > 0) {
String lastSig = paramSignatures[paramSignatures.length - 1];
String lastParamName = paramNames[paramNames.length - 1];
if (Signature.getTypeSignatureKind(lastSig) == Signature.ARRAY_TYPE_SIGNATURE) {
String source = method.getSource();
if (source != null) {
String regex = method.getElementName() + "\\s*\\(.*([\\.]{3})\\s*" + lastParamName + "\\s*\\)";
if (Pattern.compile(regex, Pattern.MULTILINE).matcher(source).find()) {
paramSignatures[paramSignatures.length - 1] = lastSig.replaceFirst("^\\[", "|");
}
}
}
}
return paramSignatures;
}
/**
* To get resolved return type signature of the given method. Generic types are substituted within the method context.
*
* @param method
* a scout method
* @return an array of the parameter signatures
* @throws CoreException
*/
public static String getReturnTypeSignatureResolved(IMethod method, IType contextType) throws CoreException {
String returnTypeSignature = method.getReturnType();
IType methodDeclaringType = method.getDeclaringType();
returnTypeSignature = getResolvedSignature(returnTypeSignature, methodDeclaringType, contextType);
return returnTypeSignature;
}
private static String ensureSourceTypeParametersAreCorrect(String signature, IType signatureOwner) throws JavaModelException {
if (!TypeUtility.exists(signatureOwner) || signatureOwner.isBinary()) {
return signature;
}
else {
ITypeParameter[] typeParameters = signatureOwner.getTypeParameters();
if (typeParameters != null && typeParameters.length > 0) {
for (ITypeParameter tp : typeParameters) {
if (CompareUtility.equals(tp.getElementName(), Signature.getSignatureSimpleName(signature))) {
return new StringBuilder().append(Signature.C_TYPE_VARIABLE).append(tp.getElementName()).append(Signature.C_SEMICOLON).toString();
}
}
}
return signature;
}
}
/**
* returns a unique identifier of a scout method. The identifier looks like
* 'methodname(param1Signature,param2Signature)'.
*
* @param method
* @return
* @throws CoreException
*/
public static String getMethodIdentifier(IMethod method) throws CoreException {
StringBuilder methodIdBuilder = new StringBuilder();
methodIdBuilder.append(method.getElementName());
methodIdBuilder.append("(");
String[] resolvedParamSignatures = getMethodParameterSignatureResolved(method, method.getDeclaringType());
for (int i = 0; i < resolvedParamSignatures.length; i++) {
methodIdBuilder.append(resolvedParamSignatures[i]);
if (i + 1 < resolvedParamSignatures.length) {
methodIdBuilder.append(",");
}
}
methodIdBuilder.append(")");
return methodIdBuilder.toString();
}
private static String findTypeParameterSignature(String typeParameterSignature, IType signatureOwner, IType contextType) throws CoreException {
if (!TypeUtility.exists(contextType) || !TypeUtility.exists(signatureOwner)) {
return typeParameterSignature;
}
String paramTypeName = Signature.getSignatureSimpleName(typeParameterSignature);
List<IType> hierarchyList = new ArrayList<IType>();
if (contextType != null) {
ITypeHierarchy superTypeHierarchy = contextType.newSupertypeHierarchy(null);
IType visitorType = contextType;
while (visitorType != null && !visitorType.equals(signatureOwner)) {
hierarchyList.add(0, visitorType);
visitorType = superTypeHierarchy.getSuperclass(visitorType);
}
}
// check requested Parameter
String[] ownerParameterSignatures = signatureOwner.getTypeParameterSignatures();
int parameterIndex = -1;
for (int i = 0; i < ownerParameterSignatures.length; i++) {
String paramSig = ownerParameterSignatures[i];
String paramName = PARAM_SIG_REPLACEMENT_REGEX.matcher(paramSig).replaceAll("$1");
paramSig = PARAM_SIG_REPLACEMENT_REGEX.matcher(paramSig).replaceAll("$2");
if (contextType == null) {
String signature = getResolvedSignature(paramSig, signatureOwner, null);
return signature;
}
else if (paramTypeName.equals(paramName)) {
parameterIndex = i;
break;
}
}
if (parameterIndex < 0) {
return SignatureCache.createTypeSignature(Object.class.getName());
}
for (IType hType : hierarchyList) {
String superClassSignature = hType.getSuperclassTypeSignature();
if (StringUtility.isNullOrEmpty(superClassSignature)) {
return SignatureCache.createTypeSignature(Object.class.getName());
}
String[] superClassParameterSignatures = Signature.getTypeArguments(superClassSignature);
if (superClassParameterSignatures.length < parameterIndex + 1) {
return SignatureCache.createTypeSignature(Object.class.getName());
}
else {
// translate
String signature = getResolvedSignature(superClassParameterSignatures[parameterIndex], hType, contextType);
return signature;
}
}
return typeParameterSignature;
}
/**
* Gets the fully qualified name of the given signature.
*
* @param signature
* @return The fully qualified name of the given signature.
*/
public static String getFullyQualifiedName(String signature) {
signature = Signature.getTypeErasure(signature);
int arrayCount = Signature.getArrayCount(signature);
if (arrayCount > 0) {
signature = signature.substring(arrayCount);
}
String fqn = Signature.toString(signature);
return fqn;
}
/**
* Checks if the given signature contains type arguments.
*
* @param sig
* The signature to check
* @return true if the given signature has type arguments, false otherwise.
*/
public static boolean isGenericSignature(String sig) {
String[] params = Signature.getTypeArguments(sig);
return params != null && params.length > 0;
}
public static String getQualifiedSignature(String signature, IType jdtType) throws JavaModelException {
if (getTypeSignatureKind(signature) == Signature.BASE_TYPE_SIGNATURE) {
return signature;
}
else {
Matcher m = QUALIFIED_SIG_REGEX.matcher(signature);
if (m.find()) {
String prefix = m.group(1);
String simpleSignature = m.group(2);
String postfix = m.group(3);
if (startsWith(simpleSignature, Signature.C_UNRESOLVED)) {
String simpleName = Signature.getSignatureSimpleName(simpleSignature + Signature.C_SEMICOLON);
String referencedTypeSignature = getReferencedTypeSignature(jdtType, simpleName, false);
if (referencedTypeSignature != null) {
simpleSignature = SIG_END.matcher(referencedTypeSignature).replaceAll("$1");
signature = prefix + simpleSignature + postfix;
}
}
String[] typeArguments = Signature.getTypeArguments(signature);
for (String typeArg : typeArguments) {
signature.replaceFirst("^([^<]*\\<.*)(" + quoteRegexSpecialCharacters(typeArg) + ")(.*)$", "$1" + getQualifiedSignature(typeArg, jdtType) + "$3");
}
}
else {
SdkUtilActivator.logWarning("could not qualify types of signature '" + signature + "'");
}
return signature;
}
}
public static void resolveGenericParametersInSuperHierarchy(String signature, String superTypeSignature, String[] interfaceSignatures, LinkedHashMap<String/*fullyQualifiedName*/, ITypeGenericMapping> collector) throws CoreException {
resolveGenericParametersInSuperHierarchy(signature, new String[0], superTypeSignature, interfaceSignatures, collector);
}
private static void resolveGenericParametersInSuperHierarchy(String signature, String[] parameterSignatures, String superTypeSignature, String[] interfaceSignatures, LinkedHashMap<String/*fullyQualifiedName*/, ITypeGenericMapping> collector) throws CoreException {
TypeGenericMapping typeDesc = new TypeGenericMapping(Signature.getSignatureQualifier(signature) + "." + Signature.getSignatureSimpleName(signature));
String[] localParameterSignatures = Signature.getTypeParameters(signature);
if (localParameterSignatures.length > 0) {
for (int i = 0; i < localParameterSignatures.length; i++) {
typeDesc.addParameter(Signature.getSignatureSimpleName(localParameterSignatures[i]), parameterSignatures[i]);
}
}
collector.put(typeDesc.getFullyQualifiedName(), typeDesc);
// super type
if (superTypeSignature != null) {
String[] superTypeParameterSignatures = new String[0];
IType superType = TypeUtility.getTypeBySignature(superTypeSignature);
if (TypeUtility.exists(superType)) {
String[] typeParameters = Signature.getTypeArguments(superTypeSignature);
superTypeParameterSignatures = new String[typeParameters.length];
for (int i = 0; i < typeParameters.length; i++) {//String typeParameter: typeParameters){
if (Signature.getTypeSignatureKind(typeParameters[i]) == Signature.TYPE_VARIABLE_SIGNATURE) {
superTypeParameterSignatures[i] = typeDesc.getParameterSignature(Signature.getSignatureSimpleName(typeParameters[i]));
}
else {
superTypeParameterSignatures[i] = typeParameters[i];
}
}
resolveGenericParametersInSuperHierarchy(superType, superTypeParameterSignatures, superType.newSupertypeHierarchy(new NullProgressMonitor()), collector);
}
}
// interfaces
if (interfaceSignatures != null) {
for (String interfaceSignature : interfaceSignatures) {
IType interfaceType = TypeUtility.getTypeBySignature(interfaceSignature);
if (TypeUtility.exists(interfaceType)) {
String[] typeParameters = Signature.getTypeParameters(interfaceSignature);
String[] intefaceTypeParameterSignatures = new String[typeParameters.length];
for (int i = 0; i < typeParameters.length; i++) {//String typeParameter: typeParameters){
if (Signature.getTypeSignatureKind(typeParameters[i]) == Signature.TYPE_VARIABLE_SIGNATURE) {
intefaceTypeParameterSignatures[i] = typeDesc.getParameterSignature(Signature.getSignatureSimpleName(typeParameters[i]));
}
}
resolveGenericParametersInSuperHierarchy(interfaceType, intefaceTypeParameterSignatures, interfaceType.newSupertypeHierarchy(new NullProgressMonitor()), collector);
}
}
}
}
public static String resolveGenericParameterInSuperHierarchy(IType startType, ITypeHierarchy superHierarchy, String genericDefiningSuperTypeFqn, String paramName) throws CoreException {
LinkedHashMap<String, ITypeGenericMapping> collector = new LinkedHashMap<String, ITypeGenericMapping>();
resolveGenericParametersInSuperHierarchy(startType, new String[]{}, superHierarchy, collector);
ITypeGenericMapping genericMapping = collector.get(genericDefiningSuperTypeFqn);
if (genericMapping != null) {
return genericMapping.getParameterSignature(paramName);
}
return null;
}
public static void resolveGenericParametersInSuperHierarchy(IType type, ITypeHierarchy hierarchy, LinkedHashMap<String/*fullyQualifiedName*/, ITypeGenericMapping> collector) throws CoreException {
resolveGenericParametersInSuperHierarchy(type, new String[]{}, hierarchy, collector);
}
private static void resolveGenericParametersInSuperHierarchy(IType type, String[] parameterSignatures, ITypeHierarchy hierarchy, LinkedHashMap<String/*fullyQualifiedName*/, ITypeGenericMapping> collector) throws CoreException {
if (!TypeUtility.exists(type)) {
return;
}
TypeGenericMapping typeDesc = new TypeGenericMapping(type.getFullyQualifiedName());
ITypeParameter[] typeParameters = type.getTypeParameters();
HashMap<String, String> paramsUnresolved = new HashMap<String, String>(typeParameters.length);
for (ITypeParameter par : typeParameters) {
String[] boundsSignatures = par.getBoundsSignatures();
if (boundsSignatures != null && boundsSignatures.length > 0) {
paramsUnresolved.put(par.getElementName(), par.getBoundsSignatures()[0]);
}
}
for (int i = 0; i < typeParameters.length; i++) {
if (parameterSignatures.length > i) {
typeDesc.addParameter(typeParameters[i].getElementName(), parameterSignatures[i]);
}
else {
String[] boundsSignatures = typeParameters[i].getBoundsSignatures();
if (boundsSignatures != null && boundsSignatures.length > 0) {
typeDesc.addParameter(typeParameters[i].getElementName(), getResolvedSignature(type, paramsUnresolved, boundsSignatures[0]));
}
else {
typeDesc.addParameter(typeParameters[i].getElementName(), SignatureCache.createTypeSignature(Object.class.getName()));
}
}
}
collector.put(typeDesc.getFullyQualifiedName(), typeDesc);
// super class
if (!Flags.isInterface(type.getFlags())) {
String superclassTypeSignature = type.getSuperclassTypeSignature();
if (StringUtility.hasText(superclassTypeSignature)) {
String[] superParameterSigs = Signature.getTypeArguments(superclassTypeSignature);
String[] superclassParameterSignatures = new String[superParameterSigs.length];
for (int i = 0; i < superclassParameterSignatures.length; i++) {
String resolvedSignature = getResolvedSignature(type, typeDesc.getParameters(), superParameterSigs[i]);
String signatureQualifier = Signature.getSignatureQualifier(resolvedSignature);
String signatureSimpleName = Signature.getSignatureSimpleName(resolvedSignature);
if (StringUtility.isNullOrEmpty(signatureQualifier) && typeDesc.getParameterSignature(signatureSimpleName) != null) {
// resolve parameter
resolvedSignature = typeDesc.getParameterSignature(signatureSimpleName);
}
superclassParameterSignatures[i] = resolvedSignature;
}
resolveGenericParametersInSuperHierarchy(hierarchy.getSuperclass(type), superclassParameterSignatures, hierarchy, collector);
}
}
// interfaces
String[] superInterfaceTypeSignatures = type.getSuperInterfaceTypeSignatures();
for (String superInterfaceTypeSignature : superInterfaceTypeSignatures) {
String[] interfaceParameterSigs = Signature.getTypeArguments(superInterfaceTypeSignature);
String[] interfaceParameterSignatures = new String[interfaceParameterSigs.length];
for (int i = 0; i < interfaceParameterSignatures.length; i++) {
String resolvedSignature = getResolvedSignature(type, typeDesc.getParameters(), interfaceParameterSigs[i]);
String signatureQualifier = Signature.getSignatureQualifier(resolvedSignature);
String signatureSimpleName = Signature.getSignatureSimpleName(resolvedSignature);
if (StringUtility.isNullOrEmpty(signatureQualifier) && typeDesc.getParameterSignature(signatureSimpleName) != null) {
// resolve parameter
resolvedSignature = typeDesc.getParameterSignature(signatureSimpleName);
}
interfaceParameterSignatures[i] = resolvedSignature;
}
resolveGenericParametersInSuperHierarchy(TypeUtility.getTypeBySignature(superInterfaceTypeSignature), interfaceParameterSignatures, hierarchy, collector);
}
}
public static String getResolvedSignature(IType contextType, Map<String, String> parameterSignatures, String unresolvedSignature) throws JavaModelException {
StringBuilder sigBuilder = new StringBuilder();
unresolvedSignature = ensureSourceTypeParametersAreCorrect(unresolvedSignature, contextType);
switch (getTypeSignatureKind(unresolvedSignature)) {
case Signature.WILDCARD_TYPE_SIGNATURE:
sigBuilder.append(unresolvedSignature.charAt(0));
if (unresolvedSignature.length() > 1) {
sigBuilder.append(getResolvedSignature(contextType, parameterSignatures, unresolvedSignature.substring(1)));
}
break;
case Signature.ARRAY_TYPE_SIGNATURE:
sigBuilder.append(Signature.C_ARRAY);
sigBuilder.append(getResolvedSignature(contextType, parameterSignatures, unresolvedSignature.substring(1)));
break;
case ARBITRARY_ARRAY_SIGNATURE:
sigBuilder.append(C_ARBITRARY_ARRAY);
sigBuilder.append(getResolvedSignature(contextType, parameterSignatures, unresolvedSignature.substring(1)));
break;
case Signature.BASE_TYPE_SIGNATURE:
if (endsWith(unresolvedSignature, Signature.C_NAME_END)) {
unresolvedSignature = unresolvedSignature.substring(0, unresolvedSignature.length() - 1);
}
sigBuilder.append(unresolvedSignature);
break;
case Signature.TYPE_VARIABLE_SIGNATURE:
// try to resolve type
String sig = parameterSignatures.get(Signature.getSignatureSimpleName(unresolvedSignature));
if (startsWith(sig, Signature.C_UNRESOLVED) && TypeUtility.exists(contextType)) {
String simpleName = Signature.getSignatureSimpleName(sig);
String referencedTypeSignature = getReferencedTypeSignature(contextType, simpleName, false);
if (referencedTypeSignature != null) {
sig = referencedTypeSignature;
}
}
sigBuilder.append(sig);
break;
case Signature.CLASS_TYPE_SIGNATURE:
String[] typeArguments = Signature.getTypeArguments(unresolvedSignature);
unresolvedSignature = Signature.getTypeErasure(unresolvedSignature);
unresolvedSignature = SIG_REPLACEMENT_REGEX.matcher(unresolvedSignature).replaceAll(".");
if (startsWith(unresolvedSignature, Signature.C_UNRESOLVED)) {
// unresolved
if (StringUtility.hasText(Signature.getSignatureQualifier(unresolvedSignature))) {
// kind of a qualified signature
IType t = TypeUtility.getTypeBySignature(unresolvedSignature);
if (TypeUtility.exists(t)) {
unresolvedSignature = SignatureCache.createTypeSignature(t.getFullyQualifiedName().replace('$', '.'));
}
}
else if (TypeUtility.exists(contextType)) {
String simpleName = Signature.getSignatureSimpleName(unresolvedSignature);
String referencedTypeSignature = getReferencedTypeSignature(contextType, simpleName, false);
if (referencedTypeSignature != null) {
unresolvedSignature = referencedTypeSignature;
}
}
}
if (endsWith(unresolvedSignature, Signature.C_NAME_END)) {
unresolvedSignature = unresolvedSignature.substring(0, unresolvedSignature.length() - 1);
}
sigBuilder.append(unresolvedSignature);
if (typeArguments != null && typeArguments.length > 0) {
sigBuilder.append(Signature.C_GENERIC_START);
for (int i = 0; i < typeArguments.length; i++) {
sigBuilder.append(getResolvedSignature(contextType, parameterSignatures, typeArguments[i]));
}
sigBuilder.append(Signature.C_GENERIC_END);
}
sigBuilder.append(Signature.C_NAME_END);
break;
default:
SdkUtilActivator.logWarning("unhandled signature type: '" + Signature.getTypeSignatureKind(unresolvedSignature) + "'");
break;
}
return sigBuilder.toString();
}
/**
* @return The resolved signature
* @see TypeUtility#getReferencedTypeFqn(IType, String, boolean)
*/
public static String getReferencedTypeSignature(IType declaringType, String typeName, boolean searchOnClassPath) throws JavaModelException {
String referencedTypeFqn = TypeUtility.getReferencedTypeFqn(declaringType, typeName, searchOnClassPath);
if (referencedTypeFqn != null) {
return SignatureCache.createTypeSignature(referencedTypeFqn);
}
return null;
}
}