1 /*
   2  * Copyright (c) 2003, 2017, 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.util.List;
  29 
  30 import javax.lang.model.element.Element;
  31 import javax.lang.model.element.ModuleElement;
  32 import javax.lang.model.element.PackageElement;
  33 import javax.lang.model.element.TypeElement;
  34 import javax.lang.model.element.VariableElement;
  35 import javax.lang.model.type.TypeMirror;
  36 import javax.lang.model.util.SimpleElementVisitor9;
  37 
  38 import com.sun.source.doctree.DocTree;
  39 import com.sun.source.doctree.IndexTree;
  40 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  41 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  42 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  43 import jdk.javadoc.internal.doclets.formats.html.markup.Links;
  44 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
  45 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
  46 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
  47 import jdk.javadoc.internal.doclets.toolkit.Content;
  48 import jdk.javadoc.internal.doclets.toolkit.builders.SerializedFormBuilder;
  49 import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter;
  50 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
  51 import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
  52 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  53 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  54 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
  55 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  56 
  57 /**
  58  * The taglet writer that writes HTML.
  59  *
  60  *  <p><b>This is NOT part of any supported API.
  61  *  If you write code that depends on this, you do so at your own risk.
  62  *  This code and its internal interfaces are subject to change or
  63  *  deletion without notice.</b>
  64  *
  65  * @author Jamie Ho
  66  * @author Bhavesh Patel (Modified)
  67  */
  68 
  69 public class TagletWriterImpl extends TagletWriter {
  70 
  71     private final HtmlDocletWriter htmlWriter;
  72     private final HtmlConfiguration configuration;
  73     private final Utils utils;
  74 
  75     public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence) {
  76         super(isFirstSentence);
  77         this.htmlWriter = htmlWriter;
  78         configuration = htmlWriter.configuration;
  79         this.utils = configuration.utils;
  80     }
  81 
  82     /**
  83      * {@inheritDoc}
  84      */
  85     public Content getOutputInstance() {
  86         return new ContentBuilder();
  87     }
  88 
  89     /**
  90      * {@inheritDoc}
  91      */
  92     protected Content codeTagOutput(Element element, DocTree tag) {
  93         CommentHelper ch = utils.getCommentHelper(element);
  94         StringContent content = new StringContent(utils.normalizeNewlines(ch.getText(tag)));
  95         Content result = HtmlTree.CODE(content);
  96         return result;
  97     }
  98 
  99     protected Content indexTagOutput(Element element, DocTree tag) {
 100         CommentHelper ch = utils.getCommentHelper(element);
 101         IndexTree itt = (IndexTree)tag;
 102 
 103         String tagText =  ch.getText(itt.getSearchTerm());
 104         if (tagText.charAt(0) == '"' && tagText.charAt(tagText.length() - 1) == '"') {
 105             tagText = tagText.substring(1, tagText.length() - 1);
 106         }
 107         String desc = ch.getText(itt.getDescription());
 108 
 109         String anchorName = configuration.links.getName(tagText);
 110         Content result = HtmlTree.A_ID(HtmlStyle.searchTagResult, anchorName, new StringContent(tagText));
 111         if (configuration.createindex && !tagText.isEmpty()) {
 112             SearchIndexItem si = new SearchIndexItem();
 113             si.setLabel(tagText);
 114             si.setDescription(desc);
 115             new SimpleElementVisitor9<Void, Void>() {
 116                 @Override
 117                 public Void visitModule(ModuleElement e, Void p) {
 118                     si.setUrl(DocPaths.moduleSummary(e).getPath() + "#" + anchorName);
 119                     si.setHolder(utils.getFullyQualifiedName(element));
 120                     return null;
 121                 }
 122 
 123                 @Override
 124                 public Void visitPackage(PackageElement e, Void p) {
 125                     si.setUrl(DocPath.forPackage(e).getPath()
 126                             + "/" + DocPaths.PACKAGE_SUMMARY.getPath() + "#" + anchorName);
 127                     si.setHolder(utils.getSimpleName(element));
 128                     return null;
 129                 }
 130 
 131                 @Override
 132                 public Void visitType(TypeElement e, Void p) {
 133                     si.setUrl(DocPath.forClass(utils, e).getPath() + "#" + anchorName);
 134                     si.setHolder(utils.getFullyQualifiedName(e));
 135                     return null;
 136                 }
 137 
 138                 @Override
 139                 public Void visitVariable(VariableElement e, Void p) {
 140                     TypeElement te = utils.getEnclosingTypeElement(e);
 141                     si.setUrl(DocPath.forClass(utils, te).getPath() + "#" + anchorName);
 142                     si.setHolder(utils.getFullyQualifiedName(e) + "." + utils.getSimpleName(e));
 143                     return null;
 144                 }
 145 
 146                 @Override
 147                 protected Void defaultAction(Element e, Void p) {
 148                     TypeElement te = utils.getEnclosingTypeElement(e);
 149                     si.setUrl(DocPath.forClass(utils, te).getPath() + "#" + anchorName);
 150                     si.setHolder(utils.getFullyQualifiedName(e));
 151                     return null;
 152                 }
 153             }.visit(element);
 154             si.setCategory(configuration.getContent("doclet.SearchTags").toString());
 155             configuration.tagSearchIndex.add(si);
 156         }
 157         return result;
 158     }
 159 
 160     /**
 161      * {@inheritDoc}
 162      */
 163     public Content getDocRootOutput() {
 164         String path;
 165         if (htmlWriter.pathToRoot.isEmpty())
 166             path = ".";
 167         else
 168             path = htmlWriter.pathToRoot.getPath();
 169         return new StringContent(path);
 170     }
 171 
 172     /**
 173      * {@inheritDoc}
 174      */
 175     public Content deprecatedTagOutput(Element element) {
 176         ContentBuilder result = new ContentBuilder();
 177         CommentHelper ch = utils.getCommentHelper(element);
 178         List<? extends DocTree> deprs = utils.getBlockTags(element, DocTree.Kind.DEPRECATED);
 179         if (utils.isTypeElement(element)) {
 180             if (utils.isDeprecated(element)) {
 181                 result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
 182                         htmlWriter.getDeprecatedPhrase(element)));
 183                 if (!deprs.isEmpty()) {
 184                     List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0));
 185                     if (!commentTags.isEmpty()) {
 186                         result.addContent(commentTagsToOutput(null, element, commentTags, false));
 187                     }
 188                 }
 189             }
 190         } else {
 191             if (utils.isDeprecated(element)) {
 192                 result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
 193                         htmlWriter.getDeprecatedPhrase(element)));
 194                 if (!deprs.isEmpty()) {
 195                     List<? extends DocTree> bodyTags = ch.getBody(configuration, deprs.get(0));
 196                     Content body = commentTagsToOutput(null, element, bodyTags, false);
 197                     if (!body.isEmpty())
 198                         result.addContent(HtmlTree.DIV(HtmlStyle.deprecationComment, body));
 199                 }
 200             } else {
 201                 Element ee = utils.getEnclosingTypeElement(element);
 202                 if (utils.isDeprecated(ee)) {
 203                     result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel,
 204                         htmlWriter.getDeprecatedPhrase(ee)));
 205                 }
 206             }
 207         }
 208         return result;
 209     }
 210 
 211     /**
 212      * {@inheritDoc}
 213      */
 214     protected Content literalTagOutput(Element element, DocTree tag) {
 215         CommentHelper ch = utils.getCommentHelper(element);
 216         Content result = new StringContent(utils.normalizeNewlines(ch.getText(tag)));
 217         return result;
 218     }
 219 
 220     /**
 221      * {@inheritDoc}
 222      */
 223     public Content getParamHeader(String header) {
 224         HtmlTree result = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.paramLabel,
 225                 new StringContent(header)));
 226         return result;
 227     }
 228 
 229     /**
 230      * {@inheritDoc}
 231      */
 232     public Content paramTagOutput(Element element, DocTree paramTag, String paramName) {
 233         ContentBuilder body = new ContentBuilder();
 234         CommentHelper ch = utils.getCommentHelper(element);
 235         body.addContent(HtmlTree.CODE(new RawHtml(paramName)));
 236         body.addContent(" - ");
 237         List<? extends DocTree> description = ch.getDescription(configuration, paramTag);
 238         body.addContent(htmlWriter.commentTagsToContent(paramTag, element, description, false));
 239         HtmlTree result = HtmlTree.DD(body);
 240         return result;
 241     }
 242 
 243     /**
 244      * {@inheritDoc}
 245      */
 246     public Content propertyTagOutput(Element element, DocTree tag, String prefix) {
 247         Content body = new ContentBuilder();
 248         CommentHelper ch = utils.getCommentHelper(element);
 249         body.addContent(new RawHtml(prefix));
 250         body.addContent(" ");
 251         body.addContent(HtmlTree.CODE(new RawHtml(ch.getText(tag))));
 252         body.addContent(".");
 253         Content result = HtmlTree.P(body);
 254         return result;
 255     }
 256 
 257     /**
 258      * {@inheritDoc}
 259      */
 260     public Content returnTagOutput(Element element, DocTree returnTag) {
 261         ContentBuilder result = new ContentBuilder();
 262         CommentHelper ch = utils.getCommentHelper(element);
 263         result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.returnLabel,
 264                 new StringContent(configuration.getText("doclet.Returns")))));
 265         result.addContent(HtmlTree.DD(htmlWriter.commentTagsToContent(
 266                 returnTag, element, ch.getDescription(configuration, returnTag), false)));
 267         return result;
 268     }
 269 
 270     /**
 271      * {@inheritDoc}
 272      */
 273     public Content seeTagOutput(Element holder, List<? extends DocTree> seeTags) {
 274         ContentBuilder body = new ContentBuilder();
 275         if (!seeTags.isEmpty()) {
 276             for (DocTree dt : seeTags) {
 277                 appendSeparatorIfNotEmpty(body);
 278                 body.addContent(htmlWriter.seeTagToContent(holder, dt));
 279             }
 280         }
 281         if (utils.isVariableElement(holder) && ((VariableElement)holder).getConstantValue() != null &&
 282                 htmlWriter instanceof ClassWriterImpl) {
 283             //Automatically add link to constant values page for constant fields.
 284             appendSeparatorIfNotEmpty(body);
 285             DocPath constantsPath =
 286                     htmlWriter.pathToRoot.resolve(DocPaths.CONSTANT_VALUES);
 287             String whichConstant =
 288                     ((ClassWriterImpl) htmlWriter).getTypeElement().getQualifiedName() + "." +
 289                     utils.getSimpleName(holder);
 290             DocLink link = constantsPath.fragment(whichConstant);
 291             body.addContent(Links.createLink(link,
 292                     new StringContent(configuration.getText("doclet.Constants_Summary"))));
 293         }
 294         if (utils.isClass(holder) && utils.isSerializable((TypeElement)holder)) {
 295             //Automatically add link to serialized form page for serializable classes.
 296             if (SerializedFormBuilder.serialInclude(utils, holder) &&
 297                       SerializedFormBuilder.serialInclude(utils, utils.containingPackage(holder))) {
 298                 appendSeparatorIfNotEmpty(body);
 299                 DocPath serialPath = htmlWriter.pathToRoot.resolve(DocPaths.SERIALIZED_FORM);
 300                 DocLink link = serialPath.fragment(utils.getFullyQualifiedName(holder));
 301                 body.addContent(Links.createLink(link,
 302                         new StringContent(configuration.getText("doclet.Serialized_Form"))));
 303             }
 304         }
 305         if (body.isEmpty())
 306             return body;
 307 
 308         ContentBuilder result = new ContentBuilder();
 309         result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.seeLabel,
 310                 new StringContent(configuration.getText("doclet.See_Also")))));
 311         result.addContent(HtmlTree.DD(body));
 312         return result;
 313 
 314     }
 315 
 316     private void appendSeparatorIfNotEmpty(ContentBuilder body) {
 317         if (!body.isEmpty()) {
 318             body.addContent(", ");
 319             body.addContent(DocletConstants.NL);
 320         }
 321     }
 322 
 323     /**
 324      * {@inheritDoc}
 325      */
 326     public Content simpleTagOutput(Element element, List<? extends DocTree> simpleTags, String header) {
 327         CommentHelper ch = utils.getCommentHelper(element);
 328         ContentBuilder result = new ContentBuilder();
 329         result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.simpleTagLabel, new RawHtml(header))));
 330         ContentBuilder body = new ContentBuilder();
 331         boolean many = false;
 332         for (DocTree simpleTag : simpleTags) {
 333             if (many) {
 334                 body.addContent(", ");
 335             }
 336             List<? extends DocTree> bodyTags = ch.getBody(configuration, simpleTag);
 337             body.addContent(htmlWriter.commentTagsToContent(simpleTag, element, bodyTags, false));
 338             many = true;
 339         }
 340         result.addContent(HtmlTree.DD(body));
 341         return result;
 342     }
 343 
 344     /**
 345      * {@inheritDoc}
 346      */
 347     public Content simpleTagOutput(Element element, DocTree simpleTag, String header) {
 348         ContentBuilder result = new ContentBuilder();
 349         result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.simpleTagLabel, new RawHtml(header))));
 350         CommentHelper ch = utils.getCommentHelper(element);
 351         List<? extends DocTree> description = ch.getDescription(configuration, simpleTag);
 352         Content body = htmlWriter.commentTagsToContent(simpleTag, element, description, false);
 353         result.addContent(HtmlTree.DD(body));
 354         return result;
 355     }
 356 
 357     /**
 358      * {@inheritDoc}
 359      */
 360     public Content getThrowsHeader() {
 361         HtmlTree result = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.throwsLabel,
 362                 new StringContent(configuration.getText("doclet.Throws"))));
 363         return result;
 364     }
 365 
 366     /**
 367      * {@inheritDoc}
 368      */
 369     public Content throwsTagOutput(Element element, DocTree throwsTag) {
 370         ContentBuilder body = new ContentBuilder();
 371         CommentHelper ch = utils.getCommentHelper(element);
 372         Element exception = ch.getException(configuration, throwsTag);
 373         Content excName;
 374         if (exception == null) {
 375             excName = new RawHtml(ch.getExceptionName(throwsTag).toString());
 376         } else if (exception.asType() == null) {
 377             excName = new RawHtml(utils.getFullyQualifiedName(exception));
 378         } else {
 379             LinkInfoImpl link = new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER,
 380                                                  exception.asType());
 381             link.excludeTypeBounds = true;
 382             excName = htmlWriter.getLink(link);
 383         }
 384         body.addContent(HtmlTree.CODE(excName));
 385         List<? extends DocTree> description = ch.getDescription(configuration, throwsTag);
 386         Content desc = htmlWriter.commentTagsToContent(throwsTag, element, description, false);
 387         if (desc != null && !desc.isEmpty()) {
 388             body.addContent(" - ");
 389             body.addContent(desc);
 390         }
 391         HtmlTree result = HtmlTree.DD(body);
 392         return result;
 393     }
 394 
 395     /**
 396      * {@inheritDoc}
 397      */
 398     public Content throwsTagOutput(TypeMirror throwsType) {
 399         HtmlTree result = HtmlTree.DD(HtmlTree.CODE(htmlWriter.getLink(
 400                 new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER, throwsType))));
 401         return result;
 402     }
 403 
 404     /**
 405      * {@inheritDoc}
 406      */
 407     public Content valueTagOutput(VariableElement field, String constantVal, boolean includeLink) {
 408         return includeLink ?
 409             htmlWriter.getDocLink(LinkInfoImpl.Kind.VALUE_TAG, field,
 410                 constantVal, false) : new RawHtml(constantVal);
 411     }
 412 
 413     /**
 414      * {@inheritDoc}
 415      */
 416     public Content commentTagsToOutput(DocTree holderTag, List<? extends DocTree> tags) {
 417         return commentTagsToOutput(holderTag, null, tags, false);
 418     }
 419 
 420     /**
 421      * {@inheritDoc}
 422      */
 423     public Content commentTagsToOutput(Element holder, List<? extends DocTree> tags) {
 424         return commentTagsToOutput(null, holder, tags, false);
 425     }
 426 
 427     /**
 428      * {@inheritDoc}
 429      */
 430     public Content commentTagsToOutput(DocTree holderTag,
 431         Element holder, List<? extends DocTree> tags, boolean isFirstSentence) {
 432         return htmlWriter.commentTagsToContent(holderTag, holder,
 433                 tags, isFirstSentence);
 434     }
 435 
 436     /**
 437      * {@inheritDoc}
 438      */
 439     public BaseConfiguration configuration() {
 440         return configuration;
 441     }
 442 }