Skip to content

Commit 1660756

Browse files
committed
Add Sort Members functionality to Eclipse formatter
1 parent 13d24ae commit 1660756

File tree

15 files changed

+921
-39
lines changed

15 files changed

+921
-39
lines changed

lib-extra/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ p2deps {
8787
into 'jdtCompileOnly', {
8888
p2repo 'https://download.eclipse.org/eclipse/updates/4.26/'
8989
install 'org.eclipse.jdt.core'
90+
install 'org.eclipse.jdt.core.manipulation'
9091
}
9192
}
9293

@@ -95,4 +96,3 @@ spotbugs {
9596
// LOW|MEDIUM|DEFAULT|HIGH (low = sensitive to even minor mistakes).
9697
reportLevel = com.github.spotbugs.snom.Confidence.valueOf('LOW')
9798
}
98-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
package com.diffplug.spotless.extra.glue.jdt;
2+
3+
import java.util.Comparator;
4+
import java.util.List;
5+
import java.util.StringTokenizer;
6+
7+
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
8+
9+
import org.eclipse.jdt.core.Flags;
10+
import org.eclipse.jdt.core.dom.ASTNode;
11+
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
12+
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
13+
import org.eclipse.jdt.core.dom.BodyDeclaration;
14+
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
15+
import org.eclipse.jdt.core.dom.FieldDeclaration;
16+
import org.eclipse.jdt.core.dom.MethodDeclaration;
17+
import org.eclipse.jdt.core.dom.Modifier;
18+
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
19+
import org.eclipse.jdt.core.dom.Type;
20+
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
21+
import org.eclipse.jdt.core.util.CompilationUnitSorter;
22+
23+
class DefaultJavaElementComparator implements Comparator<BodyDeclaration> {
24+
25+
static final int TYPE_INDEX = 0;
26+
static final int CONSTRUCTORS_INDEX = 1;
27+
static final int METHOD_INDEX = 2;
28+
static final int FIELDS_INDEX = 3;
29+
static final int INIT_INDEX = 4;
30+
static final int STATIC_FIELDS_INDEX = 5;
31+
static final int STATIC_INIT_INDEX = 6;
32+
static final int STATIC_METHODS_INDEX = 7;
33+
static final int ENUM_CONSTANTS_INDEX = 8;
34+
static final int N_CATEGORIES = 9;
35+
static final int PUBLIC_INDEX = 0;
36+
static final int PRIVATE_INDEX = 1;
37+
static final int PROTECTED_INDEX = 2;
38+
static final int DEFAULT_INDEX = 3;
39+
static final int N_VISIBILITIES = 4;
40+
41+
private final boolean doNotSortFields;
42+
private final int[] memberCategoryOffsets;
43+
private final boolean sortByVisibility;
44+
private final int[] visibilityOffsets;
45+
46+
static DefaultJavaElementComparator of(
47+
boolean doNotSortFields,
48+
String memberCategoryPreferences,
49+
boolean sortByVisibility,
50+
String visibilityPreferences) {
51+
52+
int[] memberCategoryOffsets = new int[9];
53+
boolean success = fillMemberCategoryOffsets(memberCategoryPreferences, memberCategoryOffsets);
54+
if (!success) {
55+
String defaultValue = "T,SF,SI,SM,F,I,C,M";
56+
fillMemberCategoryOffsets(defaultValue, memberCategoryOffsets);
57+
}
58+
59+
int[] visibilityOffsets = new int[4];
60+
boolean success2 = fillVisibilityOffsets(visibilityPreferences, visibilityOffsets);
61+
if (!success2) {
62+
String defaultValue = "B,V,R,D";
63+
fillVisibilityOffsets(defaultValue, visibilityOffsets);
64+
}
65+
66+
return new DefaultJavaElementComparator(doNotSortFields, memberCategoryOffsets, sortByVisibility, visibilityOffsets);
67+
}
68+
69+
DefaultJavaElementComparator(
70+
boolean doNotSortFields,
71+
int[] memberCategoryOffsets,
72+
boolean sortByVisibility,
73+
int[] visibilityOffsets) {
74+
75+
this.doNotSortFields = doNotSortFields;
76+
this.memberCategoryOffsets = memberCategoryOffsets;
77+
this.sortByVisibility = sortByVisibility;
78+
this.visibilityOffsets = visibilityOffsets;
79+
}
80+
81+
static boolean fillVisibilityOffsets(String preferencesString, int[] offsets) {
82+
StringTokenizer tokenizer = new StringTokenizer(preferencesString, ",");
83+
int i = 0;
84+
while (tokenizer.hasMoreTokens()) {
85+
String token = tokenizer.nextToken();
86+
if (token != null) {
87+
switch (token) {
88+
case "B":
89+
offsets[PUBLIC_INDEX] = i++;
90+
break;
91+
case "D":
92+
offsets[DEFAULT_INDEX] = i++;
93+
break;
94+
case "R":
95+
offsets[PROTECTED_INDEX] = i++;
96+
break;
97+
case "V":
98+
offsets[PRIVATE_INDEX] = i++;
99+
}
100+
}
101+
}
102+
return i == N_VISIBILITIES;
103+
}
104+
105+
static boolean fillMemberCategoryOffsets(String preferencesString, int[] offsets) {
106+
StringTokenizer tokenizer = new StringTokenizer(preferencesString, ",");
107+
int i = 0;
108+
offsets[8] = i++;
109+
while (tokenizer.hasMoreTokens()) {
110+
String token = tokenizer.nextToken();
111+
if (token != null) {
112+
switch (token) {
113+
case "C":
114+
offsets[CONSTRUCTORS_INDEX] = i++;
115+
break;
116+
case "F":
117+
offsets[FIELDS_INDEX] = i++;
118+
break;
119+
case "I":
120+
offsets[INIT_INDEX] = i++;
121+
break;
122+
case "M":
123+
offsets[METHOD_INDEX] = i++;
124+
break;
125+
case "T":
126+
offsets[TYPE_INDEX] = i++;
127+
break;
128+
case "SF":
129+
offsets[STATIC_FIELDS_INDEX] = i++;
130+
break;
131+
case "SI":
132+
offsets[STATIC_INIT_INDEX] = i++;
133+
break;
134+
case "SM":
135+
offsets[STATIC_METHODS_INDEX] = i++;
136+
}
137+
}
138+
}
139+
return i == N_CATEGORIES;
140+
}
141+
142+
private int category(BodyDeclaration bodyDeclaration) {
143+
switch (bodyDeclaration.getNodeType()) {
144+
case ASTNode.METHOD_DECLARATION: {
145+
MethodDeclaration method = (MethodDeclaration) bodyDeclaration;
146+
if (method.isConstructor()) {
147+
return CONSTRUCTORS_INDEX;
148+
}
149+
int flags = method.getModifiers();
150+
if (Modifier.isStatic(flags))
151+
return STATIC_METHODS_INDEX;
152+
else
153+
return METHOD_INDEX;
154+
}
155+
case ASTNode.FIELD_DECLARATION: {
156+
if (JdtFlags.isStatic(bodyDeclaration))
157+
return STATIC_FIELDS_INDEX;
158+
else
159+
return FIELDS_INDEX;
160+
}
161+
case ASTNode.INITIALIZER: {
162+
int flags = bodyDeclaration.getModifiers();
163+
if (Modifier.isStatic(flags))
164+
return STATIC_INIT_INDEX;
165+
else
166+
return INIT_INDEX;
167+
}
168+
case ASTNode.TYPE_DECLARATION:
169+
case ASTNode.ENUM_DECLARATION:
170+
case ASTNode.ANNOTATION_TYPE_DECLARATION:
171+
return TYPE_INDEX;
172+
case ASTNode.ENUM_CONSTANT_DECLARATION:
173+
return ENUM_CONSTANTS_INDEX;
174+
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
175+
return METHOD_INDEX; // reusing the method index
176+
177+
}
178+
return 0; // should never happen
179+
}
180+
181+
private int getCategoryIndex(int category) {
182+
return memberCategoryOffsets[category];
183+
}
184+
185+
private int getVisibilityIndex(int modifierFlags) {
186+
int kind = 3;
187+
if (Flags.isPublic(modifierFlags)) {
188+
kind = 0;
189+
} else if (Flags.isProtected(modifierFlags)) {
190+
kind = 2;
191+
} else if (Flags.isPrivate(modifierFlags)) {
192+
kind = 1;
193+
}
194+
return this.visibilityOffsets[kind];
195+
}
196+
197+
/**
198+
* This comparator follows the contract defined in CompilationUnitSorter.sort.
199+
*
200+
* @see Comparator#compare(java.lang.Object, java.lang.Object)
201+
* @see CompilationUnitSorter#sort(int, org.eclipse.jdt.core.ICompilationUnit, int[], java.util.Comparator, int, org.eclipse.core.runtime.IProgressMonitor)
202+
*/
203+
@Override
204+
public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) {
205+
boolean preserved1 = doNotSortFields && isSortPreserved(bodyDeclaration1);
206+
boolean preserved2 = doNotSortFields && isSortPreserved(bodyDeclaration2);
207+
208+
// Bug 407759: need to use a common category for all isSortPreserved members that are to be sorted in the same group:
209+
int cat1 = category(bodyDeclaration1);
210+
if (preserved1) {
211+
cat1 = sortPreservedCategory(cat1);
212+
}
213+
int cat2 = category(bodyDeclaration2);
214+
if (preserved2) {
215+
cat2 = sortPreservedCategory(cat2);
216+
}
217+
218+
if (cat1 != cat2) {
219+
return getCategoryIndex(cat1) - getCategoryIndex(cat2);
220+
}
221+
222+
// cat1 == cat2 implies preserved1 == preserved2
223+
224+
if (preserved1) {
225+
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
226+
}
227+
228+
if (sortByVisibility) {
229+
int flags1 = JdtFlags.getVisibilityCode(bodyDeclaration1);
230+
int flags2 = JdtFlags.getVisibilityCode(bodyDeclaration2);
231+
int vis = getVisibilityIndex(flags1) - getVisibilityIndex(flags2);
232+
if (vis != 0) {
233+
return vis;
234+
}
235+
}
236+
237+
switch (bodyDeclaration1.getNodeType()) {
238+
case ASTNode.METHOD_DECLARATION: {
239+
MethodDeclaration method1 = (MethodDeclaration) bodyDeclaration1;
240+
MethodDeclaration method2 = (MethodDeclaration) bodyDeclaration2;
241+
242+
if (sortByVisibility) {
243+
int vis = getVisibilityIndex(method1.getModifiers()) - getVisibilityIndex(method2.getModifiers());
244+
if (vis != 0) {
245+
return vis;
246+
}
247+
}
248+
249+
String name1 = method1.getName().getIdentifier();
250+
String name2 = method2.getName().getIdentifier();
251+
252+
// method declarations (constructors) are sorted by name
253+
int cmp = name1.compareTo(name2);
254+
if (cmp != 0) {
255+
return cmp;
256+
}
257+
258+
// if names equal, sort by parameter types
259+
List<SingleVariableDeclaration> parameters1 = method1.parameters();
260+
List<SingleVariableDeclaration> parameters2 = method2.parameters();
261+
int length1 = parameters1.size();
262+
int length2 = parameters2.size();
263+
264+
int len = Math.min(length1, length2);
265+
for (int i = 0; i < len; i++) {
266+
SingleVariableDeclaration param1 = parameters1.get(i);
267+
SingleVariableDeclaration param2 = parameters2.get(i);
268+
cmp = buildSignature(param1.getType()).compareTo(buildSignature(param2.getType()));
269+
if (cmp != 0) {
270+
return cmp;
271+
}
272+
}
273+
if (length1 != length2) {
274+
return length1 - length2;
275+
}
276+
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
277+
}
278+
case ASTNode.FIELD_DECLARATION: {
279+
FieldDeclaration field1 = (FieldDeclaration) bodyDeclaration1;
280+
FieldDeclaration field2 = (FieldDeclaration) bodyDeclaration2;
281+
282+
String name1 = ((VariableDeclarationFragment) field1.fragments().get(0)).getName().getIdentifier();
283+
String name2 = ((VariableDeclarationFragment) field2.fragments().get(0)).getName().getIdentifier();
284+
285+
// field declarations are sorted by name
286+
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
287+
}
288+
case ASTNode.INITIALIZER: {
289+
// preserve relative order
290+
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
291+
}
292+
case ASTNode.TYPE_DECLARATION:
293+
case ASTNode.ENUM_DECLARATION:
294+
case ASTNode.ANNOTATION_TYPE_DECLARATION: {
295+
AbstractTypeDeclaration type1 = (AbstractTypeDeclaration) bodyDeclaration1;
296+
AbstractTypeDeclaration type2 = (AbstractTypeDeclaration) bodyDeclaration2;
297+
298+
String name1 = type1.getName().getIdentifier();
299+
String name2 = type2.getName().getIdentifier();
300+
301+
// typedeclarations are sorted by name
302+
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
303+
}
304+
case ASTNode.ENUM_CONSTANT_DECLARATION: {
305+
EnumConstantDeclaration decl1 = (EnumConstantDeclaration) bodyDeclaration1;
306+
EnumConstantDeclaration decl2 = (EnumConstantDeclaration) bodyDeclaration2;
307+
308+
String name1 = decl1.getName().getIdentifier();
309+
String name2 = decl2.getName().getIdentifier();
310+
311+
// enum constants declarations are sorted by name
312+
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
313+
}
314+
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: {
315+
AnnotationTypeMemberDeclaration decl1 = (AnnotationTypeMemberDeclaration) bodyDeclaration1;
316+
AnnotationTypeMemberDeclaration decl2 = (AnnotationTypeMemberDeclaration) bodyDeclaration2;
317+
318+
String name1 = decl1.getName().getIdentifier();
319+
String name2 = decl2.getName().getIdentifier();
320+
321+
// enum constants declarations are sorted by name
322+
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
323+
}
324+
}
325+
return 0;
326+
}
327+
328+
private static int sortPreservedCategory(int category) {
329+
switch (category) {
330+
case STATIC_FIELDS_INDEX:
331+
case STATIC_INIT_INDEX:
332+
return STATIC_FIELDS_INDEX;
333+
case FIELDS_INDEX:
334+
case INIT_INDEX:
335+
return FIELDS_INDEX;
336+
default:
337+
return category;
338+
}
339+
}
340+
341+
private boolean isSortPreserved(BodyDeclaration bodyDeclaration) {
342+
switch (bodyDeclaration.getNodeType()) {
343+
case ASTNode.FIELD_DECLARATION:
344+
case ASTNode.ENUM_CONSTANT_DECLARATION:
345+
case ASTNode.INITIALIZER:
346+
return true;
347+
default:
348+
return false;
349+
}
350+
}
351+
352+
private int preserveRelativeOrder(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) {
353+
int value1 = ((Integer) bodyDeclaration1.getProperty(CompilationUnitSorter.RELATIVE_ORDER));
354+
int value2 = ((Integer) bodyDeclaration2.getProperty(CompilationUnitSorter.RELATIVE_ORDER));
355+
return value1 - value2;
356+
}
357+
358+
private int compareNames(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2, String name1, String name2) {
359+
int cmp = name1.compareTo(name2);
360+
if (cmp != 0) {
361+
return cmp;
362+
}
363+
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
364+
}
365+
366+
private String buildSignature(Type type) {
367+
return ASTNodes.asString(type);
368+
}
369+
}

0 commit comments

Comments
 (0)