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