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.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 }