001/* 002 * Copyright (c) 1998, 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.HashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.Set; 033import java.util.SortedSet; 034import java.util.TreeSet; 035 036import javax.lang.model.element.Element; 037import javax.lang.model.element.PackageElement; 038import javax.lang.model.element.TypeElement; 039import javax.tools.Diagnostic; 040 041import org.jdrupes.mdoclet.internal.doclets.formats.html.Navigation.PageMode; 042import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder; 043import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle; 044import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlTree; 045import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.TagName; 046import org.jdrupes.mdoclet.internal.doclets.toolkit.Content; 047import org.jdrupes.mdoclet.internal.doclets.toolkit.util.ClassTree; 048import org.jdrupes.mdoclet.internal.doclets.toolkit.util.ClassUseMapper; 049import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocFileIOException; 050import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPath; 051import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPaths; 052import org.jdrupes.mdoclet.internal.doclets.toolkit.util.Utils; 053 054/** 055 * Generate class usage information. 056 */ 057public class ClassUseWriter extends SubWriterHolderWriter { 058 059 final TypeElement typeElement; 060 Set<PackageElement> pkgToPackageAnnotations = null; 061 final Map<PackageElement, List<Element>> pkgToClassTypeParameter; 062 final Map<PackageElement, List<Element>> pkgToClassAnnotations; 063 final Map<PackageElement, List<Element>> pkgToMethodTypeParameter; 064 final Map<PackageElement, List<Element>> pkgToMethodArgTypeParameter; 065 final Map<PackageElement, List<Element>> pkgToMethodReturnTypeParameter; 066 final Map<PackageElement, List<Element>> pkgToMethodAnnotations; 067 final Map<PackageElement, List<Element>> pkgToMethodParameterAnnotations; 068 final Map<PackageElement, List<Element>> pkgToFieldTypeParameter; 069 final Map<PackageElement, List<Element>> pkgToFieldAnnotations; 070 final Map<PackageElement, List<Element>> pkgToSubclass; 071 final Map<PackageElement, List<Element>> pkgToSubinterface; 072 final Map<PackageElement, List<Element>> pkgToImplementingClass; 073 final Map<PackageElement, List<Element>> pkgToField; 074 final Map<PackageElement, List<Element>> pkgToMethodReturn; 075 final Map<PackageElement, List<Element>> pkgToMethodArgs; 076 final Map<PackageElement, List<Element>> pkgToMethodThrows; 077 final Map<PackageElement, List<Element>> pkgToConstructorAnnotations; 078 final Map<PackageElement, 079 List<Element>> pkgToConstructorParameterAnnotations; 080 final Map<PackageElement, List<Element>> pkgToConstructorArgs; 081 final Map<PackageElement, List<Element>> pkgToConstructorArgTypeParameter; 082 final Map<PackageElement, List<Element>> pkgToConstructorThrows; 083 final SortedSet<PackageElement> pkgSet; 084 final MethodWriterImpl methodSubWriter; 085 final ConstructorWriterImpl constrSubWriter; 086 final FieldWriterImpl fieldSubWriter; 087 final NestedClassWriterImpl classSubWriter; 088 089 /** 090 * Constructor. 091 * 092 * @param filename the file to be generated. 093 */ 094 public ClassUseWriter(HtmlConfiguration configuration, 095 ClassUseMapper mapper, DocPath filename, 096 TypeElement typeElement) { 097 super(configuration, filename); 098 this.typeElement = typeElement; 099 if (mapper.classToPackageAnnotations.containsKey(typeElement)) { 100 pkgToPackageAnnotations 101 = new TreeSet<>(comparators.makeClassUseComparator()); 102 pkgToPackageAnnotations 103 .addAll(mapper.classToPackageAnnotations.get(typeElement)); 104 } 105 configuration.currentTypeElement = typeElement; 106 this.pkgSet = new TreeSet<>(comparators.makePackageComparator()); 107 this.pkgToClassTypeParameter = pkgDivide(mapper.classToClassTypeParam); 108 this.pkgToClassAnnotations = pkgDivide(mapper.classToClassAnnotations); 109 this.pkgToMethodTypeParameter 110 = pkgDivide(mapper.classToMethodTypeParam); 111 this.pkgToMethodArgTypeParameter 112 = pkgDivide(mapper.classToMethodArgTypeParam); 113 this.pkgToFieldTypeParameter = pkgDivide(mapper.classToFieldTypeParam); 114 this.pkgToFieldAnnotations = pkgDivide(mapper.annotationToField); 115 this.pkgToMethodReturnTypeParameter 116 = pkgDivide(mapper.classToMethodReturnTypeParam); 117 this.pkgToMethodAnnotations 118 = pkgDivide(mapper.classToMethodAnnotations); 119 this.pkgToMethodParameterAnnotations 120 = pkgDivide(mapper.classToMethodParamAnnotation); 121 this.pkgToSubclass = pkgDivide(mapper.classToSubclass); 122 this.pkgToSubinterface = pkgDivide(mapper.classToSubinterface); 123 this.pkgToImplementingClass 124 = pkgDivide(mapper.classToImplementingClass); 125 this.pkgToField = pkgDivide(mapper.classToField); 126 this.pkgToMethodReturn = pkgDivide(mapper.classToMethodReturn); 127 this.pkgToMethodArgs = pkgDivide(mapper.classToMethodArgs); 128 this.pkgToMethodThrows = pkgDivide(mapper.classToMethodThrows); 129 this.pkgToConstructorAnnotations 130 = pkgDivide(mapper.classToConstructorAnnotations); 131 this.pkgToConstructorParameterAnnotations 132 = pkgDivide(mapper.classToConstructorParamAnnotation); 133 this.pkgToConstructorArgs = pkgDivide(mapper.classToConstructorArgs); 134 this.pkgToConstructorArgTypeParameter 135 = pkgDivide(mapper.classToConstructorArgTypeParam); 136 this.pkgToConstructorThrows 137 = pkgDivide(mapper.classToConstructorThrows); 138 // tmp test 139 if (pkgSet.size() > 0 && 140 mapper.classToPackage.containsKey(this.typeElement) && 141 !pkgSet.equals(mapper.classToPackage.get(this.typeElement))) { 142 configuration.reporter.print(Diagnostic.Kind.WARNING, 143 "Internal error: package sets don't match: " 144 + pkgSet + " with: " 145 + mapper.classToPackage.get(this.typeElement)); 146 } 147 148 methodSubWriter = new MethodWriterImpl(this); 149 constrSubWriter = new ConstructorWriterImpl(this); 150 constrSubWriter.setFoundNonPubConstructor(true); 151 fieldSubWriter = new FieldWriterImpl(this); 152 classSubWriter = new NestedClassWriterImpl(this); 153 } 154 155 /** 156 * Write out class use pages. 157 * 158 * @param configuration the configuration for this doclet 159 * @param classTree the class tree hierarchy 160 * @throws DocFileIOException if there is an error while generating the documentation 161 */ 162 public static void generate(HtmlConfiguration configuration, 163 ClassTree classTree) throws DocFileIOException { 164 ClassUseMapper mapper = new ClassUseMapper(configuration, classTree); 165 boolean nodeprecated = configuration.getOptions().noDeprecated(); 166 Utils utils = configuration.utils; 167 for (TypeElement aClass : configuration.getIncludedTypeElements()) { 168 // If -nodeprecated option is set and the containing package is 169 // marked 170 // as deprecated, do not generate the class-use page. We will still 171 // generate 172 // the class-use page if the class is marked as deprecated but the 173 // containing 174 // package is not since it could still be linked from that 175 // package-use page. 176 if (!(nodeprecated && 177 utils.isDeprecated(utils.containingPackage(aClass)))) 178 ClassUseWriter.generate(configuration, mapper, aClass); 179 } 180 for (PackageElement pkg : configuration.packages) { 181 // If -nodeprecated option is set and the package is marked 182 // as deprecated, do not generate the package-use page. 183 if (!(nodeprecated && utils.isDeprecated(pkg))) 184 PackageUseWriter.generate(configuration, mapper, pkg); 185 } 186 } 187 188 private Map<PackageElement, List<Element>> pkgDivide( 189 Map<TypeElement, ? extends List<? extends Element>> classMap) { 190 Map<PackageElement, List<Element>> map = new HashMap<>(); 191 List<? extends Element> elements = classMap.get(typeElement); 192 if (elements != null) { 193 elements.sort(comparators.makeClassUseComparator()); 194 for (Element e : elements) { 195 PackageElement pkg = utils.containingPackage(e); 196 pkgSet.add(pkg); 197 map.computeIfAbsent(pkg, k -> new ArrayList<>()).add(e); 198 } 199 } 200 return map; 201 } 202 203 /** 204 * Generate a class page. 205 * 206 * @throws DocFileIOException if there is a problem while generating the documentation 207 */ 208 public static void generate(HtmlConfiguration configuration, 209 ClassUseMapper mapper, 210 TypeElement typeElement) throws DocFileIOException { 211 ClassUseWriter clsgen; 212 DocPath path = configuration.docPaths.forPackage(typeElement) 213 .resolve(DocPaths.CLASS_USE) 214 .resolve(configuration.docPaths.forName(typeElement)); 215 clsgen = new ClassUseWriter(configuration, mapper, path, typeElement); 216 clsgen.generateClassUseFile(); 217 } 218 219 /** 220 * Generate the class use elements. 221 * 222 * @throws DocFileIOException if there is a problem while generating the documentation 223 */ 224 protected void generateClassUseFile() throws DocFileIOException { 225 HtmlTree body = getClassUseHeader(); 226 Content mainContent = new ContentBuilder(); 227 if (pkgSet.size() > 0) { 228 addClassUse(mainContent); 229 } else { 230 mainContent.add(contents.getContent("doclet.ClassUse_No.usage.of.0", 231 utils.getFullyQualifiedName(typeElement))); 232 } 233 bodyContents.addMainContent(mainContent); 234 bodyContents.setFooter(getFooter()); 235 body.add(bodyContents); 236 String description = getDescription("use", typeElement); 237 printHtmlDocument(null, description, body); 238 } 239 240 /** 241 * Add the class use documentation. 242 * 243 * @param content the content to which the class use information will be added 244 */ 245 protected void addClassUse(Content content) { 246 Content c = new ContentBuilder(); 247 if (configuration.packages.size() > 1) { 248 addPackageList(c); 249 addPackageAnnotationList(c); 250 } 251 addClassList(c); 252 content.add(c); 253 } 254 255 /** 256 * Add the packages elements that use the given class. 257 * 258 * @param content the content to which the packages elements will be added 259 */ 260 protected void addPackageList(Content content) { 261 Content caption = contents.getContent( 262 "doclet.ClassUse_Packages.that.use.0", 263 getLink(new HtmlLinkInfo(configuration, 264 HtmlLinkInfo.Kind.PLAIN, typeElement))); 265 var table = new Table<Void>(HtmlStyle.summaryTable) 266 .setCaption(caption) 267 .setHeader(getPackageTableHeader()) 268 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); 269 for (PackageElement pkg : pkgSet) { 270 addPackageUse(pkg, table); 271 } 272 content.add(table); 273 } 274 275 /** 276 * Add the package annotation elements. 277 * 278 * @param content the content to which the package annotation elements will be added 279 */ 280 protected void addPackageAnnotationList(Content content) { 281 if (!utils.isAnnotationInterface(typeElement) || 282 pkgToPackageAnnotations == null || 283 pkgToPackageAnnotations.isEmpty()) { 284 return; 285 } 286 Content caption = contents.getContent( 287 "doclet.ClassUse_PackageAnnotation", 288 getLink(new HtmlLinkInfo(configuration, 289 HtmlLinkInfo.Kind.PLAIN, typeElement))); 290 291 var table = new Table<Void>(HtmlStyle.summaryTable) 292 .setCaption(caption) 293 .setHeader(getPackageTableHeader()) 294 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); 295 for (PackageElement pkg : pkgToPackageAnnotations) { 296 Content summary = new ContentBuilder(); 297 addSummaryComment(pkg, summary); 298 table.addRow(getPackageLink(pkg, getLocalizedPackageName(pkg)), 299 summary); 300 } 301 content.add(table); 302 } 303 304 /** 305 * Add the class elements that use the given class. 306 * 307 * @param content the content to which the class elements will be added 308 */ 309 protected void addClassList(Content content) { 310 var ul = HtmlTree.UL(HtmlStyle.blockList); 311 for (PackageElement pkg : pkgSet) { 312 var section = HtmlTree.SECTION(HtmlStyle.detail) 313 .setId(htmlIds.forPackage(pkg)); 314 Content link = contents.getContent("doclet.ClassUse_Uses.of.0.in.1", 315 getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, 316 typeElement)), 317 getPackageLink(pkg, getLocalizedPackageName(pkg))); 318 var heading 319 = HtmlTree.HEADING(Headings.TypeUse.SUMMARY_HEADING, link); 320 section.add(heading); 321 addClassUse(pkg, section); 322 ul.add(HtmlTree.LI(section)); 323 } 324 var li = HtmlTree.SECTION(HtmlStyle.classUses, ul); 325 content.add(li); 326 } 327 328 /** 329 * Add the package use information. 330 * 331 * @param pkg the package that uses the given class 332 * @param table the table to which the package use information will be added 333 */ 334 protected void addPackageUse(PackageElement pkg, Table<?> table) { 335 Content pkgLink = links.createLink(htmlIds.forPackage(pkg), 336 getLocalizedPackageName(pkg)); 337 Content summary = new ContentBuilder(); 338 addSummaryComment(pkg, summary); 339 table.addRow(pkgLink, summary); 340 } 341 342 /** 343 * Add the class use information. 344 * 345 * @param pkg the package that uses the given class 346 * @param content the content to which the class use information will be added 347 */ 348 protected void addClassUse(PackageElement pkg, Content content) { 349 Content classLink = getLink(new HtmlLinkInfo(configuration, 350 HtmlLinkInfo.Kind.PLAIN, typeElement)); 351 Content pkgLink = getPackageLink(pkg, getLocalizedPackageName(pkg)); 352 classSubWriter.addUseInfo(pkgToClassAnnotations.get(pkg), 353 contents.getContent("doclet.ClassUse_Annotation", classLink, 354 pkgLink), 355 content); 356 classSubWriter.addUseInfo(pkgToClassTypeParameter.get(pkg), 357 contents.getContent("doclet.ClassUse_TypeParameter", classLink, 358 pkgLink), 359 content); 360 classSubWriter.addUseInfo(pkgToSubclass.get(pkg), 361 contents.getContent("doclet.ClassUse_Subclass", classLink, 362 pkgLink), 363 content); 364 classSubWriter.addUseInfo(pkgToSubinterface.get(pkg), 365 contents.getContent("doclet.ClassUse_Subinterface", classLink, 366 pkgLink), 367 content); 368 classSubWriter.addUseInfo(pkgToImplementingClass.get(pkg), 369 contents.getContent("doclet.ClassUse_ImplementingClass", classLink, 370 pkgLink), 371 content); 372 fieldSubWriter.addUseInfo(pkgToField.get(pkg), 373 contents.getContent("doclet.ClassUse_Field", classLink, 374 pkgLink), 375 content); 376 fieldSubWriter.addUseInfo(pkgToFieldAnnotations.get(pkg), 377 contents.getContent("doclet.ClassUse_FieldAnnotations", classLink, 378 pkgLink), 379 content); 380 fieldSubWriter.addUseInfo(pkgToFieldTypeParameter.get(pkg), 381 contents.getContent("doclet.ClassUse_FieldTypeParameter", classLink, 382 pkgLink), 383 content); 384 methodSubWriter.addUseInfo(pkgToMethodAnnotations.get(pkg), 385 contents.getContent("doclet.ClassUse_MethodAnnotations", classLink, 386 pkgLink), 387 content); 388 methodSubWriter.addUseInfo(pkgToMethodParameterAnnotations.get(pkg), 389 contents.getContent("doclet.ClassUse_MethodParameterAnnotations", 390 classLink, 391 pkgLink), 392 content); 393 methodSubWriter.addUseInfo(pkgToMethodTypeParameter.get(pkg), 394 contents.getContent("doclet.ClassUse_MethodTypeParameter", 395 classLink, 396 pkgLink), 397 content); 398 methodSubWriter.addUseInfo(pkgToMethodReturn.get(pkg), 399 contents.getContent("doclet.ClassUse_MethodReturn", classLink, 400 pkgLink), 401 content); 402 methodSubWriter.addUseInfo(pkgToMethodReturnTypeParameter.get(pkg), 403 contents.getContent("doclet.ClassUse_MethodReturnTypeParameter", 404 classLink, 405 pkgLink), 406 content); 407 methodSubWriter.addUseInfo(pkgToMethodArgs.get(pkg), 408 contents.getContent("doclet.ClassUse_MethodArgs", classLink, 409 pkgLink), 410 content); 411 methodSubWriter.addUseInfo(pkgToMethodArgTypeParameter.get(pkg), 412 contents.getContent("doclet.ClassUse_MethodArgsTypeParameters", 413 classLink, 414 pkgLink), 415 content); 416 methodSubWriter.addUseInfo(pkgToMethodThrows.get(pkg), 417 contents.getContent("doclet.ClassUse_MethodThrows", classLink, 418 pkgLink), 419 content); 420 constrSubWriter.addUseInfo(pkgToConstructorAnnotations.get(pkg), 421 contents.getContent("doclet.ClassUse_ConstructorAnnotations", 422 classLink, 423 pkgLink), 424 content); 425 constrSubWriter.addUseInfo( 426 pkgToConstructorParameterAnnotations.get(pkg), 427 contents.getContent( 428 "doclet.ClassUse_ConstructorParameterAnnotations", classLink, 429 pkgLink), 430 content); 431 constrSubWriter.addUseInfo(pkgToConstructorArgs.get(pkg), 432 contents.getContent("doclet.ClassUse_ConstructorArgs", classLink, 433 pkgLink), 434 content); 435 constrSubWriter.addUseInfo(pkgToConstructorArgTypeParameter.get(pkg), 436 contents.getContent("doclet.ClassUse_ConstructorArgsTypeParameters", 437 classLink, 438 pkgLink), 439 content); 440 constrSubWriter.addUseInfo(pkgToConstructorThrows.get(pkg), 441 contents.getContent("doclet.ClassUse_ConstructorThrows", classLink, 442 pkgLink), 443 content); 444 } 445 446 /** 447 * Get the header for the class use listing. 448 * 449 * @return the class use header 450 */ 451 protected HtmlTree getClassUseHeader() { 452 String cltype = resources.getText(switch (typeElement.getKind()) { 453 case ANNOTATION_TYPE -> "doclet.AnnotationType"; 454 case INTERFACE -> "doclet.Interface"; 455 case RECORD -> "doclet.RecordClass"; 456 case ENUM -> "doclet.Enum"; 457 default -> "doclet.Class"; 458 }); 459 String clname = utils.getFullyQualifiedName(typeElement); 460 String title = resources.getText("doclet.Window_ClassUse_Header", 461 cltype, clname); 462 HtmlTree body = getBody(getWindowTitle(title)); 463 ContentBuilder headingContent = new ContentBuilder(); 464 headingContent 465 .add(contents.getContent("doclet.ClassUse_Title", cltype)); 466 headingContent.add(new HtmlTree(TagName.BR)); 467 headingContent.add(clname); 468 var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, 469 HtmlStyle.title, headingContent); 470 var div = HtmlTree.DIV(HtmlStyle.header, heading); 471 bodyContents.setHeader(getHeader(PageMode.USE, typeElement)) 472 .addMainContent(div); 473 return body; 474 } 475 476 @Override 477 protected Navigation getNavBar(PageMode pageMode, Element element) { 478 Content mdleLinkContent 479 = getModuleLink(utils.elementUtils.getModuleOf(typeElement), 480 contents.moduleLabel); 481 Content classLinkContent = getLink(new HtmlLinkInfo( 482 configuration, HtmlLinkInfo.Kind.PLAIN, typeElement) 483 .label(resources.getText("doclet.Class")) 484 .skipPreview(true)); 485 return super.getNavBar(pageMode, element) 486 .setNavLinkModule(mdleLinkContent) 487 .setNavLinkClass(classLinkContent); 488 } 489}