/*******************************************************************************
* 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.che.ide.ext.java.jdt.internal.corext.util;
import org.eclipse.che.ide.ext.java.jdt.core.Signature;
import java.util.Arrays;
/**
* 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 <code>add</code> method of
* an instance of <code>java.util.List<? extends Number></code>.
* <p>
* The only possible value that has that type is <code>null</code>.
* </p>
* <p/>
* The representation of the null type is the signature of a type variable
* named <code>null</code> ({@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 <code>java.lang.Object</code> ({@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 <code>true</code> if <code>signature</code> is the
* signature of the <code>java.lang.Object</code> type.
*
* @param signature
* the signature
* @return <code>true</code> if <code>signature</code> is the
* signature of the <code>java.lang.Object</code> type,
* <code>false</code> otherwise
*/
public static boolean isJavaLangObject(String signature) {
return OBJECT_SIGNATURE.equals(signature);
}
/**
* Returns the upper bound of a type signature. Returns the signature of <code>java.lang.Object</code> if
* <code>signature</code> is a lower bound (<code>? super T</code>); returns
* the signature of the type <code>T</code> of an upper bound (<code>? extends T</code>)
* or <code>signature</code> itself if it is not a bound signature.
*
* @param signature
* the signature
* @return the upper bound signature of <code>signature</code>
*/
public static String getUpperBound(String signature) {
return String.valueOf(getUpperBound(signature.toCharArray()));
}
/**
* Returns the upper bound of a type signature. Returns the signature of <code>java.lang.Object</code> if
* <code>signature</code> is a lower bound (<code>? super T</code>); returns
* the signature of the type <code>T</code> of an upper bound (<code>? extends T</code>)
* or <code>signature</code> itself if it is not a bound signature.
*
* @param signature
* the signature
* @return the upper bound signature of <code>signature</code>
*/
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 <code>signature</code> is a wildcard or upper bound (<code>? extends T</code>);
* returns the signature of the type <code>T</code> of a lower bound (<code>? super T</code>)
* or <code>signature</code> itself if it is not a bound signature.
*
* @param signature
* the signature
* @return the lower bound signature of <code>signature</code>
*/
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 <code>signature</code> is a wildcard or upper bound (<code>? extends T</code>);
* returns the signature of the type <code>T</code> of a lower bound (<code>? super T</code>)
* or <code>signature</code> itself if it is not a bound signature.
*
* @param signature
* the signature
* @return the lower bound signature of <code>signature</code>
*/
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
// * <code>signature</code>.
// *
// * @param signature the signature to qualify
// * @param context the type inside which an unqualified type will be
// * resolved to find the qualifier, or <code>null</code> 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
* <code>[< typeVariableName : formalTypeDecl >] ( paramTypeSig1* ) retTypeSig</code>
* and returns it with any parameter signatures filtered through
* <code>getLowerBound</code> and the return type filtered through
* <code>getUpperBound</code>. Any preceding formal type variable
* declarations are removed.
* <p>
* TODO this is a temporary workaround for
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=83600
* </p>
*
* @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;
}
}