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.MacroRequest; 038import org.apache.maven.doxia.macro.manager.MacroManager; 039import org.apache.maven.doxia.macro.manager.MacroNotFoundException; 040import org.apache.maven.doxia.sink.Sink; 041import org.apache.maven.doxia.sink.impl.CreateAnchorsForIndexEntriesFactory; 042import org.apache.maven.doxia.sink.impl.SinkWrapperFactory; 043import org.apache.maven.doxia.sink.impl.SinkWrapperFactoryComparator; 044 045/** 046 * An abstract base class that defines some convenience methods for parsers. 047 * Provides a macro mechanism to give dynamic functionalities for the parsing. 048 * 049 * @author Jason van Zyl 050 * @since 1.0 051 */ 052public abstract class AbstractParser implements Parser { 053 /** Indicates that a second parsing is required. */ 054 private boolean secondParsing = false; 055 056 @Inject 057 private MacroManager macroManager; 058 059 @Inject 060 private Collection<SinkWrapperFactory> automaticallyRegisteredSinkWrapperFactories; 061 062 private final Collection<SinkWrapperFactory> manuallyRegisteredSinkWrapperFactories = new LinkedList<>(); 063 064 /** 065 * Emit Doxia comment events when parsing comments? 066 */ 067 private boolean emitComments = true; 068 069 private boolean emitAnchors = false; 070 071 private static final String DOXIA_VERSION; 072 073 static { 074 final Properties props = new Properties(); 075 final InputStream is = AbstractParser.class.getResourceAsStream( 076 "/META-INF/maven/org.apache.maven.doxia/doxia-core/pom.properties"); 077 078 if (is == null) { 079 props.setProperty("version", "unknown"); // should not happen 080 } else { 081 try { 082 props.load(is); 083 } catch (IOException ex) { 084 props.setProperty("version", "unknown"); // should not happen 085 } finally { 086 try { 087 is.close(); 088 } catch (IOException ex) { 089 // oh well... 090 } 091 } 092 } 093 094 DOXIA_VERSION = props.getProperty("version"); 095 } 096 097 /** 098 * {@inheritDoc} 099 * 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}