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