Skip to content

Commit

Permalink
fix: Fix NullPointerException in SniperJavaPrettyPrinter.printTypes (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
slarse authored Nov 5, 2020
1 parent dae5bd2 commit ad8be93
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 13 deletions.
18 changes: 18 additions & 0 deletions src/main/java/spoon/support/sniper/SniperJavaPrettyPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package spoon.support.sniper;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
Expand All @@ -29,6 +30,7 @@
import spoon.support.Experimental;
import spoon.support.comparator.CtLineElementComparator;
import spoon.support.modelobs.ChangeCollector;
import spoon.support.reflect.declaration.CtCompilationUnitImpl;
import spoon.support.sniper.internal.ChangeResolver;
import spoon.support.sniper.internal.CollectionSourceFragment;
import spoon.support.sniper.internal.ElementPrinterEvent;
Expand Down Expand Up @@ -108,6 +110,22 @@ private TokenWriter createTokenWriterListener(TokenWriter tokenWriter) {
return new TokenWriterProxy(this, tokenWriter);
}

@Override
public String printTypes(CtType<?>... type) {
CtCompilationUnit cu = getUnambiguousCompilationUnit(type);
calculate(cu, Arrays.asList(type));
return getResult();
}

private static CtCompilationUnit getUnambiguousCompilationUnit(CtType<?>[] type) {
CtCompilationUnit sentinel = new CtCompilationUnitImpl();
return Arrays.stream(type)
.map(ctType -> (CtCompilationUnit) ctType.getFactory().CompilationUnit().getOrCreate(ctType))
.reduce((prev, next) -> prev == next ? next : sentinel)
.filter(unit -> unit != sentinel)
.orElseThrow(() -> new IllegalArgumentException("mismatching or missing compilation unit"));
}

@Override
public void calculate(CtCompilationUnit compilationUnit, List<CtType<?>> types) {
sourceCompilationUnit = compilationUnit;
Expand Down
72 changes: 59 additions & 13 deletions src/test/java/spoon/test/prettyprinter/TestSniperPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,47 @@ public void testPrintAfterRemoveOfFormalTypeParamsAndChangeOfReturnType() {
});
}

@Test
public void testPrintTypesProducesFullOutputForSingleTypeCompilationUnit() {
// contract: printTypes() should produce the same output as launcher.prettyprint() for a
// single-type compilation unit

// there is no particular reason for using the YamlRepresenter resource, it simply already
// existed and filled the role it needed to
String resourceName = "visibility.YamlRepresenter";
String inputPath = getResourcePath(resourceName);

Launcher printTypesLauncher = createLauncherWithSniperPrinter();
printTypesLauncher.addInputResource(inputPath);
printTypesLauncher.buildModel();
String printTypesString = printTypesLauncher.createPrettyPrinter()
.printTypes(printTypesLauncher.getModel().getAllTypes().toArray(new CtType[0]));

testSniper(resourceName, ctType -> {}, (type, prettyPrint) -> {
assertEquals(prettyPrint, printTypesString);
});
}

@Test
public void testPrintTypesThrowsWhenPassedTypesFromMultipleCompilationUnits() {
// contract: printTypes() should raise an IllegalArgumentException if it is passed types
// from multiple CUs

Launcher launcher = createLauncherWithSniperPrinter();
// there is no particular reason for the choice of these two resources, other than that
// they are different from each other and existed at the time of writing this test
launcher.addInputResource(getResourcePath("visibility.YamlRepresenter"));
launcher.addInputResource(getResourcePath("spoon.test.variable.Tacos"));
CtType<?>[] types = launcher.buildModel().getAllTypes().toArray(new CtType<?>[0]);

try {
launcher.getEnvironment().createPrettyPrinter().printTypes(types);
fail("Expected an IllegalArgumentException");
} catch (IllegalArgumentException e) {
// pass
}
}

/**
* 1) Runs spoon using sniper mode,
* 2) runs `typeChanger` to modify the code,
Expand All @@ -337,20 +378,8 @@ public void testPrintAfterRemoveOfFormalTypeParamsAndChangeOfReturnType() {
* @param resultChecker a code which checks that printed sources are as expected
*/
private void testSniper(String testClass, Consumer<CtType<?>> transformation, BiConsumer<CtType<?>, String> resultChecker) {
Launcher launcher = new Launcher();
Launcher launcher = createLauncherWithSniperPrinter();
launcher.addInputResource(getResourcePath(testClass));
launcher.getEnvironment().setPrettyPrinterCreator(() -> {
SniperJavaPrettyPrinter printer = new SniperJavaPrettyPrinter(launcher.getEnvironment());
printer.setPreprocessors(Collections.unmodifiableList(Arrays.<Processor<CtElement>>asList(
//remove unused imports first. Do not add new imports at time when conflicts are not resolved
new ImportCleaner().setCanAddImports(false),
//solve conflicts, the current imports are relevant too
new ImportConflictDetector(),
//compute final imports
new ImportCleaner()
)));
return printer;
});
launcher.buildModel();
Factory f = launcher.getFactory();

Expand All @@ -366,6 +395,23 @@ private void testSniper(String testClass, Consumer<CtType<?>> transformation, Bi
resultChecker.accept(ctClass, getContentOfPrettyPrintedClassFromDisk(ctClass));
}

private static Launcher createLauncherWithSniperPrinter() {
Launcher launcher = new Launcher();
launcher.getEnvironment().setPrettyPrinterCreator(() -> {
SniperJavaPrettyPrinter printer = new SniperJavaPrettyPrinter(launcher.getEnvironment());
printer.setPreprocessors(Collections.unmodifiableList(Arrays.<Processor<CtElement>>asList(
//remove unused imports first. Do not add new imports at time when conflicts are not resolved
new ImportCleaner().setCanAddImports(false),
//solve conflicts, the current imports are relevant too
new ImportConflictDetector(),
//compute final imports
new ImportCleaner()
)));
return printer;
});
return launcher;
}

private String getContentOfPrettyPrintedClassFromDisk(CtType<?> type) {
File outputFile = getFileForType(type);

Expand Down

0 comments on commit ad8be93

Please sign in to comment.