001/* 002 * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package org.jdrupes.mdoclet.internal.doclets.formats.html; 027 028import java.util.ArrayList; 029import java.util.List; 030 031import javax.lang.model.element.Element; 032import javax.lang.model.element.ExecutableElement; 033import javax.lang.model.element.TypeElement; 034import javax.lang.model.element.TypeParameterElement; 035import javax.lang.model.type.TypeMirror; 036 037import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder; 038import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Entity; 039import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle; 040import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlTree; 041import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Links; 042import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.TagName; 043import org.jdrupes.mdoclet.internal.doclets.toolkit.Content; 044import org.jdrupes.mdoclet.internal.doclets.toolkit.MemberSummaryWriter; 045import org.jdrupes.mdoclet.internal.doclets.toolkit.MemberWriter; 046import org.jdrupes.mdoclet.internal.doclets.toolkit.Resources; 047import org.jdrupes.mdoclet.internal.doclets.toolkit.taglets.DeprecatedTaglet; 048import org.jdrupes.mdoclet.internal.doclets.toolkit.util.Utils; 049 050import com.sun.source.doctree.DocTree; 051 052/** 053 * The base class for member writers. 054 */ 055public abstract class AbstractMemberWriter 056 implements MemberSummaryWriter, MemberWriter { 057 058 protected final HtmlConfiguration configuration; 059 protected final HtmlOptions options; 060 protected final Utils utils; 061 protected final SubWriterHolderWriter writer; 062 protected final Contents contents; 063 protected final Resources resources; 064 protected final Links links; 065 protected final HtmlIds htmlIds; 066 067 protected final TypeElement typeElement; 068 069 public AbstractMemberWriter(SubWriterHolderWriter writer, 070 TypeElement typeElement) { 071 this.configuration = writer.configuration; 072 this.options = configuration.getOptions(); 073 this.writer = writer; 074 this.typeElement = typeElement; 075 this.utils = configuration.utils; 076 this.contents = configuration.getContents(); 077 this.resources = configuration.docResources; 078 this.links = writer.links; 079 this.htmlIds = configuration.htmlIds; 080 } 081 082 public AbstractMemberWriter(SubWriterHolderWriter writer) { 083 this(writer, null); 084 } 085 086 /* ----- abstracts ----- */ 087 088 /** 089 * Adds the summary label for the member. 090 * 091 * @param content the content to which the label will be added 092 */ 093 public abstract void addSummaryLabel(Content content); 094 095 /** 096 * Returns the summary table header for the member. 097 * 098 * @param member the member to be documented 099 * 100 * @return the summary table header 101 */ 102 public abstract TableHeader getSummaryTableHeader(Element member); 103 104 private Table<Element> summaryTable; 105 106 private Table<Element> getSummaryTable() { 107 if (summaryTable == null) { 108 summaryTable = createSummaryTable(); 109 } 110 return summaryTable; 111 } 112 113 /** 114 * Creates the summary table for this element. 115 * The table should be created and initialized if needed, and configured 116 * so that it is ready to add content with {@link Table#addRow(Content[])} 117 * and similar methods. 118 * 119 * @return the summary table 120 */ 121 protected abstract Table<Element> createSummaryTable(); 122 123 /** 124 * Adds inherited summary label for the member. 125 * 126 * @param typeElement the type element to which to link to 127 * @param content the content to which the inherited summary label will be added 128 */ 129 public abstract void addInheritedSummaryLabel(TypeElement typeElement, 130 Content content); 131 132 /** 133 * Adds the summary type for the member. 134 * 135 * @param member the member to be documented 136 * @param content the content to which the type will be added 137 */ 138 protected abstract void addSummaryType(Element member, Content content); 139 140 /** 141 * Adds the summary link for the member. 142 * 143 * @param typeElement the type element to be documented 144 * @param member the member to be documented 145 * @param content the content to which the link will be added 146 */ 147 protected void addSummaryLink(TypeElement typeElement, Element member, 148 Content content) { 149 addSummaryLink(HtmlLinkInfo.Kind.PLAIN, typeElement, member, content); 150 } 151 152 /** 153 * Adds the summary link for the member. 154 * 155 * @param context the id of the context where the link will be printed 156 * @param typeElement the type element to be documented 157 * @param member the member to be documented 158 * @param content the content to which the summary link will be added 159 */ 160 protected abstract void addSummaryLink(HtmlLinkInfo.Kind context, 161 TypeElement typeElement, Element member, Content content); 162 163 /** 164 * Adds the inherited summary link for the member. 165 * 166 * @param typeElement the type element to be documented 167 * @param member the member to be documented 168 * @param target the content to which the inherited summary link will be added 169 */ 170 protected abstract void addInheritedSummaryLink(TypeElement typeElement, 171 Element member, Content target); 172 173 /** 174 * Returns a link for summary (deprecated, preview) pages. 175 * 176 * @param member the member being linked to 177 * 178 * @return the link 179 */ 180 protected abstract Content getSummaryLink(Element member); 181 182 /** 183 * Adds the modifiers and type for the member in the member summary. 184 * 185 * @param member the member to add the modifiers and type for 186 * @param type the type to add 187 * @param target the content to which the modifiers and type will be added 188 */ 189 protected void addModifiersAndType(Element member, TypeMirror type, 190 Content target) { 191 var code = new HtmlTree(TagName.CODE); 192 addModifiers(member, code); 193 if (type == null) { 194 code.add(switch (member.getKind()) { 195 case ENUM -> "enum"; 196 case INTERFACE -> "interface"; 197 case ANNOTATION_TYPE -> "@interface"; 198 case RECORD -> "record"; 199 default -> "class"; 200 }); 201 code.add(Entity.NO_BREAK_SPACE); 202 } else { 203 List<? extends TypeParameterElement> list 204 = utils.isExecutableElement(member) 205 ? ((ExecutableElement) member).getTypeParameters() 206 : null; 207 if (list != null && !list.isEmpty()) { 208 Content typeParameters = ((AbstractExecutableMemberWriter) this) 209 .getTypeParameters((ExecutableElement) member); 210 code.add(typeParameters); 211 // Add explicit line break between method type parameters and 212 // return type in member summary table to avoid random wrapping. 213 if (typeParameters.charCount() > 10) { 214 code.add(new HtmlTree(TagName.BR)); 215 } else { 216 code.add(Entity.NO_BREAK_SPACE); 217 } 218 } 219 code.add( 220 writer.getLink(new HtmlLinkInfo(configuration, 221 HtmlLinkInfo.Kind.LINK_TYPE_PARAMS, type) 222 .addLineBreakOpportunitiesInTypeParameters(true))); 223 } 224 target.add(code); 225 } 226 227 /** 228 * Adds the modifiers for the member. 229 * 230 * @param member the member to add the modifiers for 231 * @param target the content to which the modifiers will be added 232 */ 233 private void addModifiers(Element member, Content target) { 234 if (utils.isProtected(member)) { 235 target.add("protected "); 236 } else if (utils.isPrivate(member)) { 237 target.add("private "); 238 } else if (!utils.isPublic(member)) { // Package private 239 target.add(resources.getText("doclet.Package_private")); 240 target.add(" "); 241 } 242 if (!utils.isAnnotationInterface(member.getEnclosingElement()) 243 && utils.isMethod(member)) { 244 if (!utils.isPlainInterface(member.getEnclosingElement()) 245 && utils.isAbstract(member)) { 246 target.add("abstract "); 247 } 248 if (utils.isDefault(member)) { 249 target.add("default "); 250 } 251 } 252 if (utils.isStatic(member)) { 253 target.add("static "); 254 } 255 if (!utils.isEnum(member) && utils.isFinal(member)) { 256 target.add("final "); 257 } 258 } 259 260 /** 261 * Adds the deprecated information for the given member. 262 * 263 * @param member the member being documented. 264 * @param target the content to which the deprecated information will be added. 265 */ 266 protected void addDeprecatedInfo(Element member, Content target) { 267 Content output = (new DeprecatedTaglet()).getAllBlockTagOutput(member, 268 writer.getTagletWriterInstance(false)); 269 if (!output.isEmpty()) { 270 target.add(HtmlTree.DIV(HtmlStyle.deprecationBlock, output)); 271 } 272 } 273 274 /** 275 * Adds the comment for the given member. 276 * 277 * @param member the member being documented. 278 * @param content the content to which the comment will be added. 279 */ 280 protected void addComment(Element member, Content content) { 281 if (!utils.getFullBody(member).isEmpty()) { 282 writer.addInlineComment(member, content); 283 } 284 } 285 286 /** 287 * Add the preview information for the given member. 288 * 289 * @param member the member being documented. 290 * @param content the content to which the preview information will be added. 291 */ 292 protected void addPreviewInfo(Element member, Content content) { 293 writer.addPreviewInfo(member, content); 294 } 295 296 protected String name(Element member) { 297 return utils.getSimpleName(member); 298 } 299 300 /** 301 * Adds use information to the documentation. 302 * 303 * @param members list of program elements for which the use information will be added 304 * @param heading the section heading 305 * @param content the content to which the use information will be added 306 */ 307 protected void addUseInfo(List<? extends Element> members, Content heading, 308 Content content) { 309 if (members == null || members.isEmpty()) { 310 return; 311 } 312 boolean printedUseTableHeader = false; 313 var useTable = new Table<Void>(HtmlStyle.summaryTable) 314 .setCaption(heading) 315 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, 316 HtmlStyle.colLast); 317 for (Element element : members) { 318 TypeElement te = (typeElement == null) 319 ? utils.getEnclosingTypeElement(element) 320 : typeElement; 321 if (!printedUseTableHeader) { 322 useTable.setHeader(getSummaryTableHeader(element)); 323 printedUseTableHeader = true; 324 } 325 Content summaryType = new ContentBuilder(); 326 addSummaryType(element, summaryType); 327 Content typeContent = new ContentBuilder(); 328 if (te != null 329 && !utils.isConstructor(element) 330 && !utils.isTypeElement(element)) { 331 332 var name = HtmlTree.SPAN(HtmlStyle.typeNameLabel); 333 name.add(name(te) + "."); 334 typeContent.add(name); 335 } 336 addSummaryLink( 337 utils.isClass(element) || utils.isPlainInterface(element) 338 ? HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS 339 : HtmlLinkInfo.Kind.PLAIN, 340 te, element, typeContent); 341 Content desc = new ContentBuilder(); 342 writer.addSummaryLinkComment(element, desc); 343 useTable.addRow(summaryType, typeContent, desc); 344 } 345 content.add(useTable); 346 } 347 348 protected void serialWarning(Element e, String key, String a1, String a2) { 349 if (options.serialWarn()) { 350 configuration.messages.warning(e, key, a1, a2); 351 } 352 } 353 354 @Override 355 public void addMemberSummary(TypeElement tElement, Element member, 356 List<? extends DocTree> firstSentenceTrees) { 357 if (tElement != typeElement) { 358 throw new IllegalStateException(); 359 } 360 var table = getSummaryTable(); 361 List<Content> rowContents = new ArrayList<>(); 362 Content summaryType = new ContentBuilder(); 363 addSummaryType(member, summaryType); 364 if (!summaryType.isEmpty()) 365 rowContents.add(summaryType); 366 Content summaryLink = new ContentBuilder(); 367 addSummaryLink(tElement, member, summaryLink); 368 rowContents.add(summaryLink); 369 Content desc = new ContentBuilder(); 370 writer.addSummaryLinkComment(member, firstSentenceTrees, desc); 371 rowContents.add(desc); 372 table.addRow(member, rowContents); 373 } 374 375 @Override 376 public void addInheritedMemberSummary(TypeElement tElement, 377 Element nestedClass, boolean isFirst, boolean isLast, 378 Content content) { 379 writer.addInheritedMemberSummary(this, tElement, nestedClass, isFirst, 380 content); 381 } 382 383 @Override 384 public Content getInheritedSummaryHeader(TypeElement tElement) { 385 Content c = writer.getMemberInherited(); 386 writer.addInheritedSummaryHeader(this, tElement, c); 387 return c; 388 } 389 390 @Override 391 public Content getInheritedSummaryLinks() { 392 return new HtmlTree(TagName.CODE); 393 } 394 395 @Override 396 public Content getSummaryTable(TypeElement tElement) { 397 if (tElement != typeElement) { 398 throw new IllegalStateException(); 399 } 400 return getSummaryTable(); 401 } 402 403 @Override 404 public Content getMember(Content memberContent) { 405 return writer.getMember(memberContent); 406 } 407 408 @Override 409 public Content getMemberList() { 410 return writer.getMemberList(); 411 } 412 413 @Override 414 public Content getMemberListItem(Content memberContent) { 415 return writer.getMemberListItem(memberContent); 416 } 417 418}