001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.doxia.parser; 020 021import javax.inject.Inject; 022 023import java.io.File; 024import java.io.IOException; 025import java.io.InputStream; 026import java.io.Reader; 027import java.io.StringReader; 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Properties; 034 035import org.apache.maven.doxia.macro.Macro; 036import org.apache.maven.doxia.macro.MacroExecutionException; 037import org.apache.maven.doxia.macro.MacroExecutor; 038import org.apache.maven.doxia.macro.MacroRequest; 039import org.apache.maven.doxia.macro.manager.MacroManager; 040import org.apache.maven.doxia.macro.manager.MacroNotFoundException; 041import org.apache.maven.doxia.sink.Sink; 042import org.apache.maven.doxia.sink.impl.CreateAnchorsForIndexEntriesFactory; 043import org.apache.maven.doxia.sink.impl.SinkWrapperFactory; 044import org.apache.maven.doxia.sink.impl.SinkWrapperFactoryComparator; 045 046/** 047 * An abstract base class that defines some convenience methods for parsers. 048 * Provides a macro mechanism to give dynamic functionalities for the parsing. 049 * 050 * @author Jason van Zyl 051 * @since 1.0 052 */ 053public abstract class AbstractParser implements Parser, MacroExecutor { 054 /** Indicates that a second parsing is required. */ 055 private boolean secondParsing = false; 056 057 @Inject 058 private MacroManager macroManager; 059 060 @Inject 061 private Collection<SinkWrapperFactory> automaticallyRegisteredSinkWrapperFactories; 062 063 private final Collection<SinkWrapperFactory> manuallyRegisteredSinkWrapperFactories = new LinkedList<>(); 064 065 /** 066 * Emit Doxia comment events when parsing comments? 067 */ 068 private boolean emitComments = true; 069 070 private boolean emitAnchors = false; 071 072 private MacroExecutor macroExecutor = null; 073 074 private static final String DOXIA_VERSION; 075 076 static { 077 final Properties props = new Properties(); 078 final InputStream is = AbstractParser.class.getResourceAsStream( 079 "/META-INF/maven/org.apache.maven.doxia/doxia-core/pom.properties"); 080 081 if (is == null) { 082 props.setProperty("version", "unknown"); // should not happen 083 } else { 084 try { 085 props.load(is); 086 } catch (IOException ex) { 087 props.setProperty("version", "unknown"); // should not happen 088 } finally { 089 try { 090 is.close(); 091 } catch (IOException ex) { 092 // oh well... 093 } 094 } 095 } 096 097 DOXIA_VERSION = props.getProperty("version"); 098 } 099 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}