src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java

Print this page


   1 /*
   2  * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.doclets.formats.html;
  27 
  28 import java.io.*;
  29 import java.text.SimpleDateFormat;
  30 import java.util.*;
  31 import java.util.regex.Matcher;
  32 import java.util.regex.Pattern;
  33 
  34 import com.sun.javadoc.*;
  35 import com.sun.tools.doclets.formats.html.markup.*;
  36 import com.sun.tools.doclets.internal.toolkit.*;
  37 import com.sun.tools.doclets.internal.toolkit.taglets.*;
  38 import com.sun.tools.doclets.internal.toolkit.util.*;
  39 import com.sun.tools.javac.util.StringUtils;







  40 






















































  41 /**
  42  * Class for the Html Format Code Generation specific to JavaDoc.
  43  * This Class contains methods related to the Html Code Generation which
  44  * are used extensively while generating the entire documentation.
  45  *
  46  *  <p><b>This is NOT part of any supported API.
  47  *  If you write code that depends on this, you do so at your own risk.
  48  *  This code and its internal interfaces are subject to change or
  49  *  deletion without notice.</b>
  50  *
  51  * @since 1.2
  52  * @author Atul M Dambalkar
  53  * @author Robert Field
  54  * @author Bhavesh Patel (Modified)
  55  */
  56 public class HtmlDocletWriter extends HtmlDocWriter {
  57 
  58     /**
  59      * Relative path from the file getting generated to the destination
  60      * directory. For example, if the file getting generated is


 193                 "  }" + DocletConstants.NL +
 194                 "  else {" + DocletConstants.NL +
 195                 "    allClassesLink.style.display = \"none\";" + DocletConstants.NL +
 196                 "  }" + DocletConstants.NL +
 197                 "  //-->" + DocletConstants.NL;
 198         Content scriptContent = new RawHtml(scriptCode);
 199         script.addContent(scriptContent);
 200         Content div = HtmlTree.DIV(script);
 201         Content div_noscript = HtmlTree.DIV(getResource("doclet.No_Script_Message"));
 202         Content noScript = HtmlTree.NOSCRIPT(div_noscript);
 203         div.addContent(noScript);
 204         return div;
 205     }
 206 
 207     /**
 208      * Add method information.
 209      *
 210      * @param method the method to be documented
 211      * @param dl the content tree to which the method information will be added
 212      */
 213     private void addMethodInfo(MethodDoc method, Content dl) {
 214         ClassDoc[] intfacs = method.containingClass().interfaces();
 215         MethodDoc overriddenMethod = method.overriddenMethod();

 216         // Check whether there is any implementation or overridden info to be
 217         // printed. If no overridden or implementation info needs to be
 218         // printed, do not print this section.
 219         if ((intfacs.length > 0 &&
 220                 new ImplementedMethods(method, this.configuration).build().length > 0) ||
 221                 overriddenMethod != null) {
 222             MethodWriterImpl.addImplementsInfo(this, method, dl);
 223             if (overriddenMethod != null) {
 224                 MethodWriterImpl.addOverridden(this,
 225                         method.overriddenType(), overriddenMethod, dl);


 226             }
 227         }
 228     }
 229 
 230     /**
 231      * Adds the tags information.
 232      *
 233      * @param doc the doc for which the tags will be generated
 234      * @param htmltree the documentation tree to which the tags will be added
 235      */
 236     protected void addTagsInfo(Doc doc, Content htmltree) {
 237         if (configuration.nocomment) {
 238             return;
 239         }
 240         Content dl = new HtmlTree(HtmlTag.DL);
 241         if (doc instanceof MethodDoc) {
 242             addMethodInfo((MethodDoc) doc, dl);
 243         }
 244         Content output = new ContentBuilder();
 245         TagletWriter.genTagOuput(configuration.tagletManager, doc,
 246             configuration.tagletManager.getCustomTaglets(doc),
 247                 getTagletWriterInstance(false), output);
 248         dl.addContent(output);
 249         htmltree.addContent(dl);
 250     }
 251 
 252     /**
 253      * Check whether there are any tags for Serialization Overview
 254      * section to be printed.
 255      *
 256      * @param field the FieldDoc object to check for tags.
 257      * @return true if there are tags to be printed else return false.
 258      */
 259     protected boolean hasSerializationOverviewTags(FieldDoc field) {
 260         Content output = new ContentBuilder();
 261         TagletWriter.genTagOuput(configuration.tagletManager, field,
 262             configuration.tagletManager.getCustomTaglets(field),
 263                 getTagletWriterInstance(false), output);
 264         return !output.isEmpty();
 265     }
 266 
 267     /**
 268      * Returns a TagletWriter that knows how to write HTML.
 269      *
 270      * @return a TagletWriter that knows how to write HTML.
 271      */
 272     public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
 273         return new TagletWriterImpl(this, isFirstSentence);
 274     }
 275 
 276     /**
 277      * Get Package link, with target frame.
 278      *
 279      * @param pd The link will be to the "package-summary.html" page for this package
 280      * @param target name of the target frame
 281      * @param label tag for the link
 282      * @return a content for the target package link
 283      */
 284     public Content getTargetPackageLink(PackageDoc pd, String target,
 285             Content label) {
 286         return getHyperLink(pathString(pd, DocPaths.PACKAGE_SUMMARY), label, "", target);
 287     }
 288 
 289     /**
 290      * Get Profile Package link, with target frame.
 291      *
 292      * @param pd the packageDoc object
 293      * @param target name of the target frame
 294      * @param label tag for the link
 295      * @param profileName the name of the profile being documented
 296      * @return a content for the target profile packages link
 297      */
 298     public Content getTargetProfilePackageLink(PackageDoc pd, String target,
 299             Content label, String profileName) {
 300         return getHyperLink(pathString(pd, DocPaths.profilePackageSummary(profileName)),
 301                 label, "", target);
 302     }
 303 
 304     /**
 305      * Get Profile link, with target frame.
 306      *
 307      * @param target name of the target frame
 308      * @param label tag for the link
 309      * @param profileName the name of the profile being documented
 310      * @return a content for the target profile link
 311      */
 312     public Content getTargetProfileLink(String target, Content label,
 313             String profileName) {
 314         return getHyperLink(pathToRoot.resolve(
 315                 DocPaths.profileSummary(profileName)), label, "", target);
 316     }
 317 
 318     /**
 319      * Get the type name for profile search.
 320      *
 321      * @param cd the classDoc object for which the type name conversion is needed
 322      * @return a type name string for the type
 323      */
 324     public String getTypeNameForProfile(ClassDoc cd) {
 325         StringBuilder typeName =
 326                 new StringBuilder((cd.containingPackage()).name().replace(".", "/"));
 327         typeName.append("/")
 328                 .append(cd.name().replace(".", "$"));
 329         return typeName.toString();
 330     }
 331 
 332     /**
 333      * Check if a type belongs to a profile.
 334      *
 335      * @param cd the classDoc object that needs to be checked
 336      * @param profileValue the profile in which the type needs to be checked
 337      * @return true if the type is in the profile
 338      */
 339     public boolean isTypeInProfile(ClassDoc cd, int profileValue) {
 340         return (configuration.profiles.getProfile(getTypeNameForProfile(cd)) <= profileValue);
 341     }
 342 
 343     public void addClassesSummary(ClassDoc[] classes, String label,
 344             String tableSummary, String[] tableHeader, Content summaryContentTree,
 345             int profileValue) {
 346         if(classes.length > 0) {
 347             Arrays.sort(classes);
 348             Content caption = getTableCaption(new RawHtml(label));
 349             Content table = (configuration.isOutputHtml5())
 350                     ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption)
 351                     : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption);
 352             table.addContent(getSummaryTableHeader(tableHeader, "col"));
 353             Content tbody = new HtmlTree(HtmlTag.TBODY);
 354             for (int i = 0; i < classes.length; i++) {
 355                 if (!isTypeInProfile(classes[i], profileValue)) {


 356                     continue;
 357                 }
 358                 if (!utils.isCoreClass(classes[i]) ||
 359                     !configuration.isGeneratedDoc(classes[i])) {
 360                     continue;
 361                 }
 362                 Content classContent = getLink(new LinkInfoImpl(
 363                         configuration, LinkInfoImpl.Kind.PACKAGE, classes[i]));
 364                 Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent);
 365                 HtmlTree tr = HtmlTree.TR(tdClass);
 366                 if (i%2 == 0)
 367                     tr.addStyle(HtmlStyle.altColor);
 368                 else
 369                     tr.addStyle(HtmlStyle.rowColor);
 370                 HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD);
 371                 tdClassDescription.addStyle(HtmlStyle.colLast);
 372                 if (utils.isDeprecated(classes[i])) {
 373                     tdClassDescription.addContent(deprecatedLabel);
 374                     if (classes[i].tags("deprecated").length > 0) {
 375                         addSummaryDeprecatedComment(classes[i],
 376                             classes[i].tags("deprecated")[0], tdClassDescription);
 377                     }


 378                 }
 379                 else
 380                     addSummaryComment(classes[i], tdClassDescription);
 381                 tr.addContent(tdClassDescription);
 382                 tbody.addContent(tr);
 383             }
 384             table.addContent(tbody);
 385             summaryContentTree.addContent(table);
 386         }
 387     }
 388 
 389     /**
 390      * Generates the HTML document tree and prints it out.
 391      *
 392      * @param metakeywords Array of String keywords for META tag. Each element
 393      *                     of the array is assigned to a separate META tag.
 394      *                     Pass in null for no array
 395      * @param includeScript true if printing windowtitle script
 396      *                      false for files that appear in the left-hand frames
 397      * @param body the body htmltree to be included in the document
 398      */
 399     public void printHtmlDocument(String[] metakeywords, boolean includeScript,
 400             Content body) throws IOException {
 401         Content htmlDocType = configuration.isOutputHtml5()
 402                 ? DocType.HTML5
 403                 : DocType.TRANSITIONAL;
 404         Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
 405         Content head = new HtmlTree(HtmlTag.HEAD);
 406         head.addContent(getGeneratedBy(!configuration.notimestamp));
 407         head.addContent(getTitle());
 408         Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE,
 409                 (configuration.charset.length() > 0) ?
 410                         configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET);
 411         head.addContent(meta);
 412         if (!configuration.notimestamp) {
 413             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
 414             meta = HtmlTree.META(configuration.isOutputHtml5()
 415                     ? "dc.created"
 416                     : "date", dateFormat.format(new Date()));
 417             head.addContent(meta);
 418         }
 419         if (metakeywords != null) {


 521                 navDiv.addContent(a);
 522                 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink(
 523                     getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks,
 524                     skipNavLinks.toString(), ""));
 525                 navDiv.addContent(skipLinkContent);
 526             }
 527             if (header) {
 528                 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW));
 529             } else {
 530                 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW));
 531             }
 532             HtmlTree navList = new HtmlTree(HtmlTag.UL);
 533             navList.addStyle(HtmlStyle.navList);
 534             navList.addAttr(HtmlAttr.TITLE,
 535                             configuration.getText("doclet.Navigation"));
 536             if (configuration.createoverview) {
 537                 navList.addContent(getNavLinkContents());
 538             }
 539             if (configuration.packages.size() == 1) {
 540                 navList.addContent(getNavLinkPackage(configuration.packages.first()));
 541             } else if (configuration.packages.size() > 1) {
 542                 navList.addContent(getNavLinkPackage());
 543             }
 544             navList.addContent(getNavLinkClass());
 545             if(configuration.classuse) {
 546                 navList.addContent(getNavLinkClassUse());
 547             }
 548             if(configuration.createtree) {
 549                 navList.addContent(getNavLinkTree());
 550             }
 551             if(!(configuration.nodeprecated ||
 552                      configuration.nodeprecatedlist)) {
 553                 navList.addContent(getNavLinkDeprecated());
 554             }
 555             if(configuration.createindex) {
 556                 navList.addContent(getNavLinkIndex());
 557             }
 558             if (!configuration.nohelp) {
 559                 navList.addContent(getNavLinkHelp());
 560             }
 561             navDiv.addContent(navList);
 562             Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header));
 563             navDiv.addContent(aboutDiv);
 564             if (header) {
 565                 fixedNavDiv.addContent(navDiv);
 566             } else {
 567                 tree.addContent(navDiv);
 568             }
 569             Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious());
 570             ulNav.addContent(getNavLinkNext());
 571             Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
 572             Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists());
 573             ulFrames.addContent(getNavHideLists(filename));
 574             subDiv.addContent(ulFrames);
 575             HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
 576             ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString());
 577             subDiv.addContent(ulAllClasses);
 578             if (header && configuration.createindex) {
 579                 HtmlTree inputText = HtmlTree.INPUT("text", "search");
 580                 HtmlTree inputReset = HtmlTree.INPUT("reset", "reset");
 581                 Content searchTxt = configuration.getResource("doclet.search");
 582                 searchTxt.addContent(getSpace());
 583                 HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt));
 584                 liInput.addContent(inputText);
 585                 liInput.addContent(inputReset);
 586                 HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput);
 587                 subDiv.addContent(ulSearch);
 588             }
 589             subDiv.addContent(getAllClassesLinkScript(allClassesId.toString()));
 590             addSummaryDetailLinks(subDiv);
 591             if (header) {
 592                 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP));
 593                 fixedNavDiv.addContent(subDiv);
 594                 fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR);
 595                 tree.addContent(fixedNavDiv);
 596             } else {
 597                 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
 598                 tree.addContent(subDiv);
 599                 tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR);
 600             }
 601             if (configuration.allowTag(HtmlTag.NAV)) {
 602                 htmlTree.addContent(tree);
 603             }
 604         }
 605     }
 606 
 607     /**
 608      * Get the word "NEXT" to indicate that no link is available.  Override
 609      * this method to customize next link.


 631     }
 632 
 633     /**
 634      * Get link to the "overview-summary.html" page.
 635      *
 636      * @return a content tree for the link
 637      */
 638     protected Content getNavLinkContents() {
 639         Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY),
 640                 overviewLabel, "", "");
 641         Content li = HtmlTree.LI(linkContent);
 642         return li;
 643     }
 644 
 645     /**
 646      * Get link to the "package-summary.html" page for the package passed.
 647      *
 648      * @param pkg Package to which link will be generated
 649      * @return a content tree for the link
 650      */
 651     protected Content getNavLinkPackage(PackageDoc pkg) {
 652         Content linkContent = getPackageLink(pkg,
 653                 packageLabel);
 654         Content li = HtmlTree.LI(linkContent);
 655         return li;
 656     }
 657 
 658     /**
 659      * Get the word "Package" , to indicate that link is not available here.
 660      *
 661      * @return a content tree for the link
 662      */
 663     protected Content getNavLinkPackage() {
 664         Content li = HtmlTree.LI(packageLabel);
 665         return li;
 666     }
 667 
 668     /**
 669      * Get the word "Use", to indicate that link is not available.
 670      *
 671      * @return a content tree for the link
 672      */
 673     protected Content getNavLinkClassUse() {


 734      * Get "NO FRAMES" link, to switch to the non-frame version of the output.
 735      *
 736      * @param link File to be linked
 737      * @return a content tree for the link
 738      */
 739     protected Content getNavHideLists(DocPath link) {
 740         Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top");
 741         Content li = HtmlTree.LI(noFramesContent);
 742         return li;
 743     }
 744 
 745     /**
 746      * Get "Tree" link in the navigation bar. If there is only one package
 747      * specified on the command line, then the "Tree" link will be to the
 748      * only "package-tree.html" file otherwise it will be to the
 749      * "overview-tree.html" file.
 750      *
 751      * @return a content tree for the link
 752      */
 753     protected Content getNavLinkTree() {
 754         Content treeLinkContent;
 755         PackageDoc[] packages = configuration.root.specifiedPackages();
 756         if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) {
 757             treeLinkContent = getHyperLink(pathString(packages[0],
 758                     DocPaths.PACKAGE_TREE), treeLabel,
 759                     "", "");
 760         } else {
 761             treeLinkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
 762                     treeLabel, "", "");
 763         }
 764         Content li = HtmlTree.LI(treeLinkContent);
 765         return li;
 766     }
 767 
 768     /**
 769      * Get the overview tree link for the main tree.
 770      *
 771      * @param label the label for the link
 772      * @return a content tree for the link
 773      */
 774     protected Content getNavLinkMainTree(String label) {
 775         Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
 776                 new StringContent(label));
 777         Content li = HtmlTree.LI(mainTreeContent);
 778         return li;
 779     }
 780 
 781     /**
 782      * Get the word "Class", to indicate that class link is not available.
 783      *
 784      * @return a content tree for the link
 785      */
 786     protected Content getNavLinkClass() {


 842         DocPath helpfilenm;
 843         if (helpfile.isEmpty()) {
 844             helpfilenm = DocPaths.HELP_DOC;
 845         } else {
 846             DocFile file = DocFile.createFileForInput(configuration, helpfile);
 847             helpfilenm = DocPath.create(file.getName());
 848         }
 849         Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm),
 850                 helpLabel, "", "");
 851         Content li = HtmlTree.LI(linkContent);
 852         return li;
 853     }
 854 
 855     /**
 856      * Get summary table header.
 857      *
 858      * @param header the header for the table
 859      * @param scope the scope of the headers
 860      * @return a content tree for the header
 861      */
 862     public Content getSummaryTableHeader(String[] header, String scope) {
 863         Content tr = new HtmlTree(HtmlTag.TR);
 864         int size = header.length;
 865         Content tableHeader;
 866         if (size == 1) {
 867             tableHeader = new StringContent(header[0]);
 868             tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader));
 869             return tr;
 870         }
 871         for (int i = 0; i < size; i++) {
 872             tableHeader = new StringContent(header[i]);
 873             if(i == 0)
 874                 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader));
 875             else if(i == (size - 1))
 876                 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader));
 877             else
 878                 tr.addContent(HtmlTree.TH(scope, tableHeader));
 879         }
 880         return tr;
 881     }
 882 
 883     /**
 884      * Get table caption.
 885      *
 886      * @param rawText the caption for the table which could be raw Html
 887      * @return a content tree for the caption
 888      */
 889     public Content getTableCaption(Content title) {
 890         Content captionSpan = HtmlTree.SPAN(title);
 891         Content space = getSpace();
 892         Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);


 926         return getMarkerAnchor(sectionName.getName() + getName(anchorName), null);
 927     }
 928 
 929     /**
 930      * Get the marker anchor which will be added to the documentation tree.
 931      *
 932      * @param anchorName the anchor name or id attribute
 933      * @param anchorContent the content that should be added to the anchor
 934      * @return a content tree for the marker anchor
 935      */
 936     public Content getMarkerAnchor(String anchorName, Content anchorContent) {
 937         if (anchorContent == null)
 938             anchorContent = new Comment(" ");
 939         Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent);
 940         return markerAnchor;
 941     }
 942 
 943     /**
 944      * Returns a packagename content.
 945      *
 946      * @param packageDoc the package to check
 947      * @return package name content
 948      */
 949     public Content getPackageName(PackageDoc packageDoc) {
 950         return packageDoc == null || packageDoc.name().isEmpty() ?
 951             defaultPackageLabel :
 952             getPackageLabel(packageDoc.name());
 953     }
 954 
 955     /**
 956      * Returns a package name label.
 957      *
 958      * @param packageName the package name
 959      * @return the package name content
 960      */
 961     public Content getPackageLabel(String packageName) {
 962         return new StringContent(packageName);
 963     }
 964 
 965     /**
 966      * Add package deprecation information to the documentation tree
 967      *
 968      * @param deprPkgs list of deprecated packages
 969      * @param headingKey the caption for the deprecated package table
 970      * @param tableSummary the summary for the deprecated package table
 971      * @param tableHeader table headers for the deprecated package table
 972      * @param contentTree the content tree to which the deprecated package table will be added
 973      */
 974     protected void addPackageDeprecatedAPI(List<Doc> deprPkgs, String headingKey,
 975             String tableSummary, String[] tableHeader, Content contentTree) {
 976         if (deprPkgs.size() > 0) {
 977             Content caption = getTableCaption(configuration.getResource(headingKey));
 978             Content table = (configuration.isOutputHtml5())
 979                     ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption)
 980                     : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption);
 981             table.addContent(getSummaryTableHeader(tableHeader, "col"));
 982             Content tbody = new HtmlTree(HtmlTag.TBODY);
 983             for (int i = 0; i < deprPkgs.size(); i++) {
 984                 PackageDoc pkg = (PackageDoc) deprPkgs.get(i);

 985                 HtmlTree td = HtmlTree.TD(HtmlStyle.colOne,
 986                         getPackageLink(pkg, getPackageName(pkg)));
 987                 if (pkg.tags("deprecated").length > 0) {
 988                     addInlineDeprecatedComment(pkg, pkg.tags("deprecated")[0], td);

 989                 }
 990                 HtmlTree tr = HtmlTree.TR(td);
 991                 if (i % 2 == 0) {
 992                     tr.addStyle(HtmlStyle.altColor);
 993                 } else {
 994                     tr.addStyle(HtmlStyle.rowColor);
 995                 }
 996                 tbody.addContent(tr);
 997             }
 998             table.addContent(tbody);
 999             Content li = HtmlTree.LI(HtmlStyle.blockList, table);
