| /******************************************************************************* |
| * Copyright (c) 2000, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.template.java; |
| |
| import java.util.Arrays; |
| |
| import org.eclipse.jdt.core.CompletionProposal; |
| 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 <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); |
| } |
| |
| StringBuilder res= new StringBuilder("("); //$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; |
| } |
| |
| public static String getQualifiedTypeName(CompletionProposal proposal) { |
| return String.valueOf(Signature.toCharArray(Signature |
| .getTypeErasure(proposal.getSignature()))); |
| } |
| |
| public static String getSimpleTypeName(CompletionProposal proposal) { |
| return Signature.getSimpleName(getQualifiedTypeName(proposal)); |
| } |
| } |