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, and if equal again
84 * the names of the enclosing modules.
85 *
86 * @return Comparator
87 */
88 public Comparator<Element> makeAllClassesComparator() {
89 if (allClassesComparator == null) {
90 allClassesComparator = new ElementComparator() {
91 @Override
92 public int compare(Element e1, Element e2) {
93 int result = compareNames(e1, e2);
94 if (result == 0)
95 result = compareFullyQualifiedNames(e1, e2);
96 if (result == 0)
97 result = compareModuleNames(e1, e2);
98 return result;
99 }
100 };
101 }
102 return allClassesComparator;
103 }
104
105 private Comparator<Element> packageComparator = null;
106
107 /**
108 * Returns a Comparator for packages, by comparing the fully qualified names,
109 * and if those are equal the names of the enclosing modules.
110 *
111 * @return a Comparator
112 */
113 public Comparator<Element> makePackageComparator() {
114 if (packageComparator == null) {
115 packageComparator = new ElementComparator() {
116 @Override
117 public int compare(Element pkg1, Element pkg2) {
118 int result = compareFullyQualifiedNames(pkg1, pkg2);
119 if (result == 0)
120 result = compareModuleNames(pkg1, pkg2);
121 return result;
122 }
123 };
124 }
125 return packageComparator;
126 }
127
128 private Comparator<Element> deprecatedComparator = null;
129
130 /**
131 * Returns a Comparator for deprecated items listed on deprecated list page, by comparing the
132 * fully qualified names, and if those are equal the names of the enclosing modules.
133 *
134 * @return a Comparator
135 */
136 public Comparator<Element> makeDeprecatedComparator() {
137 if (deprecatedComparator == null) {
138 deprecatedComparator = new ElementComparator() {
139 @Override
140 public int compare(Element e1, Element e2) {
141 int result = compareFullyQualifiedNames(e1, e2);
142 if (result == 0)
143 result = compareModuleNames(e1, e2);
144 return result;
145 }
146 };
147 }
148 return deprecatedComparator;
149 }
150
151 private Comparator<SerialFieldTree> serialFieldTreeComparator = null;
152
153 /**
154 * Returns a Comparator for SerialFieldTree.
155 * @return a Comparator
156 */
157 public Comparator<SerialFieldTree> makeSerialFieldTreeComparator() {
158 if (serialFieldTreeComparator == null) {
159 serialFieldTreeComparator = (SerialFieldTree o1, SerialFieldTree o2) -> {
160 String s1 = o1.getName().toString();
161 String s2 = o2.getName().toString();
162 return s1.compareTo(s2);
163 };
164 }
165 return serialFieldTreeComparator;
166 }
167
168 /**
169 * Returns a general purpose comparator.
170 * @return a Comparator
171 */
172 public Comparator<Element> makeGeneralPurposeComparator() {
173 return makeClassUseComparator();
174 }
175
176 private Comparator<Element> overrideUseComparator = null;
177
178 /**
179 * Returns a Comparator for overrides and implements,
180 * used primarily on methods, compares the name first,
181 * then compares the simple names of the enclosing
182 * TypeElement and the fully qualified name of the enclosing TypeElement.
183 * @return a Comparator
184 */
185 public Comparator<Element> makeOverrideUseComparator() {
186 if (overrideUseComparator == null) {
187 overrideUseComparator = new ElementComparator() {
188 @Override
189 public int compare(Element o1, Element o2) {
190 int result = utils.compareStrings(utils.getSimpleName(o1), utils.getSimpleName(o2));
191 if (result != 0) {
192 return result;
193 }
194 if (!utils.isTypeElement(o1) && !utils.isTypeElement(o2) && !utils.isPackage(o1) && !utils.isPackage(o2)) {
195 TypeElement t1 = utils.getEnclosingTypeElement(o1);
196 TypeElement t2 = utils.getEnclosingTypeElement(o2);
197 result = utils.compareStrings(utils.getSimpleName(t1), utils.getSimpleName(t2));
198 if (result != 0)
199 return result;
200 }
201 result = utils.compareStrings(utils.getFullyQualifiedName(o1), utils.getFullyQualifiedName(o2));
202 if (result != 0)
203 return result;
204 return compareElementKinds(o1, o2);
205 }
206 };
207 }
208 return overrideUseComparator;
209 }
210
211 private Comparator<Element> indexUseComparator = null;
212
213 /**
214 * Returns an {@code Element} Comparator for index file presentations, and are sorted as follows.
215 * If comparing modules and/or packages then simply compare the qualified names,
216 * if comparing a module or a package with a type/member then compare the
217 * FullyQualifiedName of the module or a package with the SimpleName of the entity,
218 * otherwise:
219 * 1. compare the ElementKind ex: Module, Package, Interface etc.
220 * 2a. if equal and if the type is of ExecutableElement(Constructor, Methods),
221 * a case insensitive comparison of parameter the type signatures
222 * 2b. if equal, case sensitive comparison of the type signatures
223 * 3. if equal, compare the FQNs of the entities
224 * 4. finally, if equal, compare the names of the enclosing modules
225 * @return an element comparator for index file use
226 */
227 public Comparator<Element> makeIndexElementComparator() {
228 if (indexUseComparator == null) {
229 indexUseComparator = new ElementComparator() {
230 /**
231 * Compares two elements.
232 *
233 * @param e1 - an element.
234 * @param e2 - an element.
235 * @return a negative integer, zero, or a positive integer as the first
236 * argument is less than, equal to, or greater than the second.
237 */
238 @Override
239 public int compare(Element e1, Element e2) {
240 int result;
241 // first, compare names as appropriate
242 if ((utils.isModule(e1) || utils.isPackage(e1)) && (utils.isModule(e2) || utils.isPackage(e2))) {
243 result = compareFullyQualifiedNames(e1, e2);
244 } else if (utils.isModule(e1) || utils.isPackage(e1)) {
245 result = utils.compareStrings(utils.getFullyQualifiedName(e1), utils.getSimpleName(e2));
246 } else if (utils.isModule(e2) || utils.isPackage(e2)) {
247 result = utils.compareStrings(utils.getSimpleName(e1), utils.getFullyQualifiedName(e2));
248 } else {
249 result = compareNames(e1, e2);
250 }
251 if (result != 0) {
252 return result;
253 }
254 // if names are the same, compare element kinds
255 result = compareElementKinds(e1, e2);
256 if (result != 0) {
257 return result;
258 }
259 // if element kinds are the same, and are methods,
260 // compare the method parameters
261 if (hasParameters(e1)) {
262 List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters();
263 List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters();
264 result = compareParameters(false, parameters1, parameters2);
265 if (result != 0) {
266 return result;
267 }
268 result = compareParameters(true, parameters1, parameters2);
269 if (result != 0) {
270 return result;
271 }
272 }
273 // else fall back on fully qualified names
274 result = compareFullyQualifiedNames(e1, e2);
275 if (result != 0)
276 return result;
277 return compareModuleNames(e1, e2);
278 }
279 };
280 }
281 return indexUseComparator;
282 }
283
284 /**
285 * Returns a comparator for the {@code IndexItem}s in the index page. This is a composite
286 * comparator that must be able to compare all kinds {@code Element}s as well as
287 * {@code SearchIndexItem}s.
288 *
289 * @return a comparator for index page items.
290 */
291 public Comparator<IndexItem> makeIndexComparator(boolean classesOnly) {
292 Comparator<Element> elementComparator = classesOnly
293 ? makeAllClassesComparator()
294 : makeIndexElementComparator();
295 Comparator<SearchIndexItem> searchTagComparator =
296 makeGenericSearchIndexComparator();
297
298 return (o1, o2) -> {
299 // Compare two elements
300 if (o1.getElement() != null && o2.getElement() != null) {
301 return elementComparator.compare(o1.getElement(), o2.getElement());
302 }
303 // Compare two search tags
304 if (o1.getSearchTag() != null && o2.getSearchTag() != null) {
305 return searchTagComparator.compare(o1.getSearchTag(), o2.getSearchTag());
306 }
307 // Compare an element with a search tag.
308 // Compares labels, if those are equal put the search tag first.
309 int d = utils.compareStrings(o1.getLabel(), o2.getLabel());
310 if (d == 0) {
311 d = o1.getElement() == null ? 1 : -1;
312 }
313 return d;
314 };
315 }
316
317 private Comparator<TypeMirror> typeMirrorClassUseComparator = null;
318
319 /**
320 * Compares the FullyQualifiedNames of two TypeMirrors
321 * @return
322 */
323 public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() {
324 if (typeMirrorClassUseComparator == null) {
325 typeMirrorClassUseComparator = (TypeMirror type1, TypeMirror type2) -> {
326 String s1 = utils.getQualifiedTypeName(type1);
327 String s2 = utils.getQualifiedTypeName(type2);
328 return utils.compareStrings(s1, s2);
329 };
330 }
331 return typeMirrorClassUseComparator;
332 }
333
334 private Comparator<TypeMirror> typeMirrorIndexUseComparator = null;
335
336 /**
337 * Compares the SimpleNames of TypeMirrors if equal then the
338 * FullyQualifiedNames of TypeMirrors.
339 *
340 * @return
341 */
342 public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() {
343 if (typeMirrorIndexUseComparator == null) {
344 typeMirrorIndexUseComparator = (TypeMirror t1, TypeMirror t2) -> {
345 int result = utils.compareStrings(utils.getTypeName(t1, false), utils.getTypeName(t2, false));
346 if (result != 0)
347 return result;
348 return utils.compareStrings(utils.getQualifiedTypeName(t1), utils.getQualifiedTypeName(t2));
349 };
350 }
351 return typeMirrorIndexUseComparator;
352 }
353
354 private Comparator<Element> classUseComparator = null;
355
356 /**
357 * Comparator for ClassUse presentations, and sorts as follows:
358 * 1. member names
359 * 2. then fully qualified member names
360 * 3. then parameter types if applicable
361 * 4. the element kinds ie. package, class, interface etc.
362 * 5. finally the name of the enclosing modules
363 * @return a comparator to sort classes and members for class use
364 */
365 public Comparator<Element> makeClassUseComparator() {
366 if (classUseComparator == null) {
367 classUseComparator = new ElementComparator() {
368 /**
369 * Compares two Elements.
370 *
371 * @param e1 - an element.
372 * @param e2 - an element.
373 * @return a negative integer, zero, or a positive integer as the first
374 * argument is less than, equal to, or greater than the second.
375 */
376 @Override
377 public int compare(Element e1, Element e2) {
378 int result = compareNames(e1, e2);
379 if (result != 0) {
380 return result;
381 }
382 result = compareFullyQualifiedNames(e1, e2);
383 if (result != 0) {
384 return result;
385 }
386 if (hasParameters(e1) && hasParameters(e2)) {
387 List<? extends VariableElement> parameters1 = ((ExecutableElement)e1).getParameters();
388 List<? extends VariableElement> parameters2 = ((ExecutableElement)e2).getParameters();
389 result = compareParameters(false, parameters1, parameters2);
390 if (result != 0) {
391 return result;
392 }
393 result = compareParameters(true, parameters1, parameters2);
394 }
395 if (result != 0) {
396 return result;
397 }
398 result = compareElementKinds(e1, e2);
399 if (result != 0) {
400 return result;
401 }
402 return compareModuleNames(e1, e2);
403 }
404 };
405 }
406 return classUseComparator;
407 }
408
409 /**
410 * A general purpose comparator to sort Element entities, basically provides the building blocks
411 * for creating specific comparators for an use-case.
412 */
413 private abstract class ElementComparator implements Comparator<Element> {
414 public ElementComparator() { }
415
416 /**
417 * compares two parameter arrays by first comparing the length of the arrays, and
418 * then each Type of the parameter in the array.
419 * @param params1 the first parameter array.
420 * @param params2 the first parameter array.
421 * @return a negative integer, zero, or a positive integer as the first
422 * argument is less than, equal to, or greater than the second.
423 */
424 protected int compareParameters(boolean caseSensitive, List<? extends VariableElement> params1,
425 List<? extends VariableElement> params2) {
426
427 return utils.compareStrings(caseSensitive, getParametersAsString(params1),
428 getParametersAsString(params2));
429 }
430
431 String getParametersAsString(List<? extends VariableElement> params) {
432 StringBuilder sb = new StringBuilder();
433 for (VariableElement param : params) {
434 TypeMirror t = param.asType();
435 // prefix P for primitive and R for reference types, thus items will
436 // be ordered lexically and correctly.
437 sb.append(getTypeCode(t)).append("-").append(t).append("-");
438 }
439 return sb.toString();
440 }
441
442 private String getTypeCode(TypeMirror t) {
443 return new SimpleTypeVisitor9<String, Void>() {
444
445 @Override
446 public String visitPrimitive(PrimitiveType t, Void p) {
447 return "P";
448 }
449 @Override
450 public String visitArray(ArrayType t, Void p) {
451 return visit(t.getComponentType());
452 }
453 @Override
454 protected String defaultAction(TypeMirror e, Void p) {
455 return "R";
456 }
457
458 }.visit(t);
459 }
460
461 /**
462 * Compares two Elements, typically the name of a method,
463 * field or constructor.
464 * @param e1 the first Element.
465 * @param e2 the second Element.
466 * @return a negative integer, zero, or a positive integer as the first
467 * argument is less than, equal to, or greater than the second.
468 */
469 protected int compareNames(Element e1, Element e2) {
470 return utils.compareStrings(utils.getSimpleName(e1), utils.getSimpleName(e2));
471 }
472
473 /**
474 * Compares the fully qualified names of the entities
475 * @param e1 the first Element.
476 * @param e2 the first Element.
477 * @return a negative integer, zero, or a positive integer as the first
478 * argument is less than, equal to, or greater than the second.
479 */
480 protected int compareFullyQualifiedNames(Element e1, Element e2) {
481 // add simplename to be compatible
482 String thisElement = getFullyQualifiedName(e1);
483 String thatElement = getFullyQualifiedName(e2);
484 return utils.compareStrings(thisElement, thatElement);
485 }
486
487 /**
488 * Compares the name of the modules of two elements.
489 * @param e1 the first element
490 * @param e2 the second element
491 * @return a negative integer, zero, or a positive integer as the first
492 * argument is less than, equal to, or greater than the second
493 */
494 protected int compareModuleNames(Element e1, Element e2) {
495 ModuleElement m1 = utils.elementUtils.getModuleOf(e1);
496 ModuleElement m2 = utils.elementUtils.getModuleOf(e2);
497 if (m1 != null && m2 != null) {
498 return compareFullyQualifiedNames(m1, m2);
499 } else if (m1 != null) {
500 return 1;
501 } else if (m2 != null) {
502 return -1;
503 }
504 return 0;
505 }
506
507 protected int compareElementKinds(Element e1, Element e2) {
508 return Integer.compare(getKindIndex(e1), getKindIndex(e2));
509 }
510
511 private int getKindIndex(Element e) {
512 switch (e.getKind()) {
513 case MODULE: return 0;
514 case PACKAGE: return 1;
515 case CLASS: return 2;
516 case ENUM: return 3;
517 case ENUM_CONSTANT: return 4;
518 case RECORD: return 5;
519 case INTERFACE: return 6;
520 case ANNOTATION_TYPE: return 7;
521 case FIELD: return 8;
522 case CONSTRUCTOR: return 9;
523 case METHOD: return 10;
524 default: throw new IllegalArgumentException(e.getKind().toString());
525 }
526 }
527
528 @SuppressWarnings("preview")
529 boolean hasParameters(Element e) {
530 return new SimpleElementVisitor14<Boolean, Void>() {
531 @Override
532 public Boolean visitExecutable(ExecutableElement e, Void p) {
533 return true;
534 }
535
536 @Override
537 protected Boolean defaultAction(Element e, Void p) {
538 return false;
539 }
540
541 }.visit(e);
542 }
543
544 /**
545 * The fully qualified names of the entities, used solely by the comparator.
546 *
547 * @return a negative integer, zero, or a positive integer as the first argument is less
548 * than, equal to, or greater than the second.
549 */
550 @SuppressWarnings("preview")
551 private String getFullyQualifiedName(Element e) {
552 return new SimpleElementVisitor14<String, Void>() {
553 @Override
554 public String visitModule(ModuleElement e, Void p) {
555 return e.getQualifiedName().toString();
556 }
557
558 @Override
559 public String visitPackage(PackageElement e, Void p) {
560 return e.getQualifiedName().toString();
561 }
562
563 @Override
564 public String visitExecutable(ExecutableElement e, Void p) {
565 // For backward compatibility
566 return getFullyQualifiedName(e.getEnclosingElement())
567 + "." + e.getSimpleName().toString();
568 }
569
570 @Override
571 public String visitType(TypeElement e, Void p) {
572 return e.getQualifiedName().toString();
573 }
574
575 @Override
576 protected String defaultAction(Element e, Void p) {
577 return utils.getEnclosingTypeElement(e).getQualifiedName().toString()
578 + "." + e.getSimpleName().toString();
579 }
580 }.visit(e);
581 }
582 }
583
584 /**
585 * Returns a Comparator for SearchIndexItems representing types. Items are
586 * compared by short name, or full string representation if names are equal.
587 *
588 * @return a Comparator
589 */
590 public Comparator<SearchIndexItem> makeTypeSearchIndexComparator() {
591 return (SearchIndexItem sii1, SearchIndexItem sii2) -> {
592 int result = utils.compareStrings(sii1.getSimpleName(), sii2.getSimpleName());
593 if (result == 0) {
594 // TreeSet needs this to be consistent with equal so we do
595 // a plain comparison of string representations as fallback.
596 result = sii1.toString().compareTo(sii2.toString());
597 }
598 return result;
599 };
600 }
601
602 private Comparator<SearchIndexItem> genericSearchIndexComparator = null;
603
604 /**
605 * Returns a Comparator for SearchIndexItems representing modules, packages, or members.
606 * Items are compared by label (member name plus signature for members, package name for
607 * packages, and module name for modules). If labels are equal then full string
608 * representation is compared.
609 *
610 * @return a Comparator
611 */
612 public Comparator<SearchIndexItem> makeGenericSearchIndexComparator() {
613 if (genericSearchIndexComparator == null) {
614 genericSearchIndexComparator = (SearchIndexItem sii1, SearchIndexItem sii2) -> {
615 int result = utils.compareStrings(sii1.getLabel(), sii2.getLabel());
616 if (result == 0) {
617 // TreeSet needs this to be consistent with equal so we do
618 // a plain comparison of string representations as fallback.
619 result = sii1.toString().compareTo(sii2.toString());
620 }
621 return result;
622 };
623 }
624 return genericSearchIndexComparator;
625 }
626
627 }