1000             Content ul = HtmlTree.UL(HtmlStyle.blockList, li);
1001             contentTree.addContent(ul);
1002         }
1003     }
1004 
1005     /**
1006      * Return the path to the class page for a classdoc.
1007      *
1008      * @param cd   Class to which the path is requested.
1009      * @param name Name of the file(doesn't include path).
1010      */
1011     protected DocPath pathString(ClassDoc cd, DocPath name) {
1012         return pathString(cd.containingPackage(), name);
1013     }
1014 
1015     /**
1016      * Return path to the given file name in the given package. So if the name
1017      * passed is "Object.html" and the name of the package is "java.lang", and
1018      * if the relative path is "../.." then returned string will be
1019      * "../../java/lang/Object.html"
1020      *
1021      * @param pd Package in which the file name is assumed to be.
1022      * @param name File name, to which path string is.
1023      */
1024     protected DocPath pathString(PackageDoc pd, DocPath name) {
1025         return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name));
1026     }
1027 
1028     /**
1029      * Given a package, return the name to be used in HTML anchor tag.
1030      * @param packageDoc the package.
1031      * @return the name to be used in HTML anchor tag.
1032      */
1033     public String getPackageAnchorName(PackageDoc packageDoc) {
1034         return packageDoc == null || packageDoc.name().length() == 0 ?
1035             SectionName.UNNAMED_PACKAGE_ANCHOR.getName() : packageDoc.name();

1036     }
1037 
1038     /**
1039      * Return the link to the given package.
1040      *
1041      * @param pkg the package to link to.
1042      * @param label the label for the link.
1043      * @return a content tree for the package link.
1044      */
1045     public Content getPackageLink(PackageDoc pkg, String label) {
1046         return getPackageLink(pkg, new StringContent(label));
1047     }
1048 







1049     /**
1050      * Return the link to the given package.
1051      *
1052      * @param pkg the package to link to.
1053      * @param label the label for the link.
1054      * @return a content tree for the package link.
1055      */
1056     public Content getPackageLink(PackageDoc pkg, Content label) {
1057         boolean included = pkg != null && pkg.isIncluded();
1058         if (! included) {
1059             for (PackageDoc p : configuration.packages) {
1060                 if (p.equals(pkg)) {
1061                     included = true;
1062                     break;
1063                 }
1064             }
1065         }
1066         if (included || pkg == null) {
1067             return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
1068                     label);
1069         } else {
1070             DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(pkg));
1071             if (crossPkgLink != null) {
1072                 return getHyperLink(crossPkgLink, label);
1073             } else {
1074                 return label;
1075             }
1076         }
1077     }
1078 
1079     public Content italicsClassName(ClassDoc cd, boolean qual) {
1080         Content name = new StringContent((qual)? cd.qualifiedName(): cd.name());
1081         return (cd.isInterface())?  HtmlTree.SPAN(HtmlStyle.interfaceName, name): name;


1082     }
1083 
1084     /**
1085      * Add the link to the content tree.
1086      *
1087      * @param doc program element doc for which the link will be added
1088      * @param label label for the link
1089      * @param htmltree the content tree to which the link will be added
1090      */
1091     public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) {
1092         if (doc == null) {
1093             return;
1094         }
1095         ClassDoc cd = doc.containingClass();
1096         if (cd == null) {
1097             //d must be a class doc since in has no containing class.
1098             cd = (ClassDoc) doc;
1099         }
1100         DocPath href = pathToRoot
1101                 .resolve(DocPaths.SOURCE_OUTPUT)
1102                 .resolve(DocPath.forClass(cd));
1103         Content linkContent = getHyperLink(href.fragment(SourceToHTMLConverter.getAnchorName(doc)), label, "", "");

1104         htmltree.addContent(linkContent);
1105     }
1106 
1107     /**
1108      * Return the link to the given class.
1109      *
1110      * @param linkInfo the information about the link.
1111      *
1112      * @return the link for the given class.
1113      */
1114     public Content getLink(LinkInfoImpl linkInfo) {
1115         LinkFactoryImpl factory = new LinkFactoryImpl(this);
1116         return factory.getLink(linkInfo);
1117     }
1118 
1119     /**
1120      * Return the type parameters for the given class.
1121      *
1122      * @param linkInfo the information about the link.
1123      * @return the type for the given class.


1138      * be null or empty string if no member is being referenced.
1139      * @param label the label for the external link.
1140      * @param strong true if the link should be strong.
1141      * @param style the style of the link.
1142      * @param code true if the label should be code font.
1143      */
1144     public Content getCrossClassLink(String qualifiedClassName, String refMemName,
1145                                     Content label, boolean strong, String style,
1146                                     boolean code) {
1147         String className = "";
1148         String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
1149         int periodIndex;
1150         while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
1151             className = packageName.substring(periodIndex + 1, packageName.length()) +
1152                 (className.length() > 0 ? "." + className : "");
1153             Content defaultLabel = new StringContent(className);
1154             if (code)
1155                 defaultLabel = HtmlTree.CODE(defaultLabel);
1156             packageName = packageName.substring(0, periodIndex);
1157             if (getCrossPackageLink(packageName) != null) {
1158                 //The package exists in external documentation, so link to the external
1159                 //class (assuming that it exists).  This is definitely a limitation of
1160                 //the -link option.  There are ways to determine if an external package
1161                 //exists, but no way to determine if the external class exists.  We just
1162                 //have to assume that it does.


1163                 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
1164                                 className + ".html", refMemName);
1165                 return getHyperLink(link,
1166                     (label == null) || label.isEmpty() ? defaultLabel : label,
1167                     strong, style,
1168                     configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
1169                     "");
1170             }
1171         }
1172         return null;
1173     }
1174 
1175     public boolean isClassLinkable(ClassDoc cd) {
1176         if (cd.isIncluded()) {
1177             return configuration.isGeneratedDoc(cd);
1178         }
1179         return configuration.extern.isExternal(cd);
1180     }
1181 
1182     public DocLink getCrossPackageLink(String pkgName) {
1183         return configuration.extern.getExternalLink(pkgName, pathToRoot,
1184             DocPaths.PACKAGE_SUMMARY.getPath());
1185     }
1186 
1187     /**
1188      * Get the class link.
1189      *
1190      * @param context the id of the context where the link will be added
1191      * @param cd the class doc to link to
1192      * @return a content tree for the link
1193      */
1194     public Content getQualifiedClassLink(LinkInfoImpl.Kind context, ClassDoc cd) {
1195         return getLink(new LinkInfoImpl(configuration, context, cd)
1196                 .label(configuration.getClassName(cd)));
1197     }
1198 
1199     /**
1200      * Add the class link.
1201      *
1202      * @param context the id of the context where the link will be added
1203      * @param cd the class doc to link to
1204      * @param contentTree the content tree to which the link will be added
1205      */
1206     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) {
1207         addPreQualifiedClassLink(context, cd, false, contentTree);
1208     }
1209 
1210     /**
1211      * Retrieve the class link with the package portion of the label in
1212      * plain text.  If the qualifier is excluded, it will not be included in the
1213      * link label.
1214      *
1215      * @param cd the class to link to.
1216      * @param isStrong true if the link should be strong.
1217      * @return the link with the package portion of the label in plain text.
1218      */
1219     public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context,
1220             ClassDoc cd, boolean isStrong) {
1221         ContentBuilder classlink = new ContentBuilder();
1222         PackageDoc pd = cd.containingPackage();
1223         if (pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
1224             classlink.addContent(getPkgName(cd));
1225         }
1226         classlink.addContent(getLink(new LinkInfoImpl(configuration,
1227                 context, cd).label(cd.name()).strong(isStrong)));
1228         return classlink;
1229     }
1230 
1231     /**
1232      * Add the class link with the package portion of the label in
1233      * plain text. If the qualifier is excluded, it will not be included in the
1234      * link label.
1235      *
1236      * @param context the id of the context where the link will be added
1237      * @param cd the class to link to
1238      * @param isStrong true if the link should be strong
1239      * @param contentTree the content tree to which the link with be added
1240      */
1241     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context,
1242             ClassDoc cd, boolean isStrong, Content contentTree) {
1243         PackageDoc pd = cd.containingPackage();
1244         if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
1245             contentTree.addContent(getPkgName(cd));
1246         }
1247         contentTree.addContent(getLink(new LinkInfoImpl(configuration,
1248                 context, cd).label(cd.name()).strong(isStrong)));



1249     }
1250 
1251     /**
1252      * Add the class link, with only class name as the strong link and prefixing
1253      * plain package name.
1254      *
1255      * @param context the id of the context where the link will be added
1256      * @param cd the class to link to
1257      * @param contentTree the content tree to which the link with be added
1258      */
1259     public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) {
1260         addPreQualifiedClassLink(context, cd, true, contentTree);
1261     }
1262 
1263     /**
1264      * Get the link for the given member.
1265      *
1266      * @param context the id of the context where the link will be added
1267      * @param doc the member being linked to
1268      * @param label the label for the link
1269      * @return a content tree for the doc link
1270      */
1271     public Content getDocLink(LinkInfoImpl.Kind context, MemberDoc doc, String label) {
1272         return getDocLink(context, doc.containingClass(), doc,
1273                 new StringContent(label));
1274     }
1275 
1276     /**
1277      * Return the link for the given member.
1278      *
1279      * @param context the id of the context where the link will be printed.
1280      * @param doc the member being linked to.
1281      * @param label the label for the link.
1282      * @param strong true if the link should be strong.
1283      * @return the link for the given member.
1284      */
1285     public Content getDocLink(LinkInfoImpl.Kind context, MemberDoc doc, String label,
1286             boolean strong) {
1287         return getDocLink(context, doc.containingClass(), doc, label, strong);
1288     }
1289 
1290     /**
1291      * Return the link for the given member.
1292      *
1293      * @param context the id of the context where the link will be printed.
1294      * @param classDoc the classDoc that we should link to.  This is not
1295      *                 necessarily equal to doc.containingClass().  We may be
1296      *                 inheriting comments.
1297      * @param doc the member being linked to.
1298      * @param label the label for the link.
1299      * @param strong true if the link should be strong.
1300      * @return the link for the given member.
1301      */
1302     public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc,
1303             String label, boolean strong) {
1304         return getDocLink(context, classDoc, doc, label, strong, false);
1305     }
1306     public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc,

1307             Content label, boolean strong) {
1308         return getDocLink(context, classDoc, doc, label, strong, false);
1309     }
1310 
1311    /**
1312      * Return the link for the given member.
1313      *
1314      * @param context the id of the context where the link will be printed.
1315      * @param classDoc the classDoc that we should link to.  This is not
1316      *                 necessarily equal to doc.containingClass().  We may be
1317      *                 inheriting comments.
1318      * @param doc the member being linked to.
1319      * @param label the label for the link.
1320      * @param strong true if the link should be strong.
1321      * @param isProperty true if the doc parameter is a JavaFX property.
1322      * @return the link for the given member.
1323      */
1324     public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc,
1325             String label, boolean strong, boolean isProperty) {
1326         return getDocLink(context, classDoc, doc, new StringContent(check(label)), strong, isProperty);
1327     }
1328 
1329     String check(String s) {
1330         if (s.matches(".*[&<>].*"))throw new IllegalArgumentException(s);


1331         return s;
1332     }
1333 
1334     public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc,
1335             Content label, boolean strong, boolean isProperty) {
1336         if (! (doc.isIncluded() ||
1337             utils.isLinkable(classDoc, configuration))) {
1338             return label;
1339         } else if (doc instanceof ExecutableMemberDoc) {
1340             ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
1341             return getLink(new LinkInfoImpl(configuration, context, classDoc)
1342                 .label(label).where(getName(getAnchor(emd, isProperty))).strong(strong));
1343         } else if (doc instanceof MemberDoc) {
1344             return getLink(new LinkInfoImpl(configuration, context, classDoc)
1345                 .label(label).where(getName(doc.name())).strong(strong));




1346         } else {
1347             return label;
1348         }
1349     }
1350 
1351     /**
1352      * Return the link for the given member.
1353      *
1354      * @param context the id of the context where the link will be added
1355      * @param classDoc the classDoc that we should link to.  This is not
1356      *                 necessarily equal to doc.containingClass().  We may be
1357      *                 inheriting comments
1358      * @param doc the member being linked to
1359      * @param label the label for the link
1360      * @return the link for the given member
1361      */
1362     public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc,
1363             Content label) {
1364         if (! (doc.isIncluded() ||
1365             utils.isLinkable(classDoc, configuration))) {
1366             return label;
1367         } else if (doc instanceof ExecutableMemberDoc) {
1368             ExecutableMemberDoc emd = (ExecutableMemberDoc) doc;
1369             return getLink(new LinkInfoImpl(configuration, context, classDoc)
1370                 .label(label).where(getName(getAnchor(emd))));
1371         } else if (doc instanceof MemberDoc) {
1372             return getLink(new LinkInfoImpl(configuration, context, classDoc)
1373                 .label(label).where(getName(doc.name())));

1374         } else {
1375             return label;
1376         }
1377     }
1378 
1379     public String getAnchor(ExecutableMemberDoc emd) {
1380         return getAnchor(emd, false);
1381     }
1382 
1383     public String getAnchor(ExecutableMemberDoc emd, boolean isProperty) {
1384         if (isProperty) {
1385             return emd.name();
1386         }
1387         StringBuilder signature = new StringBuilder(emd.signature());
1388         StringBuilder signatureParsed = new StringBuilder();
1389         int counter = 0;
1390         for (int i = 0; i < signature.length(); i++) {
1391             char c = signature.charAt(i);
1392             if (c == '<') {
1393                 counter++;
1394             } else if (c == '>') {
1395                 counter--;
1396             } else if (counter == 0) {
1397                 signatureParsed.append(c);
1398             }
1399         }
1400         return emd.name() + signatureParsed.toString();
1401     }
1402 
1403     public Content seeTagToContent(SeeTag see) {
1404         String tagName = see.name();
1405         if (! (tagName.startsWith("@link") || tagName.equals("@see"))) {

1406             return new ContentBuilder();
1407         }
1408 
1409         String seetext = replaceDocRootDir(utils.normalizeNewlines(see.text()));
1410 
1411         //Check if @see is an href or "string"

1412         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
1413             return new RawHtml(seetext);
1414         }


