001/* 002 * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package org.jdrupes.mdoclet.internal.doclets.toolkit.util; 027 028import com.sun.source.doctree.SerialFieldTree; 029 030import javax.lang.model.element.Element; 031import javax.lang.model.element.ExecutableElement; 032import javax.lang.model.element.ModuleElement; 033import javax.lang.model.element.PackageElement; 034import javax.lang.model.element.TypeElement; 035import javax.lang.model.element.VariableElement; 036import javax.lang.model.type.ArrayType; 037import javax.lang.model.type.PrimitiveType; 038import javax.lang.model.type.TypeMirror; 039import javax.lang.model.util.SimpleElementVisitor14; 040import javax.lang.model.util.SimpleTypeVisitor9; 041import java.util.Comparator; 042import java.util.List; 043 044/** 045 * A collection of {@code Comparator} factory methods. 046 */ 047public class Comparators { 048 049 private final Utils utils; 050 051 Comparators(Utils utils) { 052 this.utils = utils; 053 } 054 055 private Comparator<Element> moduleComparator = null; 056 057 /** 058 * Comparator for ModuleElements, simply compares the fully qualified names 059 * @return a Comparator 060 */ 061 public Comparator<Element> makeModuleComparator() { 062 if (moduleComparator == null) { 063 moduleComparator = new ElementComparator() { 064 @Override 065 public int compare(Element mod1, Element mod2) { 066 return compareFullyQualifiedNames(mod1, mod2); 067 } 068 }; 069 } 070 return moduleComparator; 071 } 072 073 private Comparator<Element> allClassesComparator = null; 074 075 /** 076 * Returns a Comparator for all classes, compares the simple names of 077 * TypeElement, if equal then the fully qualified names, and if equal again 078 * the names of the enclosing modules. 079 * 080 * @return Comparator 081 */ 082 public Comparator<Element> makeAllClassesComparator() { 083 if (allClassesComparator == null) { 084 allClassesComparator = new ElementComparator() { 085 @Override 086 public int compare(Element e1, Element e2) { 087 int result = compareNames(e1, e2); 088 if (result == 0) 089 result = compareFullyQualifiedNames(e1, e2); 090 if (result == 0) 091 result = compareModuleNames(e1, e2); 092 return result; 093 } 094 }; 095 } 096 return allClassesComparator; 097 } 098 099 private Comparator<Element> packageComparator = null; 100 101 /** 102 * Returns a Comparator for packages, by comparing the fully qualified names, 103 * and if those are equal the names of the enclosing modules. 104 * 105 * @return a Comparator 106 */ 107 public Comparator<Element> makePackageComparator() { 108 if (packageComparator == null) { 109 packageComparator = new ElementComparator() { 110 @Override 111 public int compare(Element pkg1, Element pkg2) { 112 int result = compareFullyQualifiedNames(pkg1, pkg2); 113 if (result == 0) 114 result = compareModuleNames(pkg1, pkg2); 115 return result; 116 } 117 }; 118 } 119 return packageComparator; 120 } 121 122 private Comparator<Element> summaryComparator = null; 123 124 /** 125 * Returns a Comparator for items listed on summary list pages 126 * (like deprecated or preview summary pages), by comparing the 127 * fully qualified names, and if those are equal the names of the enclosing modules. 128 * 129 * @return a Comparator 130 */ 131 public Comparator<Element> makeSummaryComparator() { 132 if (summaryComparator == null) { 133 summaryComparator = new ElementComparator() { 134 @Override 135 public int compare(Element e1, Element e2) { 136 int result = compareFullyQualifiedNames(e1, e2); 137 if (result != 0) { 138 return result; 139 } 140 // if elements are executable compare their parameter arrays 141 result = compareParameters(e1, e2); 142 if (result != 0) { 143 return result; 144 } 145 return compareModuleNames(e1, e2); 146 } 147 }; 148 } 149 return summaryComparator; 150 } 151 152 private Comparator<SerialFieldTree> serialFieldTreeComparator = null; 153 154 /** 155 * Returns a Comparator for SerialFieldTree. 156 * @return a Comparator 157 */ 158 public Comparator<SerialFieldTree> makeSerialFieldTreeComparator() { 159 if (serialFieldTreeComparator == null) { 160 serialFieldTreeComparator 161 = (SerialFieldTree o1, SerialFieldTree o2) -> { 162 String s1 = o1.getName().toString(); 163 String s2 = o2.getName().toString(); 164 return s1.compareTo(s2); 165 }; 166 } 167 return serialFieldTreeComparator; 168 } 169 170 /** 171 * Returns a general purpose comparator. 172 * @return a Comparator 173 */ 174 public Comparator<Element> makeGeneralPurposeComparator() { 175 return makeClassUseComparator(); 176 } 177 178 private Comparator<Element> overrideUseComparator = null; 179 180 /** 181 * Returns a Comparator for overrides and implements, 182 * used primarily on methods, compares the name first, 183 * then compares the simple names of the enclosing 184 * TypeElement and the fully qualified name of the enclosing TypeElement. 185 * @return a Comparator 186 */ 187 public Comparator<Element> makeOverrideUseComparator() { 188 if (overrideUseComparator == null) { 189 overrideUseComparator = new ElementComparator() { 190 @Override 191 public int compare(Element o1, Element o2) { 192 int result = utils.compareStrings(utils.getSimpleName(o1), 193 utils.getSimpleName(o2)); 194 if (result != 0) { 195 return result; 196 } 197 if (!utils.isTypeElement(o1) && !utils.isTypeElement(o2) 198 && !utils.isPackage(o1) && !utils.isPackage(o2)) { 199 TypeElement t1 = utils.getEnclosingTypeElement(o1); 200 TypeElement t2 = utils.getEnclosingTypeElement(o2); 201 result = utils.compareStrings(utils.getSimpleName(t1), 202 utils.getSimpleName(t2)); 203 if (result != 0) 204 return result; 205 } 206 result 207 = utils.compareStrings(utils.getFullyQualifiedName(o1), 208 utils.getFullyQualifiedName(o2)); 209 if (result != 0) 210 return result; 211 return compareElementKinds(o1, o2); 212 } 213 }; 214 } 215 return overrideUseComparator; 216 } 217 218 private Comparator<Element> indexUseComparator = null; 219 220 /** 221 * Returns an {@code Element} Comparator for index file presentations, and are sorted as follows. 222 * If comparing modules and/or packages then simply compare the qualified names, 223 * if comparing a module or a package with a type/member then compare the 224 * FullyQualifiedName of the module or a package with the SimpleName of the entity, 225 * otherwise: 226 * 1. compare the ElementKind ex: Module, Package, Interface etc. 227 * 2a. if equal and if the type is of ExecutableElement(Constructor, Methods), 228 * a case insensitive comparison of parameter the type signatures 229 * 2b. if equal, case sensitive comparison of the type signatures 230 * 3. if equal, compare the FQNs of the entities 231 * 4. finally, if equal, compare the names of the enclosing modules 232 * @return an element comparator for index file use 233 */ 234 public Comparator<Element> makeIndexElementComparator() { 235 if (indexUseComparator == null) { 236 indexUseComparator = new ElementComparator() { 237 /** 238 * Compares two elements. 239 * 240 * @param e1 - an element. 241 * @param e2 - an element. 242 * @return a negative integer, zero, or a positive integer as the first 243 * argument is less than, equal to, or greater than the second. 244 */ 245 @Override 246 public int compare(Element e1, Element e2) { 247 // first, compare names as appropriate 248 int result = utils.compareStrings(getIndexElementKey(e1), 249 getIndexElementKey(e2)); 250 if (result != 0) { 251 return result; 252 } 253 // if names are the same, compare element kinds 254 result = compareElementKinds(e1, e2); 255 if (result != 0) { 256 return result; 257 } 258 // if element kinds are the same, and are executable, 259 // compare the parameter arrays 260 result = compareParameters(e1, e2); 261 if (result != 0) { 262 return result; 263 } 264 // else fall back on fully qualified names 265 result = compareFullyQualifiedNames(e1, e2); 266 if (result != 0) 267 return result; 268 return compareModuleNames(e1, e2); 269 } 270 }; 271 } 272 return indexUseComparator; 273 } 274 275 /** 276 * {@return the element's primary key for use in the index comparator} 277 * This method can be used by other comparators which need to produce results 278 * that are consistent with the index comparator. 279 * 280 * @param element an element 281 */ 282 public String getIndexElementKey(Element element) { 283 return switch (element.getKind()) { 284 case MODULE, PACKAGE -> utils.getFullyQualifiedName(element); 285 default -> utils.getSimpleName(element); 286 }; 287 } 288 289 private Comparator<TypeMirror> typeMirrorClassUseComparator = null; 290 291 /** 292 * Returns a comparator that compares the fully qualified names of two type mirrors. 293 * 294 * @return the comparator 295 */ 296 public Comparator<TypeMirror> makeTypeMirrorClassUseComparator() { 297 if (typeMirrorClassUseComparator == null) { 298 typeMirrorClassUseComparator 299 = (TypeMirror type1, TypeMirror type2) -> { 300 String s1 = utils.getQualifiedTypeName(type1); 301 String s2 = utils.getQualifiedTypeName(type2); 302 return utils.compareStrings(s1, s2); 303 }; 304 } 305 return typeMirrorClassUseComparator; 306 } 307 308 private Comparator<TypeMirror> typeMirrorIndexUseComparator = null; 309 310 /** 311 * Returns a comparator that compares the simple names of two type mirrors, 312 * or the fully qualified names if the simple names are equal. 313 * 314 * @return the comparator 315 */ 316 public Comparator<TypeMirror> makeTypeMirrorIndexUseComparator() { 317 if (typeMirrorIndexUseComparator == null) { 318 typeMirrorIndexUseComparator = (TypeMirror t1, TypeMirror t2) -> { 319 int result = utils.compareStrings(utils.getTypeName(t1, false), 320 utils.getTypeName(t2, false)); 321 if (result != 0) 322 return result; 323 return utils.compareStrings(utils.getQualifiedTypeName(t1), 324 utils.getQualifiedTypeName(t2)); 325 }; 326 } 327 return typeMirrorIndexUseComparator; 328 } 329 330 private Comparator<Element> classUseComparator = null; 331 332 /** 333 * Comparator for ClassUse presentations, and sorts as follows: 334 * 1. member names 335 * 2. then fully qualified member names 336 * 3. then parameter types if applicable 337 * 4. the element kinds ie. package, class, interface etc. 338 * 5. finally the name of the enclosing modules 339 * @return a comparator to sort classes and members for class use 340 */ 341 public Comparator<Element> makeClassUseComparator() { 342 if (classUseComparator == null) { 343 classUseComparator = new ElementComparator() { 344 /** 345 * Compares two Elements. 346 * 347 * @param e1 - an element. 348 * @param e2 - an element. 349 * @return a negative integer, zero, or a positive integer as the first 350 * argument is less than, equal to, or greater than the second. 351 */ 352 @Override 353 public int compare(Element e1, Element e2) { 354 int result = compareNames(e1, e2); 355 if (result != 0) { 356 return result; 357 } 358 result = compareFullyQualifiedNames(e1, e2); 359 if (result != 0) { 360 return result; 361 } 362 result = compareParameters(e1, e2); 363 if (result != 0) { 364 return result; 365 } 366 result = compareElementKinds(e1, e2); 367 if (result != 0) { 368 return result; 369 } 370 return compareModuleNames(e1, e2); 371 } 372 }; 373 } 374 return classUseComparator; 375 } 376 377 /** 378 * A general purpose comparator to sort Element entities, basically provides the building blocks 379 * for creating specific comparators for an use-case. 380 */ 381 private abstract class ElementComparator implements Comparator<Element> { 382 public ElementComparator() { 383 } 384 385 /** 386 * compares two parameter arrays by first comparing the length of the arrays, and 387 * then each Type of the parameter in the array. 388 * @param params1 the first parameter array. 389 * @param params2 the first parameter array. 390 * @return a negative integer, zero, or a positive integer as the first 391 * argument is less than, equal to, or greater than the second. 392 */ 393 protected int compareParameters(boolean caseSensitive, 394 List<? extends VariableElement> params1, 395 List<? extends VariableElement> params2) { 396 397 return utils.compareStrings(caseSensitive, 398 getParametersAsString(params1), 399 getParametersAsString(params2)); 400 } 401 402 String getParametersAsString(List<? extends VariableElement> params) { 403 StringBuilder sb = new StringBuilder(); 404 for (VariableElement param : params) { 405 TypeMirror t = param.asType(); 406 // prefix P for primitive and R for reference types, thus items 407 // will 408 // be ordered lexically and correctly. 409 sb.append(getTypeCode(t)).append("-").append(t).append("-"); 410 } 411 return sb.toString(); 412 } 413 414 private String getTypeCode(TypeMirror t) { 415 return new SimpleTypeVisitor9<String, Void>() { 416 417 @Override 418 public String visitPrimitive(PrimitiveType t, Void p) { 419 return "P"; 420 } 421 422 @Override 423 public String visitArray(ArrayType t, Void p) { 424 return visit(t.getComponentType()); 425 } 426 427 @Override 428 protected String defaultAction(TypeMirror e, Void p) { 429 return "R"; 430 } 431 432 }.visit(t); 433 } 434 435 /** 436 * Compares two Elements, typically the name of a method, 437 * field or constructor. 438 * @param e1 the first Element. 439 * @param e2 the second Element. 440 * @return a negative integer, zero, or a positive integer as the first 441 * argument is less than, equal to, or greater than the second. 442 */ 443 protected int compareNames(Element e1, Element e2) { 444 return utils.compareStrings(utils.getSimpleName(e1), 445 utils.getSimpleName(e2)); 446 } 447 448 /** 449 * Compares the fully qualified names of the entities 450 * @param e1 the first Element. 451 * @param e2 the first Element. 452 * @return a negative integer, zero, or a positive integer as the first 453 * argument is less than, equal to, or greater than the second. 454 */ 455 protected int compareFullyQualifiedNames(Element e1, Element e2) { 456 // add simple name to be compatible 457 String thisElement = getFullyQualifiedName(e1); 458 String thatElement = getFullyQualifiedName(e2); 459 return utils.compareStrings(thisElement, thatElement); 460 } 461 462 /** 463 * Compares the name of the modules of two elements. 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 compareModuleNames(Element e1, Element e2) { 470 ModuleElement m1 = utils.elementUtils.getModuleOf(e1); 471 ModuleElement m2 = utils.elementUtils.getModuleOf(e2); 472 if (m1 != null && m2 != null) { 473 return compareFullyQualifiedNames(m1, m2); 474 } else if (m1 != null) { 475 return 1; 476 } else if (m2 != null) { 477 return -1; 478 } 479 return 0; 480 } 481 482 /** 483 * Compares the parameter arrays of two elements if they both are executable. 484 * @param e1 the first element 485 * @param e2 the second element 486 * @return a negative integer, zero, or a positive integer as the first 487 * argument is less than, equal to, or greater than the second 488 */ 489 protected int compareParameters(Element e1, Element e2) { 490 int result = 0; 491 if (hasParameters(e1) && hasParameters(e2)) { 492 List<? extends VariableElement> parameters1 493 = ((ExecutableElement) e1).getParameters(); 494 List<? extends VariableElement> parameters2 495 = ((ExecutableElement) e2).getParameters(); 496 result = compareParameters(false, parameters1, parameters2); 497 if (result != 0) { 498 return result; 499 } 500 result = compareParameters(true, parameters1, parameters2); 501 } 502 return result; 503 } 504 505 /** 506 * Compares the kinds of two elements. 507 * @param e1 the first element 508 * @param e2 the second element 509 * @return a negative integer, zero, or a positive integer as the first 510 * argument is less than, equal to, or greater than the second 511 */ 512 protected int compareElementKinds(Element e1, Element e2) { 513 return Integer.compare(getKindIndex(e1), getKindIndex(e2)); 514 } 515 516 private int getKindIndex(Element e) { 517 return switch (e.getKind()) { 518 case MODULE -> 0; 519 case PACKAGE -> 1; 520 case CLASS -> 2; 521 case ENUM -> 3; 522 case ENUM_CONSTANT -> 4; 523 case RECORD -> 5; 524 case INTERFACE -> 6; 525 case ANNOTATION_TYPE -> 7; 526 case FIELD -> 8; 527 case CONSTRUCTOR -> 9; 528 case METHOD -> 10; 529 default -> throw new IllegalArgumentException( 530 e.getKind().toString()); 531 }; 532 } 533 534 boolean hasParameters(Element e) { 535 return new SimpleElementVisitor14<Boolean, Void>() { 536 @Override 537 public Boolean visitExecutable(ExecutableElement e, Void p) { 538 return true; 539 } 540 541 @Override 542 protected Boolean defaultAction(Element e, Void p) { 543 return false; 544 } 545 546 }.visit(e); 547 } 548 549 /** 550 * The fully qualified names of the entities, used solely by the comparator. 551 * 552 * @return a negative integer, zero, or a positive integer as the first argument is less 553 * than, equal to, or greater than the second. 554 */ 555 private String getFullyQualifiedName(Element e) { 556 return new SimpleElementVisitor14<String, Void>() { 557 @Override 558 public String visitModule(ModuleElement e, Void p) { 559 return e.getQualifiedName().toString(); 560 } 561 562 @Override 563 public String visitPackage(PackageElement e, Void p) { 564 return e.getQualifiedName().toString(); 565 } 566 567 @Override 568 public String visitExecutable(ExecutableElement e, Void p) { 569 // For backward compatibility 570 return getFullyQualifiedName(e.getEnclosingElement()) 571 + "." + e.getSimpleName().toString(); 572 } 573 574 @Override 575 public String visitType(TypeElement e, Void p) { 576 return e.getQualifiedName().toString(); 577 } 578 579 @Override 580 protected String defaultAction(Element e, Void p) { 581 return utils.getEnclosingTypeElement(e).getQualifiedName() 582 .toString() 583 + "." + e.getSimpleName().toString(); 584 } 585 }.visit(e); 586 } 587 } 588}