Skip to content

Commit

Permalink
Merge pull request #2 from GerardPaligot/refactor_import
Browse files Browse the repository at this point in the history
Refactor management of imports and rewrites correctly static fields.
  • Loading branch information
tdurieux committed Jan 5, 2016
2 parents 5e194fe + e9287bf commit e9f4be3
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 61 deletions.
143 changes: 85 additions & 58 deletions src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@
import spoon.reflect.reference.CtLocalVariableReference;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.reference.CtParameterReference;
import spoon.reflect.reference.CtReference;
import spoon.reflect.reference.CtTypeParameterReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.reference.CtVariableReference;
Expand Down Expand Up @@ -281,6 +282,8 @@ public class BuilderContext {

boolean isLambdaParameterImplicitlyTyped = true;

boolean ignoreComputeImports = false;

/**
* Stack of all parents elements
*/
Expand Down Expand Up @@ -412,6 +415,57 @@ public CtTypeReference<?> getBoundedTypeReference(TypeBinding binding) {
return ref;
}

/**
* Try to get the declaring reference (package or type) from imports of the current
* compilation unit declaration (current class). This method returns a CtReference
* which can be a CtTypeReference if it retrieves the information in an static import,
* a CtPackageReference if it retrieves the information in an standard import, otherwise
* it returns null.
*
* @param expectedName
* Name expected in imports.
* @return CtReference which can be a CtTypeReference, a CtPackageReference or null.
*/
public CtReference getDeclaringReferenceFromImports(char[] expectedName) {
if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) {
for (ImportReference anImport : context.compilationunitdeclaration.imports) {
if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], expectedName)) {
if (anImport.isStatic()) {
int indexDeclaring = 2;
if ((anImport.bits & ASTNode.OnDemand) != 0) {
// With .*
indexDeclaring = 1;
}
char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - indexDeclaring);
char[][] className = CharOperation.subarray(anImport.getImportName(), anImport.getImportName().length - indexDeclaring, anImport.getImportName().length - (indexDeclaring - 1));
final PackageBinding aPackage = context.compilationunitdeclaration.scope.environment.createPackage(packageName);
final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(aPackage, className);
context.ignoreComputeImports = true;
final CtTypeReference<Object> typeReference = getTypeReference(declaringType);
context.ignoreComputeImports = false;
return typeReference;
} else {
char[][] chars = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1);
Binding someBinding = context.compilationunitdeclaration.scope.findImport(chars, false, false);
PackageBinding packageBinding;
if (someBinding != null && someBinding.isValidBinding() && someBinding instanceof PackageBinding) {
packageBinding = (PackageBinding) someBinding;
} else {
packageBinding = context.compilationunitdeclaration.scope.environment.createPackage(chars);
if (packageBinding == null) {
// Big crisis here. We are already in noclasspath mode but JDT doesn't support always
// creation of a package in this mode. So, if we are in this brace, we make the job of JDT...
packageBinding = new PackageBinding(chars, null, context.compilationunitdeclaration.scope.environment);
}
}
return getPackageReference(packageBinding);
}
}
}
}
return null;
}