1415 
1416         boolean plain = tagName.equalsIgnoreCase("@linkplain");
1417         Content label = plainOrCode(plain, new RawHtml(see.label()));
1418 
1419         //The text from the @see tag.  We will output this text when a label is not specified.
1420         Content text = plainOrCode(plain, new RawHtml(seetext));
1421 
1422         ClassDoc refClass = see.referencedClass();
1423         String refClassName = see.referencedClassName();
1424         MemberDoc refMem = see.referencedMember();
1425         String refMemName = see.referencedMemberName();
1426 



1427         if (refClass == null) {
1428             //@see is not referencing an included class
1429             PackageDoc refPackage = see.referencedPackage();
1430             if (refPackage != null && refPackage.isIncluded()) {
1431                 //@see is referencing an included package
1432                 if (label.isEmpty())
1433                     label = plainOrCode(plain, new StringContent(refPackage.name()));

1434                 return getPackageLink(refPackage, label);
1435             } else {
1436                 //@see is not referencing an included class or package.  Check for cross links.
1437                 Content classCrossLink;
1438                 DocLink packageCrossLink = getCrossPackageLink(refClassName);
1439                 if (packageCrossLink != null) {
1440                     //Package cross link found
1441                     return getHyperLink(packageCrossLink,
1442                         (label.isEmpty() ? text : label));
1443                 } else if ((classCrossLink = getCrossClassLink(refClassName,
1444                         refMemName, label, false, "", !plain)) != null) {
1445                     //Class cross link found (possibly to a member in the class)
1446                     return classCrossLink;
1447                 } else {
1448                     //No cross link found so print warning
1449                     configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found",
1450                             tagName, seetext);


1451                     return (label.isEmpty() ? text: label);
1452                 }
1453             }
1454         } else if (refMemName == null) {
1455             // Must be a class reference since refClass is not null and refMemName is null.
1456             if (label.isEmpty()) {
1457                 label = plainOrCode(plain, new StringContent(refClass.name()));










1458             }


1459             return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass)
1460                     .label(label));
1461         } else if (refMem == null) {
1462             // Must be a member reference since refClass is not null and refMemName is not null.
1463             // However, refMem is null, so this referenced member does not exist.
1464             return (label.isEmpty() ? text: label);
1465         } else {
1466             // Must be a member reference since refClass is not null and refMemName is not null.
1467             // refMem is not null, so this @see tag must be referencing a valid member.
1468             ClassDoc containing = refMem.containingClass();
1469             if (see.text().trim().startsWith("#") &&
1470                 ! (containing.isPublic() ||
1471                 utils.isLinkable(containing, configuration))) {
1472                 // Since the link is relative and the holder is not even being
1473                 // documented, this must be an inherited link.  Redirect it.
1474                 // The current class either overrides the referenced member or
1475                 // inherits it automatically.
1476                 if (this instanceof ClassWriterImpl) {
1477                     containing = ((ClassWriterImpl) this).getClassDoc();
1478                 } else if (!containing.isPublic()){
1479                     configuration.getDocletSpecificMsg().warning(
1480                         see.position(), "doclet.see.class_or_package_not_accessible",
1481                         tagName, containing.qualifiedName());
1482                 } else {
1483                     configuration.getDocletSpecificMsg().warning(
1484                         see.position(), "doclet.see.class_or_package_not_found",
1485                         tagName, seetext);
1486                 }
1487             }
1488             if (configuration.currentcd != containing) {
1489                 refMemName = (refMem instanceof ConstructorDoc) ?
1490                         refMemName : containing.name() + "." + refMemName;

1491             }
1492             if (refMem instanceof ExecutableMemberDoc) {
1493                 if (refMemName.indexOf('(') < 0) {
1494                     refMemName += ((ExecutableMemberDoc)refMem).signature();
1495                 }
1496             }
1497 
1498             text = plainOrCode(plain, new StringContent(refMemName));
1499 
1500             return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing,
1501                 refMem, (label.isEmpty() ? text: label), false);
1502         }
1503     }
1504 
1505     private Content plainOrCode(boolean plain, Content body) {
1506         return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
1507     }
1508 
1509     /**
1510      * Add the inline comment.
1511      *
1512      * @param doc the doc for which the inline comment will be added
1513      * @param tag the inline tag to be added
1514      * @param htmltree the content tree to which the comment will be added
1515      */
1516     public void addInlineComment(Doc doc, Tag tag, Content htmltree) {
1517         addCommentTags(doc, tag, tag.inlineTags(), false, false, htmltree);


1518     }
1519 
1520     /**
1521      * Add the inline deprecated comment.
1522      *
1523      * @param doc the doc for which the inline deprecated comment will be added
1524      * @param tag the inline tag to be added
1525      * @param htmltree the content tree to which the comment will be added
1526      */
1527     public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
1528         addCommentTags(doc, tag.inlineTags(), true, false, htmltree);

1529     }
1530 
1531     /**
1532      * Adds the summary content.
1533      *
1534      * @param doc the doc for which the summary will be generated
1535      * @param htmltree the documentation tree to which the summary will be added
1536      */
1537     public void addSummaryComment(Doc doc, Content htmltree) {
1538         addSummaryComment(doc, doc.firstSentenceTags(), htmltree);
1539     }
1540 
1541     /**
1542      * Adds the summary content.
1543      *
1544      * @param doc the doc for which the summary will be generated
1545      * @param firstSentenceTags the first sentence tags for the doc
1546      * @param htmltree the documentation tree to which the summary will be added
1547      */
1548     public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) {
1549         addCommentTags(doc, firstSentenceTags, false, true, htmltree);
1550     }
1551 
1552     public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
1553         addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree);


1554     }
1555 
1556     /**
1557      * Adds the inline comment.
1558      *
1559      * @param doc the doc for which the inline comments will be generated
1560      * @param htmltree the documentation tree to which the inline comments will be added
1561      */
1562     public void addInlineComment(Doc doc, Content htmltree) {
1563         addCommentTags(doc, doc.inlineTags(), false, false, htmltree);
1564     }
1565 
1566     /**
1567      * Adds the comment tags.
1568      *
1569      * @param doc the doc for which the comment tags will be generated
1570      * @param tags the first sentence tags for the doc
1571      * @param depr true if it is deprecated
1572      * @param first true if the first sentence tags should be added
1573      * @param htmltree the documentation tree to which the comment tags will be added
1574      */
1575     private void addCommentTags(Doc doc, Tag[] tags, boolean depr,
1576             boolean first, Content htmltree) {
1577         addCommentTags(doc, null, tags, depr, first, htmltree);
1578     }
1579 
1580     /**
1581      * Adds the comment tags.
1582      *
1583      * @param doc the doc for which the comment tags will be generated
1584      * @param holderTag the block tag context for the inline tags
1585      * @param tags the first sentence tags for the doc
1586      * @param depr true if it is deprecated
1587      * @param first true if the first sentence tags should be added
1588      * @param htmltree the documentation tree to which the comment tags will be added
1589      */
1590     private void addCommentTags(Doc doc, Tag holderTag, Tag[] tags, boolean depr,
1591             boolean first, Content htmltree) {
1592         if(configuration.nocomment){
1593             return;
1594         }
1595         Content div;
1596         Content result = commentTagsToContent(null, doc, tags, first);
1597         if (depr) {
1598             Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result);
1599             div = HtmlTree.DIV(HtmlStyle.block, italic);
1600             htmltree.addContent(div);
1601         }
1602         else {
1603             div = HtmlTree.DIV(HtmlStyle.block, result);
1604             htmltree.addContent(div);
1605         }
1606         if (tags.length == 0) {
1607             htmltree.addContent(getSpace());
1608         }
1609     }
1610 




























1611     /**
1612      * Converts inline tags and text to text strings, expanding the
1613      * inline tags along the way.  Called wherever text can contain
1614      * an inline tag, such as in comments or in free-form text arguments
1615      * to non-inline tags.
1616      *
1617      * @param holderTag    specific tag where comment resides
1618      * @param doc    specific doc where comment resides
1619      * @param tags   array of text tags and inline tags (often alternating)
1620      *               present in the text of interest for this doc
1621      * @param isFirstSentence  true if text is first sentence

1622      */
1623     public Content commentTagsToContent(Tag holderTag, Doc doc, Tag[] tags,
1624             boolean isFirstSentence) {
1625         Content result = new ContentBuilder();
1626         boolean textTagChange = false;






1627         // Array of all possible inline tags for this javadoc run
1628         configuration.tagletManager.checkTags(doc, tags, true);
1629         for (int i = 0; i < tags.length; i++) {
1630             Tag tagelem = tags[i];
1631             String tagName = tagelem.name();
1632             if (tagelem instanceof SeeTag) {
1633                 result.addContent(seeTagToContent((SeeTag) tagelem));
1634             } else if (! tagName.equals("Text")) {
1635                 boolean wasEmpty = result.isEmpty();
1636                 Content output;
1637                 if (configuration.docrootparent.length() > 0
1638                         && tagelem.name().equals("@docRoot")
1639                         && ((tags[i + 1]).text()).startsWith("/..")) {
1640                     // If Xdocrootparent switch ON, set the flag to remove the /.. occurrence after
1641                     // {@docRoot} tag in the very next Text tag.
1642                     textTagChange = true;
1643                     // Replace the occurrence of {@docRoot}/.. with the absolute link.
1644                     output = new StringContent(configuration.docrootparent);


















































1645                 } else {
1646                     output = TagletWriter.getInlineTagOuput(










































































1647                             configuration.tagletManager, holderTag,
1648                             tagelem, getTagletWriterInstance(isFirstSentence));



1649                 }
1650                 if (output != null)






1651                     result.addContent(output);
1652                 if (wasEmpty && isFirstSentence && tagelem.name().equals("@inheritDoc") && !result.isEmpty()) {
1653                     break;
1654                 } else {
1655                     continue;
1656                 }
















1657             } else {
1658                 String text = tagelem.text();
1659                 //If Xdocrootparent switch ON, remove the /.. occurrence after {@docRoot} tag.
1660                 if (textTagChange) {
1661                     text = text.replaceFirst("/..", "");
1662                     textTagChange = false;
1663                 }
1664                 //This is just a regular text tag.  The text may contain html links (<a>)
1665                 //or inline tag {@docRoot}, which will be handled as special cases.
1666                 text = redirectRelativeLinks(tagelem.holder(), text);
1667 
1668                 // Replace @docRoot only if not represented by an instance of DocRootTaglet,
1669                 // that is, only if it was not present in a source file doc comment.
1670                 // This happens when inserted by the doclet (a few lines
1671                 // above in this method).  [It might also happen when passed in on the command
1672                 // line as a text argument to an option (like -header).]
1673                 text = replaceDocRootDir(text);
1674                 if (isFirstSentence) {
1675                     text = removeNonInlineHtmlTags(text);
1676                 }
1677                 text = utils.replaceTabs(configuration, text);



1678                 text = utils.normalizeNewlines(text);































1679                 result.addContent(new RawHtml(text));

1680             }








1681         }







1682         return result;
1683     }
1684 



















1685     /**
1686      * Return true if relative links should not be redirected.
1687      *
1688      * @return Return true if a relative link should not be redirected.
1689      */
1690     private boolean shouldNotRedirectRelativeLinks() {
1691         return  this instanceof AnnotationTypeWriter ||
1692                 this instanceof ClassWriter ||
1693                 this instanceof PackageSummaryWriter;
1694     }
1695 
1696     /**
1697      * Suppose a piece of documentation has a relative link.  When you copy
1698      * that documentation to another place such as the index or class-use page,
1699      * that relative link will no longer work.  We should redirect those links
1700      * so that they will work again.
1701      * <p>
1702      * Here is the algorithm used to fix the link:
1703      * <p>
1704      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
1705      * <p>
1706      * For example, suppose com.sun.javadoc.RootDoc has this link:
1707      * {@literal <a href="package-summary.html">The package Page</a> }
1708      * <p>
1709      * If this link appeared in the index, we would redirect
1710      * the link like this:
1711      *
1712      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
1713      *
1714      * @param doc the Doc object whose documentation is being written.
1715      * @param text the text being written.
1716      *
1717      * @return the text, with all the relative links redirected to work.
1718      */
1719     private String redirectRelativeLinks(Doc doc, String text) {
1720         if (doc == null || shouldNotRedirectRelativeLinks()) {

1721             return text;
1722         }
1723 
1724         DocPath redirectPathFromRoot;
1725         if (doc instanceof ClassDoc) {
1726             redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage());
1727         } else if (doc instanceof MemberDoc) {
1728             redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage());
1729         } else if (doc instanceof PackageDoc) {
1730             redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc);
1731         } else {
1732             return text;
1733         }
1734 
1735         //Redirect all relative links.
1736         int end, begin = StringUtils.indexOfIgnoreCase(text, "<a");
1737         if(begin >= 0){
1738             StringBuilder textBuff = new StringBuilder(text);
1739 
1740             while(begin >=0){
1741                 if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) {
1742                     begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), "<a", begin + 1);
1743                     continue;
1744                 }
1745 
1746                 begin = textBuff.indexOf("=", begin) + 1;
1747                 end = textBuff.indexOf(">", begin +1);
1748                 if(begin == 0){
1749                     //Link has no equal symbol.
1750                     configuration.root.printWarning(
1751                         doc.position(),
1752                         configuration.getText("doclet.malformed_html_link_tag", text));
1753                     break;
1754                 }
1755                 if (end == -1) {
1756                     //Break without warning.  This <a> tag is not necessarily malformed.  The text
1757                     //might be missing '>' character because the href has an inline tag.
1758                     break;
1759                 }
1760 
1761                 String quote = textBuff.substring(begin, end);
1762                 quote = quote.contains("\"") ? "\"" :
1763                         quote.contains("\'") ? "\'" : null;
1764                 if (quote != null) {
1765                     begin = textBuff.indexOf(quote, begin) + 1;
1766                     end = textBuff.indexOf(quote, begin +1);
1767                     if (begin == 0 || end == -1){
1768                         //Link is missing a quote.
1769                         break;
1770                     }



1771                 }
1772                 String relativeLink = textBuff.substring(begin, end);
1773                 String relativeLinkLowerCase = StringUtils.toLowerCase(relativeLink);
1774                 if (!(relativeLinkLowerCase.startsWith("mailto:") ||
1775                         relativeLinkLowerCase.startsWith("http:") ||
1776                         relativeLinkLowerCase.startsWith("https:") ||
1777                         relativeLinkLowerCase.startsWith("file:"))) {
1778                     relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/"
1779                         + redirectPathFromRoot.resolve(relativeLink).getPath();
1780                     textBuff.replace(begin, end, relativeLink);
1781                 }
1782                 begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), "<a", begin + 1);
1783             }
1784             return textBuff.toString();
1785         }
1786         return text;
1787     }
1788 
1789     static final Set<String> blockTags = new HashSet<>();
1790     static {
1791         for (HtmlTag t: HtmlTag.values()) {
1792             if (t.blockType == HtmlTag.BlockType.BLOCK)
1793                 blockTags.add(t.value);
1794         }
1795     }
1796 
1797     public static String removeNonInlineHtmlTags(String text) {
1798         final int len = text.length();
1799 
1800         int startPos = 0;                     // start of text to copy
1801         int lessThanPos = text.indexOf('<');  // position of latest '<'
1802         if (lessThanPos < 0) {
1803             return text;
1804         }
1805 
1806         StringBuilder result = new StringBuilder();
1807     main: while (lessThanPos != -1) {
1808             int currPos = lessThanPos + 1;
1809             if (currPos == len)
1810                 break;
1811             char ch = text.charAt(currPos);
1812             if (ch == '/') {
1813                 if (++currPos == len)
1814                     break;
1815                 ch = text.charAt(currPos);
1816             }
1817             int tagPos = currPos;
1818             while (isHtmlTagLetterOrDigit(ch)) {
1819                 if (++currPos == len)
1820                     break main;
1821                 ch = text.charAt(currPos);
1822             }
1823             if (ch == '>' && blockTags.contains(StringUtils.toLowerCase(text.substring(tagPos, currPos)))) {
1824                 result.append(text, startPos, lessThanPos);
1825                 startPos = currPos + 1;
1826             }
1827             lessThanPos = text.indexOf('<', currPos);
1828         }
1829         result.append(text.substring(startPos));
1830 
1831         return result.toString();
1832     }
1833 
1834     private static boolean isHtmlTagLetterOrDigit(char ch) {
1835         return ('a' <= ch && ch <= 'z') ||
1836                 ('A' <= ch && ch <= 'Z') ||
1837                 ('1' <= ch && ch <= '6');
1838     }
1839 
1840     /**
1841      * Add a link to the stylesheet file.
1842      *
1843      * @param head the content tree to which the files will be added
1844      */
1845     public void addStyleSheetProperties(Content head) {
1846         String stylesheetfile = configuration.stylesheetfile;
1847         DocPath stylesheet;
1848         if (stylesheetfile.isEmpty()) {
1849             stylesheet = DocPaths.STYLESHEET;
1850         } else {
1851             DocFile file = DocFile.createFileForInput(configuration, stylesheetfile);
1852             stylesheet = DocPath.create(file.getName());
1853         }
1854         HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
1855                 pathToRoot.resolve(stylesheet).getPath(),
1856                 "Style");
1857         head.addContent(link);
1858         if (configuration.createindex) {
1859             HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css",


1886         }
1887     }
1888 
1889     /**
1890      * Add a link to the JQuery javascript file.
1891      *
1892      * @param head the content tree to which the files will be added
1893      * @param filePath the DocPath of the file that needs to be added
1894      */
1895     private void addJQueryFile(Content head, DocPath filePath) {
1896         HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT(
1897                 pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath());
1898         head.addContent(jqyeryScriptFile);
1899     }
1900 
1901     /**
1902      * According to
1903      * <cite>The Java&trade; Language Specification</cite>,
1904      * all the outer classes and static nested classes are core classes.
1905      */
1906     public boolean isCoreClass(ClassDoc cd) {
1907         return cd.containingClass() == null || cd.isStatic();
1908     }
1909 
1910     /**
1911      * Adds the annotatation types for the given packageDoc.
1912      *
1913      * @param packageDoc the package to write annotations for.
1914      * @param htmltree the documentation tree to which the annotation info will be
1915      *        added
1916      */
1917     public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) {
1918         addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree);
1919     }
1920 
1921     /**
1922      * Add the annotation types of the executable receiver.
1923      *
1924      * @param method the executable to write the receiver annotations for.
1925      * @param descList list of annotation description.
1926      * @param htmltree the documentation tree to which the annotation info will be
1927      *        added
1928      */
1929     public void addReceiverAnnotationInfo(ExecutableMemberDoc method, AnnotationDesc[] descList,
1930             Content htmltree) {
1931         addAnnotationInfo(0, method, descList, false, htmltree);
1932     }
1933 











