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