@SuppressWarnings("unchecked")
public <T> CtExecutableReference<T> getExecutableReference(MethodBinding exec) {
if (exec == null) {
Expand All @@ -423,18 +477,9 @@ public <T> CtExecutableReference<T> getExecutableReference(MethodBinding exec) {
ref.setType((CtTypeReference<T>) getTypeReference(exec.returnType));

if (exec instanceof ProblemMethodBinding) {
// We try to check in imports if there is the correct package of the type.
if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) {
for (ImportReference anImport : context.compilationunitdeclaration.imports) {
if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], exec.constantPoolName())) {
char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 2);
char[][] className = CharOperation.subarray(anImport.getImportName(), anImport.getImportName().length - 2, anImport.getImportName().length - 1);
final PackageBinding aPackage = context.compilationunitdeclaration.scope.environment.createPackage(packageName);
final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(aPackage, className);
ref.setDeclaringType(getTypeReference(declaringType));
break;
}
}
final CtReference declaringType = getDeclaringReferenceFromImports(exec.constantPoolName());
if (declaringType instanceof CtTypeReference) {
ref.setDeclaringType((CtTypeReference<?>) declaringType);
}
if (exec.isConstructor()) {
// super() invocation have a good declaring class.
Expand Down Expand Up @@ -535,27 +580,12 @@ public <T> CtTypeReference<T> getTypeReference(TypeBinding binding) {
ref = factory.Core().createTypeReference();
ref.setSimpleName(new String(binding.sourceName()));
ref.setPackage(getPackageReference(binding.getPackage()));
// We try to check in imports if there is the correct package of the type.
if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) {
for (ImportReference anImport : context.compilationunitdeclaration.imports) {
if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], binding.sourceName())) {
char[][] chars = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1);
Binding someBinding = context.compilationunitdeclaration.scope.findImport(chars, false, false);
PackageBinding packageBinding;
if (someBinding != null && someBinding.isValidBinding() && someBinding instanceof PackageBinding) {
packageBinding = (PackageBinding) someBinding;
} else {
packageBinding = context.compilationunitdeclaration.scope.environment.createPackage(chars);
if (packageBinding == null) {
// Big crisis here. We are already in noclasspath mode since the check `binding instance MissingTypeBinding`
// but JDT doesn't support always creation of a package in this mode. So, if we are in this brace, we make
// the job of JDT...
packageBinding = new PackageBinding(chars, null, context.compilationunitdeclaration.scope.environment);
}
}
ref.setPackage(getPackageReference(packageBinding));
break;
}
if (!context.ignoreComputeImports) {
final CtReference declaring = references.getDeclaringReferenceFromImports(binding.sourceName());
if (declaring instanceof CtPackageReference) {
ref.setPackage((CtPackageReference) declaring);
} else if (declaring instanceof CtTypeReference) {
ref.setDeclaringType((CtTypeReference) declaring);
}
}
} else if (binding instanceof BinaryTypeBinding) {
Expand Down Expand Up @@ -2392,25 +2422,20 @@ public boolean visit(MessageSend messageSend, BlockScope scope) {
if (messageSend.receiver.resolvedType == null) {
// It is crisis dude! static context, we don't have much more information.
if (messageSend.receiver instanceof SingleNameReference) {
final CtTypeReference<Object> typeReference = factory.Core().createTypeReference();
CtTypeReference<Object> typeReference = factory.Core().createTypeReference();
typeReference.setSimpleName(messageSend.receiver.toString());
if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) {
for (ImportReference anImport : context.compilationunitdeclaration.imports) {
if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], ((SingleNameReference) messageSend.receiver).token)) {
char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1);
CtPackageReference packageRef = factory.Core().createPackageReference();
packageRef.setSimpleName(CharOperation.toString(packageName));
typeReference.setPackage(packageRef);
break;
}
}
final CtReference declaring = references.getDeclaringReferenceFromImports(((SingleNameReference) messageSend.receiver).token);
if (declaring instanceof CtPackageReference) {
typeReference.setPackage((CtPackageReference) declaring);
} else if (declaring instanceof CtTypeReference) {
typeReference = (CtTypeReference<Object>) declaring;
}
ref.setDeclaringType(typeReference);
} else if (messageSend.receiver instanceof QualifiedNameReference) {
QualifiedNameReference qualifiedNameReference = (QualifiedNameReference) messageSend.receiver;

char[][] packageName = CharOperation.subarray(qualifiedNameReference.tokens, 0, qualifiedNameReference.tokens.length - 2);
char[][] className = CharOperation.subarray(qualifiedNameReference.tokens, qualifiedNameReference.tokens.length - 2, qualifiedNameReference.tokens.length - 1);
char[][] packageName = CharOperation.subarray(qualifiedNameReference.tokens, 0, qualifiedNameReference.tokens.length - 1);
char[][] className = CharOperation.subarray(qualifiedNameReference.tokens, qualifiedNameReference.tokens.length - 1, qualifiedNameReference.tokens.length);
if (packageName.length > 0) {
final PackageBinding aPackage = context.compilationunitdeclaration.scope.environment.createPackage(packageName);
final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(aPackage, className);
Expand Down Expand Up @@ -2773,8 +2798,15 @@ public boolean visit(QualifiedNameReference qualifiedNameReference, BlockScope s
va = factory.Core().createFieldRead();
}
va.setVariable(references.getVariableReference((ProblemBinding) qualifiedNameReference.binding));
// In no classpath mode and with qualified name, the type given by JDT is wrong...
if (va.getVariable() instanceof CtFieldReference) {
final char[][] declaringClass = CharOperation.subarray(qualifiedNameReference.tokens, 0, qualifiedNameReference.tokens.length - 1);
final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(null, declaringClass);
((CtFieldReference) va.getVariable()).setDeclaringType(references.getTypeReference(declaringType));
((CtFieldReference) va.getVariable()).setStatic(true);
}
// In no classpath mode and with qualified name, the binding don't have a good name.
va.getVariable().setSimpleName(createTypeName(qualifiedNameReference.getName()));
va.getVariable().setSimpleName(createTypeName(CharOperation.subarray(qualifiedNameReference.tokens, qualifiedNameReference.tokens.length - 1, qualifiedNameReference.tokens.length)));
context.enter(va, qualifiedNameReference);
return false;
} else {
Expand Down Expand Up @@ -2851,18 +2883,13 @@ public boolean visit(SingleNameReference singleNameReference, BlockScope scope)
} else if (singleNameReference.binding instanceof ProblemBinding) {
if (context.stack.peek().element instanceof CtInvocation) {
final CtTypeAccess<Object> ta = factory.Core().createTypeAccess();
final CtTypeReference<Object> typeReference = factory.Core().createTypeReference();
CtTypeReference<Object> typeReference = factory.Core().createTypeReference();
typeReference.setSimpleName(new String(singleNameReference.binding.readableName()));
if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) {
for (ImportReference anImport : context.compilationunitdeclaration.imports) {
if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], singleNameReference.token)) {
char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1);
CtPackageReference packageRef = factory.Core().createPackageReference();
packageRef.setSimpleName(CharOperation.toString(packageName));
typeReference.setPackage(packageRef);
break;
}
}
final CtReference declaring = references.getDeclaringReferenceFromImports(singleNameReference.token);
if (declaring instanceof CtPackageReference) {
typeReference.setPackage((CtPackageReference) declaring);
} else if (declaring instanceof CtTypeReference) {
typeReference = (CtTypeReference<Object>) declaring;
}
ta.setType(typeReference);
context.enter(ta, singleNameReference);
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/spoon/test/imports/ImportTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ public boolean matches(CtInvocation<?> element) {
assertCorrectInvocation(new Expected().name("makeBurritos").target("Tacos.Burritos").declaringType("Burritos").typeIsNull(false), elements.get(7));

// Invocation for a static method in an inner class with the declaring class specified.
assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("C.D").typeIsNull(true), elements.get(8));
assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("D").typeIsNull(true), elements.get(8));