1934     /**
1935      * Adds the annotatation types for the given doc.
1936      *
1937      * @param doc the package to write annotations for
1938      * @param htmltree the content tree to which the annotation types will be added
1939      */
1940     public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) {
1941         addAnnotationInfo(doc, doc.annotations(), htmltree);
1942     }
1943 
1944     /**
1945      * Add the annotatation types for the given doc and parameter.
1946      *
1947      * @param indent the number of spaces to indent the parameters.
1948      * @param doc the doc to write annotations for.
1949      * @param param the parameter to write annotations for.
1950      * @param tree the content tree to which the annotation types will be added
1951      */
1952     public boolean addAnnotationInfo(int indent, Doc doc, Parameter param,
1953             Content tree) {
1954         return addAnnotationInfo(indent, doc, param.annotations(), false, tree);
1955     }
1956 
1957     /**
1958      * Adds the annotatation types for the given doc.
1959      *
1960      * @param doc the doc to write annotations for.
1961      * @param descList the array of {@link AnnotationDesc}.
1962      * @param htmltree the documentation tree to which the annotation info will be
1963      *        added
1964      */
1965     private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList,
1966             Content htmltree) {
1967         addAnnotationInfo(0, doc, descList, true, htmltree);
1968     }
1969 
1970     /**
1971      * Adds the annotation types for the given doc.
1972      *
1973      * @param indent the number of extra spaces to indent the annotations.
1974      * @param doc the doc to write annotations for.
1975      * @param descList the array of {@link AnnotationDesc}.
1976      * @param htmltree the documentation tree to which the annotation info will be
1977      *        added
1978      */
1979     private boolean addAnnotationInfo(int indent, Doc doc,
1980             AnnotationDesc[] descList, boolean lineBreak, Content htmltree) {
1981         List<Content> annotations = getAnnotations(indent, descList, lineBreak);
1982         String sep ="";
1983         if (annotations.isEmpty()) {
1984             return false;
1985         }
1986         for (Content annotation: annotations) {
1987             htmltree.addContent(sep);
1988             htmltree.addContent(annotation);
1989             if (!lineBreak) {
1990                 sep = " ";
1991             }
1992         }
1993         return true;
1994     }
1995 
1996    /**
1997      * Return the string representations of the annotation types for
1998      * the given doc.
1999      *
2000      * @param indent the number of extra spaces to indent the annotations.
2001      * @param descList the array of {@link AnnotationDesc}.
2002      * @param linkBreak if true, add new line between each member value.
2003      * @return an array of strings representing the annotations being
2004      *         documented.
2005      */
2006     private List<Content> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) {
2007         return getAnnotations(indent, descList, linkBreak, true);
2008     }
2009 






2010     /**
2011      * Return the string representations of the annotation types for
2012      * the given doc.
2013      *
2014      * A {@code null} {@code elementType} indicates that all the
2015      * annotations should be returned without any filtering.
2016      *
2017      * @param indent the number of extra spaces to indent the annotations.
2018      * @param descList the array of {@link AnnotationDesc}.
2019      * @param linkBreak if true, add new line between each member value.
2020      * @param elementType the type of targeted element (used for filtering
2021      *        type annotations from declaration annotations)
2022      * @return an array of strings representing the annotations being
2023      *         documented.
2024      */
2025     public List<Content> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak,
2026             boolean isJava5DeclarationLocation) {
2027         List<Content> results = new ArrayList<>();
2028         ContentBuilder annotation;
2029         for (AnnotationDesc aDesc : descList) {
2030             AnnotationTypeDoc annotationDoc = aDesc.annotationType();
2031             // If an annotation is not documented, do not add it to the list. If
2032             // the annotation is of a repeatable type, and if it is not documented
2033             // and also if its container annotation is not documented, do not add it
2034             // to the list. If an annotation of a repeatable type is not documented
2035             // but its container is documented, it will be added to the list.
2036             if (!utils.isDocumentedAnnotation(annotationDoc) &&
2037                 (!isAnnotationDocumented && !isContainerDocumented)) {
2038                 continue;
2039             }
2040             /* TODO: check logic here to correctly handle declaration
2041              * and type annotations.
2042             if  (util.isDeclarationAnnotation(annotationDoc, isJava5DeclarationLocation)) {
2043                 continue;
2044             }*/
2045             annotation = new ContentBuilder();
2046             isAnnotationDocumented = false;
2047             LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2048                                                      LinkInfoImpl.Kind.ANNOTATION, annotationDoc);
2049             AnnotationDesc.ElementValuePair[] pairs = aDesc.elementValues();
2050             // If the annotation is synthesized, do not print the container.
2051             if (aDesc.isSynthesized()) {
2052                 for (AnnotationDesc.ElementValuePair pair : pairs) {
2053                     AnnotationValue annotationValue = pair.value();
2054                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2055                     if (annotationValue.value() instanceof AnnotationValue[]) {
2056                         AnnotationValue[] annotationArray =
2057                                 (AnnotationValue[]) annotationValue.value();
2058                         annotationTypeValues.addAll(Arrays.asList(annotationArray));
2059                     } else {
2060                         annotationTypeValues.add(annotationValue);
2061                     }








2062                     String sep = "";
2063                     for (AnnotationValue av : annotationTypeValues) {
2064                         annotation.addContent(sep);
2065                         annotation.addContent(annotationValueToContent(av));
2066                         sep = " ";
2067                     }
2068                 }
2069             }
2070             else if (isAnnotationArray(pairs)) {
2071                 // If the container has 1 or more value defined and if the
2072                 // repeatable type annotation is not documented, do not print
2073                 // the container.
2074                 if (pairs.length == 1 && isAnnotationDocumented) {
2075                     AnnotationValue[] annotationArray =
2076                             (AnnotationValue[]) (pairs[0].value()).value();
2077                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2078                     annotationTypeValues.addAll(Arrays.asList(annotationArray));










2079                     String sep = "";
2080                     for (AnnotationValue av : annotationTypeValues) {
2081                         annotation.addContent(sep);
2082                         annotation.addContent(annotationValueToContent(av));
2083                         sep = " ";
2084                     }
2085                 }
2086                 // If the container has 1 or more value defined and if the
2087                 // repeatable type annotation is not documented, print the container.
2088                 else {
2089                     addAnnotations(annotationDoc, linkInfo, annotation, pairs,
2090                                    indent, false);
2091                 }
2092             }
2093             else {
2094                 addAnnotations(annotationDoc, linkInfo, annotation, pairs,
2095                                indent, linkBreak);
2096             }
2097             annotation.addContent(linkBreak ? DocletConstants.NL : "");
2098             results.add(annotation);
2099         }
2100         return results;
2101     }
2102 
2103     /**
2104      * Add annotation to the annotation string.
2105      *
2106      * @param annotationDoc the annotation being documented
2107      * @param linkInfo the information about the link
2108      * @param annotation the annotation string to which the annotation will be added
2109      * @param pairs annotation type element and value pairs
2110      * @param indent the number of extra spaces to indent the annotations.
2111      * @param linkBreak if true, add new line between each member value
2112      */
2113     private void addAnnotations(AnnotationTypeDoc annotationDoc, LinkInfoImpl linkInfo,
2114             ContentBuilder annotation, AnnotationDesc.ElementValuePair[] pairs,
2115             int indent, boolean linkBreak) {
2116         linkInfo.label = new StringContent("@" + annotationDoc.name());
2117         annotation.addContent(getLink(linkInfo));
2118         if (pairs.length > 0) {
2119             annotation.addContent("(");
2120             for (int j = 0; j < pairs.length; j++) {
2121                 if (j > 0) {



2122                     annotation.addContent(",");
2123                     if (linkBreak) {
2124                         annotation.addContent(DocletConstants.NL);
2125                         int spaces = annotationDoc.name().length() + 2;
2126                         for (int k = 0; k < (spaces + indent); k++) {
2127                             annotation.addContent(" ");
2128                         }
2129                     }
2130                 }
2131                 annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2132                         pairs[j].element(), pairs[j].element().name(), false));
2133                 annotation.addContent("=");
2134                 AnnotationValue annotationValue = pairs[j].value();
2135                 List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2136                 if (annotationValue.value() instanceof AnnotationValue[]) {
2137                     AnnotationValue[] annotationArray =
2138                             (AnnotationValue[]) annotationValue.value();
2139                     annotationTypeValues.addAll(Arrays.asList(annotationArray));
2140                 } else {
2141                     annotationTypeValues.add(annotationValue);
2142                 }






2143                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{");
2144                 String sep = "";
2145                 for (AnnotationValue av : annotationTypeValues) {
2146                     annotation.addContent(sep);
2147                     annotation.addContent(annotationValueToContent(av));
2148                     sep = ",";
2149                 }
2150                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}");
2151                 isContainerDocumented = false;
2152             }
2153             annotation.addContent(")");
2154         }
2155     }
2156 
2157     /**
2158      * Check if the annotation contains an array of annotation as a value. This
2159      * check is to verify if a repeatable type annotation is present or not.
2160      *
2161      * @param pairs annotation type element and value pairs
2162      *
2163      * @return true if the annotation contains an array of annotation as a value.
2164      */
2165     private boolean isAnnotationArray(AnnotationDesc.ElementValuePair[] pairs) {
2166         AnnotationValue annotationValue;
2167         for (AnnotationDesc.ElementValuePair pair : pairs) {
2168             annotationValue = pair.value();
2169             if (annotationValue.value() instanceof AnnotationValue[]) {
2170                 AnnotationValue[] annotationArray =
2171                         (AnnotationValue[]) annotationValue.value();
2172                 if (annotationArray.length > 1) {
2173                     if (annotationArray[0].value() instanceof AnnotationDesc) {
2174                         AnnotationTypeDoc annotationDoc =
2175                                 ((AnnotationDesc) annotationArray[0].value()).annotationType();
2176                         isContainerDocumented = true;
2177                         if (utils.isDocumentedAnnotation(annotationDoc)) {





2178                             isAnnotationDocumented = true;
2179                         }
2180                         return true;
2181                     }



2182                 }

2183             }
2184         }
2185         return false;
2186     }
2187 












2188     private Content annotationValueToContent(AnnotationValue annotationValue) {
2189         if (annotationValue.value() instanceof Type) {
2190             Type type = (Type) annotationValue.value();
2191             if (type.asClassDoc() != null) {




2192                 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2193                     LinkInfoImpl.Kind.ANNOTATION, type);
2194                 linkInfo.label = new StringContent((type.asClassDoc().isIncluded() ?
2195                     type.typeName() :
2196                     type.qualifiedTypeName()) + type.dimension() + ".class");

2197                 return getLink(linkInfo);
2198             } else {
2199                 return new StringContent(type.typeName() + type.dimension() + ".class");
2200             }
2201         } else if (annotationValue.value() instanceof AnnotationDesc) {
2202             List<Content> list = getAnnotations(0,
2203                 new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()},
2204                     false);





2205             ContentBuilder buf = new ContentBuilder();
2206             for (Content c: list) {
2207                 buf.addContent(c);
2208             }
2209             return buf;
2210         } else if (annotationValue.value() instanceof MemberDoc) {


2211             return getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2212                 (MemberDoc) annotationValue.value(),
2213                 ((MemberDoc) annotationValue.value()).name(), false);
2214          } else {












2215             return new StringContent(annotationValue.toString());
2216          }

