1616
1717package com .palantir .javaformat .java ;
1818
19+ import static com .google .common .base .Preconditions .checkState ;
20+ import static com .google .common .collect .Iterables .getLast ;
21+
1922import com .google .common .collect .ImmutableList ;
2023import com .google .common .collect .Ordering ;
2124import com .google .common .collect .Range ;
2629import com .sun .tools .javac .parser .Tokens .TokenKind ;
2730import java .util .ArrayList ;
2831import java .util .Collection ;
29- import java .util .Collections ;
3032import java .util .Iterator ;
3133import java .util .List ;
3234import java .util .Map ;
35+ import javax .annotation .Nullable ;
3336import javax .lang .model .element .Modifier ;
3437
3538/** Fixes sequences of modifiers to be in JLS order. */
@@ -40,6 +43,74 @@ static JavaInput reorderModifiers(String text) throws FormatterException {
4043 return reorderModifiers (new JavaInput (text ), ImmutableList .of (Range .closedOpen (0 , text .length ())));
4144 }
4245
46+ /**
47+ * The tokens that make up one modifier. Usually a single token (e.g. for {@code public}), but a modifier that
48+ * contains a {@code -} (e.g. {@code non-sealed}) is lexed as three tokens.
49+ */
50+ static final class ModifierTokens implements Comparable <ModifierTokens > {
51+ private final ImmutableList <Token > tokens ;
52+
53+ @ Nullable
54+ private final Modifier modifier ;
55+
56+ static ModifierTokens create (ImmutableList <Token > tokens ) {
57+ return new ModifierTokens (tokens , asModifier (tokens ));
58+ }
59+
60+ static ModifierTokens empty () {
61+ return new ModifierTokens (ImmutableList .of (), null );
62+ }
63+
64+ private ModifierTokens (ImmutableList <Token > tokens , @ Nullable Modifier modifier ) {
65+ this .tokens = tokens ;
66+ this .modifier = modifier ;
67+ }
68+
69+ boolean isEmpty () {
70+ return tokens .isEmpty () || modifier == null ;
71+ }
72+
73+ @ SuppressWarnings ("for-rollout:NullAway" )
74+ Modifier modifier () {
75+ return modifier ;
76+ }
77+
78+ ImmutableList <Token > tokens () {
79+ return tokens ;
80+ }
81+
82+ private Token first () {
83+ return tokens .get (0 );
84+ }
85+
86+ private Token last () {
87+ return getLast (tokens );
88+ }
89+
90+ int startPosition () {
91+ return first ().getTok ().getPosition ();
92+ }
93+
94+ int endPosition () {
95+ return last ().getTok ().getPosition () + last ().getTok ().length ();
96+ }
97+
98+ ImmutableList <? extends Tok > getToksBefore () {
99+ return first ().getToksBefore ();
100+ }
101+
102+ ImmutableList <? extends Tok > getToksAfter () {
103+ return last ().getToksAfter ();
104+ }
105+
106+ @ Override
107+ @ SuppressWarnings ("for-rollout:NullAway" )
108+ public int compareTo (ModifierTokens o ) {
109+ checkState (!isEmpty ()); // empty ModifierTokens are filtered out prior to sorting
110+ return modifier .compareTo (o .modifier );
111+ }
112+ }
113+
43114 /** Reorders all modifiers in the given text and within the given character ranges to be in JLS order. */
44115 static JavaInput reorderModifiers (JavaInput javaInput , Collection <Range <Integer >> characterRanges )
45116 throws FormatterException {
@@ -52,43 +123,37 @@ static JavaInput reorderModifiers(JavaInput javaInput, Collection<Range<Integer>
52123 Iterator <? extends Token > it = javaInput .getTokens ().iterator ();
53124 TreeRangeMap <Integer , String > replacements = TreeRangeMap .create ();
54125 while (it .hasNext ()) {
55- Token token = it .next ();
56- if (!tokenRanges .contains (token .getTok ().getIndex ())) {
57- continue ;
58- }
59- Modifier mod = asModifier (token );
60- if (mod == null ) {
126+ ModifierTokens tokens = getModifierTokens (it );
127+ if (tokens .isEmpty ()
128+ || !tokens .tokens ().stream ()
129+ .allMatch (token -> tokenRanges .contains (token .getTok ().getIndex ()))) {
61130 continue ;
62131 }
63132
64- List <Token > modifierTokens = new ArrayList <>();
65- List <Modifier > mods = new ArrayList <>();
133+ List <ModifierTokens > modifierTokens = new ArrayList <>();
66134
67- int begin = token .getTok ().getPosition ();
68- mods .add (mod );
69- modifierTokens .add (token );
135+ int begin = tokens .startPosition ();
136+ modifierTokens .add (tokens );
70137
71138 int end = -1 ;
72139 while (it .hasNext ()) {
73- token = it .next ();
74- mod = asModifier (token );
75- if (mod == null ) {
140+ tokens = getModifierTokens (it );
141+ if (tokens .isEmpty ()) {
76142 break ;
77143 }
78- mods .add (mod );
79- modifierTokens .add (token );
80- end = token .getTok ().getPosition () + token .getTok ().length ();
144+ modifierTokens .add (tokens );
145+ end = tokens .endPosition ();
81146 }
82147
83- if (!Ordering .natural ().isOrdered (mods )) {
84- Collections . sort ( mods );
148+ if (!Ordering .natural ().isOrdered (modifierTokens )) {
149+ List < ModifierTokens > sorted = Ordering . natural (). sortedCopy ( modifierTokens );
85150 StringBuilder replacement = new StringBuilder ();
86- for (int i = 0 ; i < mods .size (); i ++) {
151+ for (int i = 0 ; i < sorted .size (); i ++) {
87152 if (i > 0 ) {
88153 addTrivia (replacement , modifierTokens .get (i ).getToksBefore ());
89154 }
90- replacement .append (mods .get (i ).toString ());
91- if (i < (modifierTokens .size () - 1 )) {
155+ replacement .append (sorted .get (i ).modifier ());
156+ if (i < (sorted .size () - 1 )) {
92157 addTrivia (replacement , modifierTokens .get (i ).getToksAfter ());
93158 }
94159 }
@@ -104,9 +169,45 @@ private static void addTrivia(StringBuilder replacement, ImmutableList<? extends
104169 }
105170 }
106171
172+ /**
173+ * Consumes the tokens of one modifier from the iterator: the next token, plus the two after it when it is the
174+ * {@code non} of a hyphenated modifier such as {@code non-sealed}. The result is empty if they are not a modifier.
175+ */
176+ private static ModifierTokens getModifierTokens (Iterator <? extends Token > it ) {
177+ Token token = it .next ();
178+ ImmutableList .Builder <Token > result = ImmutableList .builder ();
179+ result .add (token );
180+ if (!token .getTok ().getText ().equals ("non" )) {
181+ return ModifierTokens .create (result .build ());
182+ }
183+ if (!it .hasNext ()) {
184+ return ModifierTokens .empty ();
185+ }
186+ Token dash = it .next ();
187+ result .add (dash );
188+ if (!dash .getTok ().getText ().equals ("-" ) || !it .hasNext ()) {
189+ return ModifierTokens .empty ();
190+ }
191+ result .add (it .next ());
192+ return ModifierTokens .create (result .build ());
193+ }
194+
195+ @ Nullable
196+ private static Modifier asModifier (ImmutableList <Token > tokens ) {
197+ if (tokens .size () == 1 ) {
198+ return asModifier (tokens .get (0 ));
199+ }
200+ Modifier modifier = asModifier (getLast (tokens ));
201+ if (modifier == null ) {
202+ return null ;
203+ }
204+ return Modifier .valueOf ("NON_" + modifier .name ());
205+ }
206+
107207 /**
108208 * Returns the given token as a {@link javax.lang.model.element.Modifier}, or {@code null} if it is not a modifier.
109209 */
210+ @ Nullable
110211 @ SuppressWarnings ("for-rollout:NullAway" )
111212 private static Modifier asModifier (Token token ) {
112213 TokenKind kind = ((JavaInput .Tok ) token .getTok ()).kind ();
@@ -140,8 +241,6 @@ private static Modifier asModifier(Token token) {
140241 }
141242 }
142243 switch (token .getTok ().getText ()) {
143- case "non-sealed" :
144- return Modifier .valueOf ("NON_SEALED" );
145244 case "sealed" :
146245 return Modifier .valueOf ("SEALED" );
147246 default :
0 commit comments