001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.cocoon.transformation;
019:
020: import net.sourceforge.chaperon.build.ParserAutomatonBuilder;
021: import net.sourceforge.chaperon.model.grammar.Grammar;
022: import net.sourceforge.chaperon.model.grammar.GrammarFactory;
023: import net.sourceforge.chaperon.process.ParserAutomaton;
024: import net.sourceforge.chaperon.process.ParserProcessor;
025:
026: import org.apache.avalon.excalibur.pool.Recyclable;
027: import org.apache.avalon.framework.activity.Disposable;
028: import org.apache.avalon.framework.logger.LogEnabled;
029: import org.apache.avalon.framework.logger.Logger;
030: import org.apache.avalon.framework.parameters.ParameterException;
031: import org.apache.avalon.framework.parameters.Parameterizable;
032: import org.apache.avalon.framework.parameters.Parameters;
033: import org.apache.avalon.framework.service.ServiceException;
034: import org.apache.avalon.framework.service.ServiceManager;
035: import org.apache.avalon.framework.service.Serviceable;
036:
037: import org.apache.cocoon.ProcessingException;
038: import org.apache.cocoon.caching.CacheableProcessingComponent;
039: import org.apache.cocoon.components.source.SourceUtil;
040: import org.apache.cocoon.environment.SourceResolver;
041: import org.apache.cocoon.xml.XMLConsumer;
042:
043: import org.apache.excalibur.source.Source;
044: import org.apache.excalibur.source.SourceException;
045: import org.apache.excalibur.source.SourceValidity;
046: import org.apache.excalibur.store.Store;
047:
048: import org.xml.sax.SAXException;
049:
050: import java.io.IOException;
051: import java.io.Serializable;
052:
053: import java.util.Map;
054:
055: /**
056: * This transfomer transforms lexical tokens in a XML file into a XML hirachy by using a grammar
057: * file.
058: *
059: * <p>
060: * Input:
061: * </p>
062: * <pre>
063: * <lexemes xmlns="http://chaperon.sourceforge.net/schema/lexemes/1.0">
064: * <lexeme symbol="word" text="Text"/>
065: * <lexeme symbol="number" text="123"/>
066: * <lexeme symbol="word" text="bla"/>
067: * </lexemes>
068: * </pre>
069: *
070: * <p>
071: * were transform into the following output:
072: * </p>
073: * <pre>
074: * <sentence xmlns="http://chaperon.sourceforge.net/schema/syntaxtree/1.0">
075: * <word>Text</word>
076: * <number>123</number>
077: * <word>bla</word>
078: * </sentence>
079: * </pre>
080: *
081: * @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
082: * @version CVS $Id: ParserTransformer.java 433543 2006-08-22 06:22:54Z crossley $
083: */
084: public class ParserTransformer extends ParserProcessor implements
085: Transformer, LogEnabled, Serviceable, Parameterizable,
086: Recyclable, Disposable, CacheableProcessingComponent {
087: private String grammar = null;
088: private Source grammarSource = null;
089: private Logger logger = null;
090: private ServiceManager manager = null;
091: private SourceResolver resolver = null;
092:
093: /**
094: * Provide component with a logger.
095: *
096: * @param logger the logger
097: */
098: public void enableLogging(Logger logger) {
099: this .logger = logger;
100:
101: // TODO: check if the loglevel is correct LogKitLogger -> Logger
102: // setLog(new AvalonLogger(logger));
103: //setLog(new ConsoleLog());
104: }
105:
106: /**
107: * Pass the ServiceManager to the object. The Serviceable implementation should use the
108: * specified ServiceManager to acquire the services it needs for execution.
109: *
110: * @param manager The ServiceManager which this Serviceable uses.
111: */
112: public void service(ServiceManager manager) {
113: this .manager = manager;
114: }
115:
116: /**
117: * Provide component with parameters.
118: *
119: * @param parameters the parameters
120: *
121: * @throws ParameterException if parameters are invalid
122: */
123: public void parameterize(Parameters parameters)
124: throws ParameterException {
125: setFlatten(parameters.getParameterAsBoolean("flatten", false));
126:
127: //setRecovery(parameters.getParameterAsBoolean("recovery", false));
128: setLocalizable(parameters.getParameterAsBoolean("localizable",
129: false));
130: }
131:
132: /**
133: * Set the <code>XMLConsumer</code> that will receive XML data.
134: *
135: * @param consumer
136: */
137: public void setConsumer(XMLConsumer consumer) {
138: setContentHandler(consumer);
139: setLexicalHandler(consumer);
140: }
141:
142: /**
143: * Set the SourceResolver, objectModel Map, the source and sitemap Parameters used to process the
144: * request.
145: *
146: * @param resolver Source resolver
147: * @param objectmodel Object model
148: * @param src Source
149: * @param parameters Parameters
150: *
151: * @throws IOException
152: * @throws ProcessingException
153: * @throws SAXException
154: */
155: public void setup(SourceResolver resolver, Map objectmodel,
156: String src, Parameters parameters)
157: throws ProcessingException, SAXException, IOException {
158: this .resolver = resolver;
159:
160: setFailSafe(parameters.getParameterAsBoolean("failsafe", false));
161:
162: Store store = null;
163: try {
164: this .grammar = src;
165:
166: this .grammarSource = resolver.resolveURI(this .grammar);
167:
168: // Retrieve the parser automaton from the transient store
169: store = (Store) this .manager.lookup(Store.TRANSIENT_STORE);
170:
171: ParserAutomatonEntry entry = (ParserAutomatonEntry) store
172: .get(this .grammarSource.getURI());
173:
174: // If the parser automaton has changed, rebuild the parser automaton
175: if ((entry == null)
176: || (entry.getValidity() == null)
177: || ((entry.getValidity().isValid(this .grammarSource
178: .getValidity())) <= 0)) {
179: this .logger.info("(Re)building the automaton from '"
180: + this .grammarSource.getURI() + "'");
181:
182: //SAXConfigurationHandler confighandler = new SAXConfigurationHandler();
183: if (this .grammarSource.getInputStream() == null)
184: throw new ProcessingException("Source '"
185: + this .grammarSource.getURI()
186: + "' not found");
187:
188: GrammarFactory factory = new GrammarFactory();
189: SourceUtil.toSAX(this .manager, this .grammarSource,
190: null, factory);
191:
192: //Configuration config = confighandler.getConfiguration();
193: //Grammar grammar = GrammarFactory.createGrammar(config);
194: Grammar grammar = factory.getGrammar();
195:
196: if (grammar == null)
197: throw new ProcessingException(
198: "Error while reading the grammar from "
199: + src);
200:
201: ParserAutomatonBuilder builder = new ParserAutomatonBuilder(
202: grammar /*, new AvalonLogger(logger)*/);
203:
204: ParserAutomaton automaton = builder
205: .getParserAutomaton();
206: setParserAutomaton(builder.getParserAutomaton());
207:
208: this .logger.info("Store automaton into store for '"
209: + this .grammarSource.getURI() + "'");
210: store.store(this .grammarSource.getURI(),
211: new ParserAutomatonEntry(automaton,
212: this .grammarSource.getValidity()));
213: } else {
214: this .logger.info("Getting automaton from store for '"
215: + this .grammarSource.getURI() + "'");
216: setParserAutomaton(entry.getParserAutomaton());
217: }
218: } catch (SourceException se) {
219: throw new ProcessingException("Error during resolving of '"
220: + src + "'.", se);
221: } catch (ServiceException se) {
222: throw new ProcessingException(
223: "Could not lookup for service", se);
224: } finally {
225: if (store != null)
226: this .manager.release(store);
227: }
228: }
229:
230: /**
231: * Generate the unique key. This key must be unique inside the space of this component.
232: *
233: * @return The generated key hashes the src
234: */
235: public Serializable getKey() {
236: return this .grammarSource.getURI();
237: }
238:
239: /**
240: * Generate the validity object.
241: *
242: * @return The generated validity object or <code>null</code> if the component is currently not
243: * cacheable.
244: */
245: public SourceValidity getValidity() {
246: return this .grammarSource.getValidity();
247: }
248:
249: /**
250: * Recycle this component. All instance variables are set to <code>null</code>.
251: */
252: public void recycle() {
253: if ((this .resolver != null) && (this .grammarSource != null)) {
254: this .resolver.release(this .grammarSource);
255: this .grammarSource = null;
256: }
257: }
258:
259: /**
260: * The dispose operation is called at the end of a components lifecycle.
261: */
262: public void dispose() {
263: if ((this .resolver != null) && (this .grammarSource != null)) {
264: this .resolver.release(this .grammarSource);
265: this .grammarSource = null;
266: }
267:
268: this .manager = null;
269: }
270:
271: /**
272: * This class represent a entry in a store to cache the parser automaton.
273: */
274: public static class ParserAutomatonEntry implements Serializable {
275: private SourceValidity validity = null;
276: private ParserAutomaton automaton = null;
277:
278: /**
279: * Create a new entry.
280: *
281: * @param automaton Parser automaton.
282: * @param validity Validity for the grammar file.
283: */
284: public ParserAutomatonEntry(ParserAutomaton automaton,
285: SourceValidity validity) {
286: this .automaton = automaton;
287: this .validity = validity;
288: }
289:
290: /**
291: * Return the validity of the grammar file.
292: *
293: * @return Validity of the grammar file.
294: */
295: public SourceValidity getValidity() {
296: return this .validity;
297: }
298:
299: /**
300: * Return the parser automaton.
301: *
302: * @return Parser automaton.
303: */
304: public ParserAutomaton getParserAutomaton() {
305: return this .automaton;
306: }
307:
308: private void writeObject(java.io.ObjectOutputStream out)
309: throws IOException {
310: out.writeObject(validity);
311: out.writeObject(automaton);
312: }
313:
314: private void readObject(java.io.ObjectInputStream in)
315: throws IOException, ClassNotFoundException {
316: validity = (SourceValidity) in.readObject();
317: automaton = (ParserAutomaton) in.readObject();
318: }
319: }
320: }
|