2217     }
2218 
2219     /**
2220      * Return the configuation for this doclet.
2221      *
2222      * @return the configuration for this doclet.
2223      */
2224     public Configuration configuration() {
2225         return configuration;
2226     }
2227 }
   1 /*
   2  * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.javadoc.internal.doclets.formats.html;
  27 
  28 import java.io.*;
  29 import java.text.SimpleDateFormat;
  30 import java.util.*;
  31 import java.util.regex.Matcher;
  32 import java.util.regex.Pattern;
  33 
  34 import javax.lang.model.element.AnnotationMirror;
  35 import javax.lang.model.element.AnnotationValue;
  36 import javax.lang.model.element.Element;
  37 import javax.lang.model.element.ExecutableElement;
  38 import javax.lang.model.element.Name;
  39 import javax.lang.model.element.PackageElement;
  40 import javax.lang.model.element.TypeElement;
  41 import javax.lang.model.element.VariableElement;
  42 import javax.lang.model.type.DeclaredType;
  43 import javax.lang.model.type.TypeMirror;
  44 import javax.lang.model.util.SimpleAnnotationValueVisitor9;
  45 import javax.lang.model.util.SimpleElementVisitor9;
  46 import javax.lang.model.util.SimpleTypeVisitor9;
  47 
  48 import com.sun.source.doctree.AttributeTree;
  49 import com.sun.source.doctree.AttributeTree.ValueKind;
  50 import com.sun.source.doctree.CommentTree;
  51 import com.sun.source.doctree.DocRootTree;
  52 import com.sun.source.doctree.DocTree;
  53 import com.sun.source.doctree.DocTree.Kind;
  54 import com.sun.source.doctree.EndElementTree;
  55 import com.sun.source.doctree.EntityTree;
  56 import com.sun.source.doctree.ErroneousTree;
  57 import com.sun.source.doctree.InheritDocTree;
  58 import com.sun.source.doctree.IndexTree;
  59 import com.sun.source.doctree.LinkTree;
  60 import com.sun.source.doctree.LiteralTree;
  61 import com.sun.source.doctree.SeeTree;
  62 import com.sun.source.doctree.StartElementTree;
  63 import com.sun.source.doctree.TextTree;
  64 import com.sun.source.util.SimpleDocTreeVisitor;
  65 import com.sun.tools.javac.util.DefinedBy;
  66 import com.sun.tools.javac.util.DefinedBy.Api;
  67 
  68 import jdk.javadoc.internal.doclets.formats.html.markup.Comment;
  69 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  70 import jdk.javadoc.internal.doclets.formats.html.markup.DocType;
  71 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
  72 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
  73 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter;
  74 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument;
  75 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  76 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
  77 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  78 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
  79 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
  80 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
  81 import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
  82 import jdk.javadoc.internal.doclets.toolkit.Configuration;
  83 import jdk.javadoc.internal.doclets.toolkit.Content;
  84 import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter;
  85 import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet;
  86 import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter;
  87 import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
  88 import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
  89 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  90 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  91 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
  92 import jdk.javadoc.internal.doclets.toolkit.util.ImplementedMethods;
  93 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  94 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
  95 
  96 import static com.sun.source.doctree.AttributeTree.ValueKind.*;
  97 import static com.sun.source.doctree.DocTree.Kind.*;
  98 import static jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter.CONTENT_TYPE;
  99 import static jdk.javadoc.internal.doclets.toolkit.util.CommentHelper.SPACER;
 100 
 101 
 102 /**
 103  * Class for the Html Format Code Generation specific to JavaDoc.
 104  * This Class contains methods related to the Html Code Generation which
 105  * are used extensively while generating the entire documentation.
 106  *
 107  *  <p><b>This is NOT part of any supported API.
 108  *  If you write code that depends on this, you do so at your own risk.
 109  *  This code and its internal interfaces are subject to change or
 110  *  deletion without notice.</b>
 111  *
 112  * @since 1.2
 113  * @author Atul M Dambalkar
 114  * @author Robert Field
 115  * @author Bhavesh Patel (Modified)
 116  */
 117 public class HtmlDocletWriter extends HtmlDocWriter {
 118 
 119     /**
 120      * Relative path from the file getting generated to the destination
 121      * directory. For example, if the file getting generated is


 254                 "  }" + DocletConstants.NL +
 255                 "  else {" + DocletConstants.NL +
 256                 "    allClassesLink.style.display = \"none\";" + DocletConstants.NL +
 257                 "  }" + DocletConstants.NL +
 258                 "  //-->" + DocletConstants.NL;
 259         Content scriptContent = new RawHtml(scriptCode);
 260         script.addContent(scriptContent);
 261         Content div = HtmlTree.DIV(script);
 262         Content div_noscript = HtmlTree.DIV(getResource("doclet.No_Script_Message"));
 263         Content noScript = HtmlTree.NOSCRIPT(div_noscript);
 264         div.addContent(noScript);
 265         return div;
 266     }
 267 
 268     /**
 269      * Add method information.
 270      *
 271      * @param method the method to be documented
 272      * @param dl the content tree to which the method information will be added
 273      */
 274     private void addMethodInfo(ExecutableElement method, Content dl) {
 275         TypeElement enclosing = utils.getEnclosingTypeElement(method);
 276         List<? extends TypeMirror> intfacs = enclosing.getInterfaces();
 277         ExecutableElement overriddenMethod = utils.overriddenMethod(method);
 278         // Check whether there is any implementation or overridden info to be
 279         // printed. If no overridden or implementation info needs to be
 280         // printed, do not print this section.
 281         if ((!intfacs.isEmpty()
 282                 && new ImplementedMethods(method, this.configuration).build().isEmpty() == false)
 283                 || overriddenMethod != null) {
 284             MethodWriterImpl.addImplementsInfo(this, method, dl);
 285             if (overriddenMethod != null) {
 286                 MethodWriterImpl.addOverridden(this,
 287                         utils.overriddenType(method),
 288                         overriddenMethod,
 289                         dl);
 290             }
 291         }
 292     }
 293 
 294     /**
 295      * Adds the tags information.
 296      *
 297      * @param e the Element for which the tags will be generated
 298      * @param htmltree the documentation tree to which the tags will be added
 299      */
 300     protected void addTagsInfo(Element e, Content htmltree) {
 301         if (configuration.nocomment) {
 302             return;
 303         }
 304         Content dl = new HtmlTree(HtmlTag.DL);
 305         if (utils.isExecutableElement(e) && !utils.isConstructor(e)) {
 306             addMethodInfo((ExecutableElement)e, dl);
 307         }
 308         Content output = new ContentBuilder();
 309         TagletWriter.genTagOutput(configuration.tagletManager, e,
 310             configuration.tagletManager.getCustomTaglets(e),
 311                 getTagletWriterInstance(false), output);
 312         dl.addContent(output);
 313         htmltree.addContent(dl);
 314     }
 315 
 316     /**
 317      * Check whether there are any tags for Serialization Overview
 318      * section to be printed.
 319      *
 320      * @param field the VariableElement object to check for tags.
 321      * @return true if there are tags to be printed else return false.
 322      */
 323     protected boolean hasSerializationOverviewTags(VariableElement field) {
 324         Content output = new ContentBuilder();
 325         TagletWriter.genTagOutput(configuration.tagletManager, field,
 326                 configuration.tagletManager.getCustomTaglets(field),
 327                 getTagletWriterInstance(false), output);
 328         return !output.isEmpty();
 329     }
 330 
 331     /**
 332      * Returns a TagletWriter that knows how to write HTML.
 333      *
 334      * @return a TagletWriter that knows how to write HTML.
 335      */
 336     public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
 337         return new TagletWriterImpl(this, isFirstSentence);
 338     }
 339 
 340     /**
 341      * Get Package link, with target frame.
 342      *
 343      * @param pkg The link will be to the "package-summary.html" page for this package
 344      * @param target name of the target frame
 345      * @param label tag for the link
 346      * @return a content for the target package link
 347      */
 348     public Content getTargetPackageLink(PackageElement pkg, String target,
 349             Content label) {
 350         return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), label, "", target);
 351     }
 352 














 353 
 354     public void addClassesSummary(SortedSet<TypeElement> classes, String label,
 355             String tableSummary, List<String> tableHeader, Content summaryContentTree) {
 356         if (!classes.isEmpty()) {









































 357             Content caption = getTableCaption(new RawHtml(label));
 358             Content table = (configuration.isOutputHtml5())
 359                     ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption)
 360                     : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption);
 361             table.addContent(getSummaryTableHeader(tableHeader, "col"));
 362             Content tbody = new HtmlTree(HtmlTag.TBODY);
 363             boolean altColor = true;
 364             for (TypeElement te : classes) {
 365                 if (!utils.isCoreClass(te) ||
 366                     !configuration.isGeneratedDoc(te)) {
 367                     continue;
 368                 }




 369                 Content classContent = getLink(new LinkInfoImpl(
 370                         configuration, LinkInfoImpl.Kind.PACKAGE, te));
 371                 Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent);
 372                 HtmlTree tr = HtmlTree.TR(tdClass);
 373                 tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
 374                 altColor = !altColor;


 375                 HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD);
 376                 tdClassDescription.addStyle(HtmlStyle.colLast);
 377                 if (utils.isDeprecated(te)) {
 378                     tdClassDescription.addContent(deprecatedLabel);
 379                     List<? extends DocTree> tags = utils.getDeprecatedTrees(te);
 380                     if (!tags.isEmpty()) {
 381                         addSummaryDeprecatedComment(te, tags.get(0), tdClassDescription);
 382                     }
 383                 } else {
 384                     addSummaryComment(te, tdClassDescription);
 385                 }


 386                 tr.addContent(tdClassDescription);
 387                 tbody.addContent(tr);
 388             }
 389             table.addContent(tbody);
 390             summaryContentTree.addContent(table);
 391         }
 392     }
 393 
 394     /**
 395      * Generates the HTML document tree and prints it out.
 396      *
 397      * @param metakeywords Array of String keywords for META tag. Each element
 398      *                     of the array is assigned to a separate META tag.
 399      *                     Pass in null for no array
 400      * @param includeScript true if printing windowtitle script
 401      *                      false for files that appear in the left-hand frames
 402      * @param body the body htmltree to be included in the document
 403      */
 404     public void printHtmlDocument(List<String> metakeywords, boolean includeScript,
 405             Content body) throws IOException {
 406         Content htmlDocType = configuration.isOutputHtml5()
 407                 ? DocType.HTML5
 408                 : DocType.TRANSITIONAL;
 409         Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
 410         Content head = new HtmlTree(HtmlTag.HEAD);
 411         head.addContent(getGeneratedBy(!configuration.notimestamp));
 412         head.addContent(getTitle());
 413         Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE,
 414                 (configuration.charset.length() > 0) ?
 415                         configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET);
 416         head.addContent(meta);
 417         if (!configuration.notimestamp) {
 418             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
 419             meta = HtmlTree.META(configuration.isOutputHtml5()
 420                     ? "dc.created"
 421                     : "date", dateFormat.format(new Date()));
 422             head.addContent(meta);
 423         }
 424         if (metakeywords != null) {


 526                 navDiv.addContent(a);
 527                 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink(
 528                     getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks,
 529                     skipNavLinks.toString(), ""));
 530                 navDiv.addContent(skipLinkContent);
 531             }
 532             if (header) {
 533                 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW));
 534             } else {
 535                 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW));
 536             }
 537             HtmlTree navList = new HtmlTree(HtmlTag.UL);
 538             navList.addStyle(HtmlStyle.navList);
 539             navList.addAttr(HtmlAttr.TITLE,
 540                             configuration.getText("doclet.Navigation"));
 541             if (configuration.createoverview) {
 542                 navList.addContent(getNavLinkContents());
 543             }
 544             if (configuration.packages.size() == 1) {
 545                 navList.addContent(getNavLinkPackage(configuration.packages.first()));
 546             } else if (!configuration.packages.isEmpty()) {
 547                 navList.addContent(getNavLinkPackage());
 548             }
 549             navList.addContent(getNavLinkClass());
 550             if(configuration.classuse) {
 551                 navList.addContent(getNavLinkClassUse());
 552             }
 553             if(configuration.createtree) {
 554                 navList.addContent(getNavLinkTree());
 555             }
 556             if(!(configuration.nodeprecated ||
 557                      configuration.nodeprecatedlist)) {
 558                 navList.addContent(getNavLinkDeprecated());
 559             }
 560             if(configuration.createindex) {
 561                 navList.addContent(getNavLinkIndex());
 562             }
 563             if (!configuration.nohelp) {
 564                 navList.addContent(getNavLinkHelp());
 565             }
 566             navDiv.addContent(navList);
 567             Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header));
 568             navDiv.addContent(aboutDiv);
 569             if (header) {
 570                 fixedNavDiv.addContent(navDiv);
 571             } else {
 572                 tree.addContent(navDiv);
 573             }
 574             Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious());
 575             ulNav.addContent(getNavLinkNext());
 576             Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
 577             Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists());
 578             ulFrames.addContent(getNavHideLists(filename));
 579             subDiv.addContent(ulFrames);
 580             HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
 581             ulAllClasses.addAttr(HtmlAttr.ID, allClassesId);
 582             subDiv.addContent(ulAllClasses);
 583             if (header && configuration.createindex) {
 584                 HtmlTree inputText = HtmlTree.INPUT("text", "search");
 585                 HtmlTree inputReset = HtmlTree.INPUT("reset", "reset");
 586                 Content searchTxt = configuration.getResource("doclet.search");
 587                 searchTxt.addContent(getSpace());
 588                 HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt));
 589                 liInput.addContent(inputText);
 590                 liInput.addContent(inputReset);
 591                 HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput);
 592                 subDiv.addContent(ulSearch);
 593             }
 594             subDiv.addContent(getAllClassesLinkScript(allClassesId));
 595             addSummaryDetailLinks(subDiv);
 596             if (header) {
 597                 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP));
 598                 fixedNavDiv.addContent(subDiv);
 599                 fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR);
 600                 tree.addContent(fixedNavDiv);
 601             } else {
 602                 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
 603                 tree.addContent(subDiv);
 604                 tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR);
 605             }
 606             if (configuration.allowTag(HtmlTag.NAV)) {
 607                 htmlTree.addContent(tree);
 608             }
 609         }
 610     }
 611 
 612     /**
 613      * Get the word "NEXT" to indicate that no link is available.  Override
 614      * this method to customize next link.


 636     }
 637 
 638     /**
 639      * Get link to the "overview-summary.html" page.
 640      *
 641      * @return a content tree for the link
 642      */
 643     protected Content getNavLinkContents() {
 644         Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY),
 645                 overviewLabel, "", "");
 646         Content li = HtmlTree.LI(linkContent);
 647         return li;
 648     }
 649 
 650     /**
 651      * Get link to the "package-summary.html" page for the package passed.
 652      *
 653      * @param pkg Package to which link will be generated
 654      * @return a content tree for the link
 655      */
 656     protected Content getNavLinkPackage(PackageElement pkg) {
 657         Content linkContent = getPackageLink(pkg, packageLabel);

 658         Content li = HtmlTree.LI(linkContent);
 659         return li;
 660     }
 661 
 662     /**
 663      * Get the word "Package" , to indicate that link is not available here.
 664      *
 665      * @return a content tree for the link
 666      */
 667     protected Content getNavLinkPackage() {
 668         Content li = HtmlTree.LI(packageLabel);
 669         return li;
 670     }
 671 
 672     /**
 673      * Get the word "Use", to indicate that link is not available.
 674      *
 675      * @return a content tree for the link
 676      */
 677     protected Content getNavLinkClassUse() {


 738      * Get "NO FRAMES" link, to switch to the non-frame version of the output.
 739      *
 740      * @param link File to be linked
 741      * @return a content tree for the link
 742      */
 743     protected Content getNavHideLists(DocPath link) {
 744         Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top");
 745         Content li = HtmlTree.LI(noFramesContent);
 746         return li;
 747     }
 748 
 749     /**
 750      * Get "Tree" link in the navigation bar. If there is only one package
 751      * specified on the command line, then the "Tree" link will be to the
 752      * only "package-tree.html" file otherwise it will be to the
 753      * "overview-tree.html" file.
 754      *
 755      * @return a content tree for the link
 756      */
 757     protected Content getNavLinkTree() {
 758         List<PackageElement> packages = new ArrayList<>(utils.getSpecifiedPackages());
 759         DocPath docPath = packages.size() == 1 && utils.getSpecifiedClasses().isEmpty()
 760                 ? pathString(packages.get(0), DocPaths.PACKAGE_TREE)
 761                 : pathToRoot.resolve(DocPaths.OVERVIEW_TREE);
 762         return HtmlTree.LI(getHyperLink(docPath, treeLabel, "", ""));




 763     }



 764 
 765     /**
 766      * Get the overview tree link for the main tree.
 767      *
 768      * @param label the label for the link
 769      * @return a content tree for the link
 770      */
 771     protected Content getNavLinkMainTree(String label) {
 772         Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
 773                 new StringContent(label));
 774         Content li = HtmlTree.LI(mainTreeContent);
 775         return li;
 776     }
 777 
 778     /**
 779      * Get the word "Class", to indicate that class link is not available.
 780      *
 781      * @return a content tree for the link
 782      */
 783     protected Content getNavLinkClass() {


 839         DocPath helpfilenm;
 840         if (helpfile.isEmpty()) {
 841             helpfilenm = DocPaths.HELP_DOC;
 842         } else {
 843             DocFile file = DocFile.createFileForInput(configuration, helpfile);
 844             helpfilenm = DocPath.create(file.getName());
 845         }
 846         Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm),
 847                 helpLabel, "", "");
 848         Content li = HtmlTree.LI(linkContent);
 849         return li;
 850     }
 851 
 852     /**
 853      * Get summary table header.
 854      *
 855      * @param header the header for the table
 856      * @param scope the scope of the headers
 857      * @return a content tree for the header
 858      */
 859     public Content getSummaryTableHeader(List<String> header, String scope) {
 860         Content tr = new HtmlTree(HtmlTag.TR);
 861         final int size = header.size();
 862         Content tableHeader;
 863         if (size == 1) {
 864             tableHeader = new StringContent(header.get(0));
 865             tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader));
 866             return tr;
 867         }
 868         for (int i = 0; i < size; i++) {
 869             tableHeader = new StringContent(header.get(i));
 870             if(i == 0)
 871                 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader));
 872             else if(i == (size - 1))
 873                 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader));
 874             else
 875                 tr.addContent(HtmlTree.TH(scope, tableHeader));
 876         }
 877         return tr;
 878     }
 879 
 880     /**
 881      * Get table caption.
 882      *
 883      * @param rawText the caption for the table which could be raw Html
 884      * @return a content tree for the caption
 885      */
 886     public Content getTableCaption(Content title) {
 887         Content captionSpan = HtmlTree.SPAN(title);
 888         Content space = getSpace();
 889         Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);


 923         return getMarkerAnchor(sectionName.getName() + getName(anchorName), null);
 924     }
 925 
 926     /**
 927      * Get the marker anchor which will be added to the documentation tree.
 928      *
 929      * @param anchorName the anchor name or id attribute
 930      * @param anchorContent the content that should be added to the anchor
 931      * @return a content tree for the marker anchor
 932      */
 933     public Content getMarkerAnchor(String anchorName, Content anchorContent) {
 934         if (anchorContent == null)
 935             anchorContent = new Comment(" ");
 936         Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent);
 937         return markerAnchor;
 938     }
 939 
 940     /**
 941      * Returns a packagename content.
 942      *
 943      * @param packageElement the package to check
 944      * @return package name content
 945      */
 946     public Content getPackageName(PackageElement packageElement) {
 947         return packageElement == null || packageElement.isUnnamed()
 948                 ? defaultPackageLabel
 949                 : getPackageLabel(packageElement.getQualifiedName().toString());
 950     }
 951 
 952     /**
 953      * Returns a package name label.
 954      *
 955      * @param packageName the package name
 956      * @return the package name content
 957      */
 958     public Content getPackageLabel(String packageName) {
 959         return new StringContent(packageName);
 960     }
 961 
 962     /**
 963      * Add package deprecation information to the documentation tree
 964      *
 965      * @param deprPkgs list of deprecated packages
 966      * @param headingKey the caption for the deprecated package table
 967      * @param tableSummary the summary for the deprecated package table
 968      * @param tableHeader table headers for the deprecated package table
 969      * @param contentTree the content tree to which the deprecated package table will be added
 970      */
 971     protected void addPackageDeprecatedAPI(SortedSet<Element> deprPkgs, String headingKey,
 972             String tableSummary, List<String> tableHeader, Content contentTree) {
 973         if (deprPkgs.size() > 0) {
 974             Content caption = getTableCaption(configuration.getResource(headingKey));
 975             Content table = (configuration.isOutputHtml5())
 976                     ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption)
 977                     : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption);
 978             table.addContent(getSummaryTableHeader(tableHeader, "col"));
 979             Content tbody = new HtmlTree(HtmlTag.TBODY);
 980             boolean altColor = true;
 981             for (Element e : deprPkgs) {
 982                 PackageElement pkg = (PackageElement) e;
 983                 HtmlTree td = HtmlTree.TD(HtmlStyle.colOne,
 984                         getPackageLink(pkg, getPackageName(pkg)));
 985                 List<? extends DocTree> tags = utils.getDeprecatedTrees(pkg);
 986                 if (!tags.isEmpty()) {
 987                     addInlineDeprecatedComment(pkg, tags.get(0), td);
 988                 }
 989                 HtmlTree tr = HtmlTree.TR(td);
 990                 tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
 991                 altColor = !altColor;



 992                 tbody.addContent(tr);
 993             }
 994             table.addContent(tbody);
 995             Content li = HtmlTree.LI(HtmlStyle.blockList, table);
 996             Content ul = HtmlTree.UL(HtmlStyle.blockList, li);
 997             contentTree.addContent(ul);
 998         }
 999     }
