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 }