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 }