001/*
002 * Copyright (c) 1998, 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.util;
027
028import java.util.*;
029import java.util.function.Predicate;
030
031import javax.lang.model.element.Element;
032import javax.lang.model.element.ModuleElement;
033import javax.lang.model.element.PackageElement;
034import javax.lang.model.element.RecordComponentElement;
035import javax.lang.model.element.TypeElement;
036
037import org.jdrupes.mdoclet.internal.doclets.toolkit.BaseConfiguration;
038
039/**
040 * Build list of all the summary packages, classes, constructors, fields and methods.
041 */
042public class SummaryAPIListBuilder {
043    /**
044     * List of summary type Lists.
045     */
046    private final Map<SummaryElementKind, SortedSet<Element>> summaryMap;
047    protected final BaseConfiguration configuration;
048    protected final Utils utils;
049    private final Predicate<Element> belongsToSummary;
050
051    public enum SummaryElementKind {
052        MODULE,
053        PACKAGE,
054        INTERFACE,
055        CLASS,
056        ENUM,
057        EXCEPTION_CLASS,        // no ElementKind mapping
058        RECORD_CLASS,
059        ANNOTATION_TYPE,
060        FIELD,
061        METHOD,
062        CONSTRUCTOR,
063        ENUM_CONSTANT,
064        ANNOTATION_TYPE_MEMBER // no ElementKind mapping
065    };
066
067    /**
068     * Constructor.
069     *
070     * @param configuration the current configuration of the doclet
071     */
072    public SummaryAPIListBuilder(BaseConfiguration configuration,
073            Predicate<Element> belongsToSummary) {
074        this.configuration = configuration;
075        this.utils = configuration.utils;
076        this.belongsToSummary = belongsToSummary;
077        summaryMap = new EnumMap<>(SummaryElementKind.class);
078        for (SummaryElementKind kind : SummaryElementKind.values()) {
079            summaryMap.put(kind, createSummarySet());
080        }
081    }
082
083    public boolean isEmpty() {
084        return summaryMap.values().stream().allMatch(Set::isEmpty);
085    }
086
087    /**
088     * Build the sorted list of all the summary APIs in this run.
089     * Build separate lists for summary modules, packages, classes, constructors,
090     * methods and fields.
091     */
092    protected void buildSummaryAPIInfo() {
093        SortedSet<ModuleElement> modules = configuration.modules;
094        SortedSet<Element> mset = summaryMap.get(SummaryElementKind.MODULE);
095        for (Element me : modules) {
096            if (belongsToSummary.test(me)) {
097                mset.add(me);
098                handleElement(me);
099            }
100        }
101        SortedSet<PackageElement> packages = configuration.packages;
102        SortedSet<Element> pset = summaryMap.get(SummaryElementKind.PACKAGE);
103        for (Element pe : packages) {
104            if (belongsToSummary.test(pe)) {
105                pset.add(pe);
106                handleElement(pe);
107            }
108        }
109        for (TypeElement te : configuration.getIncludedTypeElements()) {
110            SortedSet<Element> eset;
111            if (belongsToSummary.test(te)) {
112                switch (te.getKind()) {
113                case ANNOTATION_TYPE -> {
114                    eset = summaryMap.get(SummaryElementKind.ANNOTATION_TYPE);
115                    eset.add(te);
116                }
117                case CLASS -> {
118                    if (utils.isThrowable(te)) {
119                        eset = summaryMap
120                            .get(SummaryElementKind.EXCEPTION_CLASS);
121                    } else {
122                        eset = summaryMap.get(SummaryElementKind.CLASS);
123                    }
124                    eset.add(te);
125                }
126                case INTERFACE -> {
127                    eset = summaryMap.get(SummaryElementKind.INTERFACE);
128                    eset.add(te);
129                }
130                case ENUM -> {
131                    eset = summaryMap.get(SummaryElementKind.ENUM);
132                    eset.add(te);
133                }
134                case RECORD -> {
135                    eset = summaryMap.get(SummaryElementKind.RECORD_CLASS);
136                    eset.add(te);
137                }
138                }
139                handleElement(te);
140            }
141            composeSummaryList(summaryMap.get(SummaryElementKind.FIELD),
142                utils.getFields(te));
143            composeSummaryList(summaryMap.get(SummaryElementKind.METHOD),
144                utils.getMethods(te));
145            composeSummaryList(summaryMap.get(SummaryElementKind.CONSTRUCTOR),
146                utils.getConstructors(te));
147            if (utils.isEnum(te)) {
148                composeSummaryList(
149                    summaryMap.get(SummaryElementKind.ENUM_CONSTANT),
150                    utils.getEnumConstants(te));
151            }
152            if (utils.isRecord(te)) {
153                for (RecordComponentElement component : te
154                    .getRecordComponents()) {
155                    if (belongsToSummary.test(component)) {
156                        throw new AssertionError(
157                            "record components not supported in summary builders: "
158                                +
159                                "component: " + component.getSimpleName() +
160                                " of record: " + te.getQualifiedName());
161                    }
162                }
163            }
164            if (utils.isAnnotationInterface(te)) {
165                composeSummaryList(
166                    summaryMap.get(SummaryElementKind.ANNOTATION_TYPE_MEMBER),
167                    utils.getAnnotationMembers(te));
168
169            }
170        }
171    }
172
173    /**
174     * Add the members into a single list of summary members.
175     *
176     * @param sset set of summary elements
177     * @param members members to be added in the list
178     */
179    private void composeSummaryList(SortedSet<Element> sset,
180            List<? extends Element> members) {
181        for (Element member : members) {
182            if (belongsToSummary.test(member)) {
183                sset.add(member);
184                handleElement(member);
185            }
186        }
187    }
188
189    /**
190     * Return the list of summary elements of a given type.
191     *
192     * @param kind the SummaryElementKind
193     * @return
194     */
195    public SortedSet<Element> getSet(SummaryElementKind kind) {
196        return summaryMap.get(kind);
197    }
198
199    /**
200     * Return true if the list of a given type has size greater than 0.
201     *
202     * @param kind the type of list being checked.
203     */
204    public boolean hasDocumentation(SummaryElementKind kind) {
205        return !summaryMap.get(kind).isEmpty();
206    }
207
208    /**
209     * Additional extra processing of an included element.
210     *
211     * @param e element to process
212     */
213    protected void handleElement(Element e) {
214    }
215
216    /**
217     * Create a summary set of elements.
218     *
219     * @return a summary set
220     */
221    protected final SortedSet<Element> createSummarySet() {
222        return new TreeSet<>(utils.comparators.makeSummaryComparator());
223    }
224}