001/* 002 * Copyright (c) 2003, 2022, 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.toolkit.builders; 027 028import static org.jdrupes.mdoclet.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*; 029 030import java.util.*; 031 032import javax.lang.model.element.Element; 033import javax.lang.model.element.PackageElement; 034import javax.lang.model.element.TypeElement; 035import javax.lang.model.element.VariableElement; 036 037import org.jdrupes.mdoclet.internal.doclets.toolkit.ConstantsSummaryWriter; 038import org.jdrupes.mdoclet.internal.doclets.toolkit.Content; 039import org.jdrupes.mdoclet.internal.doclets.toolkit.DocletException; 040import org.jdrupes.mdoclet.internal.doclets.toolkit.util.VisibleMemberTable; 041 042/** 043 * Builds the Constants Summary Page. 044 */ 045public class ConstantsSummaryBuilder extends AbstractBuilder { 046 047 /** 048 * The maximum number of package directories shown in the headings of 049 * the constant values contents list and headings. 050 */ 051 private static final int MAX_CONSTANT_VALUE_INDEX_LENGTH = 2; 052 053 /** 054 * The writer used to write the results. 055 */ 056 protected ConstantsSummaryWriter writer; 057 058 /** 059 * The set of type elements that have constant fields. 060 */ 061 protected final Set<TypeElement> typeElementsWithConstFields; 062 063 /** 064 * The set of package-group headings. 065 */ 066 protected final Set<String> packageGroupHeadings; 067 068 /** 069 * The current package being documented. 070 */ 071 private PackageElement currentPackage; 072 073 /** 074 * The current class being documented. 075 */ 076 private TypeElement currentClass; 077 078 /** 079 * Constructs a new {@code ConstantsSummaryBuilder}. 080 * 081 * @param context the build context 082 */ 083 private ConstantsSummaryBuilder(Context context) { 084 super(context); 085 this.typeElementsWithConstFields = new HashSet<>(); 086 this.packageGroupHeadings = new TreeSet<>(utils::compareStrings); 087 } 088 089 /** 090 * Constructs a {@code ConstantsSummaryBuilder}. 091 * 092 * @param context the build context 093 * @return the new ConstantsSummaryBuilder 094 */ 095 public static ConstantsSummaryBuilder getInstance(Context context) { 096 return new ConstantsSummaryBuilder(context); 097 } 098 099 @Override 100 public void build() throws DocletException { 101 boolean anyConstants 102 = configuration.packages.stream().anyMatch(this::hasConstantField); 103 if (!anyConstants) { 104 return; 105 } 106 107 writer = configuration.getWriterFactory().getConstantsSummaryWriter(); 108 if (writer == null) { 109 // Doclet does not support this output. 110 return; 111 } 112 buildConstantSummary(); 113 } 114 115 /** 116 * Builds the constant summary page. 117 * 118 * @throws DocletException if there is a problem while building the documentation 119 */ 120 protected void buildConstantSummary() throws DocletException { 121 Content content = writer.getHeader(); 122 123 buildContents(); 124 buildConstantSummaries(); 125 126 writer.addFooter(); 127 writer.printDocument(content); 128 } 129 130 /** 131 * Builds the list of contents for the groups of packages appearing in the constants summary page. 132 */ 133 protected void buildContents() { 134 Content contentList = writer.getContentsHeader(); 135 packageGroupHeadings.clear(); 136 for (PackageElement pkg : configuration.packages) { 137 String abbrevPackageName = getAbbrevPackageName(pkg); 138 if (hasConstantField(pkg) 139 && !packageGroupHeadings.contains(abbrevPackageName)) { 140 writer.addLinkToPackageContent(abbrevPackageName, contentList); 141 packageGroupHeadings.add(abbrevPackageName); 142 } 143 } 144 writer.addContentsList(contentList); 145 } 146 147 /** 148 * Builds the summary for each documented package. 149 * 150 * @throws DocletException if there is a problem while building the documentation 151 */ 152 protected void buildConstantSummaries() throws DocletException { 153 packageGroupHeadings.clear(); 154 Content summaries = writer.getConstantSummaries(); 155 for (PackageElement aPackage : configuration.packages) { 156 if (hasConstantField(aPackage)) { 157 currentPackage = aPackage; 158 // Build the documentation for the current package. 159 buildPackageHeader(summaries); 160 buildClassConstantSummary(); 161 } 162 } 163 writer.addConstantSummaries(summaries); 164 } 165 166 /** 167 * Builds the header for the given package. 168 * 169 * @param target the content to which the package header will be added 170 */ 171 protected void buildPackageHeader(Content target) { 172 String abbrevPkgName = getAbbrevPackageName(currentPackage); 173 if (!packageGroupHeadings.contains(abbrevPkgName)) { 174 writer.addPackageGroup(abbrevPkgName, target); 175 packageGroupHeadings.add(abbrevPkgName); 176 } 177 } 178 179 /** 180 * Builds the summary for the current class. 181 * 182 * @throws DocletException if there is a problem while building the documentation 183 */ 184 protected void buildClassConstantSummary() 185 throws DocletException { 186 SortedSet<TypeElement> classes = !currentPackage.isUnnamed() 187 ? utils.getAllClasses(currentPackage) 188 : configuration.typeElementCatalog.allUnnamedClasses(); 189 Content classConstantHeader = writer.getClassConstantHeader(); 190 for (TypeElement te : classes) { 191 if (!typeElementsWithConstFields.contains(te) || 192 !utils.isIncluded(te)) { 193 continue; 194 } 195 currentClass = te; 196 // Build the documentation for the current class. 197 198 buildConstantMembers(classConstantHeader); 199 200 } 201 writer.addClassConstant(classConstantHeader); 202 } 203 204 /** 205 * Builds the summary of constant members in the class. 206 * 207 * @param target the content to which the table of constant members will be added 208 */ 209 protected void buildConstantMembers(Content target) { 210 new ConstantFieldBuilder(currentClass).buildMembersSummary(target); 211 } 212 213 /** 214 * {@return true if the given package has constant fields to document} 215 * 216 * @param pkg the package to be checked 217 */ 218 private boolean hasConstantField(PackageElement pkg) { 219 SortedSet<TypeElement> classes = !pkg.isUnnamed() 220 ? utils.getAllClasses(pkg) 221 : configuration.typeElementCatalog.allUnnamedClasses(); 222 boolean found = false; 223 for (TypeElement te : classes) { 224 if (utils.isIncluded(te) && hasConstantField(te)) { 225 found = true; 226 } 227 } 228 return found; 229 } 230 231 /** 232 * {@return true if the given class has constant fields to document} 233 * 234 * @param typeElement the class to be checked 235 */ 236 private boolean hasConstantField(TypeElement typeElement) { 237 VisibleMemberTable vmt 238 = configuration.getVisibleMemberTable(typeElement); 239 List<? extends Element> fields = vmt.getVisibleMembers(FIELDS); 240 for (Element f : fields) { 241 VariableElement field = (VariableElement) f; 242 if (field.getConstantValue() != null) { 243 typeElementsWithConstFields.add(typeElement); 244 return true; 245 } 246 } 247 return false; 248 } 249 250 /** 251 * {@return the abbreviated name for a package, containing the leading segments of the name} 252 * 253 * @param pkg the package 254 */ 255 public String getAbbrevPackageName(PackageElement pkg) { 256 if (pkg.isUnnamed()) { 257 return ""; 258 } 259 260 String packageName = utils.getPackageName(pkg); 261 int index = -1; 262 for (int j = 0; j < MAX_CONSTANT_VALUE_INDEX_LENGTH; j++) { 263 index = packageName.indexOf(".", index + 1); 264 } 265 return index == -1 ? packageName : packageName.substring(0, index); 266 } 267 268 /** 269 * Builder for the table of fields with constant values. 270 */ 271 private class ConstantFieldBuilder { 272 273 /** 274 * The type element that we are examining constants for. 275 */ 276 protected TypeElement typeElement; 277 278 /** 279 * Constructs a {@code ConstantFieldBuilder}. 280 * @param typeElement the type element that we are examining constants for 281 */ 282 public ConstantFieldBuilder(TypeElement typeElement) { 283 this.typeElement = typeElement; 284 } 285 286 /** 287 * Builds the table of constants for a given class. 288 * 289 * @param target the content to which the table of class constants will be added 290 */ 291 protected void buildMembersSummary(Content target) { 292 SortedSet<VariableElement> members = members(); 293 if (!members.isEmpty()) { 294 writer.addConstantMembers(typeElement, members, target); 295 } 296 } 297 298 /** 299 * {@return a set of visible constant fields for the given type} 300 */ 301 protected SortedSet<VariableElement> members() { 302 VisibleMemberTable vmt 303 = configuration.getVisibleMemberTable(typeElement); 304 List<Element> members = new ArrayList<>(); 305 members.addAll(vmt.getVisibleMembers(FIELDS)); 306 members.addAll(vmt.getVisibleMembers(ENUM_CONSTANTS)); 307 SortedSet<VariableElement> includes = new TreeSet<>( 308 utils.comparators.makeGeneralPurposeComparator()); 309 for (Element element : members) { 310 VariableElement member = (VariableElement) element; 311 if (member.getConstantValue() != null) { 312 includes.add(member); 313 } 314 } 315 return includes; 316 } 317 } 318}