diff --git a/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java b/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java index c1fc139cc..caabda721 100644 --- a/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java +++ b/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java @@ -1981,8 +1981,10 @@ public void visitClassDeclaration(ClassTree node) { sync(node); List breaks = visitModifiers( node.getModifiers(), Direction.VERTICAL, /* declarationAnnotationBreak= */ Optional.empty()); + List permitsTypes = getPermitsClause(node); boolean hasSuperclassType = node.getExtendsClause() != null; boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); + boolean hasPermitsTypes = !permitsTypes.isEmpty(); builder.addAll(breaks); token(node.getKind() == Tree.Kind.INTERFACE ? "interface" : "class"); builder.space(); @@ -1994,7 +1996,8 @@ public void visitClassDeclaration(ClassTree node) { { if (!node.getTypeParameters().isEmpty()) { typeParametersRest( - node.getTypeParameters(), hasSuperclassType || hasSuperInterfaceTypes ? plusFour : ZERO); + node.getTypeParameters(), + hasSuperclassType || hasSuperInterfaceTypes || hasPermitsTypes ? plusFour : ZERO); } if (hasSuperclassType) { builder.breakToFill(" "); @@ -2002,22 +2005,9 @@ public void visitClassDeclaration(ClassTree node) { builder.space(); scan(node.getExtendsClause(), null); } - if (hasSuperInterfaceTypes) { - builder.breakToFill(" "); - builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); - token(node.getKind() == Tree.Kind.INTERFACE ? "extends" : "implements"); - builder.space(); - boolean first = true; - for (Tree superInterfaceType : node.getImplementsClause()) { - if (!first) { - token(","); - builder.breakOp(" "); - } - scan(superInterfaceType, null); - first = false; - } - builder.close(); - } + classDeclarationTypeList( + node.getKind() == Tree.Kind.INTERFACE ? "extends" : "implements", node.getImplementsClause()); + classDeclarationTypeList("permits", permitsTypes); } builder.close(); if (node.getMembers() == null) { @@ -2290,6 +2280,8 @@ boolean nextIsModifier() { case "native": case "strictfp": case "default": + case "sealed": + case "non-sealed": return true; default: return false; @@ -3673,6 +3665,30 @@ protected void addBodyDeclarations( } } } + /** Gets the permits clause for the given node. This is only available in Java 15 and later. */ + protected List getPermitsClause(ClassTree node) { + return ImmutableList.of(); + } + + private void classDeclarationTypeList(String token, List types) { + if (types.isEmpty()) { + return; + } + builder.breakToFill(" "); + builder.open(types.size() > 1 ? plusFour : ZERO); + token(token); + builder.space(); + boolean first = true; + for (Tree type : types) { + if (!first) { + token(","); + builder.breakOp(" "); + } + scan(type, null); + first = false; + } + builder.close(); + } /** * The parser expands multi-variable declarations into separate single-variable declarations. All of the fragments diff --git a/palantir-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java b/palantir-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java index 187ab0d06..364b1df42 100644 --- a/palantir-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java +++ b/palantir-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java @@ -35,41 +35,6 @@ /** Fixes sequences of modifiers to be in JLS order. */ final class ModifierOrderer { - /** Returns the {@link javax.lang.model.element.Modifier} for the given token kind, or {@code null}. */ - private static Modifier getModifier(TokenKind kind) { - if (kind == null) { - return null; - } - switch (kind) { - case PUBLIC: - return Modifier.PUBLIC; - case PROTECTED: - return Modifier.PROTECTED; - case PRIVATE: - return Modifier.PRIVATE; - case ABSTRACT: - return Modifier.ABSTRACT; - case STATIC: - return Modifier.STATIC; - case DEFAULT: - return Modifier.DEFAULT; - case FINAL: - return Modifier.FINAL; - case TRANSIENT: - return Modifier.TRANSIENT; - case VOLATILE: - return Modifier.VOLATILE; - case SYNCHRONIZED: - return Modifier.SYNCHRONIZED; - case NATIVE: - return Modifier.NATIVE; - case STRICTFP: - return Modifier.STRICTFP; - default: - return null; - } - } - /** Reorders all modifiers in the given text to be in JLS order. */ static JavaInput reorderModifiers(String text) throws FormatterException { return reorderModifiers(new JavaInput(text), ImmutableList.of(Range.closedOpen(0, text.length()))); @@ -143,7 +108,44 @@ private static void addTrivia(StringBuilder replacement, ImmutableList getPermitsClause(ClassTree node) { + try { + return (List) + ClassTree.class.getMethod("getPermitsClause").invoke(node); + } catch (ReflectiveOperationException e) { + // Java < 15 + return super.getPermitsClause(node); + } + } + @Override public Void visitBindingPattern(BindingPatternTree node, Void unused) { sync(node); diff --git a/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java b/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java index 6dcf2d585..2be17094d 100644 --- a/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java +++ b/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java @@ -44,6 +44,7 @@ public final class FileBasedTests { // Test files that are only used when run with a minimum Java version private static final ImmutableSet JAVA_14_TESTS = ImmutableSet.of("ExpressionSwitch", "RSL", "Records", "Var", "I574", "I594"); + private static final ImmutableSet JAVA_15_TESTS = ImmutableSet.of("I603"); private static final ImmutableSet JAVA_16_TESTS = ImmutableSet.of("I588"); private final Class testClass; @@ -66,6 +67,8 @@ public FileBasedTests(Class testClass, String testDirName) { public static void assumeJavaVersionForTest(String testName) { if (JAVA_14_TESTS.contains(testName)) { Assumptions.assumeTrue(Formatter.getRuntimeVersion() >= 14, "Not running on jdk 14 or later"); + } else if (JAVA_15_TESTS.contains(testName)) { + Assumptions.assumeTrue(Formatter.getRuntimeVersion() >= 15, "Not running on jdk 15 or later"); } else if (JAVA_16_TESTS.contains(testName)) { Assumptions.assumeTrue(Formatter.getRuntimeVersion() >= 16, "Not running on jdk 16 or later"); } diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I603.input b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I603.input new file mode 100644 index 000000000..6cedc5300 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I603.input @@ -0,0 +1,16 @@ +class I603 { + sealed abstract class T1 {} + + sealed class T2 extends X implements Y permits Z {} + + sealed class T3 + permits + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {} + + sealed class T4 + implements + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + permits + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy {} +} diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I603.output b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I603.output new file mode 100644 index 000000000..af9d2fc18 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I603.output @@ -0,0 +1,13 @@ +class I603 { + abstract sealed class T1 {} + + sealed class T2 extends X implements Y permits Z {} + + sealed class T3 + permits Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {} + + sealed class T4 + implements Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + permits Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy {} +}