1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.maven.doxia.parser;
20
21 import javax.inject.Inject;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.Reader;
27 import java.io.StringReader;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Properties;
34
35 import org.apache.maven.doxia.macro.Macro;
36 import org.apache.maven.doxia.macro.MacroExecutionException;
37 import org.apache.maven.doxia.macro.MacroRequest;
38 import org.apache.maven.doxia.macro.manager.MacroManager;
39 import org.apache.maven.doxia.macro.manager.MacroNotFoundException;
40 import org.apache.maven.doxia.sink.Sink;
41 import org.apache.maven.doxia.sink.impl.CreateAnchorsForIndexEntriesFactory;
42 import org.apache.maven.doxia.sink.impl.SinkWrapperFactory;
43 import org.apache.maven.doxia.sink.impl.SinkWrapperFactoryComparator;
44
45 /**
46 * An abstract base class that defines some convenience methods for parsers.
47 * Provides a macro mechanism to give dynamic functionalities for the parsing.
48 *
49 * @author Jason van Zyl
50 * @since 1.0
51 */
52 public abstract class AbstractParser implements Parser {
53 /** Indicates that a second parsing is required. */
54 private boolean secondParsing = false;
55
56 @Inject
57 private MacroManager macroManager;
58
59 @Inject
60 private Collection<SinkWrapperFactory> automaticallyRegisteredSinkWrapperFactories;
61
62 private final Collection<SinkWrapperFactory> manuallyRegisteredSinkWrapperFactories = new LinkedList<>();
63
64 /**
65 * Emit Doxia comment events when parsing comments?
66 */
67 private boolean emitComments = true;
68
69 private boolean emitAnchors = false;
70
71 private static final String DOXIA_VERSION;
72
73 static {
74 final Properties props = new Properties();
75 final InputStream is = AbstractParser.class.getResourceAsStream(
76 "/META-INF/maven/org.apache.maven.doxia/doxia-core/pom.properties");
77
78 if (is == null) {
79 props.setProperty("version", "unknown"); // should not happen
80 } else {
81 try {
82 props.load(is);
83 } catch (IOException ex) {
84 props.setProperty("version", "unknown"); // should not happen
85 } finally {
86 try {
87 is.close();
88 } catch (IOException ex) {
89 // oh well...
90 }
91 }
92 }
93
94 DOXIA_VERSION = props.getProperty("version");
95 }
96
97 /**
98 * {@inheritDoc}
99 *
100 * @return a int
101 */
102 public int getType() {
103 return UNKNOWN_TYPE;
104 }
105
106 /** {@inheritDoc} */
107 public void setEmitComments(boolean emitComments) {
108 this.emitComments = emitComments;
109 }
110
111 /**
112 * <p>isEmitComments.</p>
113 *
114 * @return a boolean
115 */
116 public boolean isEmitComments() {
117 return emitComments;
118 }
119
120 @Override
121 public boolean isEmitAnchorsForIndexableEntries() {
122 return emitAnchors;
123 }
124
125 @Override
126 public void setEmitAnchorsForIndexableEntries(boolean emitAnchors) {
127 this.emitAnchors = emitAnchors;
128 }
129
130 /**
131 * Execute a macro on the given sink.
132 *
133 * @param macroId an id to lookup the macro
134 * @param request the corresponding MacroRequest
135 * @param sink the sink to receive the events
136 * @throws org.apache.maven.doxia.macro.MacroExecutionException if an error occurred during execution
137 * @throws org.apache.maven.doxia.macro.manager.MacroNotFoundException if the macro could not be found
138 */
139 // Made public right now because of the structure of the APT parser and
140 // all its inner classes.
141 public void executeMacro(String macroId, MacroRequest request, Sink sink)
142 throws MacroExecutionException, MacroNotFoundException {
143 Macro macro = getMacroManager().getMacro(macroId);
144
145 macro.execute(sink, request);
146 }
147
148 /**
149 * Returns the current base directory.
150 *
151 * @return the base directory
152 * @deprecated this does not work in multi-module builds, see DOXIA-373
153 */
154 protected File getBasedir() {
155 // TODO: This is baaad, it should come in with the request.
156 // (this is only used for macro requests, see AptParser)
157
158 String basedir = System.getProperty("basedir");
159
160 if (basedir != null) {
161 return new File(basedir);
162 }
163
164 return new File(new File("").getAbsolutePath());
165 }
166
167 /**
168 * Convenience method to parse an arbitrary string and emit events into the given sink.
169 *
170 * @param string a string that provides the source input
171 * @param sink a sink that consumes the Doxia events
172 * @throws org.apache.maven.doxia.parser.ParseException if the string could not be parsed
173 * @since 1.1
174 */
175 public void parse(String string, Sink sink) throws ParseException {
176 this.parse(string, sink, null);
177 }
178
179 /**
180 * Convenience method to parse an arbitrary string and emit events into the given sink.
181 *
182 * @param string a string that provides the source input
183 * @param sink a sink that consumes the Doxia events
184 * @param reference a string containing the reference to the source of the input string (e.g. filename)
185 * @throws org.apache.maven.doxia.parser.ParseException if the string could not be parsed
186 * @since 1.10
187 */
188 public void parse(String string, Sink sink, String reference) throws ParseException {
189 parse(new StringReader(string), sink, reference);
190 }
191
192 /** {@inheritDoc} */
193 @Override
194 public void parse(Reader source, Sink sink) throws ParseException {
195 parse(source, sink, null);
196 }
197
198 /**
199 * Creates a sink pipeline built from all registered {@link SinkWrapperFactory} objects.
200 * For secondary parsers (i.e. ones with {@link #isSecondParsing()} returning {@code true} just the given original sink is returned.
201 * @param sink
202 * @return the Sink pipeline to be used
203 */
204 protected Sink getWrappedSink(Sink sink) {
205 // no wrapping for secondary parsing
206 if (secondParsing) {
207 return sink;
208 }
209 Sink currentSink = sink;
210 for (SinkWrapperFactory factory : getSinkWrapperFactories()) {
211 currentSink = factory.createWrapper(currentSink);
212 }
213 return currentSink;
214 }
215
216 /**
217 * Set <code>secondParsing</code> to true, if this represents a secondary parsing of the same source.
218 *
219 * @param second true for second parsing
220 */
221 public void setSecondParsing(boolean second) {
222 this.secondParsing = second;
223 }
224
225 /**
226 * Indicates if we are currently parsing a second time.
227 *
228 * @return true if we are currently parsing a second time
229 * @since 1.1
230 */
231 protected boolean isSecondParsing() {
232 return secondParsing;
233 }
234
235 @Override
236 public void addSinkWrapperFactory(SinkWrapperFactory factory) {
237 manuallyRegisteredSinkWrapperFactories.add(factory);
238 }
239
240 /**
241 * Returns all sink wrapper factories (both registered automatically and manually). The collection is ordered in a way that
242 * the factories having the lowest priority come first (i.e. in reverse order).
243 * @return all sink wrapper factories in the reverse order
244 * @since 2.0.0
245 */
246 protected List<SinkWrapperFactory> getSinkWrapperFactories() {
247 List<SinkWrapperFactory> effectiveSinkWrapperFactories = new ArrayList<>();
248 if (automaticallyRegisteredSinkWrapperFactories != null) {
249 effectiveSinkWrapperFactories.addAll(automaticallyRegisteredSinkWrapperFactories);
250 }
251 effectiveSinkWrapperFactories.addAll(manuallyRegisteredSinkWrapperFactories);
252 if (emitAnchors) {
253 effectiveSinkWrapperFactories.add(new CreateAnchorsForIndexEntriesFactory());
254 }
255 Collections.sort(effectiveSinkWrapperFactories, Collections.reverseOrder(new SinkWrapperFactoryComparator()));
256 return effectiveSinkWrapperFactories;
257 }
258
259 /**
260 * Gets the current {@link MacroManager}.
261 *
262 * @return the current {@link MacroManager}
263 * @since 1.1
264 */
265 protected MacroManager getMacroManager() {
266 return macroManager;
267 }
268
269 /**
270 * Initialize the parser. This is called first by
271 * {@link #parse(java.io.Reader, org.apache.maven.doxia.sink.Sink)} and can be used
272 * to set the parser into a clear state so it can be re-used.
273 *
274 * @since 1.1.2
275 */
276 protected void init() {
277 // nop
278 }
279
280 /**
281 * The current Doxia version.
282 *
283 * @return the current Doxia version as a String
284 * @since 1.2
285 */
286 protected static String doxiaVersion() {
287 return DOXIA_VERSION;
288 }
289 }