Skip to content

Commit cc8b96f

Browse files
nowgoogle-java-format Team
authored andcommitted
Support sealed classes
This implements support for JEP 409: Sealed Classes, see https://openjdk.java.net/jeps/409. The implementation handles the sealed and non-sealed keywords and sorts them according to their JLS order. This requires us to look at the actual text of the Tok, as there’s no TokenKind for sealed and non-sealed. The optional permits clause is handled in the same manner as the implements clause. A new private method, classDeclarationTypeList, has been added to facilitate this. This fixes #603. I implemented this in a manner that I deemed fit with the surrounding code, but if I missed something, don’t hesitate to get back to me and I’ll adjust this PR to suit. Fixes #629 COPYBARA_INTEGRATE_REVIEW=#629 from now:I603 4825e33 PiperOrigin-RevId: 387160750
1 parent bf5ef1c commit cc8b96f

File tree

6 files changed

+116
-56
lines changed

6 files changed

+116
-56
lines changed

core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,8 +1961,10 @@ public void visitClassDeclaration(ClassTree node) {
19611961
node.getModifiers(),
19621962
Direction.VERTICAL,
19631963
/* declarationAnnotationBreak= */ Optional.empty());
1964+
List<? extends Tree> permitsTypes = getPermitsClause(node);
19641965
boolean hasSuperclassType = node.getExtendsClause() != null;
19651966
boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty();
1967+
boolean hasPermitsTypes = !permitsTypes.isEmpty();
19661968
builder.addAll(breaks);
19671969
token(node.getKind() == Tree.Kind.INTERFACE ? "interface" : "class");
19681970
builder.space();
@@ -1975,30 +1977,18 @@ public void visitClassDeclaration(ClassTree node) {
19751977
if (!node.getTypeParameters().isEmpty()) {
19761978
typeParametersRest(
19771979
node.getTypeParameters(),
1978-
hasSuperclassType || hasSuperInterfaceTypes ? plusFour : ZERO);
1980+
hasSuperclassType || hasSuperInterfaceTypes || hasPermitsTypes ? plusFour : ZERO);
19791981
}
19801982
if (hasSuperclassType) {
19811983
builder.breakToFill(" ");
19821984
token("extends");
19831985
builder.space();
19841986
scan(node.getExtendsClause(), null);
19851987
}
1986-
if (hasSuperInterfaceTypes) {
1987-
builder.breakToFill(" ");
1988-
builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO);
1989-
token(node.getKind() == Tree.Kind.INTERFACE ? "extends" : "implements");
1990-
builder.space();
1991-
boolean first = true;
1992-
for (Tree superInterfaceType : node.getImplementsClause()) {
1993-
if (!first) {
1994-
token(",");
1995-
builder.breakOp(" ");
1996-
}
1997-
scan(superInterfaceType, null);
1998-
first = false;
1999-
}
2000-
builder.close();
2001-
}
1988+
classDeclarationTypeList(
1989+
node.getKind() == Tree.Kind.INTERFACE ? "extends" : "implements",
1990+
node.getImplementsClause());
1991+
classDeclarationTypeList("permits", permitsTypes);
20021992
}
20031993
builder.close();
20041994
if (node.getMembers() == null) {
@@ -2277,6 +2267,8 @@ boolean nextIsModifier() {
22772267
case "native":
22782268
case "strictfp":
22792269
case "default":
2270+
case "sealed":
2271+
case "non-sealed":
22802272
return true;
22812273
default:
22822274
return false;
@@ -3549,6 +3541,31 @@ protected void addBodyDeclarations(
35493541
}
35503542
}
35513543

3544+
/** Gets the permits clause for the given node. This is only available in Java 15 and later. */
3545+
protected List<? extends Tree> getPermitsClause(ClassTree node) {
3546+
return ImmutableList.of();
3547+
}
3548+
3549+
private void classDeclarationTypeList(String token, List<? extends Tree> types) {
3550+
if (types.isEmpty()) {
3551+
return;
3552+
}
3553+
builder.breakToFill(" ");
3554+
builder.open(types.size() > 1 ? plusFour : ZERO);
3555+
token(token);
3556+
builder.space();
3557+
boolean first = true;
3558+
for (Tree type : types) {
3559+
if (!first) {
3560+
token(",");
3561+
builder.breakOp(" ");
3562+
}
3563+
scan(type, null);
3564+
first = false;
3565+
}
3566+
builder.close();
3567+
}
3568+
35523569
/**
35533570
* The parser expands multi-variable declarations into separate single-variable declarations. All
35543571
* of the fragments in the original declaration have the same start position, so we use that as a

core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -36,44 +36,6 @@
3636
/** Fixes sequences of modifiers to be in JLS order. */
3737
final class ModifierOrderer {
3838

39-
/**
40-
* Returns the {@link javax.lang.model.element.Modifier} for the given token kind, or {@code
41-
* null}.
42-
*/
43-
private static Modifier getModifier(TokenKind kind) {
44-
if (kind == null) {
45-
return null;
46-
}
47-
switch (kind) {
48-
case PUBLIC:
49-
return Modifier.PUBLIC;
50-
case PROTECTED:
51-
return Modifier.PROTECTED;
52-
case PRIVATE:
53-
return Modifier.PRIVATE;
54-
case ABSTRACT:
55-
return Modifier.ABSTRACT;
56-
case STATIC:
57-
return Modifier.STATIC;
58-
case DEFAULT:
59-
return Modifier.DEFAULT;
60-
case FINAL:
61-
return Modifier.FINAL;
62-
case TRANSIENT:
63-
return Modifier.TRANSIENT;
64-
case VOLATILE:
65-
return Modifier.VOLATILE;
66-
case SYNCHRONIZED:
67-
return Modifier.SYNCHRONIZED;
68-
case NATIVE:
69-
return Modifier.NATIVE;
70-
case STRICTFP:
71-
return Modifier.STRICTFP;
72-
default:
73-
return null;
74-
}
75-
}
76-
7739
/** Reorders all modifiers in the given text to be in JLS order. */
7840
static JavaInput reorderModifiers(String text) throws FormatterException {
7941
return reorderModifiers(
@@ -152,7 +114,44 @@ private static void addTrivia(StringBuilder replacement, ImmutableList<? extends
152114
* is not a modifier.
153115
*/
154116
private static Modifier asModifier(Token token) {
155-
return getModifier(((JavaInput.Tok) token.getTok()).kind());
117+
TokenKind kind = ((JavaInput.Tok) token.getTok()).kind();
118+
if (kind != null) {
119+
switch (kind) {
120+
case PUBLIC:
121+
return Modifier.PUBLIC;
122+
case PROTECTED:
123+
return Modifier.PROTECTED;
124+
case PRIVATE:
125+
return Modifier.PRIVATE;
126+
case ABSTRACT:
127+
return Modifier.ABSTRACT;
128+
case STATIC:
129+
return Modifier.STATIC;
130+
case DEFAULT:
131+
return Modifier.DEFAULT;
132+
case FINAL:
133+
return Modifier.FINAL;
134+
case TRANSIENT:
135+
return Modifier.TRANSIENT;
136+
case VOLATILE:
137+
return Modifier.VOLATILE;
138+
case SYNCHRONIZED:
139+
return Modifier.SYNCHRONIZED;
140+
case NATIVE:
141+
return Modifier.NATIVE;
142+
case STRICTFP:
143+
return Modifier.STRICTFP;
144+
default: // fall out
145+
}
146+
}
147+
switch (token.getTok().getText()) {
148+
case "non-sealed":
149+
return Modifier.valueOf("NON_SEALED");
150+
case "sealed":
151+
return Modifier.valueOf("SEALED");
152+
default:
153+
return null;
154+
}
156155
}
157156

158157
/** Applies replacements to the given string. */

core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ protected void handleModule(boolean first, CompilationUnitTree node) {
7171
}
7272
}
7373

74+
@Override
75+
protected List<? extends Tree> getPermitsClause(ClassTree node) {
76+
try {
77+
return (List<? extends Tree>) ClassTree.class.getMethod("getPermitsClause").invoke(node);
78+
} catch (ReflectiveOperationException e) {
79+
// Java < 15
80+
return super.getPermitsClause(node);
81+
}
82+
}
83+
7484
@Override
7585
public Void visitBindingPattern(BindingPatternTree node, Void unused) {
7686
sync(node);

core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public class FormatterIntegrationTest {
5050
private static final ImmutableSet<String> JAVA14_TESTS =
5151
ImmutableSet.of("I477", "Records", "RSLs", "Var", "ExpressionSwitch", "I574", "I594");
5252

53+
private static final ImmutableSet<String> JAVA15_TESTS = ImmutableSet.of("I603");
54+
5355
private static final ImmutableSet<String> JAVA16_TESTS = ImmutableSet.of("I588");
5456

5557
@Parameters(name = "{index}: {0}")
@@ -92,6 +94,9 @@ public static Iterable<Object[]> data() throws IOException {
9294
if (JAVA14_TESTS.contains(fileName) && getMajor() < 14) {
9395
continue;
9496
}
97+
if (JAVA15_TESTS.contains(fileName) && getMajor() < 15) {
98+
continue;
99+
}
95100
if (JAVA16_TESTS.contains(fileName) && getMajor() < 16) {
96101
continue;
97102
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class I603 {
2+
sealed abstract class T1 {}
3+
4+
sealed class T2 extends X implements Y permits Z {}
5+
6+
sealed class T3
7+
permits
8+
Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {}
9+
10+
sealed class T4
11+
implements
12+
Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
13+
permits
14+
Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
15+
Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy {}
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class I603 {
2+
abstract sealed class T1 {}
3+
4+
sealed class T2 extends X implements Y permits Z {}
5+
6+
sealed class T3
7+
permits Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {}
8+
9+
sealed class T4
10+
implements Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
11+
permits Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
12+
Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy {}
13+
}

0 commit comments

Comments
 (0)