1 /*
   2  * Copyright (c) 2003, 2016, 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 jdk.javadoc.internal.doclets.toolkit.builders;
  27 
  28 import java.io.*;
  29 import java.util.*;
  30 
  31 import javax.lang.model.element.Element;
  32 import javax.lang.model.element.PackageElement;
  33 import javax.lang.model.element.TypeElement;
  34 import javax.lang.model.element.VariableElement;
  35 
  36 import jdk.javadoc.internal.doclets.toolkit.ConstantsSummaryWriter;
  37 import jdk.javadoc.internal.doclets.toolkit.Content;
  38 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap;
  39 
  40 
  41 /**
  42  * Builds the Constants Summary Page.
  43  *
  44  *  <p><b>This is NOT part of any supported API.
  45  *  If you write code that depends on this, you do so at your own risk.
  46  *  This code and its internal interfaces are subject to change or
  47  *  deletion without notice.</b>
  48  *
  49  * @author Jamie Ho
  50  * @author Bhavesh Patel (Modified)
  51  * @since 1.5
  52  */
  53 public class ConstantsSummaryBuilder extends AbstractBuilder {
  54 
  55     /**
  56      * The root element of the constant summary XML is {@value}.
  57      */
  58     public static final String ROOT = "ConstantSummary";
  59 
  60     /**
  61      * The maximum number of package directories shown in the constant
  62      * value index.
  63      */
  64     public static final int MAX_CONSTANT_VALUE_INDEX_LENGTH = 2;
  65 
  66     /**
  67      * The writer used to write the results.
  68      */
  69     protected final ConstantsSummaryWriter writer;
  70 
  71     /**
  72      * The set of TypeElements that have constant fields.
  73      */
  74     protected final Set<TypeElement> typeElementsWithConstFields;
  75 
  76     /**
  77      * The set of printed package headers.
  78      */
  79     protected final Set<PackageElement> printedPackageHeaders;
  80 
  81     /**
  82      * The current package being documented.
  83      */
  84     private PackageElement currentPackage;
  85 
  86     /**
  87      * The current class being documented.
  88      */
  89     private TypeElement currentClass;
  90 
  91     /**
  92      * The content tree for the constant summary documentation.
  93      */
  94     private Content contentTree;
  95 
  96     /**
  97      * True if first package is listed.
  98      */
  99     private boolean first = true;
 100 
 101     /**
 102      * Construct a new ConstantsSummaryBuilder.
 103      *
 104      * @param context       the build context.
 105      * @param writer        the writer for the summary.
 106      */
 107     private ConstantsSummaryBuilder(Context context,
 108             ConstantsSummaryWriter writer) {
 109         super(context);
 110         this.writer = writer;
 111         this.typeElementsWithConstFields = new HashSet<>();
 112         this.printedPackageHeaders = new TreeSet<>(utils.makePackageComparator());
 113     }
 114 
 115     /**
 116      * Construct a ConstantsSummaryBuilder.
 117      *
 118      * @param context       the build context.
 119      * @param writer        the writer for the summary.
 120      */
 121     public static ConstantsSummaryBuilder getInstance(Context context,
 122             ConstantsSummaryWriter writer) {
 123         return new ConstantsSummaryBuilder(context, writer);
 124     }
 125 
 126     /**
 127      * {@inheritDoc}
 128      */
 129     @Override
 130     public void build() throws IOException {
 131         if (writer == null) {
 132             //Doclet does not support this output.
 133             return;
 134         }
 135         build(layoutParser.parseXML(ROOT), contentTree);
 136     }
 137 
 138     /**
 139      * {@inheritDoc}
 140      */
 141     @Override
 142     public String getName() {
 143         return ROOT;
 144     }
 145 
 146     /**
 147      * Build the constant summary.
 148      *
 149      * @param node the XML element that specifies which components to document
 150      * @param contentTree the content tree to which the documentation will be added
 151      */
 152     public void buildConstantSummary(XMLNode node, Content contentTree) throws Exception {
 153         contentTree = writer.getHeader();
 154         buildChildren(node, contentTree);
 155         writer.addFooter(contentTree);
 156         writer.printDocument(contentTree);
 157         writer.close();
 158     }
 159 
 160     /**
 161      * Build the list of packages.
 162      *
 163      * @param node the XML element that specifies which components to document
 164      * @param contentTree the content tree to which the content list will be added
 165      */
 166     public void buildContents(XMLNode node, Content contentTree) {
 167         Content contentListTree = writer.getContentsHeader();
 168         printedPackageHeaders.clear();
 169         for (PackageElement pkg : configuration.packages) {
 170             if (hasConstantField(pkg) && !hasPrintedPackageIndex(pkg)) {
 171                 writer.addLinkToPackageContent(pkg, printedPackageHeaders, contentListTree);
 172             }
 173         }
 174         writer.addContentsList(contentTree, contentListTree);
 175     }
 176 
 177     /**
 178      * Build the summary for each documented package.
 179      *
 180      * @param node the XML element that specifies which components to document
 181      * @param contentTree the tree to which the summaries will be added
 182      */
 183     public void buildConstantSummaries(XMLNode node, Content contentTree) {
 184         printedPackageHeaders.clear();
 185         Content summariesTree = writer.getConstantSummaries();
 186         for (PackageElement aPackage : configuration.packages) {
 187             if (hasConstantField(aPackage)) {
 188                 currentPackage = aPackage;
 189                 //Build the documentation for the current package.
 190                 buildChildren(node, summariesTree);
 191                 first = false;
 192             }
 193         }
 194         writer.addConstantSummaries(contentTree, summariesTree);
 195     }
 196 
 197     /**
 198      * Build the header for the given package.
 199      *
 200      * @param node the XML element that specifies which components to document
 201      * @param summariesTree the tree to which the package header will be added
 202      */
 203     public void buildPackageHeader(XMLNode node, Content summariesTree) {
 204         String parsedPackageName = utils.parsePackageName(currentPackage);
 205         PackageElement p = utils.elementUtils.getPackageElement(parsedPackageName);
 206         if (!printedPackageHeaders.contains(p)) {
 207             writer.addPackageName(currentPackage, summariesTree, first);
 208             printedPackageHeaders.add(p);
 209         }
 210     }
 211 
 212     /**
 213      * Build the summary for the current class.
 214      *
 215      * @param node the XML element that specifies which components to document
 216      * @param summariesTree the tree to which the class constant summary will be added
 217      */
 218     public void buildClassConstantSummary(XMLNode node, Content summariesTree) {
 219         SortedSet<TypeElement> classes = !currentPackage.isUnnamed()
 220                 ? utils.getAllClasses(currentPackage)
 221                 : configuration.classDocCatalog.allUnnamedClasses();
 222         Content classConstantTree = writer.getClassConstantHeader();
 223         for (TypeElement te : classes) {
 224             if (!typeElementsWithConstFields.contains(te) ||
 225                 !utils.isIncluded(te)) {
 226                 continue;
 227             }
 228             currentClass = te;
 229             //Build the documentation for the current class.
 230             buildChildren(node, classConstantTree);
 231         }
 232         writer.addClassConstant(summariesTree, classConstantTree);
 233     }
 234 
 235     /**
 236      * Build the summary of constant members in the class.
 237      *
 238      * @param node the XML element that specifies which components to document
 239      * @param classConstantTree the tree to which the constant members table
 240      *                          will be added
 241      */
 242     public void buildConstantMembers(XMLNode node, Content classConstantTree) {
 243         new ConstantFieldBuilder(currentClass).buildMembersSummary(node, classConstantTree);
 244     }
 245 
 246     /**
 247      * Return true if the given package has constant fields to document.
 248      *
 249      * @param pkg   the package being checked.
 250      * @return true if the given package has constant fields to document.
 251      */
 252     private boolean hasConstantField(PackageElement pkg) {
 253         SortedSet<TypeElement> classes = !pkg.isUnnamed()
 254                   ? utils.getAllClasses(pkg)
 255                   : configuration.classDocCatalog.allUnnamedClasses();
 256         boolean found = false;
 257         for (TypeElement te : classes) {
 258             if (utils.isIncluded(te) && hasConstantField(te)) {
 259                 found = true;
 260             }
 261         }
 262         return found;
 263     }
 264 
 265     /**
 266      * Return true if the given class has constant fields to document.
 267      *
 268      * @param typeElement the class being checked.
 269      * @return true if the given package has constant fields to document.
 270      */
 271     private boolean hasConstantField (TypeElement typeElement) {
 272         VisibleMemberMap visibleMemberMapFields = new VisibleMemberMap(typeElement,
 273             VisibleMemberMap.Kind.FIELDS, configuration);
 274         SortedSet<Element> fields = visibleMemberMapFields.getLeafClassMembers();
 275         for (Element f : fields) {
 276             VariableElement field = (VariableElement)f;
 277             if (field.getConstantValue() != null) {
 278                 typeElementsWithConstFields.add(typeElement);
 279                 return true;
 280             }
 281         }
 282         return false;
 283     }
 284 
 285     /**
 286      * Return true if the given package name has been printed.  Also
 287      * return true if the root of this package has been printed.
 288      *
 289      * @param pkgname the name of the package to check.
 290      */
 291     private boolean hasPrintedPackageIndex(PackageElement pkg) {
 292         for (PackageElement printedPkg : printedPackageHeaders) {
 293             if (utils.getPackageName(pkg).startsWith(utils.parsePackageName(printedPkg))) {
 294                 return true;
 295             }
 296         }
 297         return false;
 298     }
 299 
 300     /**
 301      * Print the table of constants.
 302      *
 303      * @author Jamie Ho
 304      * @since 1.4
 305      */
 306     private class ConstantFieldBuilder {
 307 
 308         /**
 309          * The map used to get the visible variables.
 310          */
 311         protected VisibleMemberMap visibleMemberMapFields = null;
 312 
 313         /**
 314          * The map used to get the visible variables.
 315          */
 316         protected VisibleMemberMap visibleMemberMapEnumConst = null;
 317 
 318         /**
 319          * The typeElement that we are examining constants for.
 320          */
 321         protected TypeElement typeElement;
 322 
 323         /**
 324          * Construct a ConstantFieldSubWriter.
 325          * @param typeElement the typeElement that we are examining constants for.
 326          */
 327         public ConstantFieldBuilder(TypeElement typeElement) {
 328             this.typeElement = typeElement;
 329             visibleMemberMapFields = new VisibleMemberMap(typeElement,
 330                 VisibleMemberMap.Kind.FIELDS, configuration);
 331             visibleMemberMapEnumConst = new VisibleMemberMap(typeElement,
 332                 VisibleMemberMap.Kind.ENUM_CONSTANTS, configuration);
 333         }
 334 
 335         /**
 336          * Builds the table of constants for a given class.
 337          *
 338          * @param node the XML element that specifies which components to document
 339          * @param classConstantTree the tree to which the class constants table
 340          *                          will be added
 341          */
 342         protected void buildMembersSummary(XMLNode node, Content classConstantTree) {
 343             SortedSet<VariableElement> members = members();
 344             if (!members.isEmpty()) {
 345                 writer.addConstantMembers(typeElement, members, classConstantTree);
 346             }
 347         }
 348 
 349         /**
 350          * Return the list of visible constant fields for the given TypeElement.
 351          * @return the list of visible constant fields for the given TypeElement.
 352          */
 353         protected SortedSet<VariableElement> members() {
 354             SortedSet<Element> list = visibleMemberMapFields.getLeafClassMembers();
 355             list.addAll(visibleMemberMapEnumConst.getLeafClassMembers());
 356             SortedSet<VariableElement> inclList =
 357                     new TreeSet<>(utils.makeGeneralPurposeComparator());
 358             for (Element element : list) {
 359                 VariableElement member = (VariableElement)element;
 360                 if (member.getConstantValue() != null) {
 361                     inclList.add(member);
 362                 }
 363             }
 364             return inclList;
 365         }
 366     }
 367 }