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;
027
028import java.util.Map;
029import java.util.SortedSet;
030import java.util.TreeSet;
031import java.util.function.Function;
032
033import javax.lang.model.SourceVersion;
034import javax.lang.model.element.PackageElement;
035import javax.lang.model.element.TypeElement;
036
037import org.jdrupes.mdoclet.internal.doclets.formats.html.HtmlDoclet;
038import org.jdrupes.mdoclet.internal.doclets.toolkit.builders.AbstractBuilder;
039import org.jdrupes.mdoclet.internal.doclets.toolkit.builders.BuilderFactory;
040import org.jdrupes.mdoclet.internal.doclets.toolkit.util.ClassTree;
041import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocFileIOException;
042import org.jdrupes.mdoclet.internal.doclets.toolkit.util.ElementListWriter;
043import org.jdrupes.mdoclet.internal.doclets.toolkit.util.InternalException;
044import org.jdrupes.mdoclet.internal.doclets.toolkit.util.ResourceIOException;
045import org.jdrupes.mdoclet.internal.doclets.toolkit.util.SimpleDocletException;
046import org.jdrupes.mdoclet.internal.doclets.toolkit.util.UncheckedDocletException;
047import org.jdrupes.mdoclet.internal.doclets.toolkit.util.Utils;
048
049import jdk.javadoc.doclet.Doclet;
050import jdk.javadoc.doclet.DocletEnvironment;
051import jdk.javadoc.doclet.StandardDoclet;
052
053import static javax.tools.Diagnostic.Kind.*;
054
055/**
056 * An abstract implementation of a Doclet.
057 */
058public abstract class AbstractDoclet implements Doclet {
059
060    /**
061     * The global configuration information for this run.
062     */
063    private BaseConfiguration configuration;
064
065    protected Messages messages;
066
067    /*
068     * a handle to our utility methods
069     */
070    protected Utils utils;
071
072    /**
073     * The only doclet that may use this toolkit is {@value}
074     */
075    private static final String TOOLKIT_DOCLET_NAME
076        = org.jdrupes.mdoclet.internal.doclets.formats.html.HtmlDoclet.class
077            .getName();
078
079    /**
080     * Verify that the only doclet that is using this toolkit is
081     * #TOOLKIT_DOCLET_NAME.
082     */
083    private boolean isValidDoclet() {
084        if (!getClass().getName().equals(TOOLKIT_DOCLET_NAME)) {
085            messages.error("doclet.Toolkit_Usage_Violation",
086                TOOLKIT_DOCLET_NAME);
087            return false;
088        }
089        return true;
090    }
091
092    /**
093     * The method that starts the execution of the doclet.
094     *
095     * @param docEnv   the {@link DocletEnvironment}.
096     * @return true if the doclet executed without error.  False otherwise.
097     */
098    @Override
099    public boolean run(DocletEnvironment docEnv) {
100        configuration = getConfiguration();
101        configuration.initConfiguration(docEnv, getResourceKeyMapper(docEnv));
102        utils = configuration.utils;
103        messages = configuration.getMessages();
104        BaseOptions options = configuration.getOptions();
105
106        if (!isValidDoclet()) {
107            return false;
108        }
109
110        try {
111            try {
112                startGeneration();
113                return true;
114            } catch (UncheckedDocletException e) {
115                throw (DocletException) e.getCause();
116            }
117
118        } catch (DocFileIOException e) {
119            switch (e.mode) {
120            case READ:
121                messages.error("doclet.exception.read.file",
122                    e.fileName.getPath(), e.getCause());
123                break;
124            case WRITE:
125                messages.error("doclet.exception.write.file",
126                    e.fileName.getPath(), e.getCause());
127            }
128            dumpStack(options.dumpOnError(), e);
129
130        } catch (ResourceIOException e) {
131            messages.error("doclet.exception.read.resource",
132                e.resource.getPath(), e.getCause());
133            dumpStack(options.dumpOnError(), e);
134
135        } catch (SimpleDocletException e) {
136            configuration.reporter.print(ERROR, e.getMessage());
137            dumpStack(options.dumpOnError(), e);
138
139        } catch (InternalException e) {
140            configuration.reporter.print(ERROR, e.getMessage());
141            reportInternalError(e.getCause());
142
143        } catch (DocletException | RuntimeException | Error e) {
144            messages.error("doclet.internal.exception", e);
145            reportInternalError(e);
146        }
147
148        return false;
149    }
150
151    protected Function<String, String>
152            getResourceKeyMapper(DocletEnvironment docEnv) {
153        return null;
154    }
155
156    private void reportInternalError(Throwable t) {
157        if (getClass().equals(StandardDoclet.class)
158            || getClass().equals(HtmlDoclet.class)) {
159            System.err.println(configuration.getDocResources()
160                .getText("doclet.internal.report.bug"));
161        }
162        dumpStack(true, t);
163    }
164
165    private void dumpStack(boolean enabled, Throwable t) {
166        if (enabled && t != null) {
167            t.printStackTrace(System.err);
168        }
169    }
170
171    /**
172     * Returns the SourceVersion indicating the features supported by this doclet.
173     *
174     * @return SourceVersion
175     */
176    @Override
177    public SourceVersion getSupportedSourceVersion() {
178        return SourceVersion.RELEASE_9;
179    }
180
181    /**
182     * Create the configuration instance and returns it.
183     *
184     * @return the configuration of the doclet.
185     */
186    public abstract BaseConfiguration getConfiguration();
187
188    /**
189     * Start the generation of files. Call generate methods in the individual
190     * writers, which will in turn generate the documentation files. Call the
191     * TreeWriter generation first to ensure the Class Hierarchy is built
192     * first and then can be used in the later generation.
193     *
194     * @throws DocletException if there is a problem while generating the documentation
195     */
196    private void startGeneration() throws DocletException {
197
198        // Modules with no documented classes may be specified on the
199        // command line to specify a service provider, allow these.
200        if (configuration.getSpecifiedModuleElements().isEmpty() &&
201            configuration.getIncludedTypeElements().isEmpty()) {
202            messages.error("doclet.No_Public_Classes_To_Document");
203            return;
204        }
205        if (!configuration.setOptions()) {
206            return;
207        }
208        messages.notice("doclet.build_version",
209            configuration.getDocletVersion());
210        ClassTree classTree = new ClassTree(configuration);
211
212        generateClassFiles(classTree);
213
214        ElementListWriter.generate(configuration);
215        generatePackageFiles(classTree);
216        generateModuleFiles();
217
218        generateOtherFiles(classTree);
219        configuration.tagletManager.printReport();
220    }
221
222    /**
223     * Generate additional documentation that is added to the API documentation.
224     *
225     * @param classTree the data structure representing the class tree
226     * @throws DocletException if there is a problem while generating the documentation
227     */
228    protected void generateOtherFiles(ClassTree classTree)
229            throws DocletException {
230        BuilderFactory builderFactory = configuration.getBuilderFactory();
231        AbstractBuilder constantsSummaryBuilder
232            = builderFactory.getConstantsSummaryBuilder();
233        constantsSummaryBuilder.build();
234        AbstractBuilder serializedFormBuilder
235            = builderFactory.getSerializedFormBuilder();
236        serializedFormBuilder.build();
237    }
238
239    /**
240     * Generate the module documentation.
241     *
242     * @throws DocletException if there is a problem while generating the documentation
243     *
244     */
245    protected abstract void generateModuleFiles() throws DocletException;
246
247    /**
248     * Generate the package documentation.
249     *
250     * @param classTree the data structure representing the class tree
251     * @throws DocletException if there is a problem while generating the documentation
252     */
253    protected abstract void generatePackageFiles(ClassTree classTree)
254            throws DocletException;
255
256    /**
257     * Generate the class documentation.
258     *
259     * @param arr the set of types to be documented
260     * @param classTree the data structure representing the class tree
261     * @throws DocletException if there is a problem while generating the documentation
262     */
263    protected abstract void generateClassFiles(SortedSet<TypeElement> arr,
264            ClassTree classTree)
265            throws DocletException;
266
267    /**
268     * Iterate through all classes and construct documentation for them.
269     *
270     * @param classTree the data structure representing the class tree
271     * @throws DocletException if there is a problem while generating the documentation
272     */
273    protected void generateClassFiles(ClassTree classTree)
274            throws DocletException {
275
276        SortedSet<TypeElement> classes
277            = new TreeSet<>(utils.comparators.makeGeneralPurposeComparator());
278
279        // handle classes specified as files on the command line
280        for (PackageElement pkg : configuration.typeElementCatalog.packages()) {
281            classes.addAll(configuration.typeElementCatalog.allClasses(pkg));
282        }
283
284        // handle classes specified in modules and packages on the command line
285        SortedSet<PackageElement> packages
286            = new TreeSet<>(utils.comparators.makePackageComparator());
287        packages.addAll(configuration.getSpecifiedPackageElements());
288        configuration.modulePackages.values().stream()
289            .forEach(packages::addAll);
290        for (PackageElement pkg : packages) {
291            classes.addAll(utils.getAllClasses(pkg));
292        }
293
294        generateClassFiles(classes, classTree);
295    }
296}