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