/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.transformation; import net.sourceforge.chaperon.build.ParserAutomatonBuilder; import net.sourceforge.chaperon.model.grammar.Grammar; import net.sourceforge.chaperon.model.grammar.GrammarFactory; import net.sourceforge.chaperon.process.ParserAutomaton; import net.sourceforge.chaperon.process.ParserProcessor; import org.apache.avalon.excalibur.pool.Recyclable; import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.logger.LogEnabled; import org.apache.avalon.framework.logger.Logger; import org.apache.avalon.framework.parameters.ParameterException; import org.apache.avalon.framework.parameters.Parameterizable; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.caching.CacheableProcessingComponent; import org.apache.cocoon.components.source.SourceUtil; import org.apache.cocoon.environment.SourceResolver; import org.apache.cocoon.xml.XMLConsumer; import org.apache.excalibur.source.Source; import org.apache.excalibur.source.SourceException; import org.apache.excalibur.source.SourceValidity; import org.apache.excalibur.store.Store; import org.xml.sax.SAXException; import java.io.IOException; import java.io.Serializable; import java.util.Map; /** * This transfomer transforms lexical tokens in a XML file into a XML hirachy by using a grammar * file. * * <p> * Input: * </p> * <pre> * <lexemes xmlns="http://chaperon.sourceforge.net/schema/lexemes/1.0"> * <lexeme symbol="word" text="Text"/> * <lexeme symbol="number" text="123"/> * <lexeme symbol="word" text="bla"/> * </lexemes> * </pre> * * <p> * were transform into the following output: * </p> * <pre> * <sentence xmlns="http://chaperon.sourceforge.net/schema/syntaxtree/1.0"> * <word>Text</word> * <number>123</number> * <word>bla</word> * </sentence> * </pre> * * @author <a href="mailto:stephan@apache.org">Stephan Michels </a> * @version CVS $Id$ */ public class ParserTransformer extends ParserProcessor implements Transformer, LogEnabled, Serviceable, Parameterizable, Recyclable, Disposable, CacheableProcessingComponent { private String grammar = null; private Source grammarSource = null; private Logger logger = null; private ServiceManager manager = null; private SourceResolver resolver = null; /** * Provide component with a logger. * * @param logger the logger */ public void enableLogging(Logger logger) { this.logger = logger; // TODO: check if the loglevel is correct LogKitLogger -> Logger // setLog(new AvalonLogger(logger)); //setLog(new ConsoleLog()); } /** * Pass the ServiceManager to the object. The Serviceable implementation should use the * specified ServiceManager to acquire the services it needs for execution. * * @param manager The ServiceManager which this Serviceable uses. */ public void service(ServiceManager manager) { this.manager = manager; } /** * Provide component with parameters. * * @param parameters the parameters * * @throws ParameterException if parameters are invalid */ public void parameterize(Parameters parameters) throws ParameterException { setFlatten(parameters.getParameterAsBoolean("flatten", false)); //setRecovery(parameters.getParameterAsBoolean("recovery", false)); setLocalizable(parameters.getParameterAsBoolean("localizable", false)); } /** * Set the <code>XMLConsumer</code> that will receive XML data. * * @param consumer */ public void setConsumer(XMLConsumer consumer) { setContentHandler(consumer); setLexicalHandler(consumer); } /** * Set the SourceResolver, objectModel Map, the source and sitemap Parameters used to process the * request. * * @param resolver Source resolver * @param objectmodel Object model * @param src Source * @param parameters Parameters * * @throws IOException * @throws ProcessingException * @throws SAXException */ public void setup(SourceResolver resolver, Map objectmodel, String src, Parameters parameters) throws ProcessingException, SAXException, IOException { this.resolver = resolver; setFailSafe(parameters.getParameterAsBoolean("failsafe", false)); Store store = null; try { this.grammar = src; this.grammarSource = resolver.resolveURI(this.grammar); // Retrieve the parser automaton from the transient store store = (Store)this.manager.lookup(Store.TRANSIENT_STORE); ParserAutomatonEntry entry = (ParserAutomatonEntry)store.get(this.grammarSource.getURI()); // If the parser automaton has changed, rebuild the parser automaton if ((entry==null) || (entry.getValidity()==null) || ((entry.getValidity().isValid(this.grammarSource.getValidity()))<=0)) { this.logger.info("(Re)building the automaton from '"+this.grammarSource.getURI()+"'"); //SAXConfigurationHandler confighandler = new SAXConfigurationHandler(); if (this.grammarSource.getInputStream()==null) throw new ProcessingException("Source '"+this.grammarSource.getURI()+"' not found"); GrammarFactory factory = new GrammarFactory(); SourceUtil.toSAX(this.manager, this.grammarSource, null, factory); //Configuration config = confighandler.getConfiguration(); //Grammar grammar = GrammarFactory.createGrammar(config); Grammar grammar = factory.getGrammar(); if (grammar==null) throw new ProcessingException("Error while reading the grammar from "+src); ParserAutomatonBuilder builder = new ParserAutomatonBuilder(grammar /*, new AvalonLogger(logger)*/); ParserAutomaton automaton = builder.getParserAutomaton(); setParserAutomaton(builder.getParserAutomaton()); this.logger.info("Store automaton into store for '"+this.grammarSource.getURI()+"'"); store.store(this.grammarSource.getURI(), new ParserAutomatonEntry(automaton, this.grammarSource.getValidity())); } else { this.logger.info("Getting automaton from store for '"+this.grammarSource.getURI()+"'"); setParserAutomaton(entry.getParserAutomaton()); } } catch (SourceException se) { throw new ProcessingException("Error during resolving of '"+src+"'.", se); } catch (ServiceException se) { throw new ProcessingException("Could not lookup for service", se); } finally { if (store!=null) this.manager.release(store); } } /** * Generate the unique key. This key must be unique inside the space of this component. * * @return The generated key hashes the src */ public Serializable getKey() { return this.grammarSource.getURI(); } /** * Generate the validity object. * * @return The generated validity object or <code>null</code> if the component is currently not * cacheable. */ public SourceValidity getValidity() { return this.grammarSource.getValidity(); } /** * Recycle this component. All instance variables are set to <code>null</code>. */ public void recycle() { if ((this.resolver!=null) && (this.grammarSource!=null)) { this.resolver.release(this.grammarSource); this.grammarSource = null; } } /** * The dispose operation is called at the end of a components lifecycle. */ public void dispose() { if ((this.resolver!=null) && (this.grammarSource!=null)) { this.resolver.release(this.grammarSource); this.grammarSource = null; } this.manager = null; } /** * This class represent a entry in a store to cache the parser automaton. */ public static class ParserAutomatonEntry implements Serializable { private SourceValidity validity = null; private ParserAutomaton automaton = null; /** * Create a new entry. * * @param automaton Parser automaton. * @param validity Validity for the grammar file. */ public ParserAutomatonEntry(ParserAutomaton automaton, SourceValidity validity) { this.automaton = automaton; this.validity = validity; } /** * Return the validity of the grammar file. * * @return Validity of the grammar file. */ public SourceValidity getValidity() { return this.validity; } /** * Return the parser automaton. * * @return Parser automaton. */ public ParserAutomaton getParserAutomaton() { return this.automaton; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeObject(validity); out.writeObject(automaton); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { validity = (SourceValidity)in.readObject(); automaton = (ParserAutomaton)in.readObject(); } } }