diff --git a/src/main/java/spoon/reflect/factory/CoreFactory.java b/src/main/java/spoon/reflect/factory/CoreFactory.java index dd600ef079d..9f1fdd5b7fb 100644 --- a/src/main/java/spoon/reflect/factory/CoreFactory.java +++ b/src/main/java/spoon/reflect/factory/CoreFactory.java @@ -109,10 +109,8 @@ public interface CoreFactory { * Recursively clones a given element of the metamodel and all its child * elements. * - * @param - * the element's type - * @param element - * the element + * @param the element's type + * @param element the element * @return a clone of element * @see spoon.reflect.declaration.CtElement#clone() */ @@ -496,4 +494,11 @@ SourcePosition createSourcePosition( * Creates an unbound variable used in noclasspath. */ CtUnboundVariableReference createUnboundVariableReference(); + + /** + * Creates an instance of the concrete metamodel class given as parameter. + * + * This is in particular useful when one uses reflection. + */ + CtElement create(Class klass); } diff --git a/src/main/java/spoon/support/DefaultCoreFactory.java b/src/main/java/spoon/support/DefaultCoreFactory.java index e65edd392e9..e3cf339dc47 100644 --- a/src/main/java/spoon/support/DefaultCoreFactory.java +++ b/src/main/java/spoon/support/DefaultCoreFactory.java @@ -659,4 +659,221 @@ public CtSuperAccess createSuperAccess() { return e; } + public CtElement create(Class klass) { + if (klass.equals(spoon.reflect.code.CtAnnotationFieldAccess.class)) { + return createAnnotationFieldAccess(); + } + if (klass.equals(spoon.reflect.code.CtArrayRead.class)) { + return createArrayRead(); + } + if (klass.equals(spoon.reflect.code.CtArrayWrite.class)) { + return createArrayWrite(); + } + if (klass.equals(spoon.reflect.code.CtAssert.class)) { + return createAssert(); + } + if (klass.equals(spoon.reflect.code.CtAssignment.class)) { + return createAssignment(); + } + if (klass.equals(spoon.reflect.code.CtBinaryOperator.class)) { + return createBinaryOperator(); + } + if (klass.equals(spoon.reflect.code.CtBlock.class)) { + return createBlock(); + } + if (klass.equals(spoon.reflect.code.CtBreak.class)) { + return createBreak(); + } + if (klass.equals(spoon.reflect.code.CtCase.class)) { + return createCase(); + } + if (klass.equals(spoon.reflect.code.CtCatch.class)) { + return createCatch(); + } + if (klass.equals(spoon.reflect.code.CtCatchVariable.class)) { + return createCatchVariable(); + } + if (klass.equals(spoon.reflect.code.CtCodeSnippetExpression.class)) { + return createCodeSnippetExpression(); + } + if (klass.equals(spoon.reflect.code.CtCodeSnippetStatement.class)) { + return createCodeSnippetStatement(); + } + if (klass.equals(spoon.reflect.code.CtComment.class)) { + return createComment(); + } + if (klass.equals(spoon.reflect.code.CtConditional.class)) { + return createConditional(); + } + if (klass.equals(spoon.reflect.code.CtConstructorCall.class)) { + return createConstructorCall(); + } + if (klass.equals(spoon.reflect.code.CtContinue.class)) { + return createContinue(); + } + if (klass.equals(spoon.reflect.code.CtDo.class)) { + return createDo(); + } + if (klass.equals(spoon.reflect.code.CtExecutableReferenceExpression.class)) { + return createExecutableReferenceExpression(); + } + if (klass.equals(spoon.reflect.code.CtFieldRead.class)) { + return createFieldRead(); + } + if (klass.equals(spoon.reflect.code.CtFieldWrite.class)) { + return createFieldWrite(); + } + if (klass.equals(spoon.reflect.code.CtForEach.class)) { + return createForEach(); + } + if (klass.equals(spoon.reflect.code.CtFor.class)) { + return createFor(); + } + if (klass.equals(spoon.reflect.code.CtIf.class)) { + return createIf(); + } + if (klass.equals(spoon.reflect.code.CtInvocation.class)) { + return createInvocation(); + } + if (klass.equals(spoon.reflect.code.CtLambda.class)) { + return createLambda(); + } + if (klass.equals(spoon.reflect.code.CtLiteral.class)) { + return createLiteral(); + } + if (klass.equals(spoon.reflect.code.CtLocalVariable.class)) { + return createLocalVariable(); + } + if (klass.equals(spoon.reflect.code.CtNewArray.class)) { + return createNewArray(); + } + if (klass.equals(spoon.reflect.code.CtNewClass.class)) { + return createNewClass(); + } + if (klass.equals(spoon.reflect.code.CtOperatorAssignment.class)) { + return createOperatorAssignment(); + } + if (klass.equals(spoon.reflect.code.CtReturn.class)) { + return createReturn(); + } + if (klass.equals(spoon.reflect.code.CtStatementList.class)) { + return createStatementList(); + } + if (klass.equals(spoon.reflect.code.CtSuperAccess.class)) { + return createSuperAccess(); + } + if (klass.equals(spoon.reflect.code.CtSwitch.class)) { + return createSwitch(); + } + if (klass.equals(spoon.reflect.code.CtSynchronized.class)) { + return createSynchronized(); + } + if (klass.equals(spoon.reflect.code.CtThisAccess.class)) { + return createThisAccess(); + } + if (klass.equals(spoon.reflect.code.CtThrow.class)) { + return createThrow(); + } + if (klass.equals(spoon.reflect.code.CtTry.class)) { + return createTry(); + } + if (klass.equals(spoon.reflect.code.CtTryWithResource.class)) { + return createTryWithResource(); + } + if (klass.equals(spoon.reflect.code.CtTypeAccess.class)) { + return createTypeAccess(); + } + if (klass.equals(spoon.reflect.code.CtUnaryOperator.class)) { + return createUnaryOperator(); + } + if (klass.equals(spoon.reflect.code.CtVariableRead.class)) { + return createVariableRead(); + } + if (klass.equals(spoon.reflect.code.CtVariableWrite.class)) { + return createVariableWrite(); + } + if (klass.equals(spoon.reflect.code.CtWhile.class)) { + return createWhile(); + } + if (klass.equals(spoon.reflect.declaration.CtAnnotation.class)) { + return createAnnotation(); + } + if (klass.equals(spoon.reflect.declaration.CtAnnotationMethod.class)) { + return createAnnotationMethod(); + } + if (klass.equals(spoon.reflect.declaration.CtAnnotationType.class)) { + return createAnnotationType(); + } + if (klass.equals(spoon.reflect.declaration.CtAnonymousExecutable.class)) { + return createAnonymousExecutable(); + } + if (klass.equals(spoon.reflect.declaration.CtClass.class)) { + return createClass(); + } + if (klass.equals(spoon.reflect.declaration.CtConstructor.class)) { + return createConstructor(); + } + if (klass.equals(spoon.reflect.declaration.CtEnum.class)) { + return createEnum(); + } + if (klass.equals(spoon.reflect.declaration.CtEnumValue.class)) { + return createEnumValue(); + } + if (klass.equals(spoon.reflect.declaration.CtField.class)) { + return createField(); + } + if (klass.equals(spoon.reflect.declaration.CtInterface.class)) { + return createInterface(); + } + if (klass.equals(spoon.reflect.declaration.CtMethod.class)) { + return createMethod(); + } + if (klass.equals(spoon.reflect.declaration.CtPackage.class)) { + return createPackage(); + } + if (klass.equals(spoon.reflect.declaration.CtParameter.class)) { + return createParameter(); + } + if (klass.equals(spoon.reflect.declaration.CtTypeParameter.class)) { + return createTypeParameter(); + } + if (klass.equals(spoon.reflect.reference.CtArrayTypeReference.class)) { + return createArrayTypeReference(); + } + if (klass.equals(spoon.reflect.reference.CtCatchVariableReference.class)) { + return createCatchVariableReference(); + } + if (klass.equals(spoon.reflect.reference.CtExecutableReference.class)) { + return createExecutableReference(); + } + if (klass.equals(spoon.reflect.reference.CtFieldReference.class)) { + return createFieldReference(); + } + if (klass.equals(spoon.reflect.reference.CtIntersectionTypeReference.class)) { + return createIntersectionTypeReference(); + } + if (klass.equals(spoon.reflect.reference.CtLocalVariableReference.class)) { + return createLocalVariableReference(); + } + if (klass.equals(spoon.reflect.reference.CtPackageReference.class)) { + return createPackageReference(); + } + if (klass.equals(spoon.reflect.reference.CtParameterReference.class)) { + return createParameterReference(); + } + if (klass.equals(spoon.reflect.reference.CtTypeParameterReference.class)) { + return createTypeParameterReference(); + } + if (klass.equals(spoon.reflect.reference.CtTypeReference.class)) { + return createTypeReference(); + } + if (klass.equals(spoon.reflect.reference.CtUnboundVariableReference.class)) { + return createUnboundVariableReference(); + } + if (klass.equals(spoon.reflect.reference.CtWildcardReference.class)) { + return createWildcardReference(); + } + throw new IllegalArgumentException("not instantiable by CoreFactory()"); + } + } diff --git a/src/test/java/spoon/reflect/visitor/CtInheritanceScannerTest.java b/src/test/java/spoon/reflect/visitor/CtInheritanceScannerTest.java index 9aefe099ef0..25e20c7a745 100644 --- a/src/test/java/spoon/reflect/visitor/CtInheritanceScannerTest.java +++ b/src/test/java/spoon/reflect/visitor/CtInheritanceScannerTest.java @@ -39,7 +39,9 @@ public class CtInheritanceScannerTest { public static Collection data() throws Exception { List values = new ArrayList<>(); for (Method method : CoreFactory.class.getDeclaredMethods()) { - if (method.getName().startsWith("create") && method.getReturnType().getSimpleName().startsWith("Ct")) { + if (method.getName().startsWith("create") + && method.getParameterCount() == 0 + && method.getReturnType().getSimpleName().startsWith("Ct")) { values.add(new Object[] { method.getReturnType(), method.invoke(factory.Core()) }); } } diff --git a/src/test/java/spoon/test/SpoonTestHelpers.java b/src/test/java/spoon/test/SpoonTestHelpers.java new file mode 100644 index 00000000000..96d6e745b44 --- /dev/null +++ b/src/test/java/spoon/test/SpoonTestHelpers.java @@ -0,0 +1,44 @@ +package spoon.test; + +import spoon.Launcher; +import spoon.SpoonAPI; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.ModifierKind; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class SpoonTestHelpers { + // only static methods + private SpoonTestHelpers(){ + } + + public static List> getAllInstantiableMetamodelInterfaces() { + List> result = new ArrayList<>(); + SpoonAPI interfaces = new Launcher(); + interfaces.addInputResource("src/main/java/spoon/reflect/declaration"); + interfaces.addInputResource("src/main/java/spoon/reflect/code"); + interfaces.addInputResource("src/main/java/spoon/reflect/reference"); + interfaces.buildModel(); + + SpoonAPI implementations = new Launcher(); + implementations.addInputResource("src/main/java/spoon/support/reflect/declaration"); + implementations.addInputResource("src/main/java/spoon/support/reflect/code"); + implementations.addInputResource("src/main/java/spoon/support/reflect/reference"); + implementations.buildModel(); + + for(CtType itf : interfaces.getModel().getAllTypes()) { + String impl = itf.getQualifiedName().replace("spoon.reflect", "spoon.support.reflect")+"Impl"; + CtType implClass = implementations.getFactory().Type().get(impl); + if (implClass != null && !implClass.hasModifier(ModifierKind.ABSTRACT)) { + result.add((CtType) itf); + } + } + return result; + } +} diff --git a/src/test/java/spoon/test/factory/FactoryTest.java b/src/test/java/spoon/test/factory/FactoryTest.java index 2ed27c4f7c7..834373c8a00 100644 --- a/src/test/java/spoon/test/factory/FactoryTest.java +++ b/src/test/java/spoon/test/factory/FactoryTest.java @@ -8,10 +8,12 @@ import spoon.reflect.code.CtFieldRead; import spoon.reflect.code.CtNewArray; import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.ModifierKind; import spoon.reflect.factory.CoreFactory; import spoon.reflect.factory.Factory; import spoon.reflect.factory.FactoryImpl; @@ -20,10 +22,12 @@ import spoon.support.DefaultCoreFactory; import spoon.support.StandardEnvironment; import spoon.support.reflect.declaration.CtMethodImpl; +import spoon.test.SpoonTestHelpers; import spoon.test.factory.testclasses.Foo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static spoon.testing.utils.ModelUtils.build; @@ -185,4 +189,14 @@ public boolean matches(CtPackage element) { }).size()); } + @Test + public void specificationCoreFactoryCreate() throws Exception { + // contract: all concrete metamodel classes must be instantiable by CoreFactory.create + for(CtType itf : SpoonTestHelpers.getAllInstantiableMetamodelInterfaces()) { + CtElement o = itf.getFactory().Core().create(itf.getActualClass()); + assertNotNull(o); + assertTrue(itf.getActualClass().isInstance(o)); + } + } + } diff --git a/src/test/java/spoon/test/parent/ParentContractTest.java b/src/test/java/spoon/test/parent/ParentContractTest.java index 7d0029a2cb0..b55211a274a 100644 --- a/src/test/java/spoon/test/parent/ParentContractTest.java +++ b/src/test/java/spoon/test/parent/ParentContractTest.java @@ -41,6 +41,7 @@ public static Collection data() throws Exception { List values = new ArrayList<>(); for (Method method : CoreFactory.class.getDeclaredMethods()) { if (method.getName().startsWith("create") + && method.getParameterCount() == 0 && method.getReturnType().getSimpleName().startsWith("Ct") && !CtReference.class.isAssignableFrom(method.getReturnType()) ){