diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java index e22dae5d5..372f3bb59 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -1961,8 +1961,10 @@ public void visitClassDeclaration(ClassTree node) { 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(); @@ -1975,7 +1977,7 @@ public void visitClassDeclaration(ClassTree node) { if (!node.getTypeParameters().isEmpty()) { typeParametersRest( node.getTypeParameters(), - hasSuperclassType || hasSuperInterfaceTypes ? plusFour : ZERO); + hasSuperclassType || hasSuperInterfaceTypes || hasPermitsTypes ? plusFour : ZERO); } if (hasSuperclassType) { builder.breakToFill(" "); @@ -1983,22 +1985,10 @@ 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) { @@ -2277,6 +2267,8 @@ boolean nextIsModifier() { case "native": case "strictfp": case "default": + case "sealed": + case "non-sealed": return true; default: return false; @@ -3549,6 +3541,31 @@ 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 in the original declaration have the same start position, so we use that as a diff --git a/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java b/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java index 42eaf45f3..e14b29036 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java +++ b/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java @@ -36,44 +36,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( @@ -152,7 +114,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/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java index 31d59f171..edbb12b6e 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java @@ -50,6 +50,8 @@ public class FormatterIntegrationTest { private static final ImmutableSet JAVA14_TESTS = ImmutableSet.of("I477", "Records", "RSLs", "Var", "ExpressionSwitch", "I574", "I594"); + private static final ImmutableSet JAVA15_TESTS = ImmutableSet.of("I603"); + private static final ImmutableSet JAVA16_TESTS = ImmutableSet.of("I588"); @Parameters(name = "{index}: {0}") @@ -92,6 +94,9 @@ public static Iterable data() throws IOException { if (JAVA14_TESTS.contains(fileName) && getMajor() < 14) { continue; } + if (JAVA15_TESTS.contains(fileName) && getMajor() < 15) { + continue; + } if (JAVA16_TESTS.contains(fileName) && getMajor() < 16) { continue; } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.input new file mode 100644 index 000000000..6cedc5300 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/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/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.output new file mode 100644 index 000000000..22153b688 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/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 {} +}