001/*
002 * Copyright 2013-2024 John Ericksen
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.asciidoctor.asciidoclet;
017
018import jdk.javadoc.doclet.Doclet;
019import jdk.javadoc.doclet.DocletEnvironment;
020import jdk.javadoc.doclet.Reporter;
021import jdk.javadoc.doclet.StandardDoclet;
022
023import javax.lang.model.SourceVersion;
024import java.io.IOException;
025import java.io.UncheckedIOException;
026import java.util.Arrays;
027import java.util.HashSet;
028import java.util.Locale;
029import java.util.Set;
030
031/**
032 * = Asciidoclet
033 * <p>
034 * https://github.com/asciidoctor/asciidoclet[Asciidoclet] is a Javadoc Doclet
035 * that uses https://asciidoctor.org[Asciidoctor] (via the
036 * https://github.com/asciidoctor/asciidoctorj[Asciidoctor Java integration])
037 * to interpret https://asciidoc.org[AsciiDoc] markup within Javadoc comments.
038 * <p>
039 * include::README.adoc[tags=usage]
040 * <p>
041 * == Examples
042 * <p>
043 * Custom attributes::
044 * `+{project_name}+`;; {project_name}
045 * `+{project_desc}+`;; {project_desc}
046 * `+{project_version}+`;; {project_version}
047 * <p>
048 * Code block (with syntax highlighting added by CodeRay)::
049 * +
050 * [source,java]
051 * --
052 * /**
053 * * = Asciidoclet
054 * *
055 * * A Javadoc Doclet that uses https://asciidoctor.org[Asciidoctor]
056 * * to render https://asciidoc.org[AsciiDoc] markup in Javadoc comments.
057 * *
058 * * @author https://github.com/johncarl81[John Ericksen]
059 * *\/
060 * public class Asciidoclet extends Doclet {
061 * private final Asciidoctor asciidoctor = Asciidoctor.Factory.create(); // <1>
062 *
063 * @author https://github.com/johncarl81[John Ericksen]
064 * @version {project_version}
065 * @SuppressWarnings("UnusedDeclaration") public static boolean start(RootDoc rootDoc) {
066 * new Asciidoclet().render(rootDoc); // <2>
067 * return Standard.start(rootDoc);
068 * }
069 * }
070 * --
071 * <1> Creates an instance of the Asciidoctor Java integration
072 * <2> Runs Javadoc comment strings through Asciidoctor
073 * <p>
074 * Inline code:: `code()`
075 * <p>
076 * Headings::
077 * +
078 * --
079 * [float]
080 * = Heading 1
081 * <p>
082 * [float]
083 * == Heading 2
084 * <p>
085 * [float]
086 * === Heading 3
087 * <p>
088 * [float]
089 * ==== Heading 4
090 * <p>
091 * [float]
092 * ===== Heading 5
093 * --
094 * <p>
095 * Links::
096 * Doc Writer <doc@example.com> +
097 * https://asciidoc.org[AsciiDoc] is a lightweight markup language. +
098 * Learn more about it at https://asciidoctor.org. +
099 * <p>
100 * Bullets::
101 * +
102 * --
103 * .Unnumbered
104 * * bullet
105 * * bullet
106 * - bullet
107 * - bullet
108 * * bullet
109 * ** bullet
110 * ** bullet
111 * *** bullet
112 * *** bullet
113 * **** bullet
114 * **** bullet
115 * ***** bullet
116 * ***** bullet
117 * **** bullet
118 * *** bullet
119 * ** bullet
120 * * bullet
121 * --
122 * +
123 * --
124 * .Numbered
125 * . bullet
126 * . bullet
127 * .. bullet
128 * .. bullet
129 * . bullet
130 * .. bullet
131 * ... bullet
132 * ... bullet
133 * .... bullet
134 * .... bullet
135 * ... bullet
136 * ... bullet
137 * .. bullet
138 * .. bullet
139 * . bullet
140 * --
141 * <p>
142 * Tables::
143 * +
144 * .An example table
145 * |===
146 * |Column 1 |Column 2 |Column 3
147 * <p>
148 * |1
149 * |Item 1
150 * |a
151 * <p>
152 * |2
153 * |Item 2
154 * |b
155 * <p>
156 * |3
157 * |Item 3
158 * |c
159 * |===
160 * <p>
161 * Sidebar block::
162 * +
163 * .Optional Title
164 * ****
165 * Usage: Notes in a sidebar, naturally.
166 * ****
167 * <p>
168 * Admonitions::
169 * +
170 * IMPORTANT: Check this out!
171 * @serial (or @ serialField or @ serialData)
172 * @see Asciidoclet
173 * @since 0.1.0
174 */
175public class Asciidoclet implements Doclet {
176
177    private StandardDoclet standardDoclet;
178    private DocletOptions docletOptions;
179    private Stylesheets stylesheets;
180    private Reporter reporter;
181    
182    /**
183     * Creates a new {@link Asciidoclet} object.
184     */
185    public Asciidoclet() {
186        standardDoclet = new StandardDoclet();
187    }
188
189    @Override
190    public void init(Locale locale, Reporter reporter) {
191        this.reporter = reporter;
192        this.standardDoclet.init(locale, reporter);
193        this.docletOptions = new DocletOptions(reporter);
194        this.stylesheets = new Stylesheets(reporter);
195    }
196
197    @Override
198    public String getName() {
199        return "Asciidoclet";
200    }
201
202    @Override
203    public Set<? extends Option> getSupportedOptions() {
204        Set<Option> options = new HashSet<>(standardDoclet.getSupportedOptions());
205        Arrays.stream(AsciidocletOptions.values()).map(o -> new OptionProcessor(o, docletOptions)).forEach(options::add);
206        return options;
207    }
208
209    @Override
210    public SourceVersion getSupportedSourceVersion() {
211        return SourceVersion.RELEASE_11;
212    }
213
214    @Override
215    public boolean run(DocletEnvironment environment) {
216        docletOptions.validateOptions();
217        AsciidoctorConverter converter = new AsciidoctorConverter(docletOptions, reporter);
218        boolean result;
219        try (AsciidoctorFilteredEnvironment env = new AsciidoctorFilteredEnvironment(environment, converter)) {
220            result = standardDoclet.run(env);
221        } catch (IOException e) {
222            throw new UncheckedIOException(e);
223        }
224        return result && postProcess(environment);
225    }
226
227    private boolean postProcess(DocletEnvironment environment) {
228        if (docletOptions.stylesheet().isPresent()) {
229            return true;
230        }
231        return stylesheets.copy(environment);
232    }
233}