1000 
1001     /**
1002      * Return the path to the class page for a typeElement.
1003      *
1004      * @param te   TypeElement for which the path is requested.
1005      * @param name Name of the file(doesn't include path).
1006      */
1007     protected DocPath pathString(TypeElement te, DocPath name) {
1008         return pathString(utils.containingPackage(te), name);
1009     }
1010 
1011     /**
1012      * Return path to the given file name in the given package. So if the name
1013      * passed is "Object.html" and the name of the package is "java.lang", and
1014      * if the relative path is "../.." then returned string will be
1015      * "../../java/lang/Object.html"
1016      *
1017      * @param packageElement Package in which the file name is assumed to be.
1018      * @param name File name, to which path string is.
1019      */
1020     protected DocPath pathString(PackageElement packageElement, DocPath name) {
1021         return pathToRoot.resolve(DocPath.forPackage(packageElement).resolve(name));
1022     }
1023 
1024     /**
1025      * Given a package, return the name to be used in HTML anchor tag.
1026      * @param packageElement the package.
1027      * @return the name to be used in HTML anchor tag.
1028      */
1029     public String getPackageAnchorName(PackageElement packageElement) {
1030         return packageElement == null || packageElement.isUnnamed()
1031                 ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName()
1032                 : utils.getPackageName(packageElement);
1033     }
1034 
1035     /**
1036      * Return the link to the given package.
1037      *
1038      * @param packageElement the package to link to.
1039      * @param label the label for the link.
1040      * @return a content tree for the package link.
1041      */
1042     public Content getPackageLink(PackageElement packageElement, String label) {
1043         return getPackageLink(packageElement, new StringContent(label));
1044     }
1045 
1046     public Content getPackageLink(PackageElement packageElement) {
1047         StringContent content =  packageElement.isUnnamed()
1048                 ? new StringContent()
1049                 : new StringContent(utils.getPackageName(packageElement));
1050         return getPackageLink(packageElement, content);
1051     }
1052 
1053     /**
1054      * Return the link to the given package.
1055      *
1056      * @param packageElement the package to link to.
1057      * @param label the label for the link.
1058      * @return a content tree for the package link.
1059      */
1060     public Content getPackageLink(PackageElement packageElement, Content label) {
1061         boolean included = packageElement != null && utils.isIncluded(packageElement);
1062         if (!included) {
1063             for (PackageElement p : configuration.packages) {
1064                 if (p.equals(packageElement)) {
1065                     included = true;
1066                     break;
1067                 }
1068             }
1069         }
1070         if (included || packageElement == null) {
1071             return getHyperLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY),
1072                     label);
1073         } else {
1074             DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement));
1075             if (crossPkgLink != null) {
1076                 return getHyperLink(crossPkgLink, label);
1077             } else {
1078                 return label;
1079             }
1080         }
1081     }
1082 
1083     public Content interfaceName(TypeElement typeElement, boolean qual) {
1084         Content name = new StringContent((qual)
1085                 ? typeElement.getQualifiedName().toString()
1086                 : utils.getSimpleName(typeElement));
1087         return (utils.isInterface(typeElement)) ?  HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name;
1088     }
1089 
1090     /**
1091      * Add the link to the content tree.
1092      *
1093      * @param typeElement program element typeElement for which the link will be added
1094      * @param label label for the link
1095      * @param htmltree the content tree to which the link will be added
1096      */
1097     public void addSrcLink(Element typeElement, Content label, Content htmltree) {
1098         if (typeElement == null) {
1099             return;
1100         }
1101         TypeElement te = utils.getEnclosingTypeElement(typeElement);
1102         if (te == null) {
1103             // must be a typeElement since in has no containing class.
1104             te = (TypeElement) typeElement;
1105         }
1106         DocPath href = pathToRoot
1107                 .resolve(DocPaths.SOURCE_OUTPUT)
1108                 .resolve(DocPath.forClass(utils, te));
1109         Content linkContent = getHyperLink(href
1110                 .fragment(SourceToHTMLConverter.getAnchorName(utils, typeElement)), label, "", "");
1111         htmltree.addContent(linkContent);
1112     }
1113 
1114     /**
1115      * Return the link to the given class.
1116      *
1117      * @param linkInfo the information about the link.
1118      *
1119      * @return the link for the given class.
1120      */
1121     public Content getLink(LinkInfoImpl linkInfo) {
1122         LinkFactoryImpl factory = new LinkFactoryImpl(this);
1123         return factory.getLink(linkInfo);
1124     }
1125 
1126     /**
1127      * Return the type parameters for the given class.
1128      *
1129      * @param linkInfo the information about the link.
1130      * @return the type for the given class.


1145      * be null or empty string if no member is being referenced.
1146      * @param label the label for the external link.
1147      * @param strong true if the link should be strong.
1148      * @param style the style of the link.
1149      * @param code true if the label should be code font.
1150      */
1151     public Content getCrossClassLink(String qualifiedClassName, String refMemName,
1152                                     Content label, boolean strong, String style,
1153                                     boolean code) {
1154         String className = "";
1155         String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
1156         int periodIndex;
1157         while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
1158             className = packageName.substring(periodIndex + 1, packageName.length()) +
1159                 (className.length() > 0 ? "." + className : "");
1160             Content defaultLabel = new StringContent(className);
1161             if (code)
1162                 defaultLabel = HtmlTree.CODE(defaultLabel);
1163             packageName = packageName.substring(0, periodIndex);
1164             if (getCrossPackageLink(packageName) != null) {
1165                 /*
1166                 The package exists in external documentation, so link to the external
1167                 class (assuming that it exists).  This is definitely a limitation of
1168                 the -link option.  There are ways to determine if an external package
1169                 exists, but no way to determine if the external class exists.  We just
1170                 have to assume that it does.
1171                 */
1172                 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
1173                                 className + ".html", refMemName);
1174                 return getHyperLink(link,
1175                     (label == null) || label.isEmpty() ? defaultLabel : label,
1176                     strong, style,
1177                     configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
1178                     "");
1179             }
1180         }
1181         return null;
1182     }
1183 
1184     public boolean isClassLinkable(TypeElement typeElement) {
1185         if (utils.isIncluded(typeElement)) {
1186             return configuration.isGeneratedDoc(typeElement);
1187         }
1188         return configuration.extern.isExternal(typeElement);
1189     }
1190 
1191     public DocLink getCrossPackageLink(String pkgName) {
1192         return configuration.extern.getExternalLink(pkgName, pathToRoot,
1193             DocPaths.PACKAGE_SUMMARY.getPath());
1194     }
1195 
1196     /**
1197      * Get the class link.
1198      *
1199      * @param context the id of the context where the link will be added
1200      * @param element to link to
1201      * @return a content tree for the link
1202      */
1203     public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) {
1204         LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element);
1205         return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element)));
1206     }
1207 
1208     /**
1209      * Add the class link.
1210      *
1211      * @param context the id of the context where the link will be added
1212      * @param typeElement to link to
1213      * @param contentTree the content tree to which the link will be added
1214      */
1215     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1216         addPreQualifiedClassLink(context, typeElement, false, contentTree);
1217     }
1218 
1219     /**
1220      * Retrieve the class link with the package portion of the label in
1221      * plain text.  If the qualifier is excluded, it will not be included in the
1222      * link label.
1223      *
1224      * @param typeElement the class to link to.
1225      * @param isStrong true if the link should be strong.
1226      * @return the link with the package portion of the label in plain text.
1227      */
1228     public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context,
1229             TypeElement typeElement, boolean isStrong) {
1230         ContentBuilder classlink = new ContentBuilder();
1231         PackageElement pkg = utils.containingPackage(typeElement);
1232         if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1233             classlink.addContent(getEnclosingPackageName(typeElement));
1234         }
1235         classlink.addContent(getLink(new LinkInfoImpl(configuration,
1236                 context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong)));
1237         return classlink;
1238     }
1239 
1240     /**
1241      * Add the class link with the package portion of the label in
1242      * plain text. If the qualifier is excluded, it will not be included in the
1243      * link label.
1244      *
1245      * @param context the id of the context where the link will be added
1246      * @param typeElement the class to link to
1247      * @param isStrong true if the link should be strong
1248      * @param contentTree the content tree to which the link with be added
1249      */
1250     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context,
1251             TypeElement typeElement, boolean isStrong, Content contentTree) {
1252         PackageElement pkg = utils.containingPackage(typeElement);
1253         if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1254             contentTree.addContent(getEnclosingPackageName(typeElement));
1255         }
1256         LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement)
1257                 .label(utils.getSimpleName(typeElement))
1258                 .strong(isStrong);
1259         Content link = getLink(linkinfo);
1260         contentTree.addContent(link);
1261     }
1262 
1263     /**
1264      * Add the class link, with only class name as the strong link and prefixing
1265      * plain package name.
1266      *
1267      * @param context the id of the context where the link will be added
1268      * @param typeElement the class to link to
1269      * @param contentTree the content tree to which the link with be added
1270      */
1271     public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1272         addPreQualifiedClassLink(context, typeElement, true, contentTree);
1273     }
1274 
1275     /**
1276      * Get the link for the given member.
1277      *
1278      * @param context the id of the context where the link will be added
1279      * @param element the member being linked to
1280      * @param label the label for the link
1281      * @return a content tree for the element link
1282      */
1283     public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label) {
1284         return getDocLink(context, utils.getEnclosingTypeElement(element), element,
1285                 new StringContent(label));
1286     }
1287 
1288     /**
1289      * Return the link for the given member.
1290      *
1291      * @param context the id of the context where the link will be printed.
1292      * @param element the member being linked to.
1293      * @param label the label for the link.
1294      * @param strong true if the link should be strong.
1295      * @return the link for the given member.
1296      */
1297     public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label,
1298             boolean strong) {
1299         return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong);
1300     }
1301 
1302     /**
1303      * Return the link for the given member.
1304      *
1305      * @param context the id of the context where the link will be printed.
1306      * @param typeElement the typeElement that we should link to.  This is not
1307                  necessarily equal to element.containingClass().  We may be
1308                  inheriting comments.
1309      * @param element the member being linked to.
1310      * @param label the label for the link.
1311      * @param strong true if the link should be strong.
1312      * @return the link for the given member.
1313      */
1314     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1315             String label, boolean strong) {
1316         return getDocLink(context, typeElement, element, label, strong, false);
1317     }
1318 
1319     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1320             Content label, boolean strong) {
1321         return getDocLink(context, typeElement, element, label, strong, false);
1322     }
1323 
1324    /**
1325      * Return the link for the given member.
1326      *
1327      * @param context the id of the context where the link will be printed.
1328      * @param typeElement the typeElement that we should link to.  This is not
1329                  necessarily equal to element.containingClass().  We may be
1330                  inheriting comments.
1331      * @param element the member being linked to.
1332      * @param label the label for the link.
1333      * @param strong true if the link should be strong.
1334      * @param isProperty true if the element parameter is a JavaFX property.
1335      * @return the link for the given member.
1336      */
1337     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1338             String label, boolean strong, boolean isProperty) {
1339         return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty);
1340     }
1341 
1342     String check(String s) {
1343         if (s.matches(".*[&<>].*")) {
1344             throw new IllegalArgumentException(s);
1345         }
1346         return s;
1347     }
1348 
1349     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1350             Content label, boolean strong, boolean isProperty) {
1351         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {

1352             return label;
1353         } else if (utils.isExecutableElement(element)) {
1354             ExecutableElement ee = (ExecutableElement)element;
1355             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1356                 .label(label)
1357                 .where(getName(getAnchor(ee, isProperty)))
1358                 .strong(strong));
1359         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1360             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1361                 .label(label)
1362                 .where(getName(element.getSimpleName().toString()))
1363                 .strong(strong));
1364         } else {
1365             return label;
1366         }
1367     }
1368 
1369     /**
1370      * Return the link for the given member.
1371      *
1372      * @param context the id of the context where the link will be added
1373      * @param typeElement the typeElement that we should link to.  This is not
1374                  necessarily equal to element.containingClass().  We may be
1375                  inheriting comments
1376      * @param element the member being linked to
1377      * @param label the label for the link
1378      * @return the link for the given member
1379      */
1380     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1381             Content label) {
1382         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {

1383             return label;
1384         } else if (utils.isExecutableElement(element)) {
1385             ExecutableElement emd = (ExecutableElement) element;
1386             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1387                 .label(label)
1388                 .where(getName(getAnchor(emd))));
1389         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1390             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1391                 .label(label).where(getName(element.getSimpleName().toString())));
1392         } else {
1393             return label;
1394         }
1395     }
1396 
1397     public String getAnchor(ExecutableElement executableElement) {
1398         return getAnchor(executableElement, false);
1399     }
1400 
1401     public String getAnchor(ExecutableElement executableElement, boolean isProperty) {
1402         if (isProperty) {
1403             return executableElement.getSimpleName().toString();
1404         }
1405         String signature = utils.signature(executableElement);
1406         StringBuilder signatureParsed = new StringBuilder();
1407         int counter = 0;
1408         for (int i = 0; i < signature.length(); i++) {
1409             char c = signature.charAt(i);
1410             if (c == '<') {
1411                 counter++;
1412             } else if (c == '>') {
1413                 counter--;
1414             } else if (counter == 0) {
1415                 signatureParsed.append(c);
1416             }
1417         }
1418         return utils.getSimpleName(executableElement) + signatureParsed.toString();
1419     }
1420 
1421     public Content seeTagToContent(Element element, DocTree see) {
1422 
1423         Kind kind = see.getKind();
1424         if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) {
1425             return new ContentBuilder();
1426         }
1427 
1428         CommentHelper ch = utils.getCommentHelper(element);
1429         String tagName = ch.getTagName(see);
1430         String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see)));
1431         // Check if @see is an href or "string"
1432         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
1433             return new RawHtml(seetext);
1434         }
1435         boolean isLinkPlain = kind == LINK_PLAIN;
1436         Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see)));
1437 