// Invocation for a static method in an inner class without the declaring class specified.
assertCorrectInvocation(new Expected().name("staticD").target("pack2.C.D").declaringType("D").typeIsNull(true), elements.get(9));

// Invocation for a static method in an inner class with the declaring class specified and a return type.
assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("C.D").typeIsNull(false), elements.get(10));
assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("D").typeIsNull(false), elements.get(10));

// Invocation for a static method in an inner class without the declaring class specified and a return type.
assertCorrectInvocation(new Expected().name("staticD").target("pack2.C.D").declaringType("D").typeIsNull(false), elements.get(11));
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/spoon/test/variable/AccessTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public void testVariableAccessInNoClasspath() throws Exception {
assertNotNull(element.getVariable());
}

assertEquals("java.lang.Class mclass = ((java.lang.Class)(ModelFacade.USE_CASE))", elements.get(0).getParent().toString());
assertEquals("java.lang.Class mclass = ((java.lang.Class)(org.argouml.model.ModelFacade.USE_CASE))", elements.get(0).getParent().toString());
assertEquals("new PropPanelButton(this , buttonPanel , _navUpIcon , org.argouml.i18n.Translator.localize(\"UMLMenu\", \"button.go-up\") , \"navigateNamespace\" , null)", elements.get(2).getParent().toString());
}
}

0 comments on commit e9f4be3

Please sign in to comment.