1 /*
2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.javadoc.internal.doclets.toolkit.util;
27
28 import com.sun.source.doctree.SerialFieldTree;
29 import jdk.javadoc.internal.doclets.formats.html.SearchIndexItem;
30
31 import javax.lang.model.element.Element;
32 import javax.lang.model.element.ExecutableElement;
33 import javax.lang.model.element.ModuleElement;
34 import javax.lang.model.element.PackageElement;
35 import javax.lang.model.element.TypeElement;
36 import javax.lang.model.element.VariableElement;
37 import javax.lang.model.type.ArrayType;
38 import javax.lang.model.type.PrimitiveType;
39 import javax.lang.model.type.TypeMirror;
40 import javax.lang.model.util.SimpleElementVisitor14;
41 import javax.lang.model.util.SimpleTypeVisitor9;
42 import java.util.Comparator;
43 import java.util.List;
44
45 /**
46 * A collection of {@code Comparator} factory methods.
47 *
48 * <p><b>This is NOT part of any supported API.
49 * If you write code that depends on this, you do so at your own risk.
50 * This code and its internal interfaces are subject to change or
51 * deletion without notice.</b>
52 */
53 public class Comparators {
54
55 private final Utils utils;
56
57 Comparators(Utils utils) {
58 this.utils = utils;
59 }
60
61 private Comparator<Element> moduleComparator = null;
62
63 /**
64 * Comparator for ModuleElements, simply compares the fully qualified names
65 * @return a Comparator
66 */
67 public Comparator<Element> makeModuleComparator() {
68 if (moduleComparator == null) {
69 moduleComparator = new ElementComparator() {
70 @Override
71 public int compare(Element mod1, Element mod2) {
72 return compareFullyQualifiedNames(mod1, mod2);
73 }
74 };
75 }
76 return moduleComparator;
77 }
78
79 private Comparator<Element> allClassesComparator = null;
80
81 /**
82 * Returns a Comparator for all classes, compares the simple names of
83 * TypeElement, if equal then the fully qualified names.
84 *
85 * @return Comparator
86 */
87 public Comparator<Element> makeAllClassesComparator() {
88 if (allClassesComparator == null) {
89 allClassesComparator = new ElementComparator() {
90 @Override
91 public int compare(Element e1, Element e2) {
92 int result = compareNames(e1, e2);
93 if (result == 0)
94 result = compareFullyQualifiedNames(e1, e2);
95
96 return result;
97 }
98 };
99 }
100 return allClassesComparator;
101 }
102
103 private Comparator<Element> packageComparator = null;
104
105 /**
106 * Returns a Comparator for packages, by comparing the fully qualified names.
107 *
108 * @return a Comparator
109 */
110 public Comparator<Element> makePackageComparator() {
111 if (packageComparator == null) {
112 packageComparator = new ElementComparator() {
113 @Override
114 public int compare(Element pkg1, Element pkg2) {
115 return compareFullyQualifiedNames(pkg1, pkg2);
116 }
117 };
118 }
119 return packageComparator;
120 }
121
122 private Comparator<Element> deprecatedComparator = null;
123
124 /**
125 * Returns a Comparator for deprecated items listed on deprecated list page, by comparing the
126 * fully qualified names.
127 *
128 * @return a Comparator
129 */
130 public Comparator<Element> makeDeprecatedComparator() {
131 if (deprecatedComparator == null) {
132 deprecatedComparator = new ElementComparator() {
133 @Override
134 public int compare(Element e1, Element e2) {
135 return compareFullyQualifiedNames(e1, e2);
136 }
137 };
138 }
139 return deprecatedComparator;
140 }
141
142 private Comparator<SerialFieldTree> serialFieldTreeComparator = null;
143
144 /**
145 * Returns a Comparator for SerialFieldTree.
146 * @return a Comparator
147 */
148 public Comparator<SerialFieldTree> makeSerialFieldTreeComparator() {
149 if (serialFieldTreeComparator == null) {
150 serialFieldTreeComparator = (SerialFieldTree o1, SerialFieldTree o2) -> {
151 String s1 = o1.getName().toString();
152 String s2 = o2.getName().toString();
153 return s1.compareTo(s2);
154 };
155 }
156 return serialFieldTreeComparator;
157 }
158
159 /**
160 * Returns a general purpose comparator.
161 * @return a Comparator
162 */
163 public Comparator<Element> makeGeneralPurposeComparator() {
164 return makeClassUseComparator();
165 }
166
167 private Comparator<Element> overrideUseComparator = null;
168
169 /**
170 * Returns a Comparator for overrides and implements,
171 * used primarily on methods, compares the name first,
172 * then compares the simple names of the enclosing
173 * TypeElement and the fully qualified name of the enclosing TypeElement.
174 * @return a Comparator
175 */
176 public Comparator<Element> makeOverrideUseComparator() {
177 if (overrideUseComparator == null) {
178 overrideUseComparator = new ElementComparator() {
179 @Override
180 public int compare(Element o1, Element o2) {
181 int result = utils.compareStrings(utils.getSimpleName(o1), utils.getSimpleName(o2));
182 if (result != 0) {
183 return result;
184 }
185 if (!utils.isTypeElement(o1) && !utils.isTypeElement(o2) && !utils.isPackage(o1) && !utils.isPackage(o2)) {
186 TypeElement t1 = utils.getEnclosingTypeElement(o1);
187 TypeElement t2 = utils.getEnclosingTypeElement(o2);
188 result = utils.compareStrings(utils.getSimpleName(t1), utils.getSimpleName(t2));
189 if (result != 0)
190 return result;
191 }
192 result = utils.compareStrings(utils.getFullyQualifiedName(o1), utils.getFullyQualifiedName(o2));
193 if (result != 0)
194 return result;
195 return compareElementKinds(o1, o2);
196 }
197 };
198 }
199 return overrideUseComparator;
200 }
201
202 private Comparator<Element> indexUseComparator = null;
203
204 /**
205 * Returns an {@code Element} Comparator for index file presentations, and are sorted as follows.
206 * If comparing modules and/or packages then simply compare the qualified names,
207 * if comparing a module or a package with a type/member then compare the
208 * FullyQualifiedName of the module or a package with the SimpleName of the entity,
209 * otherwise:
210 * 1. compare the ElementKind ex: Module, Package, Interface etc.
211 * 2a. if equal and if the type is of ExecutableElement(Constructor, Methods),
212 * a case insensitive comparison of parameter the type signatures
213 * 2b. if equal, case sensitive comparison of the type signatures
214 * 3. finally, if equal, compare the FQNs of the entities
215 * @return an element comparator for index file use
216 */
217 public Comparator<Element> makeIndexElementComparator() {
218 if (indexUseComparator == null) {
219 indexUseComparator = new ElementComparator() {
220 /**
221 * Compares two elements.
222 *
223 * @param e1 - an element.
224 * @param e2 - an element.
225 * @return a negative integer, zero, or a positive integer as the first
226 * argument is less than, equal to, or greater than the second.
227 */
228 @Override
229 public int compare(Element e1, Element e2) {
230 int result;
231 // first, compare names as appropriate
232 if ((utils.isModule(e1) || utils.isPackage(e1)) && (utils.isModule(e2) || utils.isPackage(e2))) {
233 result = compareFullyQualifiedNames(e1, e2);
234 } else if (utils.isModule(e1) || utils.isPackage(e1)) {
235 result = utils.compareStrings(utils.getFullyQualifiedName(e1), utils.getSimpleName(e2));
236 } else if (utils.isModule(e2) || utils.isPackage(e2)) {
237 result = utils.compareStrings(utils.getSimpleName(e1), utils.getFullyQualifiedName(e2));
238 } else {
239 result = compareNames(e1, e2);
240 }
241 if (result != 0) {
242 return result;
243 }
244 // if names are the same, compare element kinds
245 result = compareElementKinds(e1, e2);
246 if (result != 0) {
247 return result;
248 }
249 // if element kinds are the same, and are methods,
250 // compare the method parameters
251 if (hasParameters(e1)) {
252 List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters();
253 List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters();
254 result = compareParameters(false, parameters1, parameters2);
255 if (result != 0) {
256 return result;
257 }
258 result = compareParameters(true, parameters1, parameters2);
259 if (result != 0) {
260 return result;
261 }
262 }
263 // else fall back on fully qualified names
264 return compareFullyQualifiedNames(e1, e2);
265 }
266 };
267 }
268 return indexUseComparator;
269 }
270
271 /**
272 * Returns a comparator for the {@code IndexItem}s in the index page. This is a composite
273 * comparator that must be able to compare all kinds {@code Element}s as well as
274 * {@code SearchIndexItem}s.
275 *
276 * @return a comparator for index page items.
277 */
278 public Comparator<IndexItem> makeIndexComparator(boolean classesOnly) {
279 Comparator<Element> elementComparator = classesOnly
280 ? makeAllClassesComparator()
281 : makeIndexElementComparator();
282 Comparator<SearchIndexItem> searchTagComparator =
283 makeGenericSearchIndexComparator();
284
285 return (o1, o2) -> {
286 // Compare two elements
287 if (o1.getElement() != null && o2.getElement() != null) {
288 return elementComparator.compare(o1.getElement(), o2.getElement());
289 }
290 // Compare two search tags
291 if (o1.getSearchTag() != null && o2.getSearchTag() != null) {
292 return searchTagComparator.compare(o1.getSearchTag(), o2.getSearchTag());
293 }
294 // Compare an element with a search tag.
295 // Compares labels, if those are equal put the search tag first.
296 int d = utils.compareStrings(o1.getLabel(), o2.getLabel());
297 if (d == 0) {
298 d = o1.getElement() == null ? 1 : -1;
299 }
300 return d;
301 };
302 }
303
304 private Comparator<TypeMirror> typeMirrorClassUseComparator = null;
305
306 /**
307 * Compares the FullyQualifiedNames of two TypeMirrors
308 * @return
309 */
310 public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() {
311 if (typeMirrorClassUseComparator == null) {
312 typeMirrorClassUseComparator = (TypeMirror type1, TypeMirror type2) -> {
313 String s1 = utils.getQualifiedTypeName(type1);
314 String s2 = utils.getQualifiedTypeName(type2);
315 return utils.compareStrings(s1, s2);
316 };
317 }
318 return typeMirrorClassUseComparator;
319 }
320
321 private Comparator<TypeMirror> typeMirrorIndexUseComparator = null;
322
323 /**
324 * Compares the SimpleNames of TypeMirrors if equal then the
325 * FullyQualifiedNames of TypeMirrors.
326 *
327 * @return
328 */
329 public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() {
330 if (typeMirrorIndexUseComparator == null) {
331 typeMirrorIndexUseComparator = (TypeMirror t1, TypeMirror t2) -> {
332 int result = utils.compareStrings(utils.getTypeName(t1, false), utils.getTypeName(t2, false));
333 if (result != 0)
334 return result;
335 return utils.compareStrings(utils.getQualifiedTypeName(t1), utils.getQualifiedTypeName(t2));
336 };
337 }
338 return typeMirrorIndexUseComparator;
339 }
340
341 private Comparator<Element> classUseComparator = null;
342
343 /**
344 * Comparator for ClassUse presentations, and sorts as follows:
345 * 1. member names
346 * 2. then fully qualified member names
347 * 3. then parameter types if applicable
348 * 4. finally the element kinds ie. package, class, interface etc.
349 * @return a comparator to sort classes and members for class use
350 */
351 public Comparator<Element> makeClassUseComparator() {
352 if (classUseComparator == null) {
353 classUseComparator = new ElementComparator() {
354 /**
355 * Compares two Elements.
356 *
357 * @param e1 - an element.
358 * @param e2 - an element.
359 * @return a negative integer, zero, or a positive integer as the first
360 * argument is less than, equal to, or greater than the second.
361 */
362 @Override
363 public int compare(Element e1, Element e2) {
364 int result = compareNames(e1, e2);
365 if (result != 0) {
366 return result;
367 }
368 result = compareFullyQualifiedNames(e1, e2);
369 if (result != 0) {
370 return result;
371 }
372 if (hasParameters(e1) && hasParameters(e2)) {
373 List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters();
374 List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters();
375 result = compareParameters(false, parameters1, parameters2);
376 if (result != 0) {
377 return result;
378 }
379 result = compareParameters(true, parameters1, parameters2);
380 }
381 if (result != 0) {
382 return result;
383 }
384 return compareElementKinds(e1, e2);
385 }
386 };
387 }
388 return classUseComparator;
389 }
390
391 /**
392 * A general purpose comparator to sort Element entities, basically provides the building blocks
393 * for creating specific comparators for an use-case.
394 */
395 private abstract class ElementComparator implements Comparator<Element> {
396 public ElementComparator() { }
397
398 /**
399 * compares two parameter arrays by first comparing the length of the arrays, and
400 * then each Type of the parameter in the array.
401 * @param params1 the first parameter array.
402 * @param params2 the first parameter array.
403 * @return a negative integer, zero, or a positive integer as the first
404 * argument is less than, equal to, or greater than the second.
405 */
406 protected int compareParameters(boolean caseSensitive, List<? extends VariableElement> params1,
407 List<? extends VariableElement> params2) {
408
409 return utils.compareStrings(caseSensitive, getParametersAsString(params1),
410 getParametersAsString(params2));
411 }
412
413 String getParametersAsString(List<? extends VariableElement> params) {
414 StringBuilder sb = new StringBuilder();
415 for (VariableElement param : params) {
416 TypeMirror t = param.asType();
417 // prefix P for primitive and R for reference types, thus items will
418 // be ordered lexically and correctly.
419 sb.append(getTypeCode(t)).append("-").append(t).append("-");
420 }
421 return sb.toString();
422 }
423
424 private String getTypeCode(TypeMirror t) {
425 return new SimpleTypeVisitor9<String, Void>() {
426
427 @Override
428 public String visitPrimitive(PrimitiveType t, Void p) {
429 return "P";
430 }
431 @Override
432 public String visitArray(ArrayType t, Void p) {
433 return visit(t.getComponentType());
434 }
435 @Override
436 protected String defaultAction(TypeMirror e, Void p) {
437 return "R";
438 }
439
440 }.visit(t);
441 }
442
443 /**
444 * Compares two Elements, typically the name of a method,
445 * field or constructor.
446 * @param e1 the first Element.
447 * @param e2 the second Element.
448 * @return a negative integer, zero, or a positive integer as the first
449 * argument is less than, equal to, or greater than the second.
450 */
451 protected int compareNames(Element e1, Element e2) {
452 return utils.compareStrings(utils.getSimpleName(e1), utils.getSimpleName(e2));
453 }
454
455 /**
456 * Compares the fully qualified names of the entities
457 * @param e1 the first Element.
458 * @param e2 the first Element.
459 * @return a negative integer, zero, or a positive integer as the first
460 * argument is less than, equal to, or greater than the second.
461 */
462 protected int compareFullyQualifiedNames(Element e1, Element e2) {
463 // add simplename to be compatible
464 String thisElement = getFullyQualifiedName(e1);
465 String thatElement = getFullyQualifiedName(e2);
466 return utils.compareStrings(thisElement, thatElement);
467 }
468
469 protected int compareElementKinds(Element e1, Element e2) {
470 return Integer.compare(getKindIndex(e1), getKindIndex(e2));
471 }
472
473 private int getKindIndex(Element e) {
474 switch (e.getKind()) {
475 case MODULE: return 0;
476 case PACKAGE: return 1;
477 case CLASS: return 2;
478 case ENUM: return 3;
479 case ENUM_CONSTANT: return 4;
480 case RECORD: return 5;
481 case INTERFACE: return 6;
482 case ANNOTATION_TYPE: return 7;
483 case FIELD: return 8;
484 case CONSTRUCTOR: return 9;
485 case METHOD: return 10;
486 default: throw new IllegalArgumentException(e.getKind().toString());
487 }
488 }
489
490 @SuppressWarnings("preview")
491 boolean hasParameters(Element e) {
492 return new SimpleElementVisitor14<Boolean, Void>() {
493 @Override
494 public Boolean visitExecutable(ExecutableElement e, Void p) {
495 return true;
496 }
497
498 @Override
499 protected Boolean defaultAction(Element e, Void p) {
500 return false;
501 }
502
503 }.visit(e);
504 }
505
506 /**
507 * The fully qualified names of the entities, used solely by the comparator.
508 *
509 * @return a negative integer, zero, or a positive integer as the first argument is less
510 * than, equal to, or greater than the second.
511 */
512 @SuppressWarnings("preview")
513 private String getFullyQualifiedName(Element e) {
514 return new SimpleElementVisitor14<String, Void>() {
515 @Override
516 public String visitModule(ModuleElement e, Void p) {
517 return e.getQualifiedName().toString();
518 }
519
520 @Override
521 public String visitPackage(PackageElement e, Void p) {
522 return e.getQualifiedName().toString();
523 }
524
525 @Override
526 public String visitExecutable(ExecutableElement e, Void p) {
527 // For backward compatibility
528 return getFullyQualifiedName(e.getEnclosingElement())
529 + "." + e.getSimpleName().toString();
530 }
531
532 @Override
533 public String visitType(TypeElement e, Void p) {
534 return e.getQualifiedName().toString();
535 }
536
537 @Override
538 protected String defaultAction(Element e, Void p) {
539 return utils.getEnclosingTypeElement(e).getQualifiedName().toString()
540 + "." + e.getSimpleName().toString();
541 }
542 }.visit(e);
543 }
544 }
545
546 /**
547 * Returns a Comparator for SearchIndexItems representing types. Items are
548 * compared by short name, or full string representation if names are equal.
549 *
550 * @return a Comparator
551 */
552 public Comparator<SearchIndexItem> makeTypeSearchIndexComparator() {
553 return (SearchIndexItem sii1, SearchIndexItem sii2) -> {
554 int result = utils.compareStrings(sii1.getSimpleName(), sii2.getSimpleName());
555 if (result == 0) {
556 // TreeSet needs this to be consistent with equal so we do
557 // a plain comparison of string representations as fallback.
558 result = sii1.toString().compareTo(sii2.toString());
559 }
560 return result;
561 };
562 }
563
564 private Comparator<SearchIndexItem> genericSearchIndexComparator = null;
565
566 /**
567 * Returns a Comparator for SearchIndexItems representing modules, packages, or members.
568 * Items are compared by label (member name plus signature for members, package name for
569 * packages, and module name for modules). If labels are equal then full string
570 * representation is compared.
571 *
572 * @return a Comparator
573 */
574 public Comparator<SearchIndexItem> makeGenericSearchIndexComparator() {
575 if (genericSearchIndexComparator == null) {
576 genericSearchIndexComparator = (SearchIndexItem sii1, SearchIndexItem sii2) -> {
577 int result = utils.compareStrings(sii1.getLabel(), sii2.getLabel());
578 if (result == 0) {
579 // TreeSet needs this to be consistent with equal so we do
580 // a plain comparison of string representations as fallback.
581 result = sii1.toString().compareTo(sii2.toString());
582 }
583 return result;
584 };
585 }
586 return genericSearchIndexComparator;
587 }
588
589 }
--- EOF ---