/*
* Copyright (C) Chaperon. All rights reserved.
* -------------------------------------------------------------------------
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package net.sourceforge.chaperon.cocoon;
import org.apache.avalon.excalibur.pool.Poolable;
import org.apache.avalon.excalibur.pool.Recyclable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.Constants;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.caching.CacheableProcessingComponent;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.SourceResolver;
//import org.apache.cocoon.generation.AbstractGenerator;
import org.apache.cocoon.generation.ComposerGenerator;
import org.apache.cocoon.util.HashUtil;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceValidity;
import org.apache.excalibur.source.impl.validity.AggregatedValidity;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.ContentHandler;
import org.xml.sax.helpers.AttributesImpl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Enumeration;
import net.sourceforge.chaperon.grammar.Grammar;
import net.sourceforge.chaperon.grammar.SyntaxErrorException;
import net.sourceforge.chaperon.grammar.generator.SAXGrammarGenerator;
import net.sourceforge.chaperon.parser.ParserException;
import net.sourceforge.chaperon.parser.Parser;
import net.sourceforge.chaperon.parser.ParserTable;
import net.sourceforge.chaperon.parser.generator.ParserTableGenerator;
import net.sourceforge.chaperon.parser.output.EventQueue;
import net.sourceforge.chaperon.parser.output.SAXEventAdapter;
/**
* A genrator that parse text
*
* @author Stephan Michels
* @version CVS $Id: TextParserGenerator.java,v 1.14 2002/06/01 19:43:16 benedikta Exp $
*/
public class TextParserGenerator extends ComposerGenerator
implements Composable/*, Disposable, Poolable*//*, Configurable*//*,
Cacheable*/,CacheableProcessingComponent
{
/** The URI of the parser exception */
public final static String URI = "http://chaperon.sourceforge.net/schema/parser-exception/1.0";
/** Element name */
public final static String ELEMENT = "parser-exception";
/** Attribute name of the message property */
public final static String MESSAGE_ATTRIBUTE = "message";
/** Attribute name of the message property */
public final static String LINENUMBER_ATTRIBUTE = "linenr";
/** Attribute name of the message property */
public final static String COLUMNNUMBER_ATTRIBUTE = "columnnr";
/** Element name for acceptedsymbols */
public final static String ACCEPTEDSYMBOL_ELEMENT = "parser-exception-accept";
/** The input source */
private Source _inputSource = null;
private Source _grammarSource = null;
//private ComponentManager _manager = null;
private String _grammar = null;
private boolean _includeIgnorableTokens = false;
private ParserTable _parsertable = null;
/**
* Pass the Configuration to the Configurable class. This method must
* always be called after the constructor and before any other method.
*
* @param conf the class configurations.
*
* @throws ConfigurationException Thrown when a Configurable component
* cannot be configured properly, or if
* a value cannot be retrieved properly.
*/
/*public void configure(Configuration conf) throws ConfigurationException
{
System.out.println("configure");
if (conf != null)
{
Configuration[] parameter = conf.getChildren("parameter");
for (int i = 0; i < parameter.length; i++)
if (parameter[i].getAttribute("name").equals("includeignorabletokens"))
_includeIgnorableTokens = parameter[i].getAttributeAsBoolean("value", false);
else if (parameter[i].getAttribute("name").equals("grammar"))
_grammar = parameter[i].getAttribute("value");
}
}*/
/**
* Pass the ComponentManager to the composer. The Composable
* implementation should use the specified ComponentManager
* to acquire the components it needs for execution.
*
* @param manager The ComponentManager which this Composable uses.
*/
/*public void compose(ComponentManager manager) throws ComponentException
{
System.out.println("compose");
_manager = manager;
}*/
/**
* Recycle this component.
* All instance variables are set to <code>null</code>.
*/
public void recycle()
{
/*System.out.println("recycle "+toString());
System.out.println("resolver="+super.resolver);*/
//Thread.currentThread().dumpStack();
if (_inputSource!=null)
super.resolver.release(_inputSource);
_inputSource = null;
if (_grammarSource!=null)
super.resolver.release(_grammarSource);
_grammarSource = null;
super.recycle();
}
/**
* The dispose operation is called at the end of a
* components lifecycle.
*/
/*public void dispose()
{
if ((super.resolver!=null) && (_inputSource!=null))
{
super.resolver.release(_inputSource);
_inputSource = null;
}
if ((super.resolver!=null) && (_grammarSource!=null))
{
super.resolver.release(_grammarSource);
_grammarSource = null;
}
super.recycle();
}*/
/**
* 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
{
//System.out.println("setup");
super.setup(resolver, objectmodel, src, parameters);
ParserTableStore store = null;
try
{
//getLogger().debug("Try to resolve "+src);
_includeIgnorableTokens = parameters.getParameterAsBoolean("includeignorabletokens", false);
_grammar = parameters.getParameter("grammar");
/*System.out.println("includeIgnorableTokens="+_includeIgnorableTokens);
System.out.println("grammar="+_grammar);*/
store = (ParserTableStore) this.manager.lookup(ParserTableStore.ROLE);
_grammarSource = resolver.resolveURI(_grammar);
/*getLogger().debug("Validity for "+_grammarSource.getSystemId()+" from the entry in the store:"+
store.getParserTableValidity(_grammarSource.getSystemId()));
getLogger().debug("Validity from the source "+_grammarSource.getSystemId()+":"+
_grammarSource.getValidity());*/
/*System.out.println("Class of source "+_grammarSource.getClass().getName());
System.out.println("Validity for "+_grammarSource.getSystemId()+" from the entry in the store:"+
store.getParserTableValidity(_grammarSource.getSystemId()));
System.out.println("Validity from the source "+_grammarSource.getSystemId()+":"+
_grammarSource.getValidity());*/
ParserTable parsertable;
if ((!store.hasParserTable(_grammarSource.getSystemId())) ||
(store.getParserTableValidity(_grammarSource.getSystemId())==null) ||
(!store.getParserTableValidity(_grammarSource.getSystemId()).isValid(_grammarSource.getValidity())))
{
getLogger().debug("(Re)building the parsertable from '"+_grammarSource.getSystemId()+"'");
SAXGrammarGenerator grammargenerator = new SAXGrammarGenerator();
SourceUtil.toSAX(_grammarSource, grammargenerator, this.manager);
Grammar grammar = grammargenerator.getGrammar();
SyntaxErrorException see = grammar.validate();
if (see!=null)
{
getLogger().error("Grammar is not correct", see);
throw new ProcessingException("Grammar is not correct", see);
}
ParserTableGenerator parsertablegenerator = new ParserTableGenerator(grammar);
_parsertable = parsertablegenerator.getParserTable();
store.store(_grammarSource.getSystemId(), _parsertable, _grammarSource.getValidity());
}
else
{
getLogger().debug("Getting parsertable from store for '"+_grammarSource.getSystemId()+"'");
parsertable = store.getParserTable(_grammarSource.getSystemId());
}
_inputSource = resolver.resolveURI(src);
} catch (ParameterException pe)
{
getLogger().error("Error during retrieving a parameter", pe);
throw new ProcessingException("Error during retrieving a parameter", pe);
} catch (SourceException se)
{
getLogger().error("Error during resolving of '" + src + "'.", se);
throw new ProcessingException("Error during resolving of '" + src + "'.", se);
} catch (ComponentException ce)
{
getLogger().error("Could not lookup for component", ce);
throw new ProcessingException("Could not lookup for component", ce);
} catch (SyntaxErrorException see)
{
getLogger().error("Grammar is not correct", see);
throw new ProcessingException("Grammar is not correct", see);
} 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 generateKey()
{
//System.out.println("generateKey");
return "TPG("+_inputSource.getSystemId()+";"+_grammarSource.getSystemId()+")";
}
/**
* Generate the validity object.
*
* @return The generated validity object or <code>null</code> if the
* component is currently not cacheable.
*/
public SourceValidity generateValidity()
{
//System.out.println("generateValidity");
AggregatedValidity validity = new AggregatedValidity();
validity.add(_inputSource.getValidity());
validity.add(_grammarSource.getValidity());
return validity;
}
/**
* Generate XML data.
*/
public void generate() throws IOException, SAXException, ProcessingException
{
//System.out.println("generate");
TextParser parser = null;
try
{
parser = (TextParser) this.manager.lookup("net.sourceforge.chaperon.cocoon.TextParser");
EventQueue queue = parser.parse(_parsertable, _inputSource.getInputStream());
SAXEventAdapter adapter = new SAXEventAdapter(super.contentHandler, _includeIgnorableTokens, false);
queue.fireEvents(adapter);
} catch (ParserException pe)
{
getLogger().error("Could not parse source", pe);
throw new ProcessingException("Could not parse source", pe);
} catch (SourceException se)
{
getLogger().error("Could not get inputstream from source", se);
throw new ProcessingException("Could not get inputstream from source", se);
} catch (ComponentException ce)
{
getLogger().error("Could not lookup for component", ce);
throw new ProcessingException("Could not lookup for component", ce);
} finally
{
if (parser!=null)
this.manager.release(parser);
}
}
/**
* Serialize the exception to a SAX stream
*
* @param handler The content handler, which receives the SAX events
*
* @throws SAXException
*/
public void toSAX(ContentHandler handler, ParserException pe) throws SAXException
{
handler.startDocument();
AttributesImpl attributes = new AttributesImpl();
attributes.addAttribute(URI, MESSAGE_ATTRIBUTE, MESSAGE_ATTRIBUTE,
"CDATA", pe.getMessage());
attributes.addAttribute(URI, LINENUMBER_ATTRIBUTE, LINENUMBER_ATTRIBUTE,
"CDATA", String.valueOf(pe.getLineNumber()));
attributes.addAttribute(URI, COLUMNNUMBER_ATTRIBUTE, COLUMNNUMBER_ATTRIBUTE,
"CDATA", String.valueOf(pe.getColumnNumber()));
handler.startElement(URI, ELEMENT, ELEMENT, attributes);
for(int i=0; i<pe.getAcceptedSymbols().length; i++)
{
handler.startElement(URI, ACCEPTEDSYMBOL_ELEMENT, ACCEPTEDSYMBOL_ELEMENT, new AttributesImpl());
handler.characters(pe.getAcceptedSymbols()[i].toCharArray(), 0, pe.getAcceptedSymbols()[i].length());
handler.endElement(URI, ACCEPTEDSYMBOL_ELEMENT, ACCEPTEDSYMBOL_ELEMENT);
}
handler.endElement(URI, ELEMENT, ELEMENT);
handler.endDocument();
}
}