1438         //The text from the @see tag.  We will output this text when a label is not specified.
1439         Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext));
1440 
1441         TypeElement refClass = ch.getReferencedClass(configuration, see);
1442         String refClassName =  ch.getReferencedClassName(configuration, see);
1443         Element refMem =       ch.getReferencedMember(configuration, see);
1444         String refMemName =    ch.getReferencedMemberName(see);
1445 
1446         if (refMemName == null && refMem != null) {
1447             refMemName = refMem.toString();
1448         }
1449         if (refClass == null) {
1450             //@see is not referencing an included class
1451             PackageElement refPackage = ch.getReferencedPackage(configuration, see);
1452             if (refPackage != null && utils.isIncluded(refPackage)) {
1453                 //@see is referencing an included package
1454                 if (label.isEmpty())
1455                     label = plainOrCode(isLinkPlain,
1456                             new StringContent(refPackage.getQualifiedName().toString()));
1457                 return getPackageLink(refPackage, label);
1458             } else {
1459                 // @see is not referencing an included class or package.  Check for cross links.
1460                 Content classCrossLink;
1461                 DocLink packageCrossLink = getCrossPackageLink(refClassName);
1462                 if (packageCrossLink != null) {
1463                     // Package cross link found
1464                     return getHyperLink(packageCrossLink,
1465                         (label.isEmpty() ? text : label));
1466                 } else if ((classCrossLink = getCrossClassLink(refClassName,
1467                         refMemName, label, false, "", !isLinkPlain)) != null) {
1468                     // Class cross link found (possibly to a member in the class)
1469                     return classCrossLink;
1470                 } else {
1471                     // No cross link found so print warning
1472                     configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(see),
1473                             "doclet.see.class_or_package_not_found",
1474                             "@" + tagName,
1475                             seetext);
1476                     return (label.isEmpty() ? text: label);
1477                 }
1478             }
1479         } else if (refMemName == null) {
1480             // Must be a class reference since refClass is not null and refMemName is null.
1481             if (label.isEmpty()) {
1482                 /*
1483                  * it seems to me this is the right thing to do, but it causes comparator failures.
1484                  */
1485                 if (!configuration.backwardCompatibility) {
1486                     StringContent content = utils.isEnclosingPackageIncluded(refClass)
1487                             ? new StringContent(utils.getSimpleName(refClass))
1488                             : new StringContent(utils.getFullyQualifiedName(refClass));
1489                     label = plainOrCode(isLinkPlain, content);
1490                 } else {
1491                     label = plainOrCode(isLinkPlain,
1492                             new StringContent(utils.getSimpleName(refClass)));
1493                 }
1494 
1495             }
1496             return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass)
1497                     .label(label));
1498         } else if (refMem == null) {
1499             // Must be a member reference since refClass is not null and refMemName is not null.
1500             // However, refMem is null, so this referenced member does not exist.
1501             return (label.isEmpty() ? text: label);
1502         } else {
1503             // Must be a member reference since refClass is not null and refMemName is not null.
1504             // refMem is not null, so this @see tag must be referencing a valid member.
1505             TypeElement containing = utils.getEnclosingTypeElement(refMem);
1506             if (ch.getText(see).trim().startsWith("#") &&
1507                 ! (utils.isPublic(containing) || utils.isLinkable(containing))) {

1508                 // Since the link is relative and the holder is not even being
1509                 // documented, this must be an inherited link.  Redirect it.
1510                 // The current class either overrides the referenced member or
1511                 // inherits it automatically.
1512                 if (this instanceof ClassWriterImpl) {
1513                     containing = ((ClassWriterImpl) this).getTypeElement();
1514                 } else if (!utils.isPublic(containing)) {
1515                     configuration.getDocletSpecificMsg().warning(
1516                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible",
1517                         tagName, utils.getFullyQualifiedName(containing));
1518                 } else {
1519                     configuration.getDocletSpecificMsg().warning(
1520                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_found",
1521                         tagName, seetext);
1522                 }
1523             }
1524             if (configuration.currentTypeElement != containing) {
1525                 refMemName = (utils.isConstructor(refMem))
1526                         ? refMemName
1527                         : utils.getSimpleName(containing) + "." + refMemName;
1528             }
1529             if (utils.isExecutableElement(refMem)) {
1530                 if (refMemName.indexOf('(') < 0) {
1531                     refMemName += utils.makeSignature((ExecutableElement)refMem, true);
1532                 }
1533             }
1534 
1535             text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName));
1536 
1537             return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing,
1538                     refMem, (label.isEmpty() ? text: label), false);
1539         }
1540     }
1541 
1542     private Content plainOrCode(boolean plain, Content body) {
1543         return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
1544     }
1545 
1546     /**
1547      * Add the inline comment.
1548      *
1549      * @param element the Element for which the inline comment will be added
1550      * @param tag the inline tag to be added
1551      * @param htmltree the content tree to which the comment will be added
1552      */
1553     public void addInlineComment(Element element, DocTree tag, Content htmltree) {
1554         CommentHelper ch = utils.getCommentHelper(element);
1555         List<? extends DocTree> description = ch.getDescription(configuration, tag);
1556         addCommentTags(element, tag, description, false, false, htmltree);
1557     }
1558 
1559     /**
1560      * Add the inline deprecated comment.
1561      *
1562      * @param e the Element for which the inline deprecated comment will be added
1563      * @param tag the inline tag to be added
1564      * @param htmltree the content tree to which the comment will be added
1565      */
1566     public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) {
1567         CommentHelper ch = utils.getCommentHelper(e);
1568         addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree);
1569     }
1570 
1571     /**
1572      * Adds the summary content.
1573      *
1574      * @param element the Element for which the summary will be generated
1575      * @param htmltree the documentation tree to which the summary will be added
1576      */
1577     public void addSummaryComment(Element element, Content htmltree) {
1578         addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree);
1579     }
1580 
1581     /**
1582      * Adds the summary content.
1583      *
1584      * @param element the Element for which the summary will be generated
1585      * @param firstSentenceTags the first sentence tags for the doc
1586      * @param htmltree the documentation tree to which the summary will be added
1587      */
1588     public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) {
1589         addCommentTags(element, firstSentenceTags, false, true, htmltree);
1590     }
1591 
1592     public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) {
1593         CommentHelper ch = utils.getCommentHelper(element);
1594         List<? extends DocTree> body = ch.getBody(configuration, tag);
1595         addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree);
1596     }
1597 
1598     /**
1599      * Adds the inline comment.
1600      *
1601      * @param element the Element for which the inline comments will be generated
1602      * @param htmltree the documentation tree to which the inline comments will be added
1603      */
1604     public void addInlineComment(Element element, Content htmltree) {
1605         addCommentTags(element, utils.getBody(element), false, false, htmltree);
1606     }
1607 
1608     /**
1609      * Adds the comment tags.
1610      *
1611      * @param element the Element for which the comment tags will be generated
1612      * @param tags the first sentence tags for the doc
1613      * @param depr true if it is deprecated
1614      * @param first true if the first sentence tags should be added
1615      * @param htmltree the documentation tree to which the comment tags will be added
1616      */
1617     private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr,
1618             boolean first, Content htmltree) {
1619         addCommentTags(element, null, tags, depr, first, htmltree);
1620     }
1621 
1622     /**
1623      * Adds the comment tags.
1624      *
1625      * @param element for which the comment tags will be generated
1626      * @param holderTag the block tag context for the inline tags
1627      * @param tags the first sentence tags for the doc
1628      * @param depr true if it is deprecated
1629      * @param first true if the first sentence tags should be added
1630      * @param htmltree the documentation tree to which the comment tags will be added
1631      */
1632     private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr,
1633             boolean first, Content htmltree) {
1634         if(configuration.nocomment){
1635             return;
1636         }
1637         Content div;
1638         Content result = commentTagsToContent(null, element, tags, first);
1639         if (depr) {
1640             Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result);
1641             div = HtmlTree.DIV(HtmlStyle.block, italic);
1642             htmltree.addContent(div);
1643         }
1644         else {
1645             div = HtmlTree.DIV(HtmlStyle.block, result);
1646             htmltree.addContent(div);
1647         }
1648         if (tags.isEmpty()) {
1649             htmltree.addContent(getSpace());
1650         }
1651     }
1652 
1653     boolean ignoreNonInlineTag(DocTree dtree) {
1654         Name name = null;
1655         if (dtree.getKind() == Kind.START_ELEMENT) {
1656             StartElementTree setree = (StartElementTree)dtree;
1657             name = setree.getName();
1658         } else if (dtree.getKind() == Kind.END_ELEMENT) {
1659             EndElementTree eetree = (EndElementTree)dtree;
1660             name = eetree.getName();
1661         }
1662 
1663         if (name != null) {
1664             com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name);
1665             if (htmlTag != null &&
1666                     htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) {
1667                 return true;
1668             }
1669         }
1670         return false;
1671     }
1672 
1673     boolean isAllWhiteSpace(String body) {
1674         for (int i = 0 ; i < body.length(); i++) {
1675             if (!Character.isWhitespace(body.charAt(i)))
1676                 return false;
1677         }
1678         return true;
1679     }
1680 
1681     /**
1682      * Converts inline tags and text to text strings, expanding the
1683      * inline tags along the way.  Called wherever text can contain
1684      * an inline tag, such as in comments or in free-form text arguments
1685      * to non-inline tags.
1686      *
1687      * @param holderTag    specific tag where comment resides
1688      * @param element    specific element where comment resides
1689      * @param tags   array of text tags and inline tags (often alternating)
1690                present in the text of interest for this element
1691      * @param isFirstSentence  true if text is first sentence
1692      * @return a Content object
1693      */
1694     public Content commentTagsToContent(DocTree holderTag, Element element,
1695             List<? extends DocTree> tags, boolean isFirstSentence) {
1696 
1697         final Content result = new ContentBuilder() {
1698             @Override
1699             public void addContent(String text) {
1700                 super.addContent(utils.normalizeNewlines(text));
1701             }
1702         };
1703         CommentHelper ch = utils.getCommentHelper(element);
1704         // Array of all possible inline tags for this javadoc run
1705         configuration.tagletManager.checkTags(utils, element, tags, true);
1706         for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) {
1707             DocTree tag = iterator.next();
1708              // zap block tags
1709             if (isFirstSentence && ignoreNonInlineTag(tag))
1710                 continue;
1711 
1712             if (isFirstSentence && iterator.nextIndex() == tags.size() &&
1713                     (tag.getKind() == TEXT && isAllWhiteSpace(ch.getText(tag))))
1714                 continue;
1715 
1716             boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() {
1717                 // notify the next DocTree handler to take necessary action
1718                 boolean commentRemoved = false;
1719 
1720                 private boolean isLast(DocTree node) {
1721                     return node.equals(tags.get(tags.size() - 1));
1722                 }
1723 
1724                 private boolean isFirst(DocTree node) {
1725                     return node.equals(tags.get(0));
1726                 }
1727 
1728                 private boolean inAnAtag() {
1729                     if (utils.isStartElement(tag)) {
1730                         StartElementTree st = (StartElementTree)tag;
1731                         Name name = st.getName();
1732                         if (name != null) {
1733                             com.sun.tools.doclint.HtmlTag htag =
1734                                     com.sun.tools.doclint.HtmlTag.get(name);
1735                             return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A);
1736                         }
1737                     }
1738                     return false;
1739                 }
1740 
1741                 @Override @DefinedBy(Api.COMPILER_TREE)
1742                 public Boolean visitAttribute(AttributeTree node, Content c) {
1743                     StringBuilder sb = new StringBuilder(SPACER).append(node.getName());
1744                     if (node.getValueKind() == ValueKind.EMPTY) {
1745                         result.addContent(sb.toString());
1746                         return false;
1747                     }
1748                     sb.append("=");
1749                     String quote;
1750                     switch (node.getValueKind()) {
1751                         case DOUBLE:
1752                             quote = "\"";
1753                             break;
1754                         case SINGLE:
1755                             quote = "\'";
1756                             break;
1757                         default:
1758                             quote = "";
1759                             break;
1760                     }
1761                     sb.append(quote);
1762                     result.addContent(sb.toString());
1763                     Content docRootContent = new ContentBuilder();
1764 
1765                     for (DocTree dt : node.getValue()) {
1766                         if (utils.isText(dt) && inAnAtag()) {
1767                             String text = ((TextTree) dt).getBody();
1768                             if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) {
1769                                 result.addContent(configuration.docrootparent);
1770                                 docRootContent = new ContentBuilder();
1771                                 text = textCleanup(text.substring(3), isLast(node));
1772                             } else {
1773                                 if (!docRootContent.isEmpty()) {
1774                                     docRootContent = copyDocRootContent(docRootContent);
1775                                 } else {
1776                                     text = redirectRelativeLinks(element, (TextTree) dt);
1777                                 }
1778                                 text = textCleanup(text, isLast(node));
1779                             }
1780                             result.addContent(text);
1781                         } else {
1782                             docRootContent = copyDocRootContent(docRootContent);
1783                             dt.accept(this, docRootContent);
1784                         }
1785                     }
1786                     copyDocRootContent(docRootContent);
1787                     result.addContent(quote);
1788                     return false;
1789                 }
1790 
1791                 @Override @DefinedBy(Api.COMPILER_TREE)
1792                 public Boolean visitComment(CommentTree node, Content c) {
1793                     if (isFirstSentence && isFirst(node)) {
1794                         commentRemoved = true;
1795                         return this.visit(iterator.next(), c);
1796                     }
1797                     result.addContent(new RawHtml(node.getBody()));
1798                     return false;
1799                 }
1800 
1801                 private Content copyDocRootContent(Content content) {
1802                     if (!content.isEmpty()) {
1803                         result.addContent(content);
1804                         return new ContentBuilder();
1805                     }
1806                     return content;
1807                 }
1808 
1809                 @Override @DefinedBy(Api.COMPILER_TREE)
1810                 public Boolean visitDocRoot(DocRootTree node, Content c) {
1811                     Content docRootContent = TagletWriter.getInlineTagOutput(element,
1812                             configuration.tagletManager,
1813                             holderTag,
1814                             node,
1815                             getTagletWriterInstance(isFirstSentence));
1816                     if (c != null) {
1817                         c.addContent(docRootContent);
1818                     } else {
1819                         result.addContent(docRootContent);
1820                     }
1821                     return false;
1822                 }
1823 
1824                 @Override @DefinedBy(Api.COMPILER_TREE)
1825                 public Boolean visitEndElement(EndElementTree node, Content c) {
1826                     RawHtml rawHtml = new RawHtml("</" + node.getName() + ">");
1827                     result.addContent(rawHtml);
1828                     return false;
1829                 }
1830 
1831                 @Override @DefinedBy(Api.COMPILER_TREE)
1832                 public Boolean visitEntity(EntityTree node, Content c) {
1833                     result.addContent(new RawHtml(node.toString()));
1834                     return false;
1835                 }
1836 
1837                 @Override @DefinedBy(Api.COMPILER_TREE)
1838                 public Boolean visitErroneous(ErroneousTree node, Content c) {
1839                     configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(node),
1840                             "doclet.tag.invalid_usage", node);
1841                     result.addContent(new RawHtml(node.toString()));
1842                     return false;
1843                 }
1844 
1845                 @Override @DefinedBy(Api.COMPILER_TREE)
1846                 public Boolean visitInheritDoc(InheritDocTree node, Content c) {
1847                     Content output = TagletWriter.getInlineTagOutput(element,
1848                             configuration.tagletManager, holderTag,
1849                             tag, getTagletWriterInstance(isFirstSentence));
1850                     result.addContent(output);
1851                     // if we obtained the first sentence successfully, nothing more to do
1852                     return (isFirstSentence && !output.isEmpty());
1853                 }
1854 
1855                 @Override @DefinedBy(Api.COMPILER_TREE)
1856                 public Boolean visitIndex(IndexTree node, Content p) {
1857                     Content output = TagletWriter.getInlineTagOutput(element,
1858                             configuration.tagletManager, holderTag, tag,
1859                             getTagletWriterInstance(isFirstSentence));
1860                     if (output != null) {
1861                         result.addContent(output);




1862                     }
1863                     return false;
1864                 }
1865 
1866                 @Override @DefinedBy(Api.COMPILER_TREE)
1867                 public Boolean visitLink(LinkTree node, Content c) {
1868                     // we need to pass the DocTreeImpl here, so ignore node
1869                     result.addContent(seeTagToContent(element, tag));
1870                     return false;
1871                 }
1872 
1873                 @Override @DefinedBy(Api.COMPILER_TREE)
1874                 public Boolean visitLiteral(LiteralTree node, Content c) {
1875                     String s = node.getBody().toString();
1876                     if (node.getKind() == CODE) {
1877                         result.addContent(HtmlTree.CODE(new StringContent(utils.normalizeNewlines(s))));
1878 
1879                     } else {
1880                         result.addContent(s);




1881                     }
1882                     return false;
1883                 }

1884 
1885                 @Override @DefinedBy(Api.COMPILER_TREE)
1886                 public Boolean visitSee(SeeTree node, Content c) {
1887                     // we need to pass the DocTreeImpl here, so ignore node
1888                     result.addContent(seeTagToContent(element, tag));
1889                     return false;



1890                 }
1891 
1892                 @Override @DefinedBy(Api.COMPILER_TREE)
1893                 public Boolean visitStartElement(StartElementTree node, Content c) {
1894                     String text = "<" + node.getName();
1895                     text = utils.normalizeNewlines(text);
1896                     RawHtml rawHtml = new RawHtml(text);
1897                     result.addContent(rawHtml);
1898 
1899                     for (DocTree dt : node.getAttributes()) {
1900                         dt.accept(this, null);
1901                     }
1902                     result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">"));
1903                     return false;
1904                 }
1905 
1906                 private String textCleanup(String text, boolean isLast) {
1907                     return textCleanup(text, isLast, false);
1908                 }
1909 
1910                 private String textCleanup(String text, boolean isLast, boolean trimLeader) {
1911                     if (trimLeader) {
1912                         text = removeLeadingWhitespace(text);
1913                     }
1914                     if (isFirstSentence && isLast) {
1915                         text = removeTrailingWhitespace(text);
1916                     }
1917                     text = utils.replaceTabs(text);
1918                     text = utils.normalizeNewlines(text);
1919                     return text;
1920                 }
1921 
1922                 @Override @DefinedBy(Api.COMPILER_TREE)
1923                 public Boolean visitText(TextTree node, Content c) {
1924                     String text = node.getBody();
1925                     text = textCleanup(text, isLast(node), commentRemoved);
1926                     commentRemoved = false;
1927                     result.addContent(new RawHtml(text));
1928                     return false;
1929                 }
1930 
1931                 @Override @DefinedBy(Api.COMPILER_TREE)
1932                 protected Boolean defaultAction(DocTree node, Content c) {
1933                     Content output = TagletWriter.getInlineTagOutput(element,
1934                             configuration.tagletManager, holderTag, tag,
1935                             getTagletWriterInstance(isFirstSentence));
1936                     if (output != null) {
1937                         result.addContent(output);
1938                     }
1939                     return false;
1940                 }
1941 
1942             }.visit(tag, null);
1943             if (allDone)
1944                 break;
1945         }
1946         return result;
1947     }
1948 
1949     private String removeTrailingWhitespace(String text) {
1950         char[] buf = text.toCharArray();
1951         for (int i = buf.length - 1; i > 0 ; i--) {
1952             if (!Character.isWhitespace(buf[i]))
1953                 return text.substring(0, i + 1);
1954         }
1955         return text;
1956     }
1957 
1958     private String removeLeadingWhitespace(String text) {
1959         char[] buf = text.toCharArray();
1960         for (int i = 0; i < buf.length; i++) {
1961             if (!Character.isWhitespace(buf[i])) {
1962                 return text.substring(i);
1963             }
1964         }
1965         return text;
1966     }
1967 
1968     /**
1969      * Return true if relative links should not be redirected.
1970      *
1971      * @return Return true if a relative link should not be redirected.
1972      */
1973     private boolean shouldNotRedirectRelativeLinks() {
1974         return  this instanceof AnnotationTypeWriter ||
1975                 this instanceof ClassWriter ||
1976                 this instanceof PackageSummaryWriter;
1977     }
1978 
1979     /**
1980      * Suppose a piece of documentation has a relative link.  When you copy
1981      * that documentation to another place such as the index or class-use page,
1982      * that relative link will no longer work.  We should redirect those links
1983      * so that they will work again.
1984      * <p>
1985      * Here is the algorithm used to fix the link:
1986      * <p>
1987      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
1988      * <p>
1989      * For example, suppose DocletEnvironment has this link:
1990      * {@literal <a href="package-summary.html">The package Page</a> }
1991      * <p>
1992      * If this link appeared in the index, we would redirect
1993      * the link like this:
1994      *
1995      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
1996      *
1997      * @param element the Element object whose documentation is being written.
1998      * @param text the text being written.
1999      *
2000      * @return the text, with all the relative links redirected to work.
2001      */
2002     private String redirectRelativeLinks(Element element, TextTree tt) {
2003         String text = tt.getBody();
2004         if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) {
2005             return text;
2006         }
2007 
2008         DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() {
2009             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2010             public DocPath visitType(TypeElement e, Void p) {
2011                 return DocPath.forPackage(utils.containingPackage(e));





2012             }
2013 
2014             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2015             public DocPath visitPackage(PackageElement e, Void p) {
2016                 return DocPath.forPackage(e);
2017             }
2018 
2019             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2020             public DocPath visitVariable(VariableElement e, Void p) {
2021                 return DocPath.forPackage(utils.containingPackage(e));

2022             }
2023 
2024             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2025             public DocPath visitExecutable(ExecutableElement e, Void p) {
2026                 return DocPath.forPackage(utils.containingPackage(e));





2027             }





2028 
2029             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2030             protected DocPath defaultAction(Element e, Void p) {
2031                 return null;






2032             }
2033         }.visit(element);
2034         if (redirectPathFromRoot == null) {
2035             return text;
2036         }
2037         String lower = Utils.toLowerCase(text);
2038         if (!(lower.startsWith("mailto:")
2039                 || lower.startsWith("http:")
2040                 || lower.startsWith("https:")
2041                 || lower.startsWith("file:"))) {
2042             text = "{@" + (new DocRootTaglet()).getName() + "}/"
2043                     + redirectPathFromRoot.resolve(text).getPath();
2044             text = replaceDocRootDir(text);

2045         }




