From 800ce51086a0ffb5fab67d382c1e59399193e916 Mon Sep 17 00:00:00 2001 From: Pavel Vojtechovsky Date: Sun, 29 Oct 2017 12:55:31 +0100 Subject: [PATCH] feature: CtElement#getRoleInParent() --- .../spoon/reflect/declaration/CtElement.java | 7 +++ .../reflect/code/CtVariableAccessImpl.java | 12 ++-- .../reflect/declaration/CtElementImpl.java | 20 +++++++ src/test/java/spoon/test/main/MainTest.java | 60 ++++++++++++++----- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/src/main/java/spoon/reflect/declaration/CtElement.java b/src/main/java/spoon/reflect/declaration/CtElement.java index 68b98532808..47c47cec2a7 100644 --- a/src/main/java/spoon/reflect/declaration/CtElement.java +++ b/src/main/java/spoon/reflect/declaration/CtElement.java @@ -19,6 +19,7 @@ import spoon.processing.FactoryAccessor; import spoon.reflect.code.CtComment; import spoon.reflect.cu.SourcePosition; +import spoon.reflect.path.CtRole; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.CtVisitable; import spoon.reflect.visitor.Filter; @@ -255,6 +256,12 @@ List getAnnotatedChildren( */ void updateAllParentsBelow(); + /** + * @return {@link CtRole} of the parent's attribute where this element is used. + * returns null if parent doesn't links this element or if isParentInitialized()==false. + */ + CtRole getRoleInParent(); + /* * Deletes the element. For instance, delete a statement from its containing block. Warning: it may result in an incorrect AST, use at your own risk. */ diff --git a/src/main/java/spoon/support/reflect/code/CtVariableAccessImpl.java b/src/main/java/spoon/support/reflect/code/CtVariableAccessImpl.java index 7b78ad32637..f82008a0055 100644 --- a/src/main/java/spoon/support/reflect/code/CtVariableAccessImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtVariableAccessImpl.java @@ -34,15 +34,11 @@ public abstract class CtVariableAccessImpl extends CtExpressionImpl implem @Override public CtVariableReference getVariable() { - if (variable != null) { - return (CtVariableReference) variable; - } - if (getFactory() != null) { - CtVariableReference ref = getFactory().Core().createLocalVariableReference(); - ref.setParent(this); - return (CtVariableReference) ref; + if (variable == null && getFactory() != null) { + variable = getFactory().Core().createLocalVariableReference(); + variable.setParent(this); } - return null; + return (CtVariableReference) variable; } @Override diff --git a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java index d819712422c..ce3cadc5270 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java @@ -31,6 +31,7 @@ import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.CtScanner; import spoon.reflect.visitor.DefaultJavaPrettyPrinter; +import spoon.reflect.visitor.EarlyTerminatingScanner; import spoon.reflect.visitor.Filter; import spoon.reflect.visitor.ModelConsistencyChecker; import spoon.reflect.visitor.Query; @@ -375,6 +376,25 @@ public boolean hasParent(CtElement candidate) { } } + @Override + public CtRole getRoleInParent() { + if (isParentInitialized()) { + EarlyTerminatingScanner ets = new EarlyTerminatingScanner() { + @Override + public void scan(CtRole role, CtElement element) { + if (element == CtElementImpl.this) { + setResult(role); + terminate(); + } + //do not call super.scan, because we do not want scan children + } + }; + getParent().accept(ets); + return ets.getResult(); + } + return null; + } + @Override public void updateAllParentsBelow() { new ModelConsistencyChecker(getFactory().getEnvironment(), true, true).scan(this); diff --git a/src/test/java/spoon/test/main/MainTest.java b/src/test/java/spoon/test/main/MainTest.java index db1607257dc..ee5e6b6f2e1 100644 --- a/src/test/java/spoon/test/main/MainTest.java +++ b/src/test/java/spoon/test/main/MainTest.java @@ -1,6 +1,7 @@ package spoon.test.main; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.Assertion; @@ -22,6 +23,7 @@ import spoon.reflect.declaration.CtType; import spoon.reflect.declaration.CtTypeParameter; import spoon.reflect.declaration.ParentNotInitializedException; +import spoon.reflect.path.CtRole; import spoon.reflect.reference.CtArrayTypeReference; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.reference.CtFieldReference; @@ -48,13 +50,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class MainTest { - - @Test - public void testMain() throws Exception { - + + static Launcher launcher; + static CtPackage rootPackage; + + /** + * load model once into static variable and use it for more read-only tests + */ + @BeforeClass + public static void loadModel() { // we have to remove the test-classes folder // so that the precondition of --source-classpath is not violated // (target/test-classes contains src/test/resources which itself contains Java files) @@ -67,7 +75,7 @@ public void testMain() throws Exception { } String systemClassPath = classpath.substring(0, classpath.length() - 1); - Launcher launcher = new Launcher(); + launcher = new Launcher(); launcher.run(new String[] { "-i", "src/main/java", @@ -78,14 +86,23 @@ public void testMain() throws Exception { "--compliance", "8", "--level", "OFF" }); + + rootPackage = launcher.getFactory().Package().getRootPackage(); + } + + @Test + public void testMain_checkGenericContracts() { + checkGenericContracts(rootPackage); + } + + @Test + public void testMain_checkShadow() { + checkShadow(rootPackage); + } - checkGenericContracts(launcher.getFactory().Package().getRootPackage()); - - checkShadow(launcher.getFactory().Package().getRootPackage()); - - checkParentConsistency(launcher.getFactory().Package().getRootPackage()); - - checkModelIsTree(launcher.getFactory().Package().getRootPackage()); + @Test + public void testMain_checkParentConsistency() { + checkParentConsistency(rootPackage); } public void checkGenericContracts(CtPackage pack) { @@ -347,13 +364,12 @@ protected void exit(CtElement e) { assertEquals("All parents have to be consistent", 0, inconsistentParents.size()); } - - /* * contract: each element is used only once * For example this is always true: field.getType() != field.getDeclaringType() */ - private void checkModelIsTree(CtPackage rootPackage) { + @Test + public void checkModelIsTree() { Exception dummyException = new Exception("STACK"); PrinterHelper problems = new PrinterHelper(rootPackage.getFactory().getEnvironment()); Map allElements = new IdentityHashMap<>(); @@ -390,7 +406,19 @@ private String getStackTrace(Exception e) { return sw.toString(); } - + @Test + public void testMyRoleInParent() { + rootPackage.accept(new CtScanner() { + @Override + public void scan(CtRole role, CtElement element) { + if (element != null) { + //contract: getMyRoleInParent returns the expected parent + assertSame(role, element.getRoleInParent()); + } + super.scan(role, element); + } + }); + } @Test public void testTest() throws Exception {