8315464: Uncouple AllClassesIndexWriter from IndexBuilder

Reviewed-by: jjg
This commit is contained in:
Hannes Wallnöfer 2023-09-22 19:17:40 +00:00
parent 9b65b7ddbe
commit 6b8261b8d6
13 changed files with 68 additions and 115 deletions

View File

@ -28,6 +28,7 @@ package jdk.javadoc.internal.doclets.formats.html;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
@ -39,8 +40,6 @@ import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
import jdk.javadoc.internal.doclets.toolkit.util.Utils.ElementFlag; import jdk.javadoc.internal.doclets.toolkit.util.Utils.ElementFlag;
/** /**
@ -48,25 +47,19 @@ import jdk.javadoc.internal.doclets.toolkit.util.Utils.ElementFlag;
*/ */
public class AllClassesIndexWriter extends HtmlDocletWriter { public class AllClassesIndexWriter extends HtmlDocletWriter {
/**
* Index of all the classes.
*/
protected IndexBuilder indexBuilder;
/** /**
* Construct AllClassesIndexWriter object. Also initializes the indexBuilder variable in this * Construct AllClassesIndexWriter object. Also initializes the indexBuilder variable in this
* class. * class.
* *
* @param configuration The current configuration * @param configuration The current configuration
* @param indexBuilder Unicode based Index from {@link IndexBuilder}
*/ */
public AllClassesIndexWriter(HtmlConfiguration configuration, IndexBuilder indexBuilder) { public AllClassesIndexWriter(HtmlConfiguration configuration) {
super(configuration, DocPaths.ALLCLASSES_INDEX); super(configuration, DocPaths.ALLCLASSES_INDEX);
this.indexBuilder = indexBuilder;
} }
@Override @Override
public void buildPage() throws DocFileIOException { public void buildPage() throws DocFileIOException {
messages.notice("doclet.Building_Index_For_All_Classes");
String label = resources.getText("doclet.All_Classes_And_Interfaces"); String label = resources.getText("doclet.All_Classes_And_Interfaces");
Content allClassesContent = new ContentBuilder(); Content allClassesContent = new ContentBuilder();
addContents(allClassesContent); addContents(allClassesContent);
@ -97,14 +90,10 @@ public class AllClassesIndexWriter extends HtmlDocletWriter {
.addTab(contents.records, utils::isRecord) .addTab(contents.records, utils::isRecord)
.addTab(contents.exceptionClasses, utils::isThrowable) .addTab(contents.exceptionClasses, utils::isThrowable)
.addTab(contents.annotationTypes, utils::isAnnotationInterface); .addTab(contents.annotationTypes, utils::isAnnotationInterface);
for (Character unicode : indexBuilder.getFirstCharacters()) { Set<TypeElement> typeElements = getTypeElements();
for (IndexItem indexItem : indexBuilder.getItems(unicode)) { for (TypeElement typeElement : typeElements) {
TypeElement typeElement = (TypeElement) indexItem.getElement();
if (typeElement != null && utils.isCoreClass(typeElement)) {
addTableRow(table, typeElement); addTableRow(table, typeElement);
} }
}
}
Content titleContent = contents.allClassesAndInterfacesLabel; Content titleContent = contents.allClassesAndInterfacesLabel;
var pHeading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, var pHeading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING,
HtmlStyle.title, titleContent); HtmlStyle.title, titleContent);
@ -115,6 +104,24 @@ public class AllClassesIndexWriter extends HtmlDocletWriter {
} }
} }
private Set<TypeElement> getTypeElements() {
Set<TypeElement> classes = new TreeSet<>(utils.comparators.allClassesComparator());
boolean noDeprecated = options.noDeprecated();
Set<TypeElement> includedTypes = configuration.getIncludedTypeElements();
for (TypeElement typeElement : includedTypes) {
if (utils.hasHiddenTag(typeElement) || !utils.isCoreClass(typeElement)) {
continue;
}
if (noDeprecated
&& (utils.isDeprecated(typeElement)
|| utils.isDeprecated(utils.containingPackage(typeElement)))) {
continue;
}
classes.add(typeElement);
}
return classes;
}
/** /**
* Add table row. * Add table row.
* *

View File

@ -475,8 +475,8 @@ public class ConstantsSummaryWriter extends HtmlDocletWriter {
content.add(bodyContents); content.add(bodyContents);
printHtmlDocument(null, "summary of constants", content); printHtmlDocument(null, "summary of constants", content);
if (hasConstants && configuration.mainIndex != null) { if (hasConstants && configuration.indexBuilder != null) {
configuration.mainIndex.add(IndexItem.of(IndexItem.Category.TAGS, configuration.indexBuilder.add(IndexItem.of(IndexItem.Category.TAGS,
resources.getText("doclet.Constants_Summary"), path)); resources.getText("doclet.Constants_Summary"), path));
} }
} }

View File

@ -86,8 +86,8 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
*/ */
@Override @Override
public void buildPage() throws DocFileIOException { public void buildPage() throws DocFileIOException {
boolean hasExternalSpecs = configuration.mainIndex != null boolean hasExternalSpecs = configuration.indexBuilder != null
&& !configuration.mainIndex.getItems(DocTree.Kind.SPEC).isEmpty(); && !configuration.indexBuilder.getItems(DocTree.Kind.SPEC).isEmpty();
if (!hasExternalSpecs) { if (!hasExternalSpecs) {
return; return;
} }
@ -110,15 +110,15 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
.setFooter(getFooter())); .setFooter(getFooter()));
printHtmlDocument(null, "external specifications", body); printHtmlDocument(null, "external specifications", body);
if (configuration.mainIndex != null) { if (configuration.indexBuilder != null) {
configuration.mainIndex.add(IndexItem.of(IndexItem.Category.TAGS, title, path)); configuration.indexBuilder.add(IndexItem.of(IndexItem.Category.TAGS, title, path));
} }
} }
protected void checkUniqueItems() { protected void checkUniqueItems() {
Map<String, Map<String, List<IndexItem>>> itemsByURL = new HashMap<>(); Map<String, Map<String, List<IndexItem>>> itemsByURL = new HashMap<>();
Map<String, Map<String, List<IndexItem>>> itemsByTitle = new HashMap<>(); Map<String, Map<String, List<IndexItem>>> itemsByTitle = new HashMap<>();
for (IndexItem ii : configuration.mainIndex.getItems(DocTree.Kind.SPEC)) { for (IndexItem ii : configuration.indexBuilder.getItems(DocTree.Kind.SPEC)) {
if (ii.getDocTree() instanceof SpecTree st) { if (ii.getDocTree() instanceof SpecTree st) {
String url = st.getURL().toString(); String url = st.getURL().toString();
String title = ii.getLabel(); // normalized form of st.getTitle() String title = ii.getLabel(); // normalized form of st.getTitle()
@ -230,7 +230,7 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
} }
private Map<String, List<IndexItem>> groupExternalSpecs() { private Map<String, List<IndexItem>> groupExternalSpecs() {
return configuration.mainIndex.getItems(DocTree.Kind.SPEC).stream() return configuration.indexBuilder.getItems(DocTree.Kind.SPEC).stream()
.collect(groupingBy(IndexItem::getLabel, () -> new TreeMap<>(getTitleComparator()), toList())); .collect(groupingBy(IndexItem::getLabel, () -> new TreeMap<>(getTitleComparator()), toList()));
} }

View File

@ -112,7 +112,7 @@ public class HtmlConfiguration extends BaseConfiguration {
* 2. items for elements are added in bulk before generating the index files * 2. items for elements are added in bulk before generating the index files
* 3. additional items are added as needed * 3. additional items are added as needed
*/ */
public HtmlIndexBuilder mainIndex; public HtmlIndexBuilder indexBuilder;
/** /**
* The collection of deprecated items, if any, to be displayed on the deprecated-list page, * The collection of deprecated items, if any, to be displayed on the deprecated-list page,
@ -307,7 +307,7 @@ public class HtmlConfiguration extends BaseConfiguration {
} }
} }
if (options.createIndex()) { if (options.createIndex()) {
mainIndex = new HtmlIndexBuilder(this); indexBuilder = new HtmlIndexBuilder(this);
} }
docPaths = new DocPaths(utils); docPaths = new DocPaths(utils);
setCreateOverview(); setCreateOverview();

View File

@ -59,7 +59,6 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.NewAPIBuilder; import jdk.javadoc.internal.doclets.toolkit.util.NewAPIBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.PreviewAPIListBuilder; import jdk.javadoc.internal.doclets.toolkit.util.PreviewAPIListBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.ResourceIOException; import jdk.javadoc.internal.doclets.toolkit.util.ResourceIOException;
@ -273,16 +272,14 @@ public class HtmlDoclet extends AbstractDoclet {
} }
writerFactory.newSystemPropertiesWriter().buildPage(); writerFactory.newSystemPropertiesWriter().buildPage();
configuration.mainIndex.addElements(); configuration.indexBuilder.addElements();
IndexBuilder allClassesIndex = new IndexBuilder(configuration, nodeprecated, true);
allClassesIndex.addElements();
writerFactory.newAllClassesIndexWriter(allClassesIndex).buildPage(); writerFactory.newAllClassesIndexWriter().buildPage();
if (!configuration.packages.isEmpty()) { if (!configuration.packages.isEmpty()) {
writerFactory.newAllPackagesIndexWriter().buildPage(); writerFactory.newAllPackagesIndexWriter().buildPage();
} }
configuration.mainIndex.createSearchIndexFiles(); configuration.indexBuilder.createSearchIndexFiles();
IndexWriter.generate(configuration); IndexWriter.generate(configuration);
writerFactory.newSearchWriter().buildPage(); writerFactory.newSearchWriter().buildPage();
} }

View File

@ -1470,13 +1470,13 @@ public abstract class HtmlDocletWriter {
attrs.add("id=\"").add(htmlId.name()).add("\""); attrs.add("id=\"").add(htmlId.name()).add("\"");
} }
// Generate index item // Generate index item
if (!headingContent.isEmpty() && configuration.mainIndex != null) { if (!headingContent.isEmpty() && configuration.indexBuilder != null) {
String tagText = headingContent.replaceAll("\\s+", " "); String tagText = headingContent.replaceAll("\\s+", " ");
IndexItem item = IndexItem.of(element, node, tagText, IndexItem item = IndexItem.of(element, node, tagText,
getTagletWriterInstance(context).getHolderName(element), getTagletWriterInstance(context).getHolderName(element),
resources.getText("doclet.Section"), resources.getText("doclet.Section"),
new DocLink(path, id)); new DocLink(path, id));
configuration.mainIndex.add(item); configuration.indexBuilder.add(item);
} }
} }

View File

@ -42,7 +42,6 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder;
import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
/** /**
* Extensions to {@code IndexBuilder} to fill in remaining fields * Extensions to {@code IndexBuilder} to fill in remaining fields
@ -51,10 +50,8 @@ import jdk.javadoc.internal.doclets.toolkit.util.Utils;
* JavaScript files. * JavaScript files.
*/ */
public class HtmlIndexBuilder extends IndexBuilder { public class HtmlIndexBuilder extends IndexBuilder {
private final HtmlConfiguration configuration;
private final Resources resources; private final Resources resources;
private final Utils utils;
private final HtmlIds htmlIds; private final HtmlIds htmlIds;
/** /**
@ -63,11 +60,9 @@ public class HtmlIndexBuilder extends IndexBuilder {
* @param configuration the current configuration of the doclet * @param configuration the current configuration of the doclet
*/ */
HtmlIndexBuilder(HtmlConfiguration configuration) { HtmlIndexBuilder(HtmlConfiguration configuration) {
super(configuration, configuration.getOptions().noDeprecated()); super(configuration);
this.configuration = configuration; this.resources = configuration.docResources;
resources = configuration.docResources; this.htmlIds = configuration.htmlIds;
utils = configuration.utils;
htmlIds = configuration.htmlIds;
} }
/** /**
@ -79,9 +74,6 @@ public class HtmlIndexBuilder extends IndexBuilder {
@Override @Override
public void addElements() { public void addElements() {
super.addElements(); super.addElements();
if (classesOnly) {
return;
}
Map<String,Integer> duplicateLabelCheck = new HashMap<>(); Map<String,Integer> duplicateLabelCheck = new HashMap<>();
for (Character ch : getFirstCharacters()) { for (Character ch : getFirstCharacters()) {

View File

@ -79,7 +79,7 @@ public class IndexWriter extends HtmlDocletWriter {
*/ */
public static void generate(HtmlConfiguration configuration) throws DocletException { public static void generate(HtmlConfiguration configuration) throws DocletException {
var writerFactory = configuration.getWriterFactory(); var writerFactory = configuration.getWriterFactory();
IndexBuilder mainIndex = configuration.mainIndex; IndexBuilder mainIndex = configuration.indexBuilder;
List<Character> firstCharacters = mainIndex.getFirstCharacters(); List<Character> firstCharacters = mainIndex.getFirstCharacters();
if (configuration.getOptions().splitIndex()) { if (configuration.getOptions().splitIndex()) {
ListIterator<Character> iter = firstCharacters.listIterator(); ListIterator<Character> iter = firstCharacters.listIterator();
@ -104,7 +104,7 @@ public class IndexWriter extends HtmlDocletWriter {
protected IndexWriter(HtmlConfiguration configuration, DocPath path, protected IndexWriter(HtmlConfiguration configuration, DocPath path,
List<Character> allFirstCharacters, List<Character> displayFirstCharacters) { List<Character> allFirstCharacters, List<Character> displayFirstCharacters) {
super(configuration, path); super(configuration, path);
this.mainIndex = configuration.mainIndex; this.mainIndex = configuration.indexBuilder;
this.splitIndex = configuration.getOptions().splitIndex(); this.splitIndex = configuration.getOptions().splitIndex();
this.allFirstCharacters = allFirstCharacters; this.allFirstCharacters = allFirstCharacters;
this.displayFirstCharacters = displayFirstCharacters; this.displayFirstCharacters = displayFirstCharacters;

View File

@ -696,8 +696,8 @@ public class SerializedFormWriter extends SubWriterHolderWriter {
source.add(bodyContents); source.add(bodyContents);
printHtmlDocument(null, "serialized forms", source); printHtmlDocument(null, "serialized forms", source);
if (configuration.mainIndex != null) { if (configuration.indexBuilder != null) {
configuration.mainIndex.add(IndexItem.of(IndexItem.Category.TAGS, configuration.indexBuilder.add(IndexItem.of(IndexItem.Category.TAGS,
resources.getText("doclet.Serialized_Form"), path)); resources.getText("doclet.Serialized_Form"), path));
} }
} }

View File

@ -73,8 +73,8 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
@Override @Override
public void buildPage() throws DocFileIOException { public void buildPage() throws DocFileIOException {
boolean hasSystemProperties = configuration.mainIndex != null boolean hasSystemProperties = configuration.indexBuilder != null
&& !configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty(); && !configuration.indexBuilder.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
if (!hasSystemProperties) { if (!hasSystemProperties) {
return; return;
} }
@ -95,8 +95,8 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
.setFooter(getFooter())); .setFooter(getFooter()));
printHtmlDocument(null, "system properties", body); printHtmlDocument(null, "system properties", body);
if (configuration.mainIndex != null) { if (configuration.indexBuilder != null) {
configuration.mainIndex.add(IndexItem.of(IndexItem.Category.TAGS, title, path)); configuration.indexBuilder.add(IndexItem.of(IndexItem.Category.TAGS, title, path));
} }
} }
@ -127,7 +127,7 @@ public class SystemPropertiesWriter extends HtmlDocletWriter {
} }
private Map<String, List<IndexItem>> groupSystemProperties() { private Map<String, List<IndexItem>> groupSystemProperties() {
return configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).stream() return configuration.indexBuilder.getItems(DocTree.Kind.SYSTEM_PROPERTY).stream()
.collect(groupingBy(IndexItem::getLabel, TreeMap::new, Collectors.toCollection(ArrayList::new))); .collect(groupingBy(IndexItem::getLabel, TreeMap::new, Collectors.toCollection(ArrayList::new)));
} }

View File

@ -103,8 +103,8 @@ public class WriterFactory {
/** /**
* {@return a new writer for the list of "all classes"} * {@return a new writer for the list of "all classes"}
*/ */
public HtmlDocletWriter newAllClassesIndexWriter(IndexBuilder indexBuilder) { public HtmlDocletWriter newAllClassesIndexWriter() {
return new AllClassesIndexWriter(configuration, indexBuilder); return new AllClassesIndexWriter(configuration);
} }
/** /**

View File

@ -379,7 +379,7 @@ public class TagletWriter {
String holder = getHolderName(element); String holder = getHolderName(element);
IndexItem item = IndexItem.of(element, tree, tagText, holder, desc, IndexItem item = IndexItem.of(element, tree, tagText, holder, desc,
new DocLink(htmlWriter.path, id.name())); new DocLink(htmlWriter.path, id.name()));
configuration.mainIndex.add(item); configuration.indexBuilder.add(item);
} }
} }
return result; return result;

View File

@ -35,18 +35,17 @@ import javax.lang.model.element.TypeElement;
import com.sun.source.doctree.DocTree; import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
import jdk.javadoc.internal.doclets.toolkit.Messages;
import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*; import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
/** /**
* An alphabetical index of elements, search tags, and other items. * An alphabetical index of elements, search tags, and other items.
* Two tables are maintained: * Two tables are maintained:
* one is indexed by the first character of each items name; * one is indexed by the first character of each item's name;
* the other is index by the item's category, indicating the JavaScript * the other is indexed by the item's category, indicating the JavaScript
* file in which the item should be written. * file in which the item should be written.
*/ */
public class IndexBuilder { public abstract class IndexBuilder {
/** /**
* Sets of items keyed by the first character of the names (labels) * Sets of items keyed by the first character of the names (labels)
@ -65,73 +64,39 @@ public class IndexBuilder {
*/ */
private final boolean noDeprecated; private final boolean noDeprecated;
/** protected final BaseConfiguration configuration;
* Build this index only for classes? protected final Utils utils;
*/
protected final boolean classesOnly;
private final BaseConfiguration configuration;
private final Utils utils;
/** /**
* The comparator used for the sets in {@code itemsByFirstChar}. * The comparator used for the sets in {@code itemsByFirstChar}.
*/ */
private final Comparator<IndexItem> mainComparator; private final Comparator<IndexItem> mainComparator;
/**
* Creates a new {@code IndexBuilder}.
*
* @param configuration the current configuration of the doclet
* @param noDeprecated true if -nodeprecated option is used,
* false otherwise
*/
public IndexBuilder(BaseConfiguration configuration,
boolean noDeprecated)
{
this(configuration, noDeprecated, false);
}
/** /**
* Creates a new {@code IndexBuilder}. * Creates a new {@code IndexBuilder}.
* *
* @param configuration the current configuration of the doclet * @param configuration the current configuration of the doclet
* @param noDeprecated true if -nodeprecated option is used,
* false otherwise
* @param classesOnly include only classes in index
*/ */
public IndexBuilder(BaseConfiguration configuration, public IndexBuilder(BaseConfiguration configuration) {
boolean noDeprecated,
boolean classesOnly)
{
this.configuration = configuration; this.configuration = configuration;
this.utils = configuration.utils; this.utils = configuration.utils;
Messages messages = configuration.getMessages(); configuration.getMessages().notice("doclet.Building_Index");
if (classesOnly) {
messages.notice("doclet.Building_Index_For_All_Classes");
} else {
messages.notice("doclet.Building_Index");
}
this.noDeprecated = noDeprecated;
this.classesOnly = classesOnly;
noDeprecated = configuration.getOptions().noDeprecated();
itemsByFirstChar = new TreeMap<>(); itemsByFirstChar = new TreeMap<>();
itemsByCategory = new EnumMap<>(IndexItem.Category.class); itemsByCategory = new EnumMap<>(IndexItem.Category.class);
mainComparator = makeComparator();
mainComparator = classesOnly ? makeClassComparator() : makeIndexComparator();
} }
/** /**
* Adds all the selected modules, packages, types and their members to the index, * Adds all the selected modules, packages, types and their members to the index.
* or just the type elements if {@code classesOnly} is {@code true}.
*/ */
public void addElements() { public void addElements() {
Set<TypeElement> classes = configuration.getIncludedTypeElements(); Set<TypeElement> classes = configuration.getIncludedTypeElements();
indexTypeElements(classes); indexTypeElements(classes);
if (classesOnly) {
return;
}
Set<PackageElement> packages = configuration.getSpecifiedPackageElements(); Set<PackageElement> packages = configuration.getSpecifiedPackageElements();
if (packages.isEmpty()) { if (packages.isEmpty()) {
packages = classes packages = classes
@ -310,14 +275,6 @@ public class IndexBuilder {
return '*'; return '*';
} }
/**
* Returns a comparator for the all-classes list.
* @return a comparator for class element items
*/
private Comparator<IndexItem> makeClassComparator() {
return Comparator.comparing(IndexItem::getElement, utils.comparators.allClassesComparator());
}
/** /**
* Returns a comparator for the {@code IndexItem}s in the index page. * Returns a comparator for the {@code IndexItem}s in the index page.
* This is a composite comparator that must be able to compare all kinds of items: * This is a composite comparator that must be able to compare all kinds of items:
@ -325,7 +282,7 @@ public class IndexBuilder {
* *
* @return a comparator for index page items * @return a comparator for index page items
*/ */
private Comparator<IndexItem> makeIndexComparator() { private Comparator<IndexItem> makeComparator() {
// We create comparators specific to element and search tag items, and a // We create comparators specific to element and search tag items, and a
// base comparator used to compare between the two kinds of items. // base comparator used to compare between the two kinds of items.
// In order to produce consistent results, it is important that the base comparator // In order to produce consistent results, it is important that the base comparator