• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.google.googlejavaformat.java;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.Ordering;
21 import com.google.common.collect.Range;
22 import com.google.common.collect.RangeSet;
23 import com.google.common.collect.TreeRangeMap;
24 import com.google.googlejavaformat.Input.Tok;
25 import com.google.googlejavaformat.Input.Token;
26 import com.sun.tools.javac.parser.Tokens.TokenKind;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import javax.lang.model.element.Modifier;
35 
36 /** Fixes sequences of modifiers to be in JLS order. */
37 final class ModifierOrderer {
38 
39   /**
40    * Returns the {@link javax.lang.model.element.Modifier} for the given token kind, or {@code
41    * null}.
42    */
getModifier(TokenKind kind)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 
77   /** Reorders all modifiers in the given text to be in JLS order. */
reorderModifiers(String text)78   static JavaInput reorderModifiers(String text) throws FormatterException {
79     return reorderModifiers(
80         new JavaInput(text), ImmutableList.of(Range.closedOpen(0, text.length())));
81   }
82 
83   /**
84    * Reorders all modifiers in the given text and within the given character ranges to be in JLS
85    * order.
86    */
reorderModifiers(JavaInput javaInput, Collection<Range<Integer>> characterRanges)87   static JavaInput reorderModifiers(JavaInput javaInput, Collection<Range<Integer>> characterRanges)
88       throws FormatterException {
89     if (javaInput.getTokens().isEmpty()) {
90       // There weren't any tokens, possible because of a lexing error.
91       // Errors about invalid input will be reported later after parsing.
92       return javaInput;
93     }
94     RangeSet<Integer> tokenRanges = javaInput.characterRangesToTokenRanges(characterRanges);
95     Iterator<? extends Token> it = javaInput.getTokens().iterator();
96     TreeRangeMap<Integer, String> replacements = TreeRangeMap.create();
97     while (it.hasNext()) {
98       Token token = it.next();
99       if (!tokenRanges.contains(token.getTok().getIndex())) {
100         continue;
101       }
102       Modifier mod = asModifier(token);
103       if (mod == null) {
104         continue;
105       }
106 
107       List<Token> modifierTokens = new ArrayList<>();
108       List<Modifier> mods = new ArrayList<>();
109 
110       int begin = token.getTok().getPosition();
111       mods.add(mod);
112       modifierTokens.add(token);
113 
114       int end = -1;
115       while (it.hasNext()) {
116         token = it.next();
117         mod = asModifier(token);
118         if (mod == null) {
119           break;
120         }
121         mods.add(mod);
122         modifierTokens.add(token);
123         end = token.getTok().getPosition() + token.getTok().length();
124       }
125 
126       if (!Ordering.natural().isOrdered(mods)) {
127         Collections.sort(mods);
128         StringBuilder replacement = new StringBuilder();
129         for (int i = 0; i < mods.size(); i++) {
130           if (i > 0) {
131             addTrivia(replacement, modifierTokens.get(i).getToksBefore());
132           }
133           replacement.append(mods.get(i).toString());
134           if (i < (modifierTokens.size() - 1)) {
135             addTrivia(replacement, modifierTokens.get(i).getToksAfter());
136           }
137         }
138         replacements.put(Range.closedOpen(begin, end), replacement.toString());
139       }
140     }
141     return applyReplacements(javaInput, replacements);
142   }
143 
addTrivia(StringBuilder replacement, ImmutableList<? extends Tok> toks)144   private static void addTrivia(StringBuilder replacement, ImmutableList<? extends Tok> toks) {
145     for (Tok tok : toks) {
146       replacement.append(tok.getText());
147     }
148   }
149 
150   /**
151    * Returns the given token as a {@link javax.lang.model.element.Modifier}, or {@code null} if it
152    * is not a modifier.
153    */
asModifier(Token token)154   private static Modifier asModifier(Token token) {
155     return getModifier(((JavaInput.Tok) token.getTok()).kind());
156   }
157 
158   /** Applies replacements to the given string. */
applyReplacements( JavaInput javaInput, TreeRangeMap<Integer, String> replacementMap)159   private static JavaInput applyReplacements(
160       JavaInput javaInput, TreeRangeMap<Integer, String> replacementMap) throws FormatterException {
161     // process in descending order so the replacement ranges aren't perturbed if any replacements
162     // differ in size from the input
163     Map<Range<Integer>, String> ranges = replacementMap.asDescendingMapOfRanges();
164     if (ranges.isEmpty()) {
165       return javaInput;
166     }
167     StringBuilder sb = new StringBuilder(javaInput.getText());
168     for (Entry<Range<Integer>, String> entry : ranges.entrySet()) {
169       Range<Integer> range = entry.getKey();
170       sb.replace(range.lowerEndpoint(), range.upperEndpoint(), entry.getValue());
171     }
172     return new JavaInput(sb.toString());
173   }
174 }
175