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