blob: 5090f565e24eea4b297f892bc0fe0d451179fbc4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2013 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
* Stephan Herrmann - Contribution for
* Bug 425183 - [1.8][inference] make CaptureBinding18 safe
* Bug 462025 - [null][test] create tests for manipulating external null annotations
*******************************************************************************/
package org.eclipse.jdt.internal.core.util;
import java.util.ArrayList;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.codegen.ConstantPool;
/*
* Converts a binding key into a signature
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class KeyToSignature extends BindingKeyParser {
public static final int SIGNATURE = 0;
public static final int TYPE_ARGUMENTS = 1;
public static final int DECLARING_TYPE = 2;
public static final int THROWN_EXCEPTIONS = 3;
public StringBuffer signature = new StringBuffer();
private int kind;
private boolean asBinarySignature = false; // '.' vs. '/' and '$'
private ArrayList arguments = new ArrayList();
private ArrayList typeArguments = new ArrayList();
private ArrayList typeParameters = new ArrayList();
private ArrayList thrownExceptions = new ArrayList();
private int mainTypeStart = -1;
private int mainTypeEnd;
private int typeSigStart = -1;
public KeyToSignature(BindingKeyParser parser) {
super(parser);
KeyToSignature keyToSignature = (KeyToSignature) parser;
this.kind = keyToSignature.kind;
this.asBinarySignature = keyToSignature.asBinarySignature;
}
public KeyToSignature(String key, int kind) {
super(key);
this.kind = kind;
}
public KeyToSignature(String key, int kind, boolean asBinarySignature) {
super(key);
this.kind = kind;
this.asBinarySignature = asBinarySignature;
}
@Override
public void consumeArrayDimension(char[] brakets) {
this.signature.append(brakets);
}
@Override
public void consumeBaseType(char[] baseTypeSig) {
this.typeSigStart = this.signature.length();
this.signature.append(baseTypeSig);
}
@Override
public void consumeCapture(int position) {
this.signature.append('!');
this.signature.append(((KeyToSignature) this.arguments.get(0)).signature);
}
@Override
public void consumeCapture18ID(int id, int position) {
// see https://bugs.eclipse.org/429264
this.signature.append("!*"); // pretend a 'capture-of ?' //$NON-NLS-1$
}
@Override
public void consumeLocalType(char[] uniqueKey) {
this.signature = new StringBuffer();
// remove trailing semi-colon as it is added later in comsumeType()
uniqueKey = CharOperation.subarray(uniqueKey, 0, uniqueKey.length-1);
if (!this.asBinarySignature)
CharOperation.replace(uniqueKey, '/', '.');
this.signature.append(uniqueKey);
}
@Override
public void consumeMethod(char[] selector, char[] methodSignature) {
this.arguments = new ArrayList();
this.typeArguments = new ArrayList();
if (!this.asBinarySignature)
CharOperation.replace(methodSignature, '/', '.');
switch(this.kind) {
case SIGNATURE:
this.signature = new StringBuffer();
this.signature.append(methodSignature);
break;
case THROWN_EXCEPTIONS:
if (CharOperation.indexOf('^', methodSignature) > 0) {
char[][] types = Signature.getThrownExceptionTypes(methodSignature);
int length = types.length;
for (int i=0; i<length; i++) {
this.thrownExceptions.add(new String(types[i]));
}
}
break;
}
}
@Override
public void consumeMemberType(char[] simpleTypeName) {
this.signature.append('$');
this.signature.append(simpleTypeName);
}
@Override
public void consumePackage(char[] pkgName) {
this.signature.append(pkgName);
}
@Override
public void consumeParameterizedGenericMethod() {
this.typeArguments = this.arguments;
int typeParametersSize = this.arguments.size();
if (typeParametersSize > 0) {
int sigLength = this.signature.length();
char[] methodSignature = new char[sigLength];
this.signature.getChars(0, sigLength, methodSignature, 0);
char[][] typeParameterSigs = Signature.getTypeParameters(methodSignature);
if (typeParameterSigs.length != typeParametersSize)
return;
this.signature = new StringBuffer();
// type parameters
for (int i = 0; i < typeParametersSize; i++)
typeParameterSigs[i] = CharOperation.concat(Signature.C_TYPE_VARIABLE,Signature.getTypeVariable(typeParameterSigs[i]), Signature.C_SEMICOLON);
int paramStart = CharOperation.indexOf(Signature.C_PARAM_START, methodSignature);
char[] typeParametersString = CharOperation.subarray(methodSignature, 0, paramStart);
this.signature.append(typeParametersString);
// substitute parameters
this.signature.append(Signature.C_PARAM_START);
char[][] parameters = Signature.getParameterTypes(methodSignature);
for (int i = 0, parametersLength = parameters.length; i < parametersLength; i++)
substitute(parameters[i], typeParameterSigs, typeParametersSize);
this.signature.append(Signature.C_PARAM_END);
// substitute return type
char[] returnType = Signature.getReturnType(methodSignature);
substitute(returnType, typeParameterSigs, typeParametersSize);
// substitute exceptions
char[][] exceptions = Signature.getThrownExceptionTypes(methodSignature);
for (int i = 0, exceptionsLength = exceptions.length; i < exceptionsLength; i++) {
this.signature.append(Signature.C_EXCEPTION_START);
substitute(exceptions[i], typeParameterSigs, typeParametersSize);
}
}
}
/*
* Substitutes the type variables referenced in the given parameter (a parameterized type signature) with the corresponding
* type argument.
* Appends the given parameter if it is not a parameterized type signature.
*/
private void substitute(char[] parameter, char[][] typeParameterSigs, int typeParametersLength) {
for (int i = 0; i < typeParametersLength; i++) {
if (CharOperation.equals(parameter, typeParameterSigs[i])) {
String typeArgument = ((KeyToSignature) this.arguments.get(i)).signature.toString();
this.signature.append(typeArgument);
return;
}
}
int genericStart = CharOperation.indexOf(Signature.C_GENERIC_START, parameter);
if (genericStart > -1) {
this.signature.append(CharOperation.subarray(parameter, 0, genericStart));
char[][] parameters = Signature.getTypeArguments(parameter);
this.signature.append(Signature.C_GENERIC_START);
for (int j = 0, paramsLength = parameters.length; j < paramsLength; j++)
substitute(parameters[j], typeParameterSigs, typeParametersLength);
this.signature.append(Signature.C_GENERIC_END);
this.signature.append(Signature.C_SEMICOLON);
} else {
// handle array, wildcard and capture
int index = 0;
int length = parameter.length;
loop: while (index < length) {
char current = parameter[index];
switch (current) {
case Signature.C_CAPTURE:
case Signature.C_EXTENDS:
case Signature.C_SUPER:
case Signature.C_ARRAY:
this.signature.append(current);
index++;
break;
default:
break loop;
}
}
if (index > 0)
substitute(CharOperation.subarray(parameter, index, length), typeParameterSigs, typeParametersLength);
else
this.signature.append(parameter);
}
}
@Override
public void consumeParameterizedType(char[] simpleTypeName, boolean isRaw) {
if (simpleTypeName != null) {
// member type
this.signature.append(this.asBinarySignature ? '$' : '.');
this.signature.append(simpleTypeName);
}
if (!isRaw) {
this.signature.append('<');
int length = this.arguments.size();
for (int i = 0; i < length; i++) {
this.signature.append(((KeyToSignature) this.arguments.get(i)).signature);
}
this.signature.append('>');
this.typeArguments = this.arguments;
this.arguments = new ArrayList();
}
}
@Override
public void consumeParser(BindingKeyParser parser) {
this.arguments.add(parser);
}
@Override
public void consumeField(char[] fieldName) {
if (this.kind == SIGNATURE) {
this.signature = ((KeyToSignature) this.arguments.get(0)).signature;
}
}
@Override
public void consumeException() {
int size = this.arguments.size();
if (size > 0) {
for (int i=0; i<size; i++) {
this.thrownExceptions.add(((KeyToSignature) this.arguments.get(i)).signature.toString());
}
this.arguments = new ArrayList();
this.typeArguments = new ArrayList();
}
}
@Override
public void consumeFullyQualifiedName(char[] fullyQualifiedName) {
this.typeSigStart = this.signature.length();
this.signature.append('L');
if (!this.asBinarySignature)
fullyQualifiedName = CharOperation.replaceOnCopy(fullyQualifiedName, '/', '.');
this.signature.append(fullyQualifiedName);
}
@Override
public void consumeSecondaryType(char[] simpleTypeName) {
this.signature.append('~');
this.mainTypeStart = this.signature.lastIndexOf(this.asBinarySignature ? "/" : ".") + 1; //$NON-NLS-1$ //$NON-NLS-2$
if (this.mainTypeStart == 0) {
this.mainTypeStart = 1; // default package (1 for the 'L')
int i = 0;
// we need to preserve the array if needed
while (this.signature.charAt(i) == Signature.C_ARRAY) {
this.mainTypeStart ++;
i++;
}
}
this.mainTypeEnd = this.signature.length();
this.signature.append(simpleTypeName);
}
@Override
public void consumeType() {
// remove main type if needed
if (this.mainTypeStart != -1) {
this.signature.replace(this.mainTypeStart, this.mainTypeEnd, ""); //$NON-NLS-1$
}
// parameter types
int length = this.typeParameters.size();
if (length > 0) {
StringBuffer typeParametersSig = new StringBuffer();
typeParametersSig.append('<');
for (int i = 0; i < length; i++) {
char[] typeParameterSig = Signature.createTypeParameterSignature(
(char[]) this.typeParameters.get(i),
new char[][]{ ConstantPool.ObjectSignature });
typeParametersSig.append(typeParameterSig);
// TODO (jerome) add type parameter bounds in binding key
}
typeParametersSig.append('>');
this.signature.insert(this.typeSigStart, typeParametersSig.toString());
this.typeParameters = new ArrayList();
}
this.signature.append(';');
}
@Override
public void consumeTypeParameter(char[] typeParameterName) {
this.typeParameters.add(typeParameterName);
}
@Override
public void consumeTypeVariable(char[] position, char[] typeVariableName) {
this.signature = new StringBuffer();
this.signature.append('T');
this.signature.append(typeVariableName);
this.signature.append(';');
}
@Override
public void consumeTypeWithCapture() {
KeyToSignature keyToSignature = (KeyToSignature) this.arguments.get(0);
this.signature = keyToSignature.signature;
this.arguments = keyToSignature.arguments;
this.typeArguments = keyToSignature.typeArguments;
this.thrownExceptions = keyToSignature.thrownExceptions;
}
@Override
public void consumeWildCard(int wildCardKind) {
// don't put generic type in signature
this.signature = new StringBuffer();
switch (wildCardKind) {
case Wildcard.UNBOUND:
this.signature.append('*');
break;
case Wildcard.EXTENDS:
this.signature.append('+');
this.signature.append(((KeyToSignature) this.arguments.get(0)).signature);
break;
case Wildcard.SUPER:
this.signature.append('-');
this.signature.append(((KeyToSignature) this.arguments.get(0)).signature);
break;
default:
// malformed
return;
}
}
public String[] getThrownExceptions() {
int length = this.thrownExceptions.size();
String[] result = new String[length];
for (int i = 0; i < length; i++) {
result[i] = (String) this.thrownExceptions.get(i);
}
return result;
}
public String[] getTypeArguments() {
int length = this.typeArguments.size();
String[] result = new String[length];
for (int i = 0; i < length; i++) {
result[i] = ((KeyToSignature) this.typeArguments.get(i)).signature.toString();
}
return result;
}
@Override
public BindingKeyParser newParser() {
return new KeyToSignature(this);
}
@Override
public String toString() {
return this.signature.toString();
}
}