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.Collection; 030import java.util.List; 031import java.util.Set; 032import java.util.SortedSet; 033import java.util.TreeSet; 034import javax.lang.model.element.AnnotationMirror; 035import javax.lang.model.element.Element; 036import javax.lang.model.element.ModuleElement; 037import javax.lang.model.element.PackageElement; 038import javax.lang.model.element.TypeElement; 039import javax.lang.model.type.TypeMirror; 040import javax.lang.model.util.SimpleElementVisitor8; 041 042import org.jdrupes.mdoclet.internal.doclets.formats.html.Navigation.PageMode; 043import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder; 044import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Entity; 045import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlAttr; 046import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle; 047import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlTree; 048import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.TagName; 049import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Text; 050import org.jdrupes.mdoclet.internal.doclets.toolkit.ClassWriter; 051import org.jdrupes.mdoclet.internal.doclets.toolkit.Content; 052import org.jdrupes.mdoclet.internal.doclets.toolkit.taglets.ParamTaglet; 053import org.jdrupes.mdoclet.internal.doclets.toolkit.util.ClassTree; 054import org.jdrupes.mdoclet.internal.doclets.toolkit.util.CommentHelper; 055import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocFileIOException; 056import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPath; 057import org.jdrupes.mdoclet.internal.doclets.toolkit.util.VisibleMemberTable; 058 059import com.sun.source.doctree.DeprecatedTree; 060import com.sun.source.doctree.DocTree; 061 062/** 063 * Generate the Class Information Page. 064 * 065 * @see javax.lang.model.element.TypeElement 066 */ 067public class ClassWriterImpl extends SubWriterHolderWriter 068 implements ClassWriter { 069 070 private static final Set<String> suppressSubtypesSet 071 = Set.of("java.lang.Object", 072 "org.omg.CORBA.Object"); 073 074 private static final Set<String> suppressImplementingSet 075 = Set.of("java.lang.Cloneable", 076 "java.lang.constant.Constable", 077 "java.lang.constant.ConstantDesc", 078 "java.io.Serializable"); 079 080 protected final TypeElement typeElement; 081 082 protected final ClassTree classTree; 083 084 /** 085 * @param configuration the configuration data for the doclet 086 * @param typeElement the class being documented. 087 * @param classTree the class tree for the given class. 088 */ 089 public ClassWriterImpl(HtmlConfiguration configuration, 090 TypeElement typeElement, 091 ClassTree classTree) { 092 super(configuration, configuration.docPaths.forClass(typeElement)); 093 this.typeElement = typeElement; 094 configuration.currentTypeElement = typeElement; 095 this.classTree = classTree; 096 } 097 098 @Override 099 public Content getHeader(String header) { 100 HtmlTree body 101 = getBody(getWindowTitle(utils.getSimpleName(typeElement))); 102 var div = HtmlTree.DIV(HtmlStyle.header); 103 if (configuration.showModules) { 104 ModuleElement mdle = configuration.docEnv.getElementUtils() 105 .getModuleOf(typeElement); 106 var classModuleLabel = HtmlTree.SPAN(HtmlStyle.moduleLabelInType, 107 contents.moduleLabel); 108 var moduleNameDiv 109 = HtmlTree.DIV(HtmlStyle.subTitle, classModuleLabel); 110 moduleNameDiv.add(Entity.NO_BREAK_SPACE); 111 moduleNameDiv.add(getModuleLink(mdle, 112 Text.of(mdle.getQualifiedName()))); 113 div.add(moduleNameDiv); 114 } 115 PackageElement pkg = utils.containingPackage(typeElement); 116 if (!pkg.isUnnamed()) { 117 var classPackageLabel = HtmlTree.SPAN(HtmlStyle.packageLabelInType, 118 contents.packageLabel); 119 var pkgNameDiv 120 = HtmlTree.DIV(HtmlStyle.subTitle, classPackageLabel); 121 pkgNameDiv.add(Entity.NO_BREAK_SPACE); 122 Content pkgNameContent 123 = getPackageLink(pkg, getLocalizedPackageName(pkg)); 124 pkgNameDiv.add(pkgNameContent); 125 div.add(pkgNameDiv); 126 } 127 HtmlLinkInfo linkInfo = new HtmlLinkInfo(configuration, 128 HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS, typeElement) 129 .linkToSelf(false); // Let's not link to ourselves in the 130 // header 131 var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, 132 HtmlStyle.title, Text.of(header)); 133 heading.add(getTypeParameterLinks(linkInfo)); 134 div.add(heading); 135 bodyContents.setHeader(getHeader(PageMode.CLASS, typeElement)) 136 .addMainContent(MarkerComments.START_OF_CLASS_DATA) 137 .addMainContent(div); 138 return body; 139 } 140 141 @Override 142 public Content getClassContentHeader() { 143 return getContentHeader(); 144 } 145 146 @Override 147 protected Navigation getNavBar(PageMode pageMode, Element element) { 148 Content linkContent 149 = getModuleLink(utils.elementUtils.getModuleOf(element), 150 contents.moduleLabel); 151 return super.getNavBar(pageMode, element) 152 .setNavLinkModule(linkContent) 153 .setSubNavLinks(() -> { 154 List<Content> list = new ArrayList<>(); 155 VisibleMemberTable vmt 156 = configuration.getVisibleMemberTable(typeElement); 157 Set<VisibleMemberTable.Kind> summarySet 158 = VisibleMemberTable.Kind.forSummariesOf(element.getKind()); 159 for (VisibleMemberTable.Kind kind : summarySet) { 160 list.add(links.createLink(HtmlIds.forMemberSummary(kind), 161 contents.getNavLinkLabelContent(kind), 162 vmt.hasVisibleMembers(kind))); 163 } 164 return list; 165 }); 166 } 167 168 @Override 169 public void addFooter() { 170 bodyContents.addMainContent(MarkerComments.END_OF_CLASS_DATA); 171 bodyContents.setFooter(getFooter()); 172 } 173 174 @Override 175 public void printDocument(Content content) throws DocFileIOException { 176 String description = getDescription("declaration", typeElement); 177 PackageElement pkg = utils.containingPackage(typeElement); 178 List<DocPath> localStylesheets = getLocalStylesheets(pkg); 179 content.add(bodyContents); 180 printHtmlDocument( 181 configuration.metakeywords.getMetaKeywords(typeElement), 182 description, localStylesheets, content); 183 } 184 185 @Override 186 public Content getClassInfo(Content classInfo) { 187 return getMember(HtmlIds.CLASS_DESCRIPTION, HtmlStyle.classDescription, 188 classInfo); 189 } 190 191 @Override 192 protected TypeElement getCurrentPageElement() { 193 return typeElement; 194 } 195 196 @Override 197 public void addClassSignature(Content classInfo) { 198 classInfo.add(new HtmlTree(TagName.HR)); 199 classInfo.add(new Signatures.TypeSignature(typeElement, this) 200 .toContent()); 201 } 202 203 @Override 204 public void addClassDescription(Content classInfo) { 205 addPreviewInfo(classInfo); 206 if (!options.noComment()) { 207 // generate documentation for the class. 208 if (!utils.getFullBody(typeElement).isEmpty()) { 209 addInlineComment(typeElement, classInfo); 210 } 211 } 212 } 213 214 private void addPreviewInfo(Content content) { 215 addPreviewInfo(typeElement, content); 216 } 217 218 @Override 219 public void addClassTagInfo(Content classInfo) { 220 if (!options.noComment()) { 221 // Print Information about all the tags here 222 addTagsInfo(typeElement, classInfo); 223 } 224 } 225 226 /** 227 * Get the class inheritance tree for the given class. 228 * 229 * @param type the class to get the inheritance tree for 230 * @return the class inheritance tree 231 */ 232 private Content getClassInheritanceTreeContent(TypeMirror type) { 233 TypeMirror sup; 234 HtmlTree classTree = null; 235 do { 236 sup = utils.getFirstVisibleSuperClass(type); 237 var entry = HtmlTree.DIV(HtmlStyle.inheritance, 238 getClassHelperContent(type)); 239 if (classTree != null) 240 entry.add(classTree); 241 classTree = entry; 242 type = sup; 243 } while (sup != null); 244 classTree.put(HtmlAttr.TITLE, 245 contents.getContent("doclet.Inheritance_Tree").toString()); 246 return classTree; 247 } 248 249 /** 250 * Get the class helper for the given class. 251 * 252 * @param type the class to get the helper for 253 * @return the class helper 254 */ 255 private Content getClassHelperContent(TypeMirror type) { 256 Content result = new ContentBuilder(); 257 if (utils.typeUtils.isSameType(type, typeElement.asType())) { 258 Content typeParameters = getTypeParameterLinks( 259 new HtmlLinkInfo(configuration, 260 HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, 261 typeElement)); 262 if (configuration.shouldExcludeQualifier( 263 utils.containingPackage(typeElement).toString())) { 264 result.add(utils.asTypeElement(type).getSimpleName()); 265 result.add(typeParameters); 266 } else { 267 result.add(utils.asTypeElement(type).getQualifiedName()); 268 result.add(typeParameters); 269 } 270 } else { 271 Content link = getLink(new HtmlLinkInfo(configuration, 272 HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, type) 273 .label( 274 configuration.getClassName(utils.asTypeElement(type)))); 275 result.add(link); 276 } 277 return result; 278 } 279 280 @Override 281 public void addClassTree(Content target) { 282 if (!utils.isClass(typeElement)) { 283 return; 284 } 285 target.add(getClassInheritanceTreeContent(typeElement.asType())); 286 } 287 288 @Override 289 public void addParamInfo(Content target) { 290 if (utils.hasBlockTag(typeElement, DocTree.Kind.PARAM)) { 291 Content paramInfo 292 = (new ParamTaglet()).getAllBlockTagOutput(typeElement, 293 getTagletWriterInstance(false)); 294 if (!paramInfo.isEmpty()) { 295 target.add(HtmlTree.DL(HtmlStyle.notes, paramInfo)); 296 } 297 } 298 } 299 300 @Override 301 public void addSubClassInfo(Content target) { 302 if (utils.isClass(typeElement)) { 303 for (String s : suppressSubtypesSet) { 304 if (typeElement.getQualifiedName().contentEquals(s)) { 305 return; // Don't generate the list, too huge 306 } 307 } 308 Set<TypeElement> subclasses 309 = classTree.hierarchy(typeElement).subtypes(typeElement); 310 if (!subclasses.isEmpty()) { 311 var dl = HtmlTree.DL(HtmlStyle.notes); 312 dl.add(HtmlTree.DT(contents.subclassesLabel)); 313 dl.add(HtmlTree 314 .DD(getClassLinks(HtmlLinkInfo.Kind.PLAIN, subclasses))); 315 target.add(dl); 316 } 317 } 318 } 319 320 @Override 321 public void addSubInterfacesInfo(Content target) { 322 if (utils.isPlainInterface(typeElement)) { 323 Set<TypeElement> subInterfaces 324 = classTree.hierarchy(typeElement).allSubtypes(typeElement); 325 if (!subInterfaces.isEmpty()) { 326 var dl = HtmlTree.DL(HtmlStyle.notes); 327 dl.add(HtmlTree.DT(contents.subinterfacesLabel)); 328 dl.add(HtmlTree.DD(getClassLinks( 329 HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, subInterfaces))); 330 target.add(dl); 331 } 332 } 333 } 334 335 @Override 336 public void addInterfaceUsageInfo(Content target) { 337 if (!utils.isPlainInterface(typeElement)) { 338 return; 339 } 340 for (String s : suppressImplementingSet) { 341 if (typeElement.getQualifiedName().contentEquals(s)) { 342 return; // Don't generate the list, too huge 343 } 344 } 345 Set<TypeElement> implcl = classTree.implementingClasses(typeElement); 346 if (!implcl.isEmpty()) { 347 var dl = HtmlTree.DL(HtmlStyle.notes); 348 dl.add(HtmlTree.DT(contents.implementingClassesLabel)); 349 dl.add(HtmlTree.DD(getClassLinks(HtmlLinkInfo.Kind.PLAIN, implcl))); 350 target.add(dl); 351 } 352 } 353 354 @Override 355 public void addImplementedInterfacesInfo(Content target) { 356 SortedSet<TypeMirror> interfaces 357 = new TreeSet<>(comparators.makeTypeMirrorClassUseComparator()); 358 interfaces.addAll(utils.getAllInterfaces(typeElement)); 359 if (utils.isClass(typeElement) && !interfaces.isEmpty()) { 360 var dl = HtmlTree.DL(HtmlStyle.notes); 361 dl.add(HtmlTree.DT(contents.allImplementedInterfacesLabel)); 362 dl.add(HtmlTree.DD( 363 getClassLinks(HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, interfaces))); 364 target.add(dl); 365 } 366 } 367 368 @Override 369 public void addSuperInterfacesInfo(Content target) { 370 SortedSet<TypeMirror> interfaces 371 = new TreeSet<>(comparators.makeTypeMirrorIndexUseComparator()); 372 interfaces.addAll(utils.getAllInterfaces(typeElement)); 373 374 if (utils.isPlainInterface(typeElement) && !interfaces.isEmpty()) { 375 var dl = HtmlTree.DL(HtmlStyle.notes); 376 dl.add(HtmlTree.DT(contents.allSuperinterfacesLabel)); 377 dl.add(HtmlTree.DD( 378 getClassLinks(HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, interfaces))); 379 target.add(dl); 380 } 381 } 382 383 @Override 384 public void addNestedClassInfo(final Content target) { 385 Element outerClass = typeElement.getEnclosingElement(); 386 if (outerClass == null) 387 return; 388 new SimpleElementVisitor8<Void, Void>() { 389 @Override 390 public Void visitType(TypeElement e, Void p) { 391 var dl = HtmlTree.DL(HtmlStyle.notes); 392 dl.add(HtmlTree.DT(utils.isPlainInterface(e) 393 ? contents.enclosingInterfaceLabel 394 : contents.enclosingClassLabel)); 395 dl.add(HtmlTree.DD( 396 getClassLinks(HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS, 397 List.of(e)))); 398 target.add(dl); 399 return null; 400 } 401 }.visit(outerClass); 402 } 403 404 @Override 405 public void addFunctionalInterfaceInfo(Content target) { 406 if (isFunctionalInterface()) { 407 var dl = HtmlTree.DL(HtmlStyle.notes); 408 dl.add(HtmlTree.DT(contents.functionalInterface)); 409 var dd = new HtmlTree(TagName.DD); 410 dd.add(contents.functionalInterfaceMessage); 411 dl.add(dd); 412 target.add(dl); 413 } 414 } 415 416 public boolean isFunctionalInterface() { 417 List<? extends AnnotationMirror> annotationMirrors 418 = typeElement.getAnnotationMirrors(); 419 for (AnnotationMirror anno : annotationMirrors) { 420 if (utils.isFunctionalInterface(anno)) { 421 return true; 422 } 423 } 424 return false; 425 } 426 427 @Override 428 public void addClassDeprecationInfo(Content classInfo) { 429 List<? extends DeprecatedTree> deprs 430 = utils.getDeprecatedTrees(typeElement); 431 if (utils.isDeprecated(typeElement)) { 432 var deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, 433 getDeprecatedPhrase(typeElement)); 434 var div = HtmlTree.DIV(HtmlStyle.deprecationBlock, deprLabel); 435 if (!deprs.isEmpty()) { 436 CommentHelper ch = utils.getCommentHelper(typeElement); 437 DocTree dt = deprs.get(0); 438 List<? extends DocTree> commentTags = ch.getBody(dt); 439 if (!commentTags.isEmpty()) { 440 addInlineDeprecatedComment(typeElement, deprs.get(0), div); 441 } 442 } 443 classInfo.add(div); 444 } 445 } 446 447 /** 448 * Get the links to the given classes. 449 * 450 * @param context the id of the context where the links will be added 451 * @param list the classes 452 * @return the links 453 */ 454 private Content getClassLinks(HtmlLinkInfo.Kind context, 455 Collection<?> list) { 456 Content content = new ContentBuilder(); 457 boolean isFirst = true; 458 for (Object type : list) { 459 if (!isFirst) { 460 content.add(Text.of(", ")); 461 } else { 462 isFirst = false; 463 } 464 // TODO: should we simply split this method up to avoid instanceof ? 465 if (type instanceof TypeElement te) { 466 Content link = getLink( 467 new HtmlLinkInfo(configuration, context, te)); 468 content.add(HtmlTree.CODE(link)); 469 } else { 470 Content link = getLink( 471 new HtmlLinkInfo(configuration, context, 472 ((TypeMirror) type))); 473 content.add(HtmlTree.CODE(link)); 474 } 475 } 476 return content; 477 } 478 479 /** 480 * Return the TypeElement being documented. 481 * 482 * @return the TypeElement being documented. 483 */ 484 @Override 485 public TypeElement getTypeElement() { 486 return typeElement; 487 } 488 489 @Override 490 public Content getMemberDetails(Content content) { 491 var section = HtmlTree.SECTION(HtmlStyle.details, content); 492 // The following id is required by the Navigation bar 493 if (utils.isAnnotationInterface(typeElement)) { 494 section.setId(HtmlIds.ANNOTATION_TYPE_ELEMENT_DETAIL); 495 } 496 return section; 497 } 498}