| /******************************************************************************* |
| * Copyright (c) 2000, 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 |
| * Stephan Herrmann <stephan@cs.tu-berlin.de> - TypeConverters don't set enclosingType - https://bugs.eclipse.org/bugs/show_bug.cgi?id=320841 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeParameter; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ImportReference; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; |
| import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
| import org.eclipse.jdt.internal.compiler.parser.TypeConverter; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.jdt.internal.core.util.HashSetOfCharArrayArray; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| /** |
| * Converter from a binary type to an AST type declaration. |
| */ |
| public class BinaryTypeConverter extends TypeConverter { |
| |
| private CompilationResult compilationResult; |
| private HashSetOfCharArrayArray typeNames; |
| |
| public BinaryTypeConverter(ProblemReporter problemReporter, CompilationResult compilationResult, HashSetOfCharArrayArray typeNames) { |
| super(problemReporter, Signature.C_DOLLAR); |
| this.compilationResult = compilationResult; |
| this.typeNames = typeNames; |
| } |
| |
| public ImportReference[] buildImports(ClassFileReader reader) { |
| // add remaining references to the list of type names |
| // (code extracted from BinaryIndexer#extractReferenceFromConstantPool(...)) |
| int[] constantPoolOffsets = reader.getConstantPoolOffsets(); |
| int constantPoolCount = constantPoolOffsets.length; |
| for (int i = 0; i < constantPoolCount; i++) { |
| int tag = reader.u1At(constantPoolOffsets[i]); |
| char[] name = null; |
| switch (tag) { |
| case ClassFileConstants.MethodRefTag : |
| case ClassFileConstants.InterfaceMethodRefTag : |
| int constantPoolIndex = reader.u2At(constantPoolOffsets[i] + 3); |
| int utf8Offset = constantPoolOffsets[reader.u2At(constantPoolOffsets[constantPoolIndex] + 3)]; |
| name = reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1)); |
| break; |
| case ClassFileConstants.ClassTag : |
| utf8Offset = constantPoolOffsets[reader.u2At(constantPoolOffsets[i] + 1)]; |
| name = reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1)); |
| break; |
| } |
| if (name == null || (name.length > 0 && name[0] == '[')) |
| break; // skip over array references |
| this.typeNames.add(CharOperation.splitOn('/', name)); |
| } |
| |
| // convert type names into import references |
| int typeNamesLength = this.typeNames.size(); |
| ImportReference[] imports = new ImportReference[typeNamesLength]; |
| char[][][] set = this.typeNames.set; |
| int index = 0; |
| for (int i = 0, length = set.length; i < length; i++) { |
| char[][] typeName = set[i]; |
| if (typeName != null) { |
| imports[index++] = new ImportReference(typeName, new long[typeName.length]/*dummy positions*/, false/*not on demand*/, 0); |
| } |
| } |
| return imports; |
| } |
| |
| /** |
| * Convert a binary type into an AST type declaration and put it in the given compilation unit. |
| */ |
| public TypeDeclaration buildTypeDeclaration(IType type, CompilationUnitDeclaration compilationUnit) throws JavaModelException { |
| PackageFragment pkg = (PackageFragment) type.getPackageFragment(); |
| char[][] packageName = Util.toCharArrays(pkg.names); |
| |
| if (packageName.length > 0) { |
| compilationUnit.currentPackage = new ImportReference(packageName, new long[]{0}, false, ClassFileConstants.AccDefault); |
| } |
| |
| /* convert type */ |
| TypeDeclaration typeDeclaration = convert(type, null, null); |
| |
| IType alreadyComputedMember = type; |
| IType parent = type.getDeclaringType(); |
| TypeDeclaration previousDeclaration = typeDeclaration; |
| while(parent != null) { |
| TypeDeclaration declaration = convert(parent, alreadyComputedMember, previousDeclaration); |
| |
| alreadyComputedMember = parent; |
| previousDeclaration = declaration; |
| parent = parent.getDeclaringType(); |
| } |
| |
| compilationUnit.types = new TypeDeclaration[]{previousDeclaration}; |
| |
| return typeDeclaration; |
| } |
| |
| private FieldDeclaration convert(IField field, IType type) throws JavaModelException { |
| TypeReference typeReference = createTypeReference(field.getTypeSignature()); |
| if (typeReference == null) return null; |
| FieldDeclaration fieldDeclaration = new FieldDeclaration(); |
| |
| fieldDeclaration.name = field.getElementName().toCharArray(); |
| fieldDeclaration.type = typeReference; |
| fieldDeclaration.modifiers = field.getFlags(); |
| |
| return fieldDeclaration; |
| } |
| |
| private AbstractMethodDeclaration convert(IMethod method, IType type) throws JavaModelException { |
| |
| AbstractMethodDeclaration methodDeclaration; |
| |
| org.eclipse.jdt.internal.compiler.ast.TypeParameter[] typeParams = null; |
| |
| // convert 1.5 specific constructs only if compliance is 1.5 or above |
| if (this.has1_5Compliance) { |
| /* convert type parameters */ |
| ITypeParameter[] typeParameters = method.getTypeParameters(); |
| if (typeParameters != null && typeParameters.length > 0) { |
| int parameterCount = typeParameters.length; |
| typeParams = new org.eclipse.jdt.internal.compiler.ast.TypeParameter[parameterCount]; |
| for (int i = 0; i < parameterCount; i++) { |
| ITypeParameter typeParameter = typeParameters[i]; |
| typeParams[i] = |
| createTypeParameter( |
| typeParameter.getElementName().toCharArray(), |
| stringArrayToCharArray(typeParameter.getBounds()), |
| 0, |
| 0); |
| } |
| } |
| } |
| |
| if (method.isConstructor()) { |
| ConstructorDeclaration decl = new ConstructorDeclaration(this.compilationResult); |
| decl.bits &= ~ASTNode.IsDefaultConstructor; |
| decl.typeParameters = typeParams; |
| methodDeclaration = decl; |
| } else { |
| MethodDeclaration decl = type.isAnnotation() ? new AnnotationMethodDeclaration(this.compilationResult) : new MethodDeclaration(this.compilationResult); |
| /* convert return type */ |
| TypeReference typeReference = createTypeReference(method.getReturnType()); |
| if (typeReference == null) return null; |
| decl.returnType = typeReference; |
| decl.typeParameters = typeParams; |
| methodDeclaration = decl; |
| } |
| methodDeclaration.selector = method.getElementName().toCharArray(); |
| int flags = method.getFlags(); |
| boolean isVarargs = Flags.isVarargs(flags); |
| methodDeclaration.modifiers = flags & ~Flags.AccVarargs; |
| |
| /* convert arguments */ |
| String[] argumentTypeNames = method.getParameterTypes(); |
| String[] argumentNames = method.getParameterNames(); |
| int argumentCount = argumentTypeNames == null ? 0 : argumentTypeNames.length; |
| // Ignore synthetic arguments (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=212224) |
| int startIndex = (method.isConstructor() && type.isMember() && !Flags.isStatic(type.getFlags())) ? 1 : 0; |
| argumentCount -= startIndex; |
| methodDeclaration.arguments = new Argument[argumentCount]; |
| for (int i = 0; i < argumentCount; i++) { |
| String argumentTypeName = argumentTypeNames[startIndex+i]; |
| TypeReference typeReference = createTypeReference(argumentTypeName); |
| if (typeReference == null) return null; |
| if (isVarargs && i == argumentCount-1) { |
| typeReference.bits |= ASTNode.IsVarArgs; |
| } |
| methodDeclaration.arguments[i] = new Argument( |
| argumentNames[i].toCharArray(), |
| 0, |
| typeReference, |
| ClassFileConstants.AccDefault); |
| // do not care whether was final or not |
| } |
| |
| /* convert thrown exceptions */ |
| String[] exceptionTypeNames = method.getExceptionTypes(); |
| int exceptionCount = exceptionTypeNames == null ? 0 : exceptionTypeNames.length; |
| if(exceptionCount > 0) { |
| methodDeclaration.thrownExceptions = new TypeReference[exceptionCount]; |
| for (int i = 0; i < exceptionCount; i++) { |
| TypeReference typeReference = createTypeReference(exceptionTypeNames[i]); |
| if (typeReference == null) return null; |
| methodDeclaration.thrownExceptions[i] = typeReference; |
| } |
| } |
| return methodDeclaration; |
| } |
| |
| private TypeDeclaration convert(IType type, IType alreadyComputedMember,TypeDeclaration alreadyComputedMemberDeclaration) throws JavaModelException { |
| /* create type declaration - can be member type */ |
| TypeDeclaration typeDeclaration = new TypeDeclaration(this.compilationResult); |
| |
| if (type.getDeclaringType() != null) { |
| typeDeclaration.bits |= ASTNode.IsMemberType; |
| } |
| typeDeclaration.name = type.getElementName().toCharArray(); |
| typeDeclaration.modifiers = type.getFlags(); |
| |
| |
| /* set superclass and superinterfaces */ |
| if (type.getSuperclassName() != null) { |
| TypeReference typeReference = createTypeReference(type.getSuperclassTypeSignature()); |
| if (typeReference != null) { |
| typeDeclaration.superclass = typeReference; |
| typeDeclaration.superclass.bits |= ASTNode.IsSuperType; |
| } |
| } |
| |
| String[] interfaceTypes = type.getSuperInterfaceTypeSignatures(); |
| int interfaceCount = interfaceTypes == null ? 0 : interfaceTypes.length; |
| typeDeclaration.superInterfaces = new TypeReference[interfaceCount]; |
| int count = 0; |
| for (int i = 0; i < interfaceCount; i++) { |
| TypeReference typeReference = createTypeReference(interfaceTypes[i]); |
| if (typeReference != null) { |
| typeDeclaration.superInterfaces[count] = typeReference; |
| typeDeclaration.superInterfaces[count++].bits |= ASTNode.IsSuperType; |
| } |
| } |
| if (count != interfaceCount) { |
| System.arraycopy(typeDeclaration.fields, 0, typeDeclaration.superInterfaces = new TypeReference[interfaceCount], 0, interfaceCount); |
| } |
| |
| // convert 1.5 specific constructs only if compliance is 1.5 or above |
| if (this.has1_5Compliance) { |
| |
| /* convert type parameters */ |
| ITypeParameter[] typeParameters = type.getTypeParameters(); |
| if (typeParameters != null && typeParameters.length > 0) { |
| int parameterCount = typeParameters.length; |
| org.eclipse.jdt.internal.compiler.ast.TypeParameter[] typeParams = new org.eclipse.jdt.internal.compiler.ast.TypeParameter[parameterCount]; |
| for (int i = 0; i < parameterCount; i++) { |
| ITypeParameter typeParameter = typeParameters[i]; |
| typeParams[i] = |
| createTypeParameter( |
| typeParameter.getElementName().toCharArray(), |
| stringArrayToCharArray(typeParameter.getBounds()), |
| 0, |
| 0); |
| } |
| |
| typeDeclaration.typeParameters = typeParams; |
| } |
| } |
| |
| /* convert member types */ |
| IType[] memberTypes = type.getTypes(); |
| int memberTypeCount = memberTypes == null ? 0 : memberTypes.length; |
| typeDeclaration.memberTypes = new TypeDeclaration[memberTypeCount]; |
| for (int i = 0; i < memberTypeCount; i++) { |
| if(alreadyComputedMember != null && alreadyComputedMember.getFullyQualifiedName().equals(memberTypes[i].getFullyQualifiedName())) { |
| typeDeclaration.memberTypes[i] = alreadyComputedMemberDeclaration; |
| } else { |
| typeDeclaration.memberTypes[i] = convert(memberTypes[i], null, null); |
| } |
| typeDeclaration.memberTypes[i].enclosingType = typeDeclaration; |
| } |
| |
| /* convert fields */ |
| IField[] fields = type.getFields(); |
| int fieldCount = fields == null ? 0 : fields.length; |
| typeDeclaration.fields = new FieldDeclaration[fieldCount]; |
| count = 0; |
| for (int i = 0; i < fieldCount; i++) { |
| FieldDeclaration fieldDeclaration = convert(fields[i], type); |
| if (fieldDeclaration != null) { |
| typeDeclaration.fields[count++] = fieldDeclaration; |
| } |
| } |
| if (count != fieldCount) { |
| System.arraycopy(typeDeclaration.fields, 0, typeDeclaration.fields = new FieldDeclaration[count], 0, count); |
| } |
| |
| /* convert methods - need to add default constructor if necessary */ |
| IMethod[] methods = type.getMethods(); |
| int methodCount = methods == null ? 0 : methods.length; |
| |
| /* source type has a constructor ? */ |
| /* by default, we assume that one is needed. */ |
| int neededCount = 1; |
| for (int i = 0; i < methodCount; i++) { |
| if (methods[i].isConstructor()) { |
| neededCount = 0; |
| // Does not need the extra constructor since one constructor already exists. |
| break; |
| } |
| } |
| boolean isInterface = type.isInterface(); |
| neededCount = isInterface ? 0 : neededCount; |
| typeDeclaration.methods = new AbstractMethodDeclaration[methodCount + neededCount]; |
| if (neededCount != 0) { // add default constructor in first position |
| typeDeclaration.methods[0] = typeDeclaration.createDefaultConstructor(false, false); |
| } |
| boolean hasAbstractMethods = false; |
| count = 0; |
| for (int i = 0; i < methodCount; i++) { |
| AbstractMethodDeclaration method = convert(methods[i], type); |
| if (method != null) { |
| boolean isAbstract; |
| if ((isAbstract = method.isAbstract()) || isInterface) { // fix-up flag |
| method.modifiers |= ExtraCompilerModifiers.AccSemicolonBody; |
| } |
| if (isAbstract) { |
| hasAbstractMethods = true; |
| } |
| typeDeclaration.methods[neededCount + (count++)] = method; |
| } |
| } |
| if (count != methodCount) { |
| System.arraycopy(typeDeclaration.methods, 0, typeDeclaration.methods = new AbstractMethodDeclaration[count + neededCount], 0, count + neededCount); |
| } |
| if (hasAbstractMethods) { |
| typeDeclaration.bits |= ASTNode.HasAbstractMethods; |
| } |
| return typeDeclaration; |
| } |
| |
| private static char[][] stringArrayToCharArray(String[] strings) { |
| if (strings == null) return null; |
| |
| int length = strings.length; |
| if (length == 0) return CharOperation.NO_CHAR_CHAR; |
| |
| char[][] result = new char [length][]; |
| for (int i = 0; i < length; i++) { |
| result[i] = strings[i].toCharArray(); |
| } |
| |
| return result; |
| } |
| |
| private TypeReference createTypeReference(String typeSignature) { |
| TypeReference result = createTypeReference(typeSignature, 0, 0); |
| if (this.typeNames != null && result instanceof QualifiedTypeReference) { |
| this.typeNames.add(((QualifiedTypeReference)result).tokens); |
| } |
| return result; |
| } |
| } |