2046         return text;
2047     }
2048 
2049     static final Set<String> blockTags = new HashSet<>();
2050     static {
2051         for (HtmlTag t: HtmlTag.values()) {
2052             if (t.blockType == HtmlTag.BlockType.BLOCK)
2053                 blockTags.add(t.value);
2054         }
2055     }
2056 











































2057     /**
2058      * Add a link to the stylesheet file.
2059      *
2060      * @param head the content tree to which the files will be added
2061      */
2062     public void addStyleSheetProperties(Content head) {
2063         String stylesheetfile = configuration.stylesheetfile;
2064         DocPath stylesheet;
2065         if (stylesheetfile.isEmpty()) {
2066             stylesheet = DocPaths.STYLESHEET;
2067         } else {
2068             DocFile file = DocFile.createFileForInput(configuration, stylesheetfile);
2069             stylesheet = DocPath.create(file.getName());
2070         }
2071         HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
2072                 pathToRoot.resolve(stylesheet).getPath(),
2073                 "Style");
2074         head.addContent(link);
2075         if (configuration.createindex) {
2076             HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css",


2103         }
2104     }
2105 
2106     /**
2107      * Add a link to the JQuery javascript file.
2108      *
2109      * @param head the content tree to which the files will be added
2110      * @param filePath the DocPath of the file that needs to be added
2111      */
2112     private void addJQueryFile(Content head, DocPath filePath) {
2113         HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT(
2114                 pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath());
2115         head.addContent(jqyeryScriptFile);
2116     }
2117 
2118     /**
2119      * According to
2120      * <cite>The Java&trade; Language Specification</cite>,
2121      * all the outer classes and static nested classes are core classes.
2122      */
2123     public boolean isCoreClass(TypeElement typeElement) {
2124         return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement);
2125     }
2126 
2127     /**
2128      * Adds the annotation types for the given packageElement.
2129      *
2130      * @param packageElement the package to write annotations for.
2131      * @param htmltree the documentation tree to which the annotation info will be
2132      *        added
2133      */
2134     public void addAnnotationInfo(PackageElement packageElement, Content htmltree) {
2135         addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree);
2136     }
2137 
2138     /**
2139      * Add the annotation types of the executable receiver.
2140      *
2141      * @param method the executable to write the receiver annotations for.
2142      * @param descList list of annotation description.
2143      * @param htmltree the documentation tree to which the annotation info will be
2144      *        added
2145      */
2146     public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList,
2147             Content htmltree) {
2148         addAnnotationInfo(0, method, descList, false, htmltree);
2149     }
2150 
2151     /*
2152      * this is a hack to delay dealing with Annotations in the writers, the assumption
2153      * is that all necessary checks have been made to get here.
2154      */
2155     public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror,
2156             List<? extends AnnotationMirror> annotationMirrors, Content htmltree) {
2157         TypeMirror rcvrType = method.getReceiverType();
2158         List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors();
2159         addAnnotationInfo(0, method, annotationMirrors1, false, htmltree);
2160     }
2161 
2162     /**
2163      * Adds the annotatation types for the given element.
2164      *
2165      * @param element the package to write annotations for
2166      * @param htmltree the content tree to which the annotation types will be added
2167      */
2168     public void addAnnotationInfo(Element element, Content htmltree) {
2169         addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree);
2170     }
2171 
2172     /**
2173      * Add the annotatation types for the given element and parameter.
2174      *
2175      * @param indent the number of spaces to indent the parameters.
2176      * @param element the element to write annotations for.
2177      * @param param the parameter to write annotations for.
2178      * @param tree the content tree to which the annotation types will be added
2179      */
2180     public boolean addAnnotationInfo(int indent, Element element, VariableElement param,
2181             Content tree) {
2182         return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree);
2183     }
2184 
2185     /**
2186      * Adds the annotatation types for the given Element.
2187      *
2188      * @param element the element to write annotations for.
2189      * @param descList the array of {@link AnnotationDesc}.
2190      * @param htmltree the documentation tree to which the annotation info will be
2191      *        added
2192      */
2193     private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList,
2194             Content htmltree) {
2195         addAnnotationInfo(0, element, descList, true, htmltree);
2196     }
2197 
2198     /**
2199      * Adds the annotation types for the given element.
2200      *
2201      * @param indent the number of extra spaces to indent the annotations.
2202      * @param element the element to write annotations for.
2203      * @param descList the array of {@link AnnotationDesc}.
2204      * @param htmltree the documentation tree to which the annotation info will be
2205      *        added
2206      */
2207     private boolean addAnnotationInfo(int indent, Element element,
2208             List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) {
2209         List<Content> annotations = getAnnotations(indent, descList, lineBreak);
2210         String sep = "";
2211         if (annotations.isEmpty()) {
2212             return false;
2213         }
2214         for (Content annotation: annotations) {
2215             htmltree.addContent(sep);
2216             htmltree.addContent(annotation);
2217             if (!lineBreak) {
2218                 sep = " ";
2219             }
2220         }
2221         return true;
2222     }
2223 
2224    /**
2225      * Return the string representations of the annotation types for
2226      * the given doc.
2227      *
2228      * @param indent the number of extra spaces to indent the annotations.
2229      * @param descList the array of {@link AnnotationDesc}.
2230      * @param linkBreak if true, add new line between each member value.
2231      * @return an array of strings representing the annotations being
2232      *         documented.
2233      */
2234     private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) {
2235         return getAnnotations(indent, descList, linkBreak, true);
2236     }
2237 
2238     private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) {
2239         List<AnnotationMirror> descList = new ArrayList<>();
2240         descList.add(amirror);
2241         return getAnnotations(indent, descList, linkBreak, true);
2242     }
2243 
2244     /**
2245      * Return the string representations of the annotation types for
2246      * the given doc.
2247      *
2248      * A {@code null} {@code elementType} indicates that all the
2249      * annotations should be returned without any filtering.
2250      *
2251      * @param indent the number of extra spaces to indent the annotations.
2252      * @param descList the array of {@link AnnotationDesc}.
2253      * @param linkBreak if true, add new line between each member value.
2254      * @param isJava5DeclarationLocation

2255      * @return an array of strings representing the annotations being
2256      *         documented.
2257      */
2258     public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList,
2259             boolean linkBreak, boolean isJava5DeclarationLocation) {
2260         List<Content> results = new ArrayList<>();
2261         ContentBuilder annotation;
2262         for (AnnotationMirror aDesc : descList) {
2263             TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement();
2264             // If an annotation is not documented, do not add it to the list. If
2265             // the annotation is of a repeatable type, and if it is not documented
2266             // and also if its container annotation is not documented, do not add it
2267             // to the list. If an annotation of a repeatable type is not documented
2268             // but its container is documented, it will be added to the list.
2269             if (!utils.isDocumentedAnnotation(annotationElement) &&
2270                 (!isAnnotationDocumented && !isContainerDocumented)) {
2271                 continue;
2272             }
2273             /* TODO: check logic here to correctly handle declaration
2274              * and type annotations.
2275             if  (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) {
2276                 continue;
2277             }*/
2278             annotation = new ContentBuilder();
2279             isAnnotationDocumented = false;
2280             LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2281                                                      LinkInfoImpl.Kind.ANNOTATION, annotationElement);
2282             Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues();
2283             // If the annotation is synthesized, do not print the container.
2284             if (utils.configuration.workArounds.isSynthesized(aDesc)) {
2285                 for (ExecutableElement ee : pairs.keySet()) {
2286                     AnnotationValue annotationValue = pairs.get(ee);
2287                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2288 
2289                     new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2290                         @Override @DefinedBy(Api.LANGUAGE_MODEL)
2291                         public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) {
2292                             p.addAll(vals);
2293                             return null;
2294                         }
2295 
2296                         @Override @DefinedBy(Api.LANGUAGE_MODEL)
2297                         protected Void defaultAction(Object o, List<AnnotationValue> p) {
2298                             p.add(annotationValue);
2299                             return null;
2300                         }
2301                     }.visit(annotationValue, annotationTypeValues);
2302 
2303                     String sep = "";
2304                     for (AnnotationValue av : annotationTypeValues) {
2305                         annotation.addContent(sep);
2306                         annotation.addContent(annotationValueToContent(av));
2307                         sep = " ";
2308                     }
2309                 }
2310             } else if (isAnnotationArray(pairs)) {

2311                 // If the container has 1 or more value defined and if the
2312                 // repeatable type annotation is not documented, do not print
2313                 // the container.
2314                 if (pairs.size() == 1 && isAnnotationDocumented) {


2315                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2316                     for (AnnotationValue a :  pairs.values()) {
2317                         new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2318                             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2319                             public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) {
2320                                for (AnnotationValue av : vals) {
2321                                    annotationTypeValues.add(av);
2322                                }
2323                                return null;
2324                             }
2325                         }.visit(a, annotationTypeValues);
2326                     }
2327                     String sep = "";
2328                     for (AnnotationValue av : annotationTypeValues) {
2329                         annotation.addContent(sep);
2330                         annotation.addContent(annotationValueToContent(av));
2331                         sep = " ";
2332                     }
2333                 }
2334                 // If the container has 1 or more value defined and if the
2335                 // repeatable type annotation is not documented, print the container.
2336                 else {
2337                     addAnnotations(annotationElement, linkInfo, annotation, pairs,
2338                                    indent, false);
2339                 }
2340             }
2341             else {
2342                 addAnnotations(annotationElement, linkInfo, annotation, pairs,
2343                                indent, linkBreak);
2344             }
2345             annotation.addContent(linkBreak ? DocletConstants.NL : "");
2346             results.add(annotation);
2347         }
2348         return results;
2349     }
2350 
2351     /**
2352      * Add annotation to the annotation string.
2353      *
2354      * @param annotationDoc the annotation being documented
2355      * @param linkInfo the information about the link
2356      * @param annotation the annotation string to which the annotation will be added
2357      * @param pairs annotation type element and value pairs
2358      * @param indent the number of extra spaces to indent the annotations.
2359      * @param linkBreak if true, add new line between each member value
2360      */
2361     private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo,
2362         ContentBuilder annotation, Map<? extends ExecutableElement,? extends AnnotationValue>map,
2363         int indent, boolean linkBreak) {
2364         linkInfo.label = new StringContent("@" + annotationDoc.getSimpleName().toString());
2365         annotation.addContent(getLink(linkInfo));
2366         if (!map.isEmpty()) {
2367             annotation.addContent("(");
2368             boolean isFirst = true;
2369             for (ExecutableElement element : map.keySet()) {
2370                 if (isFirst) {
2371                     isFirst = false;
2372                 } else {
2373                     annotation.addContent(",");
2374                     if (linkBreak) {
2375                         annotation.addContent(DocletConstants.NL);
2376                         int spaces = annotationDoc.getSimpleName().toString().length() + 2;
2377                         for (int k = 0; k < (spaces + indent); k++) {
2378                             annotation.addContent(" ");
2379                         }
2380                     }
2381                 }
2382                 annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2383                         element, element.getSimpleName().toString(), false));
2384                 annotation.addContent("=");
2385                 AnnotationValue annotationValue = map.get(element);
2386                 List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2387                 new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() {
2388                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
2389                     public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) {
2390                         annotationTypeValues.addAll(vals);
2391                         return null;

2392                     }
2393                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
2394                     protected Void defaultAction(Object o, AnnotationValue p) {
2395                         annotationTypeValues.add(p);
2396                         return null;
2397                     }
2398                 }.visit(annotationValue, annotationValue);
2399                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{");
2400                 String sep = "";
2401                 for (AnnotationValue av : annotationTypeValues) {
2402                     annotation.addContent(sep);
2403                     annotation.addContent(annotationValueToContent(av));
2404                     sep = ",";
2405                 }
2406                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}");
2407                 isContainerDocumented = false;
2408             }
2409             annotation.addContent(")");
2410         }
2411     }
2412 
2413     /**
2414      * Check if the annotation contains an array of annotation as a value. This
2415      * check is to verify if a repeatable type annotation is present or not.
2416      *
2417      * @param pairs annotation type element and value pairs
2418      *
2419      * @return true if the annotation contains an array of annotation as a value.
2420      */
2421     private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) {
2422         AnnotationValue annotationValue;
2423         for (ExecutableElement ee : pairs.keySet()) {
2424             annotationValue = pairs.get(ee);
2425             boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2426                 @Override @DefinedBy(Api.LANGUAGE_MODEL)
2427                 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) {
2428                     if (vals.size() > 1) {
2429                         if (vals.get(0) instanceof AnnotationMirror) {


2430                             isContainerDocumented = true;
2431                             return new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2432                                 @Override @DefinedBy(Api.LANGUAGE_MODEL)
2433                                 public Boolean visitAnnotation(AnnotationMirror a, Void p) {
2434                                     isContainerDocumented = true;
2435                                     Element asElement = a.getAnnotationType().asElement();
2436                                     if (utils.isDocumentedAnnotation((TypeElement)asElement)) {
2437                                         isAnnotationDocumented = true;
2438                                     }
2439                                     return true;
2440                                 }
2441                                 @Override @DefinedBy(Api.LANGUAGE_MODEL)
2442                                 protected Boolean defaultAction(Object o, Void p) {
2443                                     return false;
2444                                 }
2445                             }.visit(vals.get(0));
2446                         }
2447                     }
2448                     return false;
2449                 }
2450 
2451                 @Override @DefinedBy(Api.LANGUAGE_MODEL)
2452                 protected Boolean defaultAction(Object o, Void p) {
2453                     return false;
2454                 }
2455             }.visit(annotationValue);
2456             if (rvalue) {
2457                 return true;
2458             }
2459         }
2460         return false;
2461     }
2462 
2463     private Content annotationValueToContent(AnnotationValue annotationValue) {
2464         return new SimpleAnnotationValueVisitor9<Content, Void>() {
2465 
2466             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2467             public Content visitType(TypeMirror t, Void p) {
2468                 return new SimpleTypeVisitor9<Content, Void>() {
2469                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
2470                     public Content visitDeclared(DeclaredType t, Void p) {
2471                         LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2472                                 LinkInfoImpl.Kind.ANNOTATION, t);
2473                         String name = utils.isIncluded(t.asElement())
2474                                 ? t.asElement().getSimpleName().toString()
2475                                 : utils.getFullyQualifiedName(t.asElement());
2476                         linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class");
2477                         return getLink(linkInfo);


2478                     }
2479                     @Override @DefinedBy(Api.LANGUAGE_MODEL)
2480                     protected Content defaultAction(TypeMirror e, Void p) {
2481                         return new StringContent(t + utils.getDimension(t) + ".class");
2482                     }
2483                 }.visit(t);
2484             }
2485             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2486             public Content visitAnnotation(AnnotationMirror a, Void p) {
2487                 List<Content> list = getAnnotations(0, a, false);
2488                 ContentBuilder buf = new ContentBuilder();
2489                 for (Content c : list) {
2490                     buf.addContent(c);
2491                 }
2492                 return buf;
2493             }
2494             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2495             public Content visitEnumConstant(VariableElement c, Void p) {
2496                 return getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2497                         c, c.getSimpleName().toString(), false);
2498             }
2499             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2500             public Content visitArray(List<? extends AnnotationValue> vals, Void p) {
2501                 ContentBuilder buf = new ContentBuilder();
2502                 String sep = "";
2503                 for (AnnotationValue av : vals) {
2504                     buf.addContent(sep);
2505                     buf.addContent(visit(av));
2506                     sep = " ";
2507                 }
2508                 return buf;
2509             }
2510             @Override @DefinedBy(Api.LANGUAGE_MODEL)
2511             protected Content defaultAction(Object o, Void p) {
2512                 return new StringContent(annotationValue.toString());
2513             }
2514         }.visit(annotationValue);
2515     }
2516 
2517     /**
2518      * Return the configuration for this doclet.
2519      *
2520      * @return the configuration for this doclet.
2521      */
2522     public Configuration configuration() {
2523         return configuration;
2524     }
2525 }