@Test public void testPrintAMethodWithGeneric() throws Exception { final Launcher launcher = new Launcher(); final Factory factory = launcher.getFactory(); factory.getEnvironment().setAutoImports(true); final SpoonCompiler compiler = launcher.createCompiler(); compiler.addInputSource(new File("./src/test/java/spoon/test/prettyprinter/testclasses/")); compiler.build(); final CtClass<?> aClass = (CtClass<?>) factory.Type().get(AClass.class); final String expected = "public List<? extends ArrayList> aMethodWithGeneric() {" + System.lineSeparator() + " return new ArrayList<>();" + System.lineSeparator() + "}"; assertEquals(expected, aClass.getMethodsByName("aMethodWithGeneric").get(0).toString()); final CtConstructorCall<?> constructorCall = aClass.getElements(new TypeFilter<CtConstructorCall<?>>(CtConstructorCall.class)).get(0); final CtTypeReference<?> ctTypeReference = constructorCall.getType().getActualTypeArguments().get(0); assertTrue(ctTypeReference instanceof CtImplicitTypeReference); assertEquals("Object", ctTypeReference.getSimpleName()); }
@Test public void printerCanPrintInvocationWithoutException() throws Exception { String packageName = "spoon.test.subclass.prettyprinter"; String className = "DefaultPrettyPrinterExample"; String qualifiedName = packageName + "." + className; SpoonCompiler comp = new Launcher().createCompiler(); List<SpoonResource> fileToBeSpooned = SpoonResourceHelper.resources( "./src/test/resources/printer-test/" + qualifiedName.replace('.', '/') + ".java"); assertEquals(1, fileToBeSpooned.size()); comp.addInputSources(fileToBeSpooned); List<SpoonResource> classpath = SpoonResourceHelper.resources( "./src/test/resources/printer-test/DefaultPrettyPrinterDependency.jar"); assertEquals(1, classpath.size()); comp.setSourceClasspath(classpath.get(0).getPath()); comp.build(); Factory factory = comp.getFactory(); CtType<?> theClass = factory.Type().get(qualifiedName); List<CtInvocation<?>> elements = Query.getElements(theClass, new TypeFilter<CtInvocation<?>>(CtInvocation.class)); assertEquals(3, elements.size()); CtInvocation<?> mathAbsInvocation = elements.get(1); assertEquals("java.lang.Math.abs(message.length())", mathAbsInvocation.toString()); }
private ITypeModel processTypeReference(CtTypeReference<?> typeReference) { String qualifiedName = typeReference.getQualifiedName(); ITypeModel existingType = registry.getType(qualifiedName); if (existingType != null) { return new ProxyType(registry, qualifiedName); } CtClass<Object> ctClass = factory.Class().get(qualifiedName); if (ctClass != null) { return processType(ctClass); } CtType<Object> ctType = factory.Type().get(qualifiedName); if (ctType != null) { return processType(ctType); } TypeModel type = new TypeModel(); type.setFullyQualifiedName(qualifiedName); registry.registerType(type); fillReference(type, typeReference); Collection<CtExecutableReference<?>> methods = typeReference.getDeclaredExecutables(); for (CtExecutableReference<?> m : methods) { IMethodModel methodModel = processMethodReference(m); type.addMethod(methodModel); } Collection<CtFieldReference<?>> fields = typeReference.getDeclaredFields(); for (CtFieldReference<?> m : fields) { IFieldModel methodModel = processFieldReference(m); type.addField(methodModel); } return new ProxyType(registry, qualifiedName); }
@Test public void testDefaultMethodInInterface() throws Exception { final CtInterface<?> ctInterface = (CtInterface<?>) factory.Type().get(InterfaceWithDefaultMethods.class); final CtMethod<?> ctMethod = ctInterface.getMethodsByName("getZonedDateTime").get(0); assertTrue("The method in the interface must to be default", ctMethod.isDefaultMethod()); final String expected = "default java.time.ZonedDateTime getZonedDateTime(java.lang.String zoneString) {" + System.lineSeparator() + " return java.time.ZonedDateTime.of(getLocalDateTime(), spoon.test.interfaces.testclasses.InterfaceWithDefaultMethods.getZoneId(zoneString));" + System.lineSeparator() + "}"; assertEquals("The default method must to be well printed", expected, ctMethod.toString()); }
@Test public void useFullyQualifiedNamesInCtElementImpl_toString() throws Exception { Factory factory = TestUtils.build(AClass.class); factory.getEnvironment().setAutoImports(false); final CtClass<?> aClass = (CtClass<?>) factory.Type().get(AClass.class); String computed = aClass.getMethodsByName("aMethod").get(0).toString(); final String expected = "public java.util.List<?> aMethod() {" + nl + " return new java.util.ArrayList<>();" + nl + "}"; assertEquals( "the toString method of CtElementImpl should not shorten type names as it has no context or import statements", expected, computed); }
@Test public void testAddSameMethodsTwoTimes() throws Exception { final Factory factory = createFactory(); final CtClass<Object> tacos = factory.Class().create("Tacos"); final CtMethod<Void> method = factory.Method() .create( tacos, new HashSet<>(), factory.Type().voidType(), "m", new ArrayList<>(), new HashSet<>()); try { tacos.addMethod(method.clone()); } catch (ConcurrentModificationException e) { fail(); } }
@Test public void superInvocationWithEnclosingInstance() throws Exception { /** * To extend a nested class an enclosing instance must be provided to call the super * constructor. */ String sourcePath = "./src/test/resources/spoon/test/prettyprinter/NestedSuperCall.java"; List<SpoonResource> files = SpoonResourceHelper.resources(sourcePath); assertEquals(1, files.size()); SpoonCompiler comp = new Launcher().createCompiler(); comp.addInputSources(files); comp.build(); Factory factory = comp.getFactory(); CtType<?> theClass = factory.Type().get("spoon.test.prettyprinter.NestedSuperCall"); assertTrue(theClass.toString().contains("nc.super(\"a\")")); }
@Test public void testRedefinesStaticMethodInSubInterface() throws Exception { final CtInterface<?> ctInterface = (CtInterface<?>) factory.Type().get(RedefinesStaticMethodInterface.class); assertEquals( "Sub interface must have only one method in its interface", 1, ctInterface.getMethods().size()); assertEquals( "Sub interface must have 6+12(from java.lang.Object) methods in its interface and its super interfaces", 18, ctInterface.getAllMethods().size()); final CtMethod<?> getZoneIdMethod = ctInterface.getMethodsByName("getZoneId").get(0); assertFalse( "Method in the sub interface mustn't be a static method", getZoneIdMethod.getModifiers().contains(ModifierKind.STATIC)); assertEquals( "Interface of the static method must be the sub interface", RedefinesStaticMethodInterface.class, getZoneIdMethod.getDeclaringType().getActualClass()); }
@Test public void testRedefinesDefaultMethodInSubInterface() throws Exception { final CtInterface<?> ctInterface = (CtInterface<?>) factory.Type().get(RedefinesDefaultMethodInterface.class); assertEquals( "Sub interface must have only one method in its interface", 1, ctInterface.getMethods().size()); assertEquals( "Sub interface must have 6+12(from java.lang.Object) methods in its interface and its super interfaces", 18, ctInterface.getAllMethods().size()); final CtMethod<?> getZonedDateTimeMethod = ctInterface.getMethodsByName("getZonedDateTime").get(0); assertFalse( "Method in the sub interface mustn't be a default method", getZonedDateTimeMethod.isDefaultMethod()); assertEquals( "Interface of the default method must be the sub interface", RedefinesDefaultMethodInterface.class, getZonedDateTimeMethod.getDeclaringType().getActualClass()); }
@Test public void autoImportUsesFullyQualifiedNameWhenImportedNameAlreadyPresent() throws Exception { Factory factory = TestUtils.build( spoon.test.prettyprinter.testclasses.sub.TypeIdentifierCollision.class, spoon.test.prettyprinter.testclasses.TypeIdentifierCollision.class); factory.getEnvironment().setAutoImports(true); final CtClass<?> aClass = (CtClass<?>) factory.Type().get(spoon.test.prettyprinter.testclasses.TypeIdentifierCollision.class); String expected = "public void setFieldUsingExternallyDefinedEnumWithSameNameAsLocal() {" + nl + " localField = spoon.test.prettyprinter.testclasses.sub.TypeIdentifierCollision.ENUM.E1.ordinal();" + nl + "}"; String computed = aClass .getMethodsByName("setFieldUsingExternallyDefinedEnumWithSameNameAsLocal") .get(0) .toString(); assertEquals( "the externally defined enum should be fully qualified to avoid a name clash with the local enum of the same name", expected, computed); expected = // This is what is expected "public void setFieldUsingLocallyDefinedEnum() {" + nl + " localField = ENUM.E1.ordinal();" + nl + "}"; expected = // This is correct however it could be more concise. "public void setFieldUsingLocallyDefinedEnum() {" + nl + " localField = TypeIdentifierCollision.ENUM.E1.ordinal();" + nl + "}"; computed = aClass.getMethodsByName("setFieldUsingLocallyDefinedEnum").get(0).toString(); assertEquals(expected, computed); expected = "public void setFieldOfClassWithSameNameAsTheCompilationUnitClass() {" + nl + " spoon.test.prettyprinter.testclasses.sub.TypeIdentifierCollision.globalField = localField;" + nl + "}"; computed = aClass .getMethodsByName("setFieldOfClassWithSameNameAsTheCompilationUnitClass") .get(0) .toString(); assertEquals( "The static field of an external type with the same identifier as the compilation unit should be fully qualified", expected, computed); expected = // This is what is expected "public void referToTwoInnerClassesWithTheSameName() {" + nl + " ClassA.VAR0 = ClassA.getNum();" + nl + " Class1.ClassA.VAR1 = Class1.ClassA.getNum();" + nl + "}"; expected = // This is correct however it could be more concise. "public void referToTwoInnerClassesWithTheSameName() {" + nl + " TypeIdentifierCollision.Class0.ClassA.VAR0 = TypeIdentifierCollision.Class0.ClassA.getNum();" + nl + " TypeIdentifierCollision.Class1.ClassA.VAR1 = TypeIdentifierCollision.Class1.ClassA.getNum();" + nl + "}"; // Ensure the ClassA of Class0 takes precedence over an import statement for ClassA in Class1, // and it's identifier can be the short version. computed = aClass.getMethodsByName("referToTwoInnerClassesWithTheSameName").get(0).toString(); assertEquals( "where inner types have the same identifier only one may be shortened and the other should be fully qualified", expected, computed); expected = "public enum ENUM {" + nl + "E1(spoon.test.prettyprinter.testclasses.sub.TypeIdentifierCollision.globalField,spoon.test.prettyprinter.testclasses.sub.TypeIdentifierCollision.ENUM.E1);" + nl + " final int NUM;" + nl + " final Enum<?> e;" + nl + " private ENUM(int num ,Enum<?> e) {" + nl + " NUM = num;" + nl + " this.e = e;" + nl + " }}"; computed = aClass.getNestedType("ENUM").toString(); assertEquals( "Parameters in an enum constructor should be fully typed when they refer to externally defined static field of a class with the same identifier as another locally defined type", expected, computed); }