/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.template.java;
import java.util.Arrays;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
/**
* Utilities for Signature operations.
*
* @see Signature
* @since 3.1
*/
public final class SignatureUtil {
/**
* The signature of the null type. This type does not really exist in the
* type system. It represents the bound of type variables that have no lower
* bound, for example the parameter type to the add
method of
* an instance of java.util.List<? extends Number>
.
*
* The only possible value that has that type is null
.
*
* The representation of the null type is the signature of a type variable
* named null
({@value}), which will only work if no such
* variable gets declared in the same context.
*/
private static final String NULL_TYPE_SIGNATURE= "Tnull;"; //$NON-NLS-1$
private static final char[] NULL_TYPE_SIGNATURE_ARRAY= NULL_TYPE_SIGNATURE.toCharArray();
/**
* The signature of java.lang.Object
({@value}).
*/
private static final String OBJECT_SIGNATURE= "Ljava.lang.Object;"; //$NON-NLS-1$
private static final char[] OBJECT_SIGNATURE_ARRAY= OBJECT_SIGNATURE.toCharArray();
private SignatureUtil() {
// do not instantiate
}
/**
* Returns true
if signature
is the
* signature of the java.lang.Object
type.
*
* @param signature the signature
* @return true
if signature
is the
* signature of the java.lang.Object
type,
* false
otherwise
*/
public static boolean isJavaLangObject(String signature) {
return OBJECT_SIGNATURE.equals(signature);
}
/**
* Returns the upper bound of a type signature. Returns the signature of java.lang.Object
if
* signature
is a lower bound (? super T
); returns
* the signature of the type T
of an upper bound (? extends T
)
* or signature
itself if it is not a bound signature.
*
* @param signature the signature
* @return the upper bound signature of signature
*/
public static String getUpperBound(String signature) {
return String.valueOf(getUpperBound(signature.toCharArray()));
}
/**
* Returns the upper bound of a type signature. Returns the signature of java.lang.Object
if
* signature
is a lower bound (? super T
); returns
* the signature of the type T
of an upper bound (? extends T
)
* or signature
itself if it is not a bound signature.
*
* @param signature the signature
* @return the upper bound signature of signature
*/
public static char[] getUpperBound(char[] signature) {
if (signature.length < 1)
return signature;
if (signature[0] == Signature.C_STAR)
return OBJECT_SIGNATURE_ARRAY;
int superIndex= indexOf(signature, Signature.C_SUPER);
if (superIndex == 0)
return OBJECT_SIGNATURE_ARRAY;
if (superIndex != -1) {
char afterSuper= signature[superIndex + 1];
if (afterSuper == Signature.C_STAR) {
char[] type= new char[signature.length - 1];
System.arraycopy(signature, 0, type, 0, superIndex);
type[superIndex]= Signature.C_STAR;
System.arraycopy(signature, superIndex + 2, type, superIndex + 1, signature.length - superIndex - 2);
return getUpperBound(type);
}
if (afterSuper == Signature.C_EXTENDS) {
int typeEnd= typeEnd(signature, superIndex + 1);
char[] type= new char[signature.length - (typeEnd - superIndex - 1)];
System.arraycopy(signature, 0, type, 0, superIndex);
type[superIndex]= Signature.C_STAR;
System.arraycopy(signature, typeEnd, type, superIndex + 1, signature.length - typeEnd);
return getUpperBound(type);
}
}
if (signature[0] == Signature.C_EXTENDS) {
char[] type= new char[signature.length - 1];
System.arraycopy(signature, 1, type, 0, signature.length - 1);
return type;
}
return signature;
}
/**
* Returns the lower bound of a type signature. Returns the null type
* signature if signature
is a wildcard or upper bound (? extends T
);
* returns the signature of the type T
of a lower bound (? super T
)
* or signature
itself if it is not a bound signature.
*
* @param signature the signature
* @return the lower bound signature of signature
*/
public static String getLowerBound(String signature) {
return String.valueOf(getLowerBound(signature.toCharArray()));
}
/**
* Returns the lower bound of a type signature. Returns the null type
* signature if signature
is a wildcard or upper bound (? extends T
);
* returns the signature of the type T
of a lower bound (? super T
)
* or signature
itself if it is not a bound signature.
*
* @param signature the signature
* @return the lower bound signature of signature
*/
public static char[] getLowerBound(char[] signature) {
if (signature.length < 1)
return signature;
if (signature.length == 1 && signature[0] == Signature.C_STAR)
return signature;
int superIndex= indexOf(signature, Signature.C_EXTENDS);
if (superIndex == 0)
return NULL_TYPE_SIGNATURE_ARRAY;
if (superIndex != -1) {
char afterSuper= signature[superIndex + 1];
if (afterSuper == Signature.C_STAR || afterSuper == Signature.C_EXTENDS)
// impossible captured type
return NULL_TYPE_SIGNATURE_ARRAY;
}
char[][] typeArguments= Signature.getTypeArguments(signature);
for (int i= 0; i < typeArguments.length; i++)
if (Arrays.equals(typeArguments[i], NULL_TYPE_SIGNATURE_ARRAY))
return NULL_TYPE_SIGNATURE_ARRAY;
if (signature[0] == Signature.C_SUPER) {
char[] type= new char[signature.length - 1];
System.arraycopy(signature, 1, type, 0, signature.length - 1);
return type;
}
return signature;
}
private static int indexOf(char[] signature, char ch) {
for (int i= 0; i < signature.length; i++) {
if (signature[i] == ch)
return i;
}
return -1;
}
/**
* Returns the fully qualified type name of the given signature, with any
* type parameters and arrays erased.
*
* @param signature the signature
* @return the fully qualified type name of the signature
* @throws IllegalArgumentException if the signature is syntactically incorrect
*/
public static String stripSignatureToFQN(String signature) throws IllegalArgumentException {
signature= Signature.getTypeErasure(signature);
signature= Signature.getElementType(signature);
return Signature.toString(signature);
}
/**
* Returns the qualified signature corresponding to
* signature
.
*
* @param signature the signature to qualify
* @param context the type inside which an unqualified type will be
* resolved to find the qualifier, or null
if no
* context is available
* @return the qualified signature
*/
public static String qualifySignature(final String signature, final IType context) {
if (context == null)
return signature;
String qualifier= Signature.getSignatureQualifier(signature);
if (qualifier.length() > 0)
return signature;
String elementType= Signature.getElementType(signature);
String erasure= Signature.getTypeErasure(elementType);
String simpleName= Signature.getSignatureSimpleName(erasure);
String genericSimpleName= Signature.getSignatureSimpleName(elementType);
int dim= Signature.getArrayCount(signature);
try {
String[][] strings= context.resolveType(simpleName);
if (strings != null && strings.length > 0)
qualifier= strings[0][0];
} catch (JavaModelException e) {
// ignore - not found
}
if (qualifier.length() == 0)
return signature;
String qualifiedType= Signature.toQualifiedName(new String[] {qualifier, genericSimpleName});
String qualifiedSignature= Signature.createTypeSignature(qualifiedType, true);
String newSignature= Signature.createArraySignature(qualifiedSignature, dim);
return newSignature;
}
/**
* Takes a method signature
* [< typeVariableName : formalTypeDecl >] ( paramTypeSig1* ) retTypeSig
* and returns it with any parameter signatures filtered through
* getLowerBound
and the return type filtered through
* getUpperBound
. Any preceding formal type variable
* declarations are removed.
*
* TODO this is a temporary workaround for * https://bugs.eclipse.org/bugs/show_bug.cgi?id=83600 *
* * @param signature the method signature to convert * @return the signature with no bounded types */ public static char[] unboundedSignature(char[] signature) { if (signature == null || signature.length < 2) return signature; final boolean BUG_83600= true; // XXX the signatures from CompletionRequestor contain a superfluous '+' // before type parameters to parameter types if (BUG_83600) { signature= fix83600(signature); } StringBuffer res= new StringBuffer("("); //$NON-NLS-1$ char[][] parameters= Signature.getParameterTypes(signature); for (int i= 0; i < parameters.length; i++) { char[] param= parameters[i]; res.append(getLowerBound(param)); } res.append(')'); res.append(getUpperBound(Signature.getReturnType(signature))); return res.toString().toCharArray(); } /** * TODO this is a temporary workaround for * https://bugs.eclipse.org/bugs/show_bug.cgi?id=83600 and * https://bugs.eclipse.org/bugs/show_bug.cgi?id=85293 * * @param signature the method signature to convert * @return the fixed signature */ public static char[] fix83600(char[] signature) { if (signature == null || signature.length < 2) return signature; return Signature.removeCapture(signature); } private static int typeEnd(char[] signature, int pos) { int depth= 0; while (pos < signature.length) { switch (signature[pos]) { case Signature.C_GENERIC_START: depth++; break; case Signature.C_GENERIC_END: if (depth == 0) return pos; depth--; break; case Signature.C_SEMICOLON: if (depth == 0) return pos + 1; break; } pos++; } return pos + 1; } }