import com.palantir.javaformat.java.Formatter; import com.palantir.javaformat.java.JavaFormatterOptions; import java.nio.file.*; import java.security.MessageDigest; import java.util.*; import org.openrewrite.*; import org.openrewrite.internal.InMemoryLargeSourceSet; import org.openrewrite.text.*; /** Source-launcher bootstrap: no project-specific artifact is resolved or installed. */ public class Format { public static void main(String[] args) throws Exception { Path root = Path.of(args[0]).toAbsolutePath().normalize(); boolean check = switch (args[1]) { case "check" -> true; case "apply" -> false; default -> throw new IllegalArgumentException("format.mode must be apply or check"); }; Path cacheFile = root.resolve("target/palantir-cache.properties"); var cache = new Properties(); if (Files.exists(cacheFile)) { try (var input = Files.newInputStream(cacheFile)) { cache.load(input); } } String configuration = digest(Files.readString(root.resolve("pom.xml")) + Files.readString(root.resolve("generated/tooling/format/Format.java")) + System.getProperty("java.version") + System.getProperty("java.class.path")); if (!configuration.equals(cache.getProperty("configuration"))) cache.clear(); cache.setProperty("configuration", configuration); List sources = new ArrayList<>(); var settings = new Properties(); try (var input = Files.newInputStream(root.resolve("engineering.properties"))) { settings.load(input); } for (String directory : settings.getProperty("FORMAT_DIRECTORIES", "backend/src").split(",")) { if (!Files.isDirectory(root.resolve(directory))) continue; try (var paths = Files.walk(root.resolve(directory))) { for (Path path : paths.filter(p -> p.toString().endsWith(".java")) .sorted() .toList()) { try { String content = Files.readString(path); String key = root.relativize(path).toString(); if (!digest(content).equals(cache.getProperty(key))) { sources.add(PlainText.builder() .sourcePath(root.relativize(path)) .text(content) .build()); } } catch (Exception error) { throw new IllegalStateException(path + ": " + error.getMessage(), error); } } } } var context = new InMemoryExecutionContext(error -> { throw new IllegalStateException("Formatting failed: " + error.getMessage(), error); }); var results = new PalantirRecipe() .run(new InMemoryLargeSourceSet(sources), context) .getChangeset() .getAllResults(); for (Result result : results) { System.out.println((check ? "Needs formatting: " : "Formatted: ") + result.getAfter().getSourcePath()); } if (check && !results.isEmpty()) { throw new IllegalStateException("Run mvn -N exec:exec@format to apply Palantir formatting."); } // Complete parsing/formatting before writing any maintained file. Each replacement is atomic. if (!check) { for (Result result : results) { Path destination = root.resolve(result.getAfter().getSourcePath()); Path temporary = Files.createTempFile(destination.getParent(), ".palantir-", ".tmp"); try { Files.writeString(temporary, result.getAfter().printAll()); Files.setPosixFilePermissions(temporary, Files.getPosixFilePermissions(destination)); Files.move( temporary, destination, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); } finally { Files.deleteIfExists(temporary); } } } for (SourceFile source : sources) { cache.setProperty( source.getSourcePath().toString(), digest(Files.readString(root.resolve(source.getSourcePath())))); } Files.createDirectories(cacheFile.getParent()); try (var output = Files.newOutputStream(cacheFile)) { cache.store(output, "Successfully formatted content only"); } System.out.println( "Palantir " + args[1] + ": " + sources.size() + " Java files, " + results.size() + " differences"); } static String digest(String value) throws Exception { return HexFormat.of() .formatHex(MessageDigest.getInstance("SHA-256") .digest(value.getBytes(java.nio.charset.StandardCharsets.UTF_8))); } public static final class PalantirRecipe extends Recipe { public String getDisplayName() { return "Palantir Java Format"; } public String getDescription() { return "Apply the pinned Palantir engine and its native import handling."; } public TreeVisitor getVisitor() { return new PlainTextVisitor() { final Formatter formatter = Formatter.createFormatter(JavaFormatterOptions.builder() .style(JavaFormatterOptions.Style.PALANTIR) .formatJavadoc(false) .build()); @Override public PlainText visitText(PlainText text, ExecutionContext context) { try { return text.withText(formatter.formatSourceAndFixImports(text.getText())); } catch (Exception | LinkageError error) { throw new IllegalStateException(text.getSourcePath() + ": " + error, error); } } }; } } }