--- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractIndexWriter.java 2019-10-30 16:23:42.999748697 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractIndexWriter.java 2019-10-30 16:23:42.647748709 -0700 @@ -38,7 +38,7 @@ import javax.lang.model.element.ModuleElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import com.sun.source.doctree.DocTree; import jdk.javadoc.internal.doclets.formats.html.markup.Entity; @@ -178,9 +178,10 @@ contentTree.add(heading); } + @SuppressWarnings("preview") protected void addDescription(Content dl, Element element) { SearchIndexItem si = new SearchIndexItem(); - new SimpleElementVisitor9() { + new SimpleElementVisitor14() { @Override public Void visitModule(ModuleElement e, Void p) { --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java 2019-10-30 16:23:44.055748660 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java 2019-10-30 16:23:43.763748670 -0700 @@ -35,6 +35,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ModuleElement; import javax.lang.model.element.PackageElement; +import javax.lang.model.element.RecordComponentElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleElementVisitor8; @@ -210,7 +211,7 @@ /** * {@inheritDoc} */ - @Override + @Override @SuppressWarnings("preview") public void addClassSignature(String modifiers, Content classInfoTree) { Content hr = new HtmlTree(HtmlTag.HR); classInfoTree.add(hr); @@ -231,6 +232,9 @@ span.add(parameterLinks); pre.add(span); } + if (utils.isRecord(typeElement)) { + pre.add(getRecordComponents(typeElement)); + } if (!utils.isInterface(typeElement)) { TypeMirror superclass = utils.getFirstVisibleSuperClass(typeElement); if (superclass != null) { @@ -266,6 +270,26 @@ classInfoTree.add(pre); } + @SuppressWarnings("preview") + private Content getRecordComponents(TypeElement typeElem) { + Content content = new ContentBuilder(); + content.add("("); + String sep = ""; + for (RecordComponentElement e : typeElement.getRecordComponents()) { + content.add(sep); + getAnnotations(e.getAnnotationMirrors(), false).stream() + .forEach(a -> { content.add(a); content.add(" "); }); + Content link = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.RECORD_COMPONENT, + e.asType())); + content.add(link); + content.add(Entity.NO_BREAK_SPACE); + content.add(e.getSimpleName()); + sep = ", "; + } + content.add(")"); + return content; + } + /** * {@inheritDoc} */ @@ -294,9 +318,9 @@ * Get the class hierarchy tree for the given class. * * @param type the class to print the hierarchy for - * @return a content tree for class inheritence + * @return a content tree for class inheritance */ - private Content getClassInheritenceTree(TypeMirror type) { + private Content getClassInheritanceTree(TypeMirror type) { TypeMirror sup; HtmlTree classTree = null; do { @@ -347,19 +371,20 @@ if (!utils.isClass(typeElement)) { return; } - classContentTree.add(getClassInheritenceTree(typeElement.asType())); + classContentTree.add(getClassInheritanceTree(typeElement.asType())); } /** * {@inheritDoc} */ @Override - public void addTypeParamInfo(Content classInfoTree) { - if (!utils.getTypeParamTrees(typeElement).isEmpty()) { - Content typeParam = (new ParamTaglet()).getTagletOutput(typeElement, + public void addParamInfo(Content classInfoTree) { + if (utils.hasBlockTag(typeElement, DocTree.Kind.PARAM)) { + Content paramInfo = (new ParamTaglet()).getTagletOutput(typeElement, getTagletWriterInstance(false)); - Content dl = HtmlTree.DL(typeParam); - classInfoTree.add(dl); + if (!paramInfo.isEmpty()) { + classInfoTree.add(HtmlTree.DL(paramInfo)); + } } } --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java 2019-10-30 16:23:45.323748616 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java 2019-10-30 16:23:44.951748629 -0700 @@ -155,6 +155,7 @@ public final Content propertyLabel; public final Content propertyDetailsLabel; public final Content propertySummaryLabel; + public final Content record; public final Content seeLabel; public final Content serializedForm; public final Content servicesLabel; @@ -282,6 +283,7 @@ propertyLabel = getContent("doclet.Property"); propertyDetailsLabel = getContent("doclet.Property_Detail"); propertySummaryLabel = getContent("doclet.Property_Summary"); + record = getContent("doclet.Record"); seeLabel = getContent("doclet.See"); serializedForm = getContent("doclet.Serialized_Form"); servicesLabel = getContent("doclet.Services"); --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java 2019-10-30 16:23:47.375748544 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java 2019-10-30 16:23:46.959748559 -0700 @@ -236,22 +236,21 @@ * {@inheritDoc} */ @Override // defined by AbstractDoclet - protected void generateClassFiles(SortedSet arr, ClassTree classtree) + protected void generateClassFiles(SortedSet typeElems, ClassTree classTree) throws DocletException { - List list = new ArrayList<>(arr); - for (TypeElement klass : list) { - if (utils.hasHiddenTag(klass) || - !(configuration.isGeneratedDoc(klass) && utils.isIncluded(klass))) { + for (TypeElement te : typeElems) { + if (utils.hasHiddenTag(te) || + !(configuration.isGeneratedDoc(te) && utils.isIncluded(te))) { continue; } - if (utils.isAnnotationType(klass)) { + if (utils.isAnnotationType(te)) { AbstractBuilder annotationTypeBuilder = configuration.getBuilderFactory() - .getAnnotationTypeBuilder(klass); + .getAnnotationTypeBuilder(te); annotationTypeBuilder.build(); } else { AbstractBuilder classBuilder = - configuration.getBuilderFactory().getClassBuilder(klass, classtree); + configuration.getBuilderFactory().getClassBuilder(te, classTree); classBuilder.build(); } } --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java 2019-10-30 16:23:48.727748497 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java 2019-10-30 16:23:48.391748509 -0700 @@ -51,7 +51,7 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleAnnotationValueVisitor9; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import javax.lang.model.util.SimpleTypeVisitor9; import com.sun.source.doctree.AttributeTree; @@ -1512,7 +1512,8 @@ @Override public Boolean visitLink(LinkTree node, Content c) { // we need to pass the DocTreeImpl here, so ignore node - result.add(seeTagToContent(element, tag)); + Content content = seeTagToContent(element, tag); + result.add(content); return false; } @@ -1660,13 +1661,14 @@ * * @return the text, with all the relative links redirected to work. */ + @SuppressWarnings("preview") private String redirectRelativeLinks(Element element, TextTree tt) { String text = tt.getBody(); if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) { return text; } - DocPath redirectPathFromRoot = new SimpleElementVisitor9() { + DocPath redirectPathFromRoot = new SimpleElementVisitor14() { @Override public DocPath visitType(TypeElement e, Void p) { return docPaths.forPackage(utils.containingPackage(e)); @@ -1749,22 +1751,22 @@ } /** - * Add the annotatation types for the given element and parameter. + * Add the annotation types for the given element and parameter. * * @param param the parameter to write annotations for. * @param tree the content tree to which the annotation types will be added */ public boolean addAnnotationInfo(VariableElement param, Content tree) { - Content annotaionInfo = getAnnotationInfo(param.getAnnotationMirrors(), false); - if (annotaionInfo.isEmpty()) { + Content annotationInfo = getAnnotationInfo(param.getAnnotationMirrors(), false); + if (annotationInfo.isEmpty()) { return false; } - tree.add(annotaionInfo); + tree.add(annotationInfo); return true; } /** - * Adds the annotatation types for the given Element. + * Adds the annotation types for the given Element. * * @param descList a list of annotation mirrors. * @param htmltree the documentation tree to which the annotation info will be --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkInfoImpl.java 2019-10-30 16:23:49.591748467 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkInfoImpl.java 2019-10-30 16:23:49.227748480 -0700 @@ -215,7 +215,12 @@ /** * A receiver type */ - RECEIVER_TYPE + RECEIVER_TYPE, + + /** + * A record component within a class signature + */ + RECORD_COMPONENT } public final HtmlConfiguration configuration; --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriterImpl.java 2019-10-30 16:23:51.047748416 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriterImpl.java 2019-10-30 16:23:50.679748429 -0700 @@ -214,6 +214,15 @@ * {@inheritDoc} */ @Override + public void addRecordSummary(SortedSet records, Content summaryContentTree) { + TableHeader tableHeader= new TableHeader(contents.record, contents.descriptionLabel); + addClassesSummary(records, resources.recordSummary, tableHeader, summaryContentTree); + } + + /** + * {@inheritDoc} + */ + @Override public void addExceptionSummary(SortedSet exceptions, Content summaryContentTree) { TableHeader tableHeader= new TableHeader(contents.exception, contents.descriptionLabel); addClassesSummary(exceptions, resources.exceptionSummary, tableHeader, summaryContentTree); --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java 2019-10-30 16:23:52.859748353 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java 2019-10-30 16:23:52.499748366 -0700 @@ -28,13 +28,20 @@ import java.util.List; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.DocTree.Kind; import com.sun.source.doctree.IndexTree; +import com.sun.source.doctree.ParamTree; import com.sun.source.doctree.SystemPropertyTree; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; @@ -190,15 +197,19 @@ /** * {@inheritDoc} */ + @SuppressWarnings("preview") public Content paramTagOutput(Element element, DocTree paramTag, String paramName) { ContentBuilder body = new ContentBuilder(); CommentHelper ch = utils.getCommentHelper(element); - body.add(HtmlTree.CODE(new RawHtml(paramName))); + // define id attributes for state components so that generated descriptions may refer to them + boolean defineID = (element.getKind() == ElementKind.RECORD) + && (paramTag instanceof ParamTree) && !((ParamTree) paramTag).isTypeParameter(); + Content nameTree = new StringContent(paramName); + body.add(HtmlTree.CODE(defineID ? HtmlTree.A_ID("param-" + paramName, nameTree) : nameTree)); body.add(" - "); List description = ch.getDescription(configuration, paramTag); body.add(htmlWriter.commentTagsToContent(paramTag, element, description, false, inSummary)); - HtmlTree result = HtmlTree.DD(body); - return result; + return HtmlTree.DD(body); } /** @@ -409,6 +420,7 @@ return configuration; } + @SuppressWarnings("preview") private Content createAnchorAndSearchIndex(Element element, String tagText, String desc){ Content result = null; if (isFirstSentence && inSummary) { @@ -427,7 +439,7 @@ si.setDescription(desc); si.setUrl(htmlWriter.path.getPath() + "#" + anchorName); DocPaths docPaths = configuration.docPaths; - new SimpleElementVisitor9() { + new SimpleElementVisitor14() { @Override public Void visitVariable(VariableElement e, Void p) { TypeElement te = utils.getEnclosingTypeElement(e); --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseConfiguration.java 2019-10-30 16:23:54.495748296 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseConfiguration.java 2019-10-30 16:23:54.131748309 -0700 @@ -26,13 +26,14 @@ package jdk.javadoc.internal.doclets.toolkit; import java.io.*; +import java.lang.ref.*; import java.util.*; import javax.lang.model.element.Element; import javax.lang.model.element.ModuleElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; @@ -1222,6 +1223,7 @@ * Splits the elements in a collection to its individual * collection. */ + @SuppressWarnings("preview") static private class Splitter { final Set mset = new LinkedHashSet<>(); @@ -1235,7 +1237,7 @@ : docEnv.getSpecifiedElements(); for (Element e : inset) { - new SimpleElementVisitor9() { + new SimpleElementVisitor14() { @Override @DefinedBy(Api.LANGUAGE_MODEL) public Void visitModule(ModuleElement e, Void p) { --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ClassWriter.java 2019-10-30 16:23:56.119748240 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ClassWriter.java 2019-10-30 16:23:55.755748252 -0700 @@ -73,11 +73,11 @@ public Content getClassInfoTreeHeader(); /** - * Add the type parameter information. + * Add the type parameter and state component information. * * @param classInfoTree content tree to which the documentation will be added */ - public void addTypeParamInfo(Content classInfoTree); + public void addParamInfo(Content classInfoTree); /** * Add all super interfaces if this is an interface. --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/CommentUtils.java 2019-10-30 16:23:57.743748183 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/CommentUtils.java 2019-10-30 16:23:57.375748196 -0700 @@ -38,20 +38,26 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; +import javax.lang.model.element.RecordComponentElement; +import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.Elements; import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; +import com.sun.source.doctree.AttributeTree; import com.sun.source.doctree.DocCommentTree; import com.sun.source.doctree.DocTree; import com.sun.source.doctree.IdentifierTree; +import com.sun.source.doctree.ParamTree; import com.sun.source.doctree.ReferenceTree; import com.sun.source.doctree.TextTree; import com.sun.source.util.DocTreeFactory; @@ -65,6 +71,7 @@ public class CommentUtils { final BaseConfiguration configuration; + final Utils utils; final Resources resources; final DocTreeFactory treeFactory; final HashMap dcTreesMap = new HashMap<>(); @@ -73,6 +80,7 @@ protected CommentUtils(BaseConfiguration configuration) { this.configuration = configuration; + utils = configuration.utils; resources = configuration.getResources(); trees = configuration.docEnv.getDocTrees(); treeFactory = trees.getDocTreeFactory(); @@ -107,17 +115,17 @@ return treeFactory.newSeeTree(list); } - public DocTree makeTextTree(String content) { - TextTree text = treeFactory.newTextTree(content); - return (DocTree) text; + public TextTree makeTextTree(String content) { + return treeFactory.newTextTree(content); } - public void setEnumValuesTree(Element e) { - Utils utils = configuration.utils; - String klassName = utils.getSimpleName(utils.getEnclosingTypeElement(e)); + public TextTree makeTextTreeForResource(String key) { + return treeFactory.newTextTree(resources.getText(key)); + } + public void setEnumValuesTree(ExecutableElement ee) { List fullBody = new ArrayList<>(); - fullBody.add(treeFactory.newTextTree(resources.getText("doclet.enum_values_doc.fullbody", klassName))); + fullBody.add(treeFactory.newTextTree(resources.getText("doclet.enum_values_doc.fullbody"))); List descriptions = new ArrayList<>(); descriptions.add(treeFactory.newTextTree(resources.getText("doclet.enum_values_doc.return"))); @@ -125,11 +133,10 @@ List tags = new ArrayList<>(); tags.add(treeFactory.newReturnTree(descriptions)); DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, tags); - dcTreesMap.put(e, new DocCommentDuo(null, docTree)); + dcTreesMap.put(ee, new DocCommentDuo(null, docTree)); } - public void setEnumValueOfTree(Element e) { - + public void setEnumValueOfTree(ExecutableElement ee) { List fullBody = new ArrayList<>(); fullBody.add(treeFactory.newTextTree(resources.getText("doclet.enum_valueof_doc.fullbody"))); @@ -137,7 +144,6 @@ List paramDescs = new ArrayList<>(); paramDescs.add(treeFactory.newTextTree(resources.getText("doclet.enum_valueof_doc.param_name"))); - ExecutableElement ee = (ExecutableElement) e; java.util.List parameters = ee.getParameters(); VariableElement param = parameters.get(0); IdentifierTree id = treeFactory.newIdentifierTree(elementUtils.getName(param.getSimpleName().toString())); @@ -161,7 +167,231 @@ DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, tags); - dcTreesMap.put(e, new DocCommentDuo(null, docTree)); + dcTreesMap.put(ee, new DocCommentDuo(null, docTree)); + } + + /** + * Generates the description for the canonical constructor for a record. + * @param ee the constructor + */ + public void setRecordConstructorTree(ExecutableElement ee) { + TypeElement te = utils.getEnclosingTypeElement(ee); + + List fullBody = + makeDescriptionWithName("doclet.record_constructor_doc.fullbody", te.getSimpleName()); + + List tags = new ArrayList<>(); + java.util.List parameters = ee.getParameters(); + for (VariableElement param : ee.getParameters()) { + Name name = param.getSimpleName(); + IdentifierTree id = treeFactory.newIdentifierTree(name); + tags.add(treeFactory.newParamTree(false, id, + makeDescriptionWithComponent("doclet.record_constructor_doc.param_name", te, name))); + } + + DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, tags); + dcTreesMap.put(ee, new DocCommentDuo(null, docTree)); + } + + /** + * Generates the description for the standard {@code equals} method for a record. + * @param ee the {@code equals} method + */ + @SuppressWarnings("preview") + public void setRecordEqualsTree(ExecutableElement ee) { + List fullBody = new ArrayList<>(); + add(fullBody, "doclet.record_equals_doc.fullbody.head"); + fullBody.add(treeFactory.newTextTree(" ")); + + List comps = ((TypeElement) ee.getEnclosingElement()).getRecordComponents(); + boolean hasPrimitiveComponents = + comps.stream().anyMatch(e -> e.asType().getKind().isPrimitive()); + boolean hasReferenceComponents = + comps.stream().anyMatch(e -> !e.asType().getKind().isPrimitive()); + if (hasPrimitiveComponents && hasReferenceComponents) { + add(fullBody, "doclet.record_equals_doc.fullbody.tail.both"); + } else if (hasPrimitiveComponents) { + add(fullBody, "doclet.record_equals_doc.fullbody.tail.primitive"); + } else if (hasReferenceComponents) { + add(fullBody, "doclet.record_equals_doc.fullbody.tail.reference"); + } + Name paramName = ee.getParameters().get(0).getSimpleName(); + IdentifierTree id = treeFactory.newIdentifierTree(paramName); + List paramDesc = + makeDescriptionWithName("doclet.record_equals_doc.param_name", paramName); + DocTree paramTree = treeFactory.newParamTree(false, id, paramDesc); + + DocTree returnTree = treeFactory.newReturnTree( + makeDescriptionWithName("doclet.record_equals_doc.return", paramName)); + + TreePath treePath = utils.getTreePath(ee.getEnclosingElement()); + DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, List.of(paramTree, returnTree)); + dcTreesMap.put(ee, new DocCommentDuo(treePath, docTree)); + } + + private void add(List contents, String resourceKey) { + // Special case to allow '{@link ...}' to appear in the string. + // A less general case would be to detect literal use of Object.equals + // A more general case would be to allow access to DocCommentParser somehow + String body = resources.getText(resourceKey); + Pattern p = Pattern.compile("\\{@link (\\S*)(.*)}"); + Matcher m = p.matcher(body); + int start = 0; + while (m.find(start)) { + if (m.start() > start) { + contents.add(treeFactory.newTextTree(body.substring(start, m.start()))); + } + ReferenceTree refTree = treeFactory.newReferenceTree(m.group(1)); + List descr = List.of(treeFactory.newTextTree(m.group(2).trim())) ; + contents.add(treeFactory.newLinkTree(refTree, descr)); + start = m.end(); + } + if (start < body.length()) { + contents.add(treeFactory.newTextTree(body.substring(start))); + } + } + + /** + * Generates the description for the standard {@code hashCode} method for a record. + * @param ee the {@code hashCode} method + */ + public void setRecordHashCodeTree(ExecutableElement ee) { + List fullBody = List.of(makeTextTreeForResource("doclet.record_hashCode_doc.fullbody")); + + DocTree returnTree = treeFactory.newReturnTree( + List.of(makeTextTreeForResource("doclet.record_hashCode_doc.return"))); + + DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, List.of(returnTree)); + dcTreesMap.put(ee, new DocCommentDuo(null, docTree)); + } + + /** + * Generates the description for the standard {@code toString} method for a record. + * @param ee the {@code toString} method + */ + public void setRecordToStringTree(ExecutableElement ee) { + List fullBody = List.of( + treeFactory.newTextTree(resources.getText("doclet.record_toString_doc.fullbody"))); + + DocTree returnTree = treeFactory.newReturnTree(List.of( + treeFactory.newTextTree(resources.getText("doclet.record_toString_doc.return")))); + + DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, List.of(returnTree)); + dcTreesMap.put(ee, new DocCommentDuo(null, docTree)); + } + + /** + * Generates the description for the accessor method for a state component of a record. + * @param ee the accessor method + */ + public void setRecordAccessorTree(ExecutableElement ee) { + TypeElement te = utils.getEnclosingTypeElement(ee); + + List fullBody = + makeDescriptionWithComponent("doclet.record_accessor_doc.fullbody", te, ee.getSimpleName()); + + DocTree returnTree = treeFactory.newReturnTree( + makeDescriptionWithComponent("doclet.record_accessor_doc.return", te, ee.getSimpleName())); + + DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, List.of(returnTree)); + dcTreesMap.put(ee, new DocCommentDuo(null, docTree)); + } + + /** + * Generates the description for the field for a state component of a record. + * @param ve the field + */ + public void setRecordFieldTree(VariableElement ve) { + TypeElement te = utils.getEnclosingTypeElement(ve); + + List fullBody = + makeDescriptionWithComponent("doclet.record_field_doc.fullbody", te, ve.getSimpleName()); + + DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, List.of()); + dcTreesMap.put(ve, new DocCommentDuo(null, docTree)); + } + + /** + * Creates a description that contains a reference to a state component of a record. + * The description is looked up as a resource, and should contain {@code {0}} where the + * reference to the component is to be inserted. The reference will be a link if the + * doc comment for the record has a {@code @param} tag for the component. + * @param key the resource key for the description + * @param elem the record element + * @param component the name of the component + * @return the description + */ + private List makeDescriptionWithComponent(String key, TypeElement elem, Name component) { + List result = new ArrayList<>(); + String text = resources.getText(key); + int index = text.indexOf("{0}"); + result.add(treeFactory.newTextTree(text.substring(0, index))); + Name A = elementUtils.getName("a"); + Name CODE = elementUtils.getName("code"); + Name HREF = elementUtils.getName("href"); + List code = List.of( + treeFactory.newStartElementTree(CODE, List.of(), false), + treeFactory.newTextTree(component.toString()), + treeFactory.newEndElementTree(CODE)); + if (hasParamForComponent(elem, component)) { + DocTree href = treeFactory.newAttributeTree(HREF, + AttributeTree.ValueKind.DOUBLE, + List.of(treeFactory.newTextTree("#param-" + component))); + result.add(treeFactory.newStartElementTree(A, List.of(href), false)); + result.addAll(code); + result.add(treeFactory.newEndElementTree(A)); + } else { + result.addAll(code); + } + result.add(treeFactory.newTextTree(text.substring(index + 3))); + return result; + } + + /** + * Returns whether or not the doc comment for a record contains an {@code @param}} + * for a state component of the record. + * @param elem the record element + * @param component the name of the component + * @return whether or not there is a {@code @param}} for the component + */ + private boolean hasParamForComponent(TypeElement elem, Name component) { + DocCommentTree elemComment = utils.getDocCommentTree(elem); + if (elemComment == null) { + return false; + } + + for (DocTree t : elemComment.getBlockTags()) { + if (t instanceof ParamTree && ((ParamTree) t).getName().getName() == component) { + return true; + } + } + + return false; + } + + /** + * Creates a description that contains the simple name of a program element + * The description is looked up as a resource, and should contain {@code {0}} where the + * name is to be inserted. T + * @param key the resource key for the description + * @param name the name + * @return the description + */ + private List makeDescriptionWithName(String key, Name name) { + String text = resources.getText(key); + int index = text.indexOf("{0}"); + if (index == -1) { + return List.of(treeFactory.newTextTree(text)); + } else { + Name CODE = elementUtils.getName("code"); + return List.of( + treeFactory.newTextTree(text.substring(0, index)), + treeFactory.newStartElementTree(CODE, List.of(), false), + treeFactory.newTextTree(name.toString()), + treeFactory.newEndElementTree(CODE), + treeFactory.newTextTree(text.substring(index + 3)) + ); + } } /* @@ -215,7 +445,7 @@ } public void setDocCommentTree(Element element, List fullBody, - List blockTags, Utils utils) { + List blockTags) { DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, blockTags); dcTreesMap.put(element, new DocCommentDuo(null, docTree)); // A method having null comment (no comment) that might need to be replaced --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PackageSummaryWriter.java 2019-10-30 16:23:59.619748118 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PackageSummaryWriter.java 2019-10-30 16:23:59.251748130 -0700 @@ -95,6 +95,15 @@ Content summaryContentTree); /** + * Adds the table of records to the documentation tree. + * + * @param records the records to document. + * @param summaryContentTree the content tree to which the summaries will be added + */ + public abstract void addRecordSummary(SortedSet records, + Content summaryContentTree); + + /** * Adds the table of exceptions to the documentation tree. * * @param exceptions the exceptions to document. --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Resources.java 2019-10-30 16:24:01.579748049 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Resources.java 2019-10-30 16:24:01.203748062 -0700 @@ -49,6 +49,7 @@ public final String exceptionSummary; public final String interfaceSummary; public final String packageSummary; + public final String recordSummary; protected ResourceBundle commonBundle; protected ResourceBundle docletBundle; @@ -76,6 +77,7 @@ this.exceptionSummary = getText("doclet.Exception_Summary"); this.interfaceSummary = getText("doclet.Interface_Summary"); this.packageSummary = getText("doclet.Package_Summary"); + this.recordSummary = getText("doclet.Record_Summary"); } /** --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ClassBuilder.java 2019-10-30 16:24:03.447747984 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ClassBuilder.java 2019-10-30 16:24:03.083747997 -0700 @@ -25,11 +25,26 @@ package jdk.javadoc.internal.doclets.toolkit.builders; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.toolkit.ClassWriter; +import jdk.javadoc.internal.doclets.toolkit.CommentUtils; import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.DocFilesHandler; import jdk.javadoc.internal.doclets.toolkit.DocletException; @@ -70,6 +85,11 @@ private final boolean isEnum; /** + * Keep track of whether or not this typeElement is an record. + */ + private final boolean isRecord; + + /** * The content tree for the class documentation. */ private Content contentTree; @@ -91,13 +111,21 @@ if (utils.isInterface(typeElement)) { isInterface = true; isEnum = false; + isRecord = false; } else if (utils.isEnum(typeElement)) { isInterface = false; isEnum = true; - utils.setEnumDocumentation(typeElement); + isRecord = false; + setEnumDocumentation(typeElement); + } else if (utils.isRecord(typeElement)) { + isInterface = false; + isEnum = false; + isRecord = true; + setRecordDocumentation(typeElement); } else { isInterface = false; isEnum = false; + isRecord = false; } } @@ -133,6 +161,8 @@ key = "doclet.Interface"; } else if (isEnum) { key = "doclet.Enum"; + } else if (isRecord) { + key = "doclet.Record"; } else { key = "doclet.Class"; } @@ -168,7 +198,7 @@ */ protected void buildClassInfo(Content classContentTree) throws DocletException { Content classInfoTree = new ContentBuilder(); - buildTypeParamInfo(classInfoTree); + buildParamInfo(classInfoTree); buildSuperInterfacesInfo(classInfoTree); buildImplementedInterfacesInfo(classInfoTree); buildSubClassInfo(classInfoTree); @@ -185,12 +215,12 @@ } /** - * Build the type parameters of this class. + * Build the type parameters and state components of this class. * * @param classInfoTree the content tree to which the documentation will be added */ - protected void buildTypeParamInfo(Content classInfoTree) { - writer.addTypeParamInfo(classInfoTree); + protected void buildParamInfo(Content classInfoTree) { + writer.addParamInfo(classInfoTree); } /** @@ -392,4 +422,96 @@ protected void buildMethodDetails(Content memberDetailsTree) throws DocletException { builderFactory.getMethodBuilder(writer).build(memberDetailsTree); } + + /** + * The documentation for values() and valueOf() in Enums are set by the + * doclet only iff the user or overridden methods are missing. + * @param elem the enum element + */ + private void setEnumDocumentation(TypeElement elem) { + CommentUtils cmtUtils = configuration.cmtUtils; + for (ExecutableElement ee : utils.getMethods(elem)) { + if (!utils.getFullBody(ee).isEmpty()) // ignore if already set + continue; + Name name = ee.getSimpleName(); + if (name.contentEquals("values") && ee.getParameters().isEmpty()) { + utils.removeCommentHelper(ee); // purge previous entry + cmtUtils.setEnumValuesTree(ee); + } else if (name.contentEquals("valueOf") && ee.getParameters().size() == 1) { + // TODO: check parameter type + utils.removeCommentHelper(ee); // purge previous entry + cmtUtils.setEnumValueOfTree(ee); + } + } + } + + /** + * Sets the documentation as needed for the mandated parts of a record type. + * This includes the canonical constructor, methods like {@code equals}, + * {@code hashCode}, {@code toString}, the accessor methods, and the underlying + * field. + * @param elem the record element + */ + + @SuppressWarnings("preview") + private void setRecordDocumentation(TypeElement elem) { + CommentUtils cmtUtils = configuration.cmtUtils; + Set componentNames = elem.getRecordComponents().stream() + .map(Element::getSimpleName) + .collect(Collectors.toSet()); + + for (ExecutableElement ee : utils.getConstructors(elem)) { + if (utils.isCanonicalRecordConstructor(ee)) { + if (utils.getFullBody(ee).isEmpty()) { + utils.removeCommentHelper(ee); // purge previous entry + cmtUtils.setRecordConstructorTree(ee); + } + // only one canonical constructor; no longer need to keep looking + break; + } + } + + for (VariableElement ve : utils.getFields(elem)) { + // The fields for the record component cannot be declared by the + // user and so cannot have any pre-existing comment. + Name name = ve.getSimpleName(); + if (componentNames.contains(name)) { + utils.removeCommentHelper(ve); // purge previous entry + cmtUtils.setRecordFieldTree(ve); + } + } + + TypeMirror objectType = utils.elementUtils.getTypeElement("java.lang.Object").asType(); + + for (ExecutableElement ee : utils.getMethods(elem)) { + if (!utils.getFullBody(ee).isEmpty()) { + continue; + } + + Name name = ee.getSimpleName(); + List params = ee.getParameters(); + if (name.contentEquals("equals")) { + if (params.size() == 1 && utils.typeUtils.isSameType(params.get(0).asType(), objectType)) { + utils.removeCommentHelper(ee); // purge previous entry + cmtUtils.setRecordEqualsTree(ee); + } + } else if (name.contentEquals("hashCode")) { + if (params.isEmpty()) { + utils.removeCommentHelper(ee); // purge previous entry + cmtUtils.setRecordHashCodeTree(ee); + } + } else if (name.contentEquals("toString")) { + if (params.isEmpty()) { + utils.removeCommentHelper(ee); // purge previous entry + cmtUtils.setRecordToStringTree(ee); + } + } else if (componentNames.contains(name)) { + if (params.isEmpty()) { + utils.removeCommentHelper(ee); // purge previous entry + cmtUtils.setRecordAccessorTree(ee); + } + } + } + + } } --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/MemberSummaryBuilder.java 2019-10-30 16:24:05.115747926 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/MemberSummaryBuilder.java 2019-10-30 16:24:04.743747939 -0700 @@ -411,7 +411,7 @@ blockTags.add(cmtutils.makeSeeTree(sb.toString(), setter)); } } - cmtutils.setDocCommentTree(member, fullBody, blockTags, utils); + cmtutils.setDocCommentTree(member, fullBody, blockTags); } /** --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/PackageSummaryBuilder.java 2019-10-30 16:24:06.663747872 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/PackageSummaryBuilder.java 2019-10-30 16:24:06.371747882 -0700 @@ -159,6 +159,7 @@ buildInterfaceSummary(summaryContentTree); buildClassSummary(summaryContentTree); buildEnumSummary(summaryContentTree); + buildRecordSummary(summaryContentTree); buildExceptionSummary(summaryContentTree); buildErrorSummary(summaryContentTree); buildAnnotationTypeSummary(summaryContentTree); @@ -214,6 +215,22 @@ } } + /** + * Build the summary for the records in this package. + * + * @param summaryContentTree the summary tree to which the record summary will + * be added + */ + protected void buildRecordSummary(Content summaryContentTree) { + SortedSet rlist = utils.isSpecified(packageElement) + ? utils.getTypeElementsAsSortedSet(utils.getRecords(packageElement)) + : configuration.typeElementCatalog.records(packageElement); + SortedSet records = utils.filterOutPrivateClasses(rlist, configuration.javafx); + if (!records.isEmpty()) { + packageWriter.addRecordSummary(records, summaryContentTree); + } + } + /** * Build the summary for the exceptions in this package. * --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java 2019-10-30 16:24:08.255747817 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java 2019-10-30 16:24:07.883747830 -0700 @@ -600,10 +600,10 @@ } /** - * Return true if any of the given typeElements have a @serialinclude tag. + * Return true if any of the given typeElements have a {@code @serial include} tag. * * @param classes the typeElements to check. - * @return true if any of the given typeElements have a @serialinclude tag. + * @return true if any of the given typeElements have a {@code @serial include} tag. */ private boolean serialClassFoundToDocument(SortedSet classes) { for (TypeElement aClass : classes) { --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties 2019-10-30 16:24:10.091747753 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties 2019-10-30 16:24:09.727747765 -0700 @@ -93,10 +93,13 @@ doclet.Default=Default: doclet.Parameters=Parameters: doclet.TypeParameters=Type Parameters: +doclet.RecordComponents=Record Components: doclet.Parameters_warn=@param argument "{0}" is not a parameter name. doclet.Parameters_dup_warn=Parameter "{0}" is documented more than once. -doclet.Type_Parameters_warn=@param argument "{0}" is not a type parameter name. -doclet.Type_Parameters_dup_warn=Type parameter "{0}" is documented more than once. +doclet.TypeParameters_warn=@param argument "{0}" is not the name of a type parameter. +doclet.TypeParameters_dup_warn=Type parameter "{0}" is documented more than once. +doclet.RecordComponents_warn=@param argument "{0}" is not the name of a record component. +doclet.RecordComponents_dup_warn=Record component "{0}" is documented more than once. doclet.Returns=Returns: doclet.Return_tag_on_void_method=@return tag cannot be used in method with void return type. doclet.See_Also=See Also: @@ -137,6 +140,7 @@ doclet.Enum_Constant_Summary=Enum Constant Summary doclet.Constructor_Summary=Constructor Summary doclet.Method_Summary=Method Summary +doclet.Record_Summary=Record Summary doclet.Interfaces=Interfaces doclet.Enums=Enums doclet.AnnotationTypes=Annotation Types @@ -160,6 +164,7 @@ doclet.interfaces=interfaces doclet.class=class doclet.classes=classes +doclet.Record=Record doclet.Error=Error doclet.error=error doclet.errors=errors @@ -268,3 +273,58 @@ doclet.enum_valueof_doc.throws_npe=\ if the argument is null + + +#Documentation for records +doclet.record_constructor_doc.fullbody=\ + Creates an instance of a {0} record. + +doclet.record_constructor_doc.param_name=\ + the value for the {0} record component + +doclet.record_equals_doc.fullbody.head=\ + Indicates whether some other object is "equal to" this one. \ + The objects are equal if the other object is of the same class \ + and if all the record components are equal. + +doclet.record_equals_doc.fullbody.tail.both=\ + Reference components are compared with \ + {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}; \ + primitive components are compared with '=='. + +doclet.record_equals_doc.fullbody.tail.primitive=\ + All components in this record are compared with '=='. + +doclet.record_equals_doc.fullbody.tail.reference=\ + All components in this record are compared with \ + {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}. + +doclet.record_equals_doc.param_name=\ + the object with which to compare + +doclet.record_equals_doc.return=\ + true if this object is the same as the {0} argument; false otherwise. + +doclet.record_hashCode_doc.fullbody=\ + Returns a hash code value for this object. \ + The value is derived from the hash code of each of the record components. + +doclet.record_hashCode_doc.return=\ + a hash code value for this object + +doclet.record_toString_doc.fullbody=\ + Returns a string representation of this record. \ + The representation contains the name of the type, followed by \ + the name and value of each of the record components. + +doclet.record_toString_doc.return=\ + a string representation of this object + +doclet.record_accessor_doc.fullbody=\ + Returns the value of the {0} record component. + +doclet.record_accessor_doc.return=\ + the value of the {0} record component + +doclet.record_field_doc.fullbody=\ + The field for the {0} record component. --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ParamTaglet.java 2019-10-30 16:24:11.727747696 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ParamTaglet.java 2019-10-30 16:24:11.359747709 -0700 @@ -29,6 +29,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DocTree; @@ -53,6 +54,14 @@ * @author Jamie Ho */ public class ParamTaglet extends BaseTaglet implements InheritableTaglet { + private enum ParamKind { + /** Parameter of an executable element. */ + PARAMETER, + /** State components of a record. */ + RECORD_COMPONENT, + /** Type parameters of an executable element or type element. */ + TYPE_PARAMETER + } /** * Construct a ParamTaglet. @@ -101,7 +110,7 @@ String pname = input.isTypeVariableParamTag ? utils.getTypeName(e.asType(), false) : utils.getSimpleName(e); - if (pname.equals(target)) { + if (pname.contentEquals(target)) { input.tagId = String.valueOf(i); break; } @@ -128,19 +137,23 @@ } @Override + @SuppressWarnings("preview") public Content getTagletOutput(Element holder, TagletWriter writer) { Utils utils = writer.configuration().utils; if (utils.isExecutableElement(holder)) { ExecutableElement member = (ExecutableElement) holder; - Content output = getTagletOutput(false, member, writer, + Content output = getTagletOutput(ParamKind.TYPE_PARAMETER, member, writer, member.getTypeParameters(), utils.getTypeParamTrees(member)); - output.add(getTagletOutput(true, member, writer, + output.add(getTagletOutput(ParamKind.PARAMETER, member, writer, member.getParameters(), utils.getParamTrees(member))); return output; } else { TypeElement typeElement = (TypeElement) holder; - return getTagletOutput(false, typeElement, writer, + Content output = getTagletOutput(ParamKind.TYPE_PARAMETER, typeElement, writer, typeElement.getTypeParameters(), utils.getTypeParamTrees(typeElement)); + output.add(getTagletOutput(ParamKind.RECORD_COMPONENT, typeElement, writer, + typeElement.getRecordComponents(), utils.getParamTrees(typeElement))); + return output; } } @@ -150,25 +163,25 @@ * * @param holder the element that holds the param tags. * @param writer the TagletWriter that will write this tag. - * @param formalParameters The array of parmeters (from type or executable + * @param formalParameters The array of parameters (from type or executable * member) to check. * * @return the content representation of these {@code @param DocTree}s. */ - private Content getTagletOutput(boolean isParameters, Element holder, + private Content getTagletOutput(ParamKind kind, Element holder, TagletWriter writer, List formalParameters, List paramTags) { Content result = writer.getOutputInstance(); Set alreadyDocumented = new HashSet<>(); if (!paramTags.isEmpty()) { result.add( - processParamTags(holder, isParameters, paramTags, + processParamTags(holder, kind, paramTags, getRankMap(writer.configuration().utils, formalParameters), writer, alreadyDocumented) ); } if (alreadyDocumented.size() != formalParameters.size()) { //Some parameters are missing corresponding @param tags. //Try to inherit them. - result.add(getInheritedTagletOutput(isParameters, holder, + result.add(getInheritedTagletOutput(kind, holder, writer, formalParameters, alreadyDocumented)); } return result; @@ -178,7 +191,7 @@ * Loop through each individual parameter, despite not having a * corresponding param tag, try to inherit it. */ - private Content getInheritedTagletOutput(boolean isParameters, Element holder, + private Content getInheritedTagletOutput(ParamKind kind, Element holder, TagletWriter writer, List formalParameters, Set alreadyDocumented) { Utils utils = writer.configuration().utils; @@ -191,16 +204,16 @@ // This parameter does not have any @param documentation. // Try to inherit it. Input input = new DocFinder.Input(writer.configuration().utils, holder, this, - Integer.toString(i), !isParameters); + Integer.toString(i), kind == ParamKind.TYPE_PARAMETER); DocFinder.Output inheritedDoc = DocFinder.search(writer.configuration(), input); if (inheritedDoc.inlineTags != null && !inheritedDoc.inlineTags.isEmpty()) { Element e = formalParameters.get(i); - String lname = isParameters + String lname = kind != ParamKind.TYPE_PARAMETER ? utils.getSimpleName(e) : utils.getTypeName(e.asType(), false); CommentHelper ch = utils.getCommentHelper(holder); ch.setOverrideElement(inheritedDoc.holder); - Content content = processParamTag(holder, isParameters, writer, + Content content = processParamTag(holder, kind, writer, inheritedDoc.holderTag, lname, alreadyDocumented.isEmpty()); @@ -230,7 +243,7 @@ when parameter documentation is inherited. * @return the Content representation of this {@code @param DocTree}. */ - private Content processParamTags(Element e, boolean isParams, + private Content processParamTags(Element e, ParamKind kind, List paramTags, Map rankMap, TagletWriter writer, Set alreadyDocumented) { Messages messages = writer.configuration().getMessages(); @@ -238,26 +251,33 @@ if (!paramTags.isEmpty()) { CommentHelper ch = writer.configuration().utils.getCommentHelper(e); for (DocTree dt : paramTags) { - String paramName = isParams - ? ch.getParameterName(dt) - : "<" + ch.getParameterName(dt) + ">"; - if (!rankMap.containsKey(ch.getParameterName(dt))) { - messages.warning(ch.getDocTreePath(dt), - isParams - ? "doclet.Parameters_warn" - : "doclet.Type_Parameters_warn", - paramName); + String name = ch.getParameterName(dt); + String paramName = kind != ParamKind.TYPE_PARAMETER + ? name.toString() + : "<" + name + ">"; + if (!rankMap.containsKey(name)) { + String key; + switch (kind) { + case PARAMETER: key = "doclet.Parameters_warn" ; break; + case TYPE_PARAMETER: key = "doclet.TypeParameters_warn" ; break; + case RECORD_COMPONENT: key = "doclet.RecordComponents_warn" ; break; + default: throw new IllegalArgumentException(kind.toString()); + } + messages.warning(ch.getDocTreePath(dt), key, paramName); } - String rank = rankMap.get(ch.getParameterName(dt)); + String rank = rankMap.get(name); if (rank != null && alreadyDocumented.contains(rank)) { - messages.warning(ch.getDocTreePath(dt), - isParams - ? "doclet.Parameters_dup_warn" - : "doclet.Type_Parameters_dup_warn", - paramName); + String key; + switch (kind) { + case PARAMETER: key = "doclet.Parameters_dup_warn" ; break; + case TYPE_PARAMETER: key = "doclet.TypeParameters_dup_warn" ; break; + case RECORD_COMPONENT: key = "doclet.RecordComponents_dup_warn" ; break; + default: throw new IllegalArgumentException(kind.toString()); } - result.add(processParamTag(e, isParams, writer, dt, - ch.getParameterName(dt), alreadyDocumented.isEmpty())); + messages.warning(ch.getDocTreePath(dt), key, paramName); + } + result.add(processParamTag(e, kind, writer, dt, + name, alreadyDocumented.isEmpty())); alreadyDocumented.add(rank); } } @@ -268,8 +288,7 @@ * Convert the individual ParamTag into Content. * * @param e the owner element - * @param isParams true if this is just a regular param tag. False - * if this is a type param tag. + * @param kind the kind of param tag * @param writer the taglet writer for output writing. * @param paramTag the tag whose inline tags will be printed. * @param name the name of the parameter. We can't rely on @@ -278,13 +297,19 @@ * @param isFirstParam true if this is the first param tag being printed. * */ - private Content processParamTag(Element e, boolean isParams, + private Content processParamTag(Element e, ParamKind kind, TagletWriter writer, DocTree paramTag, String name, boolean isFirstParam) { Content result = writer.getOutputInstance(); - String header = writer.configuration().getResources().getText( - isParams ? "doclet.Parameters" : "doclet.TypeParameters"); if (isFirstParam) { + String key; + switch (kind) { + case PARAMETER: key = "doclet.Parameters" ; break; + case TYPE_PARAMETER: key = "doclet.TypeParameters" ; break; + case RECORD_COMPONENT: key = "doclet.RecordComponents" ; break; + default: throw new IllegalArgumentException(kind.toString()); + } + String header = writer.configuration().getResources().getText(key); result.add(writer.getParamHeader(header)); } result.add(writer.paramTagOutput(e, paramTag, name)); --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java 2019-10-30 16:24:13.383747638 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java 2019-10-30 16:24:13.011747651 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import javax.tools.JavaFileManager; import javax.tools.StandardJavaFileManager; @@ -333,6 +333,7 @@ * @param trees the trees containing the comments * @param areInlineTags true if the array of tags are inline and false otherwise. */ + @SuppressWarnings("preview") public void checkTags(Element element, Iterable trees, boolean areInlineTags) { if (trees == null) { return; @@ -365,7 +366,7 @@ if (element == null) { return; } - new SimpleElementVisitor9() { + new SimpleElementVisitor14() { @Override public Void visitModule(ModuleElement e, Void p) { if (!taglet.inModule()) { @@ -521,6 +522,7 @@ case INTERFACE: case CLASS: case ENUM: + case RECORD: return blockTagletsBySite.get(Site.TYPE); case MODULE: return blockTagletsBySite.get(Site.MODULE); --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java 2019-10-30 16:24:15.035747581 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java 2019-10-30 16:24:14.675747593 -0700 @@ -25,9 +25,11 @@ package jdk.javadoc.internal.doclets.toolkit.taglets; +import java.util.EnumSet; import java.util.List; import javax.lang.model.element.Element; +import javax.lang.model.element.Name; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; @@ -233,8 +235,8 @@ tagletManager.checkTags(element, utils.getFullBody(element), true); for (Taglet taglet : taglets) { if (utils.isTypeElement(element) && taglet instanceof ParamTaglet) { - //The type parameters are documented in a special section away - //from the tag info, so skip here. + // The type parameters and state components are documented in a special + // section away from the tag info, so skip here. continue; } if (taglet instanceof DeprecatedTaglet) { --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/ClassUseMapper.java 2019-10-30 16:24:16.667747524 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/ClassUseMapper.java 2019-10-30 16:24:16.299747537 -0700 @@ -41,7 +41,7 @@ import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import javax.lang.model.util.SimpleTypeVisitor9; import javax.lang.model.util.Types; @@ -483,8 +483,9 @@ private void mapTypeParameters(final Map> map, Element element, final T holder) { - SimpleElementVisitor9 elementVisitor - = new SimpleElementVisitor9() { + @SuppressWarnings("preview") + SimpleElementVisitor14 elementVisitor + = new SimpleElementVisitor14() { private void addParameters(TypeParameterElement e) { for (TypeMirror type : utils.getBounds(e)) { @@ -560,9 +561,10 @@ * @param e whose type parameters are being checked. * @param holder owning the type parameters. */ + @SuppressWarnings("preview") private void mapAnnotations(final Map> map, Element e, final T holder) { - new SimpleElementVisitor9() { + new SimpleElementVisitor14() { void addAnnotations(Element e) { for (AnnotationMirror a : e.getAnnotationMirrors()) { --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java 2019-10-30 16:24:18.243747469 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java 2019-10-30 16:24:17.911747480 -0700 @@ -32,6 +32,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; @@ -99,7 +100,7 @@ public void setOverrideElement(Element ove) { if (this.element == ove) { - throw new AssertionError("cannot set given element as overriden element"); + throw new AssertionError("cannot set given element as overridden element"); } overriddenElement = ove; } @@ -147,6 +148,9 @@ Element getElement(BaseConfiguration c, ReferenceTree rtree) { // likely a synthesized tree if (path == null) { + // NOTE: this code path only supports module/package/type signatures + // and not member signatures. For more complete support, + // set a suitable path and avoid this branch. TypeMirror symbol = c.utils.getSymbol(rtree.getSignature()); if (symbol == null) { return null; @@ -547,7 +551,7 @@ return new SimpleDocTreeVisitor, Void>() { List asList(String content) { List out = new ArrayList<>(); - out.add((TextTree)c.cmtUtils.makeTextTree(content)); + out.add(c.cmtUtils.makeTextTree(content)); return out; } --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/TypeElementCatalog.java 2019-10-30 16:24:19.723747417 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/TypeElementCatalog.java 2019-10-30 16:24:19.431747427 -0700 @@ -74,6 +74,11 @@ private final Map> enums; /** + * Stores records for each package. + */ + private final Map> records; + + /** * Stores annotation types for each package. */ private final Map> annotationTypes; @@ -116,6 +121,7 @@ ordinaryClasses = new HashMap<>(); exceptions = new HashMap<>(); enums = new HashMap<>(); + records = new HashMap<>(); annotationTypes = new HashMap<>(); errors = new HashMap<>(); interfaces = new HashMap<>(); @@ -138,6 +144,8 @@ addTypeElement(typeElement, exceptions); } else if (utils.isEnum(typeElement)) { addTypeElement(typeElement, enums); + } else if (utils.isRecord(typeElement)) { + addTypeElement(typeElement, records); } else if (utils.isAnnotationType(typeElement)) { addTypeElement(typeElement, annotationTypes); } else if (utils.isError(typeElement)) { @@ -191,9 +199,7 @@ } /** - * Return all of the classes specified on the command-line that belong to the given package. - * - * @param packageName the name of the package specified on the command-line. + * Return all of the classes specified on the command-line that belong to the unnamed package. */ public SortedSet allUnnamedClasses() { for (PackageElement pkg : allClasses.keySet()) { @@ -214,7 +220,7 @@ /** * Return all of the errors specified on the command-line that belong to the given package. * - * @param packageName the name of the package specified on the command-line. + * @param pkg the name of the package specified on the command-line. */ public SortedSet errors(PackageElement pkg) { return getSet(errors, pkg); @@ -223,7 +229,7 @@ /** * Return all of the exceptions specified on the command-line that belong to the given package. * - * @param packageName the name of the package specified on the command-line. + * @param pkg the name of the package specified on the command-line. */ public SortedSet exceptions(PackageElement pkg) { return getSet(exceptions, pkg); @@ -232,17 +238,26 @@ /** * Return all of the enums specified on the command-line that belong to the given package. * - * @param packageName the name of the package specified on the command-line. + * @param pkg the name of the package specified on the command-line. */ public SortedSet enums(PackageElement pkg) { return getSet(enums, pkg); } /** + * Return all of the records specified on the command-line that belong to the given package. + * + * @param pkg the name of the package specified on the command-line. + */ + public SortedSet records(PackageElement pkg) { + return getSet(records, pkg); + } + + /** * Return all of the annotation types specified on the command-line that belong to the given * package. * - * @param packageName the name of the package specified on the command-line. + * @param pkg the name of the package specified on the command-line. */ public SortedSet annotationTypes(PackageElement pkg) { return getSet(annotationTypes, pkg); @@ -251,7 +266,7 @@ /** * Return all of the interfaces specified on the command-line that belong to the given package. * - * @param packageName the name of the package specified on the command-line. + * @param pkg the name of the package specified on the command-line. */ public SortedSet interfaces(PackageElement pkg) { return getSet(interfaces, pkg); @@ -261,7 +276,7 @@ * Return all of the ordinary classes specified on the command-line that belong to the given * package. * - * @param packageName the name of the package specified on the command-line. + * @param pkg the name of the package specified on the command-line. */ public SortedSet ordinaryClasses(PackageElement pkg) { return getSet(ordinaryClasses, pkg); --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java 2019-10-30 16:24:21.223747365 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java 2019-10-30 16:24:20.851747378 -0700 @@ -47,6 +47,7 @@ import javax.lang.model.element.ModuleElement; import javax.lang.model.element.ModuleElement.RequiresDirective; import javax.lang.model.element.PackageElement; +import javax.lang.model.element.RecordComponentElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; @@ -60,9 +61,9 @@ import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.ElementKindVisitor9; +import javax.lang.model.util.ElementKindVisitor14; import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import javax.lang.model.util.SimpleTypeVisitor9; import javax.lang.model.util.TypeKindVisitor9; import javax.lang.model.util.Types; @@ -301,8 +302,9 @@ return !e.getAnnotationMirrors().isEmpty(); } + @SuppressWarnings("preview") public boolean isAnnotationType(Element e) { - return new SimpleElementVisitor9() { + return new SimpleElementVisitor14() { @Override public Boolean visitExecutable(ExecutableElement e, Void p) { return visit(e.getEnclosingElement()); @@ -416,6 +418,34 @@ return typeUtils.isSubtype(e.asType(), getExternalizableType()); } + @SuppressWarnings("preview") + public boolean isRecord(TypeElement e) { + return e.getKind() == ElementKind.RECORD; + } + + @SuppressWarnings("preview") + public boolean isCanonicalRecordConstructor(ExecutableElement ee) { + TypeElement te = (TypeElement) ee.getEnclosingElement(); + List stateComps = te.getRecordComponents(); + List params = ee.getParameters(); + if (stateComps.size() != params.size()) { + return false; + } + + Iterator stateIter = stateComps.iterator(); + Iterator paramIter = params.iterator(); + while (paramIter.hasNext() && stateIter.hasNext()) { + VariableElement param = paramIter.next(); + RecordComponentElement comp = stateIter.next(); + if (!Objects.equals(param.getSimpleName(), comp.getSimpleName()) + || !typeUtils.isSameType(param.asType(), comp.asType())) { + return false; + } + } + + return true; + } + public SortedSet serializableFields(TypeElement aclass) { return configuration.workArounds.getSerializableFields(aclass); } @@ -428,85 +458,102 @@ return configuration.workArounds.definesSerializableFields( aclass); } + @SuppressWarnings("preview") public String modifiersToString(Element e, boolean trailingSpace) { - SortedSet set = new TreeSet<>(e.getModifiers()); - set.remove(Modifier.NATIVE); - set.remove(Modifier.STRICTFP); - set.remove(Modifier.SYNCHRONIZED); + SortedSet modifiers = new TreeSet<>(e.getModifiers()); + modifiers.remove(NATIVE); + modifiers.remove(STRICTFP); + modifiers.remove(SYNCHRONIZED); - return new ElementKindVisitor9>() { + return new ElementKindVisitor14>() { final StringBuilder sb = new StringBuilder(); void addVisibilityModifier(Set modifiers) { if (modifiers.contains(PUBLIC)) { - sb.append("public").append(" "); + append("public"); } else if (modifiers.contains(PROTECTED)) { - sb.append("protected").append(" "); + append("protected"); } else if (modifiers.contains(PRIVATE)) { - sb.append("private").append(" "); + append("private"); } } void addStatic(Set modifiers) { if (modifiers.contains(STATIC)) { - sb.append("static").append(" "); + append("static"); } } - void addModifers(Set modifiers) { - String s = set.stream().map(Modifier::toString).collect(Collectors.joining(" ")); - sb.append(s); - if (!s.isEmpty()) + void addModifiers(Set modifiers) { + modifiers.stream().map(Modifier::toString).forEach(this::append); + } + + void append(String s) { + if (sb.length() > 0) { sb.append(" "); + } + sb.append(s); } String finalString(String s) { - sb.append(s); + append(s); if (trailingSpace) { - if (sb.lastIndexOf(" ") == sb.length() - 1) { - return sb.toString(); - } else { - return sb.append(" ").toString(); + sb.append(" "); } - } else { - return sb.toString().trim(); - } + return sb.toString(); } @Override - public String visitTypeAsInterface(TypeElement e, SortedSet p) { - addVisibilityModifier(p); - addStatic(p); + public String visitTypeAsInterface(TypeElement e, SortedSet mods) { + addVisibilityModifier(mods); + addStatic(mods); return finalString("interface"); } @Override - public String visitTypeAsEnum(TypeElement e, SortedSet p) { - addVisibilityModifier(p); - addStatic(p); + public String visitTypeAsEnum(TypeElement e, SortedSet mods) { + addVisibilityModifier(mods); + addStatic(mods); return finalString("enum"); } @Override - public String visitTypeAsAnnotationType(TypeElement e, SortedSet p) { - addVisibilityModifier(p); - addStatic(p); + public String visitTypeAsAnnotationType(TypeElement e, SortedSet mods) { + addVisibilityModifier(mods); + addStatic(mods); return finalString("@interface"); } @Override - public String visitTypeAsClass(TypeElement e, SortedSet p) { - addModifers(p); - return finalString("class"); + public String visitTypeAsRecord(TypeElement e, SortedSet mods) { + mods.remove(FINAL); // suppress the implicit `final` + return visitTypeAsClass(e, mods); + } + + @Override + @SuppressWarnings("preview") + public String visitTypeAsClass(TypeElement e, SortedSet mods) { + Set beforeSealed = EnumSet.noneOf(Modifier.class); + Set afterSealed = EnumSet.noneOf(Modifier.class); + Set set = beforeSealed; + for (Modifier m : Modifier.values()) { + if (mods.contains(m)) { + set.add(m); + } + } + addModifiers(beforeSealed); + addModifiers(afterSealed); + String keyword = e.getKind() == ElementKind.RECORD ? "record" : "class"; + return finalString(keyword); } @Override - protected String defaultAction(Element e, SortedSet p) { - addModifers(p); + protected String defaultAction(Element e, SortedSet mods) { + addModifiers(mods); return sb.toString().trim(); } - }.visit(e, set); + }.visit(e, modifiers); } public boolean isFunctionalInterface(AnnotationMirror amirror) { @@ -593,7 +640,7 @@ public boolean isTypeElement(Element e) { switch (e.getKind()) { - case CLASS: case ENUM: case INTERFACE: case ANNOTATION_TYPE: + case CLASS: case ENUM: case INTERFACE: case ANNOTATION_TYPE: case RECORD: return true; default: return false; @@ -1363,27 +1410,6 @@ } /** - * The documentation for values() and valueOf() in Enums are set by the - * doclet only iff the user or overridden methods are missing. - * @param elem - */ - public void setEnumDocumentation(TypeElement elem) { - for (Element e : getMethods(elem)) { - ExecutableElement ee = (ExecutableElement)e; - if (!getFullBody(e).isEmpty()) // ignore if already set - continue; - if (ee.getSimpleName().contentEquals("values") && ee.getParameters().isEmpty()) { - removeCommentHelper(ee); // purge previous entry - configuration.cmtUtils.setEnumValuesTree(e); - } - if (ee.getSimpleName().contentEquals("valueOf") && ee.getParameters().size() == 1) { - removeCommentHelper(ee); // purge previous entry - configuration.cmtUtils.setEnumValueOfTree(e); - } - } - } - - /** * Returns a locale independent upper cased String. That is, it * always uses US locale, this is a clone of the one in StringUtils. * @param s to convert @@ -1760,7 +1786,7 @@ result = compareStrings(getFullyQualifiedName(o1), getFullyQualifiedName(o2)); if (result != 0) return result; - return compareElementTypeKinds(o1, o2); + return compareElementKinds(o1, o2); } }; } @@ -1809,7 +1835,7 @@ return result; } // if names are the same, compare element kinds - result = compareElementTypeKinds(e1, e2); + result = compareElementKinds(e1, e2); if (result != 0) { return result; } @@ -1917,8 +1943,9 @@ return getFullyQualifiedName(e, true); } + @SuppressWarnings("preview") public String getFullyQualifiedName(Element e, final boolean outer) { - return new SimpleElementVisitor9() { + return new SimpleElementVisitor14() { @Override public String visitModule(ModuleElement e, Void p) { return e.getQualifiedName().toString(); @@ -1985,7 +2012,7 @@ if (result != 0) { return result; } - return compareElementTypeKinds(e1, e2); + return compareElementKinds(e1, e2); } }; } @@ -1997,6 +2024,8 @@ * for creating specific comparators for an use-case. */ private abstract class ElementComparator implements Comparator { + public ElementComparator() { } + /** * compares two parameter arrays by first comparing the length of the arrays, and * then each Type of the parameter in the array. @@ -2005,21 +2034,6 @@ * @return a negative integer, zero, or a positive integer as the first * argument is less than, equal to, or greater than the second. */ - final EnumMap elementKindOrder; - public ElementComparator() { - elementKindOrder = new EnumMap<>(ElementKind.class); - elementKindOrder.put(ElementKind.MODULE, 0); - elementKindOrder.put(ElementKind.PACKAGE, 1); - elementKindOrder.put(ElementKind.CLASS, 2); - elementKindOrder.put(ElementKind.ENUM, 3); - elementKindOrder.put(ElementKind.ENUM_CONSTANT, 4); - elementKindOrder.put(ElementKind.INTERFACE, 5); - elementKindOrder.put(ElementKind.ANNOTATION_TYPE, 6); - elementKindOrder.put(ElementKind.FIELD, 7); - elementKindOrder.put(ElementKind.CONSTRUCTOR, 8); - elementKindOrder.put(ElementKind.METHOD, 9); - } - protected int compareParameters(boolean caseSensitive, List params1, List params2) { @@ -2082,12 +2096,31 @@ String thatElement = getFullyQualifiedName(e2); return compareStrings(thisElement, thatElement); } - protected int compareElementTypeKinds(Element e1, Element e2) { - return Integer.compare(elementKindOrder.get(e1.getKind()), - elementKindOrder.get(e2.getKind())); + + protected int compareElementKinds(Element e1, Element e2) { + return Integer.compare(getKindIndex(e1), getKindIndex(e2)); + } + + private int getKindIndex(Element e) { + switch (e.getKind()) { + case MODULE: return 0; + case PACKAGE: return 1; + case CLASS: return 2; + case ENUM: return 3; + case ENUM_CONSTANT: return 4; + case RECORD: return 5; + case INTERFACE: return 6; + case ANNOTATION_TYPE: return 7; + case FIELD: return 8; + case CONSTRUCTOR: return 9; + case METHOD: return 10; + default: throw new IllegalArgumentException(e.getKind().toString()); + } } + + @SuppressWarnings("preview") boolean hasParameters(Element e) { - return new SimpleElementVisitor9() { + return new SimpleElementVisitor14() { @Override public Boolean visitExecutable(ExecutableElement e, Void p) { return true; @@ -2107,8 +2140,9 @@ * @return a negative integer, zero, or a positive integer as the first argument is less * than, equal to, or greater than the second. */ + @SuppressWarnings("preview") private String getFullyQualifiedName(Element e) { - return new SimpleElementVisitor9() { + return new SimpleElementVisitor14() { @Override public String visitModule(ModuleElement e, Void p) { return e.getQualifiedName().toString(); @@ -2187,6 +2221,7 @@ out.addAll(getClasses(pkg)); out.addAll(getEnums(pkg)); out.addAll(getAnnotationTypes(pkg)); + out.addAll(getRecords(pkg)); return out; } @@ -2217,6 +2252,16 @@ return convertToTypeElement(getItems(e, false, ANNOTATION_TYPE)); } + @SuppressWarnings("preview") + public List getRecords(Element e) { + return convertToTypeElement(getItems(e, true, RECORD)); + } + + @SuppressWarnings("preview") + public List getRecordsUnfiltered(Element e) { + return convertToTypeElement(getItems(e, false, RECORD)); + } + public List getFields(Element e) { return convertToVariableElement(getItems(e, true, FIELD)); } @@ -2371,6 +2416,7 @@ List clist = getClassesUnfiltered(e); clist.addAll(getInterfacesUnfiltered(e)); clist.addAll(getAnnotationTypesUnfiltered(e)); + clist.addAll(getRecordsUnfiltered(e)); SortedSet oset = new TreeSet<>(makeGeneralPurposeComparator()); oset.addAll(clist); return oset; @@ -2391,6 +2437,7 @@ clist.addAll(getInterfaces(e)); clist.addAll(getAnnotationTypes(e)); clist.addAll(getEnums(e)); + clist.addAll(getRecords(e)); oset = new TreeSet<>(makeGeneralPurposeComparator()); oset.addAll(clist); cachedClasses.put(e, oset); @@ -2459,9 +2506,10 @@ .collect(Collectors.toList()); } + @SuppressWarnings("preview") List getItems(Element e, boolean filter, ElementKind select) { List elements = new ArrayList<>(); - return new SimpleElementVisitor9, Void>() { + return new SimpleElementVisitor14, Void>() { @Override public List visitPackage(PackageElement e, Void p) { @@ -2506,11 +2554,13 @@ return elements; } - private SimpleElementVisitor9 shouldDocumentVisitor = null; + @SuppressWarnings("preview") + private SimpleElementVisitor14 shouldDocumentVisitor = null; - protected boolean shouldDocument(Element e) { + @SuppressWarnings("preview") + public boolean shouldDocument(Element e) { if (shouldDocumentVisitor == null) { - shouldDocumentVisitor = new SimpleElementVisitor9() { + shouldDocumentVisitor = new SimpleElementVisitor14() { private boolean hasSource(TypeElement e) { return configuration.docEnv.getFileKind(e) == javax.tools.JavaFileObject.Kind.SOURCE; @@ -2560,11 +2610,13 @@ return nameCache.computeIfAbsent(e, this::getSimpleName0); } - private SimpleElementVisitor9 snvisitor = null; + @SuppressWarnings("preview") + private SimpleElementVisitor14 snvisitor = null; + @SuppressWarnings("preview") private String getSimpleName0(Element e) { if (snvisitor == null) { - snvisitor = new SimpleElementVisitor9() { + snvisitor = new SimpleElementVisitor14() { @Override public String visitModule(ModuleElement e, Void p) { return e.getQualifiedName().toString(); // temp fix for 8182736 @@ -2745,10 +2797,12 @@ return configuration.docEnv.isIncluded(e); } - private SimpleElementVisitor9 specifiedVisitor = null; + @SuppressWarnings("preview") + private SimpleElementVisitor14 specifiedVisitor = null; + @SuppressWarnings("preview") public boolean isSpecified(Element e) { if (specifiedVisitor == null) { - specifiedVisitor = new SimpleElementVisitor9() { + specifiedVisitor = new SimpleElementVisitor14() { @Override public Boolean visitModule(ModuleElement e, Void p) { return configuration.getSpecifiedModuleElements().contains(e); @@ -3196,20 +3250,20 @@ return getBlockTags(element, DocTree.Kind.EXCEPTION, DocTree.Kind.THROWS); } - public List getTypeParamTrees(Element element) { + public List getTypeParamTrees(Element element) { return getParamTrees(element, true); } - public List getParamTrees(Element element) { + public List getParamTrees(Element element) { return getParamTrees(element, false); } - private List getParamTrees(Element element, boolean isTypeParameters) { - List out = new ArrayList<>(); + private List getParamTrees(Element element, boolean isTypeParameters) { + List out = new ArrayList<>(); for (DocTree dt : getBlockTags(element, PARAM)) { ParamTree pt = (ParamTree) dt; if (pt.isTypeParameter() == isTypeParameters) { - out.add(dt); + out.add(pt); } } return out; --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberTable.java 2019-10-30 16:24:22.839747309 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberTable.java 2019-10-30 16:24:22.471747322 -0700 @@ -32,7 +32,7 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Collections; @@ -700,8 +700,9 @@ } } + @SuppressWarnings("preview") String getMemberKey(Element e) { - return new SimpleElementVisitor9() { + return new SimpleElementVisitor14() { @Override public String visitExecutable(ExecutableElement e, Void aVoid) { return e.getSimpleName() + ":" + e.getParameters().size(); --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java 2019-10-30 16:24:24.423747254 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java 2019-10-30 16:24:24.051747267 -0700 @@ -47,7 +47,7 @@ import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import javax.tools.JavaFileManager; import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileObject; @@ -985,7 +985,8 @@ return (xclasses || toolEnv.getFileKind(te) == SOURCE) && isSelected(te); } - SimpleElementVisitor9 visibleElementVisitor = null; + @SuppressWarnings("preview") + SimpleElementVisitor14 visibleElementVisitor = null; /** * Returns true if the element is selected, by applying * the access filter checks. Special treatment is applied to @@ -996,12 +997,13 @@ * @param e the element to be checked * @return true if the element is visible */ + @SuppressWarnings("preview") public boolean isSelected(Element e) { if (toolEnv.isSynthetic((Symbol) e)) { return false; } if (visibleElementVisitor == null) { - visibleElementVisitor = new SimpleElementVisitor9() { + visibleElementVisitor = new SimpleElementVisitor14() { @Override public Boolean visitType(TypeElement e, Void p) { if (!accessFilter.checkModifier(e)) { @@ -1035,7 +1037,8 @@ return visibleElementVisitor.visit(e); } - private class IncludedVisitor extends SimpleElementVisitor9 { + @SuppressWarnings("preview") + private class IncludedVisitor extends SimpleElementVisitor14 { final private Set includedCache; public IncludedVisitor() { @@ -1200,7 +1203,7 @@ ElementKind.PACKAGE, ElementKind.MODULE); - // all possible accesss levels allowed for each element + // all possible access levels allowed for each element private final EnumMap> filterMap = new EnumMap<>(ElementKind.class); @@ -1285,7 +1288,7 @@ switch (kind) { case CLASS: case METHOD: case MODULE: case PACKAGE: return kind; - case ANNOTATION_TYPE: case ENUM: case INTERFACE: + case RECORD: case ANNOTATION_TYPE: case ENUM: case INTERFACE: return ElementKind.CLASS; case CONSTRUCTOR: case ENUM_CONSTANT: case EXCEPTION_PARAMETER: case FIELD: case INSTANCE_INIT: case LOCAL_VARIABLE: case PARAMETER: --- old/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocMemberEnter.java 2019-10-30 16:24:25.887747203 -0700 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocMemberEnter.java 2019-10-30 16:24:25.599747213 -0700 @@ -31,7 +31,10 @@ import com.sun.tools.javac.comp.MemberEnter; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Names; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; @@ -61,6 +64,7 @@ final ToolEnvironment toolEnv; + protected JavadocMemberEnter(Context context) { super(context); toolEnv = ToolEnvironment.instance(context); @@ -79,7 +83,12 @@ toolEnv.setElementToTreePath(meth, treePath); } // release resources + // handle constructors for record types specially, because of downstream checks + if ((env.enclClass.mods.flags & Flags.RECORD) != 0 && TreeInfo.isConstructor(tree)) { + tree.body.stats = List.nil(); + } else { tree.body = null; + } } @Override --- old/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java 2019-10-30 16:24:27.719747139 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java 2019-10-30 16:24:27.351747152 -0700 @@ -177,9 +177,9 @@ checkOutput(Output.OUT, true, // Bad type parameter warnings. "warning - @param argument " - + "\"\" is not a type parameter name.", + + "\"\" is not the name of a type parameter.", "warning - @param argument " - + "\"\" is not a type parameter name."); + + "\"\" is not the name of a type parameter."); // Signature of subclass that has type parameters. checkOutput("pkg/TypeParameterSubClass.html", true, --- old/test/langtools/jdk/javadoc/lib/javadoc/tester/JavadocTester.java 2019-10-30 16:24:31.055747023 -0700 +++ new/test/langtools/jdk/javadoc/lib/javadoc/tester/JavadocTester.java 2019-10-30 16:24:30.707747035 -0700 @@ -144,6 +144,8 @@ public static final String FS = System.getProperty("file.separator"); public static final String PS = System.getProperty("path.separator"); public static final String NL = System.getProperty("line.separator"); + public static final String thisRelease = System.getProperty("java.specification.version"); + public static final Path currDir = Paths.get(".").toAbsolutePath().normalize(); public enum Output { --- old/test/langtools/jdk/javadoc/tool/api/basic/GetTask_DiagListenerTest.java 2019-10-30 16:24:32.563746970 -0700 +++ new/test/langtools/jdk/javadoc/tool/api/basic/GetTask_DiagListenerTest.java 2019-10-30 16:24:32.279746980 -0700 @@ -78,7 +78,7 @@ } List expect = Arrays.asList( "javadoc.note.msg", // Loading source file - "compiler.err.expected3", // class, interface, or enum expected + "compiler.err.expected3", // class, interface, enum, or __datum expected "javadoc.note.msg"); // 1 error if (!diagCodes.equals(expect)) throw new Exception("unexpected diagnostics occurred"); --- old/test/langtools/jdk/javadoc/tool/modules/ModuleTestBase.java 2019-10-30 16:24:34.255746911 -0700 +++ new/test/langtools/jdk/javadoc/tool/modules/ModuleTestBase.java 2019-10-30 16:24:33.883746924 -0700 @@ -41,7 +41,7 @@ import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleElementVisitor14; import jdk.javadoc.doclet.Doclet; import jdk.javadoc.doclet.DocletEnvironment; @@ -301,7 +301,7 @@ void printDataSet(String header, Set set) { for (Element e : set) { ps.print(header); - new SimpleElementVisitor9() { + new SimpleElementVisitor14() { @Override public Void visitModule(ModuleElement e, Void p) { ps.print(FS); @@ -347,7 +347,7 @@ @Override protected Void defaultAction(Element e, Void p) { Element encl = e.getEnclosingElement(); - CharSequence fqn = new SimpleElementVisitor9() { + CharSequence fqn = new SimpleElementVisitor14() { @Override public CharSequence visitModule(ModuleElement e, Void p) { return e.getQualifiedName(); --- old/test/langtools/jdk/javadoc/tool/reporter_generates_warnings/pkg/MyDoclet.java 2019-10-30 16:24:36.099746847 -0700 +++ new/test/langtools/jdk/javadoc/tool/reporter_generates_warnings/pkg/MyDoclet.java 2019-10-30 16:24:35.791746858 -0700 @@ -28,7 +28,7 @@ import java.util.Set; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; -import javax.lang.model.util.ElementScanner9; +import javax.lang.model.util.ElementScanner14; import javax.tools.Diagnostic; import jdk.javadoc.doclet.Doclet; @@ -94,7 +94,7 @@ return OK; } - class MyScanner extends ElementScanner9 { + class MyScanner extends ElementScanner14 { @Override public Void scan(Element e, Integer depth) { String msg = e.getKind() + " " + e; --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java 2019-10-30 16:24:37.103746812 -0700 @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8225055 + * @summary Record types + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @compile --enable-preview --source ${jdk.version} TestRecordTypes.java + * @run main/othervm --enable-preview TestRecordTypes + */ + + +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.nio.file.Path; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +public class TestRecordTypes extends JavadocTester { + public static void main(String... args) throws Exception { + TestRecordTypes tester = new TestRecordTypes(); + tester.runTests(m -> new Object[] { Path.of(m.getName()) }); + } + + private final ToolBox tb = new ToolBox(); + + // The following constants are set up for use with -linkoffline + // (but note: JDK 11 does not include java.lang.Record, so expect + // some 404 broken links until we can update this to a stable version.) + private static final String externalDocs = + "https://docs.oracle.com/en/java/javase/11/docs/api"; + private static final String localDocs = + Path.of(testSrc).resolve("jdk11").toUri().toString(); + + @Test + public void testRecordKeywordUnnamedPackage(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "public record R(int r1) { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + src.resolve("R.java").toString()); + checkExit(Exit.OK); + + checkOutput("R.html", true, + "

Record R

", + "public record R", + "R​(int r1)"); + } + + @Test + public void testRecordKeywordNamedPackage(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; public record R(int r1) { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + checkOutput("p/R.html", true, + "

Record R

", + "public record R", + "R​(int r1)"); + } + + @Test + public void testEmptyRecord(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; public record R() { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + checkOutput("p/R.html", true, + "

Record R

", + "public record R", + "R()"); + } + + @Test + public void testAtParam(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; /** This is record R. \n" + + " * @param r1 This is a component.\n" + + " */\n" + + "public record R(int r1) { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + checkOutput("p/R.html", true, + "

Record R

", + "public record R", + "
\n" + + "
Record Components:
\n" + + "
r1 - This is a component.
\n" + + "
", + "R​(int r1)"); + } + + @Test + public void testAtParamTyParam(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; /** This is record R. \n" + + " * @param r1 This is a component.\n" + + " * @param This is a type parameter.\n" + + " */\n" + + "public record R(int r1) { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + checkOutput("p/R.html", true, + "

Record R<T>

", + "public record R<T>", + "
\n" + + "
Type Parameters:
\n" + + "
T - This is a type parameter.
\n" + + "
Record Components:
\n" + + "
r1 - This is a component.
\n" + + "
", + "R​(int r1)"); + } + + @Test + public void testGeneratedComments(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; /** This is record R. \n" + + " * @param r1 This is a component.\n" + + " */\n" + + "public record R(int r1) { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + // While we don't normally test values that just come from resource files, + // in these cases, we want to verify that something non-empty was put into + // the documentation for the generated members. + checkOrder("p/R.html", + "
", + "R", + "Creates an instance of a R record.", + "
", + "equals", + "Indicates whether some other object is \"equal to\" this one.", + "hashCode", + "Returns a hash code value for this object.", + "r1", + "Returns the value of the r1 record component.", + "toString", + "Returns a string representation of this record.", + "Method Details", + "toString", + "Returns a string representation of this record. The representation " + + "contains the name of the type, followed by the name and value of " + + "each of the record components.", + "hashCode", + "Returns a hash code value for this object. The value is derived " + + "from the hash code of each of the record components.", + "equals", + "Indicates whether some other object is \"equal to\" this one. " + + "The objects are equal if the other object is of the same class " + + "and if all the record components are equal. All components " + + "in this record are compared with '=='.", + "r1", + "Returns the value of the r1 " + + "record component." + ); + } + + @Test + public void testGeneratedCommentsWithLinkOffline(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; /** This is record R. \n" + + " * @param r1 This is a component.\n" + + " */\n" + + "public record R(int r1) { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "-linkoffline", externalDocs, localDocs, + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + // While we don't normally test values that just come from resource files, + // in these cases, we want to verify that something non-empty was put into + // the documentation for the generated members. + checkOrder("p/R.html", + "
", + "R", + "Creates an instance of a R record.", + "
", + "equals", + "Indicates whether some other object is \"equal to\" this one.", + "hashCode", + "Returns a hash code value for this object.", + "r1", + "Returns the value of the r1 record component.", + "toString", + "Returns a string representation of this record.", + "Method Details", + "toString", + "Returns a string representation of this record. The representation " + + "contains the name of the type, followed by the name and value of " + + "each of the record components.", + "hashCode", + "Returns a hash code value for this object. The value is derived " + + "from the hash code of each of the record components.", + "equals", + "Indicates whether some other object is \"equal to\" this one. " + + "The objects are equal if the other object is of the same class " + + "and if all the record components are equal. All components " + + "in this record are compared with '=='.", + "r1", + "Returns the value of the r1 " + + "record component." + ); + } + + @Test + public void testGeneratedEqualsPrimitive(Path base) throws IOException { + testGeneratedEquals(base, "int a, int b", + "All components in this record are compared with '=='."); + } + + @Test + public void testGeneratedEqualsReference(Path base) throws IOException { + testGeneratedEquals(base, "Object a, Object b", + "All components in this record are compared with Objects::equals(Object,Object)"); + } + + @Test + public void testGeneratedEqualsMixed(Path base) throws IOException { + testGeneratedEquals(base, "int a, Object b", + "Reference components are compared with Objects::equals(Object,Object); " + + "primitive components are compared with '=='."); + } + + private void testGeneratedEquals(Path base, String comps, String expect) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; /** This is record R. \n" + + " */\n" + + "public record R(" + comps + ") { }"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + checkOrder("p/R.html", expect); + } + + @Test + public void testUserComments(Path base) throws IOException { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + "package p; /** This is record R. \n" + + " * @param r1 This is a component.\n" + + " */\n" + + "public record R(int r1) {\n" + + "/** User constructor. */ public R { }\n" + + "/** User equals. */ public boolean equals(Object other) { return false; }\n" + + "/** User hashCode. */ public int hashCode() { return 0; }\n" + + "/** User toString. */ public String toString() { return \"\"; }\n" + + "/** User accessor. */ public int r1() { return r1; }\n" + + "}"); + + javadoc("-d", base.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + checkOrder("p/R.html", + "
", + "R", + "User constructor.", + "
", + "equals", + "User equals.", + "hashCode", + "User hashCode.", + "r1", + "User accessor.", + "toString", + "User toString." + ); + } + + @Test + public void testExamples(Path base) throws IOException { + javadoc("-d", base.resolve("out-no-link").toString(), + "-quiet", "-noindex", + "-sourcepath", testSrc.toString(), + "-linksource", + "--enable-preview", "--source", thisRelease, + "examples"); + + checkExit(Exit.OK); + javadoc("-d", base.resolve("out-with-link").toString(), + "-quiet", "-noindex", + "-sourcepath", testSrc.toString(), + "-linksource", + "-linkoffline", externalDocs, localDocs, + "--enable-preview", "--source", thisRelease, + "examples"); + checkExit(Exit.OK); + } + + @Test + @SuppressWarnings("preview") + public void testAnnotations(Path base) throws IOException { + ElementType[] types = { + ElementType.FIELD, + ElementType.METHOD, + ElementType.PARAMETER, + ElementType.RECORD_COMPONENT + }; + for (int i = 0; i < (1 << types.length); i++) { + Set set = EnumSet.noneOf(ElementType.class); + for (int b = 0; b < types.length; b++) { + if ((i & (1 << b)) != 0) { + set.add(types[b]); + } + } + testAnnotations(base, set); + } + } + + void testAnnotations(Path base, Set types) throws IOException { + out.println("test " + types); + String name = types.isEmpty() ? "none" : types.stream() + .map(k -> k.name().toLowerCase(Locale.US)) + .collect(Collectors.joining("-")); + Path dir = base.resolve(name); + Path src = dir.resolve("src"); + String target = types.isEmpty() ? "" : types.stream() + .map(s -> "ElementType." + s) + .collect(Collectors.joining(", ", "@Target({", "})")); + tb.writeJavaFiles(src, + "package p;\n" + + "import java.lang.annotation.*;\n" + + "@Documented\n" + + target + "\n" + + " public @interface Anno { }\n", + "package p; public @interface UndocAnno { }", + "package p; public record R(@Anno int i) { }\n"); + + javadoc("-d", dir.resolve("out").toString(), + "-quiet", "-noindex", + "-sourcepath", src.toString(), + "-private", + "--enable-preview", "--source", thisRelease, + "p"); + checkExit(Exit.OK); + + checkOutput("p/R.html", false, + "UndocAnno"); + + Set t = types.isEmpty() ? EnumSet.allOf(ElementType.class) : types; + String anno = "@Anno"; + String rcAnno = t.contains(ElementType.RECORD_COMPONENT) ? anno + " " : ""; + String fAnno = t.contains(ElementType.FIELD) ? "" + anno + "\n" : ""; + String pAnno = t.contains(ElementType.PARAMETER) ? anno + "\n" : ""; + String mAnno= t.contains(ElementType.METHOD) ? "" + anno + "\n" : ""; + + checkOutput("p/R.html", true, + "
public record R("
+                        + rcAnno
+                        + "int i)\n" +
+                        "extends java.lang.Record
", + "
" + + fAnno + + "private final int" + + " i
", + "
public R" + + "​(" + + pAnno + + "int i)
", + "
" + + mAnno + + "public int" + + " i()
"); + + } +} --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/AnnotatedBinaryNode.java 2019-10-30 16:24:37.827746787 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + + +/** + * An annotated binary node. + * + * This example illustrates the use of simple annotations on + * record components. + * + * @param left the left node + * @param right the right node + */ +public record AnnotatedBinaryNode(@NonNull Node left, @NonNull Node right) { } + --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/BinaryNode.java 2019-10-30 16:24:39.223746738 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +/** + * A binary node. + * + * This example illustrates the use of user provided methods + * and documentation comments. + * + * @param left the left node + * @param right the right node + */ +public record BinaryNode(Node left, Node right) { + /** + * This is an example of a user-provided method. + * + * @param other the object with which to be compared + * @return {@code true} if and only the two are equal + */ + public boolean equals(Object other) { + return super.equals(other); + } +} --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/Coords.java 2019-10-30 16:24:40.927746679 -0700 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +/** + * Two-dimensional coordinates that can be represented + * in either Cartesian or polar form. + */ +public interface Coords { + + /** + * Returns the Cartesian x-coordinate. + * + * @return the Cartesian x-coordinate + */ + public double x(); + + /** + * Returns the Cartesian y-coordinate. + * + * @return the Cartesian y-coordinate + */ + public double y(); + + /** + * Returns the polar r-coordinate, or radius. + * + * @return the polar r-coordinate + */ + public double r(); + + /** + * Returns the polar theta-coordinate, or angle. + * + * @return the polar theta-coordinate + */ + public double theta(); + + /** + * Cartesian coordinates. + * + * @param x the x-coordinate + * @param y the y-coordinate + */ + public record Cartesian(double x, double y) implements Coords { + @Override + public double r() { + return Math.sqrt(x * x + y * y); + } + + @Override + public double theta() { + return (x == 0) ? Math.PI / 2.d : Math.atan(y / x); + } + } + + /** + * Polar coordinates. + * + * @param r the radius + * @param theta the angle + */ + public record Polar(double r, double theta) implements Coords { + @Override + public double x() { + return r * Math.cos(theta); + } + + @Override + public double y() { + return r * Math.sin(theta); + } + } +} --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/Holder.java 2019-10-30 16:24:42.763746615 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +/** + * A holder for an object. + * + * This example illustrates the use of a generic record. + * + * @param the type of the enclosed item + * @param t the item + */ +public record Holder(T t) { } --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/Node.java 2019-10-30 16:24:44.791746544 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +/** + * A node. + * + * This is just a supporting class for {@link BinaryNode}. + */ +public interface Node { } --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/NonNull.java 2019-10-30 16:24:46.195746495 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +import java.lang.annotation.*; + +/** + * A simple annotation for demo use on record components. + */ +@Documented +public @interface NonNull { } + --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/Point.java 2019-10-30 16:24:47.323746456 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +/** + * A cartesian point. + * + * This example illustrates the use of implicitly provided methods + * and documentation comments. + * + * @param x the x coordinate + * @param y the y coordinate + */ +public record Point(int x, int y) { } + --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/SerializablePoint.java 2019-10-30 16:24:48.567746413 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +import java.io.Serializable; + +/** + * A serializable cartesian point. + * + * This example illustrates the documentation of a serializable record. + * + * @param x the x coordinate + * @param y the y coordinate + */ +public record SerializablePoint(int x, int y) implements Serializable { } + --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/examples/SerializableProxy.java 2019-10-30 16:24:50.019746362 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package examples; + +import java.io.Serializable; + +/** + * A class with a record for a serialization proxy. + */ +public class SerializableProxy implements Serializable { + private int p; + private int q; + + /** + * The proxy. + * + * @param x the first item in the serialized form + * @param y the second item in the serialized form + * + * @serial include + */ + private record Proxy(int x, int y) { + /** + * Returns a deserialized object. + * + * @returns the deserialized object + */ + public SerializableProxy readResolve() { + return new SerializableProxy(x, y); + } + } + + /** + * Returns the proxy object. + * + * @return the proxy object + */ + public Object writeReplace() { + return new Proxy(p, q); + } +} --- /dev/null 2019-05-13 14:24:52.059439902 -0700 +++ new/test/langtools/jdk/javadoc/doclet/testRecordTypes/jdk11/element-list 2019-10-30 16:24:52.047746291 -0700 @@ -0,0 +1,283 @@ +module:java.base +java.io +java.lang +java.lang.annotation +java.lang.invoke +java.lang.module +java.lang.ref +java.lang.reflect +java.math +java.net +java.net.spi +java.nio +java.nio.channels +java.nio.channels.spi +java.nio.charset +java.nio.charset.spi +java.nio.file +java.nio.file.attribute +java.nio.file.spi +java.security +java.security.acl +java.security.cert +java.security.interfaces +java.security.spec +java.text +java.text.spi +java.time +java.time.chrono +java.time.format +java.time.temporal +java.time.zone +java.util +java.util.concurrent +java.util.concurrent.atomic +java.util.concurrent.locks +java.util.function +java.util.jar +java.util.regex +java.util.spi +java.util.stream +java.util.zip +javax.crypto +javax.crypto.interfaces +javax.crypto.spec +javax.net +javax.net.ssl +javax.security.auth +javax.security.auth.callback +javax.security.auth.login +javax.security.auth.spi +javax.security.auth.x500 +javax.security.cert +module:java.compiler +javax.annotation.processing +javax.lang.model +javax.lang.model.element +javax.lang.model.type +javax.lang.model.util +javax.tools +module:java.datatransfer +java.awt.datatransfer +module:java.desktop +java.applet +java.awt +java.awt.color +java.awt.desktop +java.awt.dnd +java.awt.event +java.awt.font +java.awt.geom +java.awt.im +java.awt.im.spi +java.awt.image +java.awt.image.renderable +java.awt.print +java.beans +java.beans.beancontext +javax.accessibility +javax.imageio +javax.imageio.event +javax.imageio.metadata +javax.imageio.plugins.bmp +javax.imageio.plugins.jpeg +javax.imageio.plugins.tiff +javax.imageio.spi +javax.imageio.stream +javax.print +javax.print.attribute +javax.print.attribute.standard +javax.print.event +javax.sound.midi +javax.sound.midi.spi +javax.sound.sampled +javax.sound.sampled.spi +javax.swing +javax.swing.border +javax.swing.colorchooser +javax.swing.event +javax.swing.filechooser +javax.swing.plaf +javax.swing.plaf.basic +javax.swing.plaf.metal +javax.swing.plaf.multi +javax.swing.plaf.nimbus +javax.swing.plaf.synth +javax.swing.table +javax.swing.text +javax.swing.text.html +javax.swing.text.html.parser +javax.swing.text.rtf +javax.swing.tree +javax.swing.undo +module:java.instrument +java.lang.instrument +module:java.logging +java.util.logging +module:java.management +java.lang.management +javax.management +javax.management.loading +javax.management.modelmbean +javax.management.monitor +javax.management.openmbean +javax.management.relation +javax.management.remote +javax.management.timer +module:java.management.rmi +javax.management.remote.rmi +module:java.naming +javax.naming +javax.naming.directory +javax.naming.event +javax.naming.ldap +javax.naming.spi +module:java.net.http +java.net.http +module:java.prefs +java.util.prefs +module:java.rmi +java.rmi +java.rmi.activation +java.rmi.dgc +java.rmi.registry +java.rmi.server +javax.rmi.ssl +module:java.scripting +javax.script +module:java.se +module:java.security.jgss +javax.security.auth.kerberos +org.ietf.jgss +module:java.security.sasl +javax.security.sasl +module:java.smartcardio +javax.smartcardio +module:java.sql +java.sql +javax.sql +module:java.sql.rowset +javax.sql.rowset +javax.sql.rowset.serial +javax.sql.rowset.spi +module:java.transaction.xa +javax.transaction.xa +module:java.xml +javax.xml +javax.xml.catalog +javax.xml.datatype +javax.xml.namespace +javax.xml.parsers +javax.xml.stream +javax.xml.stream.events +javax.xml.stream.util +javax.xml.transform +javax.xml.transform.dom +javax.xml.transform.sax +javax.xml.transform.stax +javax.xml.transform.stream +javax.xml.validation +javax.xml.xpath +org.w3c.dom +org.w3c.dom.bootstrap +org.w3c.dom.events +org.w3c.dom.ls +org.w3c.dom.ranges +org.w3c.dom.traversal +org.w3c.dom.views +org.xml.sax +org.xml.sax.ext +org.xml.sax.helpers +module:java.xml.crypto +javax.xml.crypto +javax.xml.crypto.dom +javax.xml.crypto.dsig +javax.xml.crypto.dsig.dom +javax.xml.crypto.dsig.keyinfo +javax.xml.crypto.dsig.spec +module:jdk.accessibility +com.sun.java.accessibility.util +module:jdk.attach +com.sun.tools.attach +com.sun.tools.attach.spi +module:jdk.charsets +module:jdk.compiler +com.sun.source.doctree +com.sun.source.tree +com.sun.source.util +com.sun.tools.javac +module:jdk.crypto.cryptoki +module:jdk.crypto.ec +module:jdk.dynalink +jdk.dynalink +jdk.dynalink.beans +jdk.dynalink.linker +jdk.dynalink.linker.support +jdk.dynalink.support +module:jdk.editpad +module:jdk.hotspot.agent +module:jdk.httpserver +com.sun.net.httpserver +com.sun.net.httpserver.spi +module:jdk.jartool +com.sun.jarsigner +jdk.security.jarsigner +module:jdk.javadoc +com.sun.javadoc +com.sun.tools.javadoc +jdk.javadoc.doclet +module:jdk.jcmd +module:jdk.jconsole +com.sun.tools.jconsole +module:jdk.jdeps +module:jdk.jdi +com.sun.jdi +com.sun.jdi.connect +com.sun.jdi.connect.spi +com.sun.jdi.event +com.sun.jdi.request +module:jdk.jdwp.agent +module:jdk.jfr +jdk.jfr +jdk.jfr.consumer +module:jdk.jlink +module:jdk.jshell +jdk.jshell +jdk.jshell.execution +jdk.jshell.spi +jdk.jshell.tool +module:jdk.jsobject +netscape.javascript +module:jdk.jstatd +module:jdk.localedata +module:jdk.management +com.sun.management +module:jdk.management.agent +module:jdk.management.jfr +jdk.management.jfr +module:jdk.naming.dns +module:jdk.naming.rmi +module:jdk.net +jdk.net +jdk.nio +module:jdk.pack +module:jdk.rmic +module:jdk.scripting.nashorn +jdk.nashorn.api.scripting +jdk.nashorn.api.tree +module:jdk.sctp +com.sun.nio.sctp +module:jdk.security.auth +com.sun.security.auth +com.sun.security.auth.callback +com.sun.security.auth.login +com.sun.security.auth.module +module:jdk.security.jgss +com.sun.security.jgss +module:jdk.xml.dom +org.w3c.dom.css +org.w3c.dom.html +org.w3c.dom.stylesheets +org.w3c.dom.xpath +module:jdk.zipfs +