/******************************************************************************* * Copyright (c) 2008, 2010 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.compiler.parser; import org.eclipse.che.ide.ext.java.jdt.core.Signature; import org.eclipse.che.ide.ext.java.jdt.core.compiler.CharOperation; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ClassFileConstants; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ASTNode; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ArrayQualifiedTypeReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ImportReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.TypeReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.Wildcard; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.ProblemReporter; import java.util.ArrayList; public abstract class TypeConverter { int namePos; protected ProblemReporter problemReporter; protected boolean has1_5Compliance; private char memberTypeSeparator; protected TypeConverter(ProblemReporter problemReporter, char memberTypeSeparator) { this.problemReporter = problemReporter; this.has1_5Compliance = problemReporter.options.originalComplianceLevel >= ClassFileConstants.JDK1_5; this.memberTypeSeparator = memberTypeSeparator; } private void addIdentifiers(String typeSignature, int start, int endExclusive, int identCount, ArrayList fragments) { if (identCount == 1) { char[] identifier; typeSignature.getChars(start, endExclusive, identifier = new char[endExclusive - start], 0); fragments.add(identifier); } else fragments.add(extractIdentifiers(typeSignature, start, endExclusive - 1, identCount)); } /* Build an import reference from an import name, e.g. java.lang.* */ protected ImportReference createImportReference(String[] importName, int start, int end, boolean onDemand, int modifiers) { int length = importName.length; long[] positions = new long[length]; long position = ((long)start << 32) + end; char[][] qImportName = new char[length][]; for (int i = 0; i < length; i++) { qImportName[i] = importName[i].toCharArray(); positions[i] = position; // dummy positions } return new ImportReference(qImportName, positions, onDemand, modifiers); } protected TypeParameter createTypeParameter(char[] typeParameterName, char[][] typeParameterBounds, int start, int end) { TypeParameter parameter = new TypeParameter(); parameter.name = typeParameterName; parameter.sourceStart = start; parameter.sourceEnd = end; if (typeParameterBounds != null) { int length = typeParameterBounds.length; if (length > 0) { parameter.type = createTypeReference(typeParameterBounds[0], start, end); if (length > 1) { parameter.bounds = new TypeReference[length - 1]; for (int i = 1; i < length; i++) { TypeReference bound = createTypeReference(typeParameterBounds[i], start, end); bound.bits |= ASTNode.IsSuperType; parameter.bounds[i - 1] = bound; } } } } return parameter; } /* Build a type reference from a readable name, e.g. java.lang.Object[][] */ protected TypeReference createTypeReference(char[] typeName, int start, int end, boolean includeGenericsAnyway) { int length = typeName.length; this.namePos = 0; return decodeType(typeName, length, start, end, true); } /* Build a type reference from a readable name, e.g. java.lang.Object[][] */ protected TypeReference createTypeReference(char[] typeName, int start, int end) { int length = typeName.length; this.namePos = 0; return decodeType(typeName, length, start, end, false); } /* Build a type reference from a type signature, e.g. Ljava.lang.Object; */ protected TypeReference createTypeReference(String typeSignature, int start, int end) { int length = typeSignature.length(); this.namePos = 0; return decodeType(typeSignature, length, start, end); } private TypeReference decodeType(String typeSignature, int length, int start, int end) { int identCount = 1; int dim = 0; int nameFragmentStart = this.namePos, nameFragmentEnd = -1; boolean nameStarted = false; ArrayList fragments = null; typeLoop: while (this.namePos < length) { char currentChar = typeSignature.charAt(this.namePos); switch (currentChar) { case Signature.C_BOOLEAN: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.BOOLEAN.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.BOOLEAN.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_BYTE: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.BYTE.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.BYTE.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_CHAR: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.CHAR.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.CHAR.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_DOUBLE: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.DOUBLE.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.DOUBLE.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_FLOAT: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.FLOAT.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.FLOAT.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_INT: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.INT.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.INT.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_LONG: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.LONG.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.LONG.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_SHORT: if (!nameStarted) { this.namePos++; if (dim == 0) return new SingleTypeReference(TypeBinding.SHORT.simpleName, ((long)start << 32) + end); else return new ArrayTypeReference(TypeBinding.SHORT.simpleName, dim, ((long)start << 32) + end); } break; case Signature.C_VOID: if (!nameStarted) { this.namePos++; return new SingleTypeReference(TypeBinding.VOID.simpleName, ((long)start << 32) + end); } break; case Signature.C_RESOLVED: case Signature.C_UNRESOLVED: case Signature.C_TYPE_VARIABLE: if (!nameStarted) { nameFragmentStart = this.namePos + 1; nameStarted = true; } break; case Signature.C_STAR: this.namePos++; Wildcard result = new Wildcard(Wildcard.UNBOUND); result.sourceStart = start; result.sourceEnd = end; return result; case Signature.C_EXTENDS: this.namePos++; result = new Wildcard(Wildcard.EXTENDS); result.bound = decodeType(typeSignature, length, start, end); result.sourceStart = start; result.sourceEnd = end; return result; case Signature.C_SUPER: this.namePos++; result = new Wildcard(Wildcard.SUPER); result.bound = decodeType(typeSignature, length, start, end); result.sourceStart = start; result.sourceEnd = end; return result; case Signature.C_ARRAY: dim++; break; case Signature.C_GENERIC_END: case Signature.C_SEMICOLON: nameFragmentEnd = this.namePos - 1; this.namePos++; break typeLoop; case Signature.C_DOLLAR: if (this.memberTypeSeparator != Signature.C_DOLLAR) break; // $FALL-THROUGH$ case Signature.C_DOT: if (!nameStarted) { nameFragmentStart = this.namePos + 1; nameStarted = true; } else if (this.namePos > nameFragmentStart) // handle name starting with a $ (see // https://bugs.eclipse.org/bugs/show_bug.cgi?id=91709) identCount++; break; case Signature.C_GENERIC_START: nameFragmentEnd = this.namePos - 1; // convert 1.5 specific constructs only if compliance is 1.5 or above if (!this.has1_5Compliance) break typeLoop; if (fragments == null) fragments = new ArrayList(2); addIdentifiers(typeSignature, nameFragmentStart, nameFragmentEnd + 1, identCount, fragments); this.namePos++; // skip '<' TypeReference[] arguments = decodeTypeArguments(typeSignature, length, start, end); // positionned on '>' at end fragments.add(arguments); identCount = 1; nameStarted = false; // next increment will skip '>' break; } this.namePos++; } if (fragments == null) { // non parameterized /* rebuild identifiers and dimensions */ if (identCount == 1) { // simple type reference if (dim == 0) { char[] nameFragment = new char[nameFragmentEnd - nameFragmentStart + 1]; typeSignature.getChars(nameFragmentStart, nameFragmentEnd + 1, nameFragment, 0); return new SingleTypeReference(nameFragment, ((long)start << 32) + end); } else { char[] nameFragment = new char[nameFragmentEnd - nameFragmentStart + 1]; typeSignature.getChars(nameFragmentStart, nameFragmentEnd + 1, nameFragment, 0); return new ArrayTypeReference(nameFragment, dim, ((long)start << 32) + end); } } else { // qualified type reference long[] positions = new long[identCount]; long pos = ((long)start << 32) + end; for (int i = 0; i < identCount; i++) { positions[i] = pos; } char[][] identifiers = extractIdentifiers(typeSignature, nameFragmentStart, nameFragmentEnd, identCount); if (dim == 0) { return new QualifiedTypeReference(identifiers, positions); } else { return new ArrayQualifiedTypeReference(identifiers, dim, positions); } } } else { // parameterized // rebuild type reference from available fragments: char[][], arguments, char[][], arguments... // check trailing qualified name if (nameStarted) { addIdentifiers(typeSignature, nameFragmentStart, nameFragmentEnd + 1, identCount, fragments); } int fragmentLength = fragments.size(); if (fragmentLength == 2) { Object firstFragment = fragments.get(0); if (firstFragment instanceof char[]) { // parameterized single type return new ParameterizedSingleTypeReference((char[])firstFragment, (TypeReference[])fragments.get(1), dim, ((long)start << 32) + end); } } // parameterized qualified type identCount = 0; for (int i = 0; i < fragmentLength; i++) { Object element = fragments.get(i); if (element instanceof char[][]) { identCount += ((char[][])element).length; } else if (element instanceof char[]) identCount++; } char[][] tokens = new char[identCount][]; TypeReference[][] arguments = new TypeReference[identCount][]; int index = 0; for (int i = 0; i < fragmentLength; i++) { Object element = fragments.get(i); if (element instanceof char[][]) { char[][] fragmentTokens = (char[][])element; int fragmentTokenLength = fragmentTokens.length; System.arraycopy(fragmentTokens, 0, tokens, index, fragmentTokenLength); index += fragmentTokenLength; } else if (element instanceof char[]) { tokens[index++] = (char[])element; } else { arguments[index - 1] = (TypeReference[])element; } } long[] positions = new long[identCount]; long pos = ((long)start << 32) + end; for (int i = 0; i < identCount; i++) { positions[i] = pos; } return new ParameterizedQualifiedTypeReference(tokens, arguments, dim, positions); } } private TypeReference decodeType(char[] typeName, int length, int start, int end, boolean includeGenericsAnyway) { int identCount = 1; int dim = 0; int nameFragmentStart = this.namePos, nameFragmentEnd = -1; ArrayList fragments = null; typeLoop: while (this.namePos < length) { char currentChar = typeName[this.namePos]; switch (currentChar) { case '?': this.namePos++; // skip '?' while (typeName[this.namePos] == ' ') this.namePos++; switch (typeName[this.namePos]) { case 's': checkSuper: { int max = TypeConstants.WILDCARD_SUPER.length - 1; for (int ahead = 1; ahead < max; ahead++) { if (typeName[this.namePos + ahead] != TypeConstants.WILDCARD_SUPER[ahead + 1]) { break checkSuper; } } this.namePos += max; Wildcard result = new Wildcard(Wildcard.SUPER); result.bound = decodeType(typeName, length, start, end, includeGenericsAnyway); result.sourceStart = start; result.sourceEnd = end; return result; } break; case 'e': checkExtends: { int max = TypeConstants.WILDCARD_EXTENDS.length - 1; for (int ahead = 1; ahead < max; ahead++) { if (typeName[this.namePos + ahead] != TypeConstants.WILDCARD_EXTENDS[ahead + 1]) { break checkExtends; } } this.namePos += max; Wildcard result = new Wildcard(Wildcard.EXTENDS); result.bound = decodeType(typeName, length, start, end, includeGenericsAnyway); result.sourceStart = start; result.sourceEnd = end; return result; } break; } Wildcard result = new Wildcard(Wildcard.UNBOUND); result.sourceStart = start; result.sourceEnd = end; return result; case '[': if (dim == 0 && nameFragmentEnd < 0) nameFragmentEnd = this.namePos - 1; dim++; break; case ']': break; case '>': case ',': break typeLoop; case '.': if (nameFragmentStart < 0) nameFragmentStart = this.namePos + 1; // member type name identCount++; break; case '<': /* * We need to convert and preserve 1.5 specific constructs either if compliance is 1.5 or above, or the caller has * explicitly requested generics to be included. The parameter includeGenericsAnyway should be used by the caller * to signal that in the calling context generics information must be internalized even when the requesting project * is 1.4. But in all cases, we must skip over them to see if there are any applicable type fragments after the * type parameters: i.e we just aren't done having seen a '<' in 1.4 mode. Because of the way type signatures are * encoded, TypeConverter.decodeType(String, int, int, int) is immune to this problem. See * https://bugs.eclipse.org/bugs/show_bug.cgi?id=325633 */ if (this.has1_5Compliance || includeGenericsAnyway) { if (fragments == null) fragments = new ArrayList(2); } nameFragmentEnd = this.namePos - 1; if (this.has1_5Compliance || includeGenericsAnyway) { char[][] identifiers = CharOperation.splitOn('.', typeName, nameFragmentStart, this.namePos); fragments.add(identifiers); } this.namePos++; // skip '<' TypeReference[] arguments = decodeTypeArguments(typeName, length, start, end, includeGenericsAnyway); // positionned // on '>' at // end if (this.has1_5Compliance || includeGenericsAnyway) { fragments.add(arguments); identCount = 0; nameFragmentStart = -1; nameFragmentEnd = -1; } // next increment will skip '>' break; } this.namePos++; } if (nameFragmentEnd < 0) nameFragmentEnd = this.namePos - 1; if (fragments == null) { // non parameterized /* rebuild identifiers and dimensions */ if (identCount == 1) { // simple type reference if (dim == 0) { char[] nameFragment; if (nameFragmentStart != 0 || nameFragmentEnd >= 0) { int nameFragmentLength = nameFragmentEnd - nameFragmentStart + 1; System.arraycopy(typeName, nameFragmentStart, nameFragment = new char[nameFragmentLength], 0, nameFragmentLength); } else { nameFragment = typeName; } return new SingleTypeReference(nameFragment, ((long)start << 32) + end); } else { int nameFragmentLength = nameFragmentEnd - nameFragmentStart + 1; char[] nameFragment = new char[nameFragmentLength]; System.arraycopy(typeName, nameFragmentStart, nameFragment, 0, nameFragmentLength); return new ArrayTypeReference(nameFragment, dim, ((long)start << 32) + end); } } else { // qualified type reference long[] positions = new long[identCount]; long pos = ((long)start << 32) + end; for (int i = 0; i < identCount; i++) { positions[i] = pos; } char[][] identifiers = CharOperation.splitOn('.', typeName, nameFragmentStart, nameFragmentEnd + 1); if (dim == 0) { return new QualifiedTypeReference(identifiers, positions); } else { return new ArrayQualifiedTypeReference(identifiers, dim, positions); } } } else { // parameterized // rebuild type reference from available fragments: char[][], arguments, char[][], arguments... // check trailing qualified name if (nameFragmentStart > 0 && nameFragmentStart < length) { char[][] identifiers = CharOperation.splitOn('.', typeName, nameFragmentStart, nameFragmentEnd + 1); fragments.add(identifiers); } int fragmentLength = fragments.size(); if (fragmentLength == 2) { char[][] firstFragment = (char[][])fragments.get(0); if (firstFragment.length == 1) { // parameterized single type return new ParameterizedSingleTypeReference(firstFragment[0], (TypeReference[])fragments.get(1), dim, ((long)start << 32) + end); } } // parameterized qualified type identCount = 0; for (int i = 0; i < fragmentLength; i++) { Object element = fragments.get(i); if (element instanceof char[][]) { identCount += ((char[][])element).length; } } char[][] tokens = new char[identCount][]; TypeReference[][] arguments = new TypeReference[identCount][]; int index = 0; for (int i = 0; i < fragmentLength; i++) { Object element = fragments.get(i); if (element instanceof char[][]) { char[][] fragmentTokens = (char[][])element; int fragmentTokenLength = fragmentTokens.length; System.arraycopy(fragmentTokens, 0, tokens, index, fragmentTokenLength); index += fragmentTokenLength; } else { arguments[index - 1] = (TypeReference[])element; } } long[] positions = new long[identCount]; long pos = ((long)start << 32) + end; for (int i = 0; i < identCount; i++) { positions[i] = pos; } return new ParameterizedQualifiedTypeReference(tokens, arguments, dim, positions); } } private TypeReference[] decodeTypeArguments(char[] typeName, int length, int start, int end, boolean includeGenericsAnyway) { ArrayList argumentList = new ArrayList(1); int count = 0; argumentsLoop: while (this.namePos < length) { TypeReference argument = decodeType(typeName, length, start, end, includeGenericsAnyway); count++; argumentList.add(argument); if (this.namePos >= length) break argumentsLoop; if (typeName[this.namePos] == '>') { break argumentsLoop; } this.namePos++; // skip ',' } TypeReference[] typeArguments = new TypeReference[count]; argumentList.toArray(typeArguments); return typeArguments; } private TypeReference[] decodeTypeArguments(String typeSignature, int length, int start, int end) { ArrayList argumentList = new ArrayList(1); int count = 0; argumentsLoop: while (this.namePos < length) { TypeReference argument = decodeType(typeSignature, length, start, end); count++; argumentList.add(argument); if (this.namePos >= length) break argumentsLoop; if (typeSignature.charAt(this.namePos) == Signature.C_GENERIC_END) { break argumentsLoop; } } TypeReference[] typeArguments = new TypeReference[count]; argumentList.toArray(typeArguments); return typeArguments; } private char[][] extractIdentifiers(String typeSignature, int start, int endInclusive, int identCount) { char[][] result = new char[identCount][]; int charIndex = start; int i = 0; while (charIndex < endInclusive) { char currentChar; if ((currentChar = typeSignature.charAt(charIndex)) == this.memberTypeSeparator || currentChar == Signature.C_DOT) { typeSignature.getChars(start, charIndex, result[i++] = new char[charIndex - start], 0); start = ++charIndex; } else charIndex++; } typeSignature.getChars(start, charIndex + 1, result[i++] = new char[charIndex - start + 1], 0); return result; } }