/******************************************************************************* * 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.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); } } /* 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; } }