/*
* 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.process.extended;
import net.sourceforge.chaperon.model.extended.ExtendedGrammar;
import org.apache.commons.logging.Log;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.LocatorImpl;
/**
* This class represents a simulation of a pushdown automata using the parser automaton class.
*
* @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
* @version CVS $Id: ExtendedGeneralParserProcessor.java,v 1.1 2004/01/04 16:49:12 benedikta Exp $
*/
public class ExtendedGeneralParserProcessor implements ContentHandler, LexicalHandler
{
public static final String NS = "http://chaperon.sourceforge.net/schema/text/1.0";
public static final String TEXT = "text";
/** Namespace for the generated SAX events. */
public static final String NS_OUTPUT = "http://chaperon.sourceforge.net/schema/syntaxtree/2.0";
public static final String OUTPUT = "output";
public static final String ERROR = "error";
private ContentHandler contentHandler = null;
private LexicalHandler lexicalHandler = null;
private Locator locator = null;
private LocatorImpl locatorImpl = null;
private static final int STATE_OUTER = 0;
private static final int STATE_INNER = 1;
private int state = STATE_OUTER;
private ExtendedParserAutomaton automaton;
private ExtendedGrammar grammar;
private boolean flatten = false;
private StackNodeSet current = new StackNodeSet();
private StackNodeSet next = new StackNodeSet();
private Log log;
private int maxActiveStates = 50;
/**
* Create a new parser processor.
*/
public ExtendedGeneralParserProcessor() {}
/**
* Create a new parser processor.
*
* @param automaton Parser automaton, which the processor should ues.
* @param handler Handler, which should receives the parser events.
* @param log Log, which should used.
*/
public ExtendedGeneralParserProcessor(ExtendedParserAutomaton automaton, Log log)
{
this.automaton = automaton;
this.log = log;
}
/**
* Set the parser automaton for the processor.
*
* @param automaton Parser automaton.
*/
public void setExtendedParserAutomaton(ExtendedParserAutomaton automaton)
{
this.automaton = automaton;
this.grammar = automaton.getExtendedGrammar();
current.setExtendedParserAutomaton(automaton);
next.setExtendedParserAutomaton(automaton);
}
/**
* Set the <code>ContentHandler</code> that will receive XML data.
*/
public void setContentHandler(ContentHandler handler)
{
this.contentHandler = handler;
}
/**
* Set the <code>LexicalHandler</code> that will receive XML data.
*/
public void setLexicalHandler(LexicalHandler handler)
{
this.lexicalHandler = handler;
}
/**
* Provide processor with a log.
*
* @param log The log.
*/
public void setLog(Log log)
{
this.log = log;
}
/**
* If the adapter should produce a more flatten XML hirachy, which means elements which the same
* name will be collapsed
*
* @param flatten True, if a more flatten hirachy should be produced.
*/
public void setFlatten(boolean flatten)
{
this.flatten = flatten;
}
/**
* Receive an object for locating the origin of SAX document events.
*/
public void setDocumentLocator(Locator locator)
{
this.locator = locator;
if (locator!=null)
{
this.locatorImpl = new LocatorImpl(locator);
contentHandler.setDocumentLocator(locatorImpl);
}
}
/**
* Receive notification of the beginning of a document.
*/
public void startDocument() throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
contentHandler.startDocument();
state = STATE_OUTER;
}
/**
* Receive notification of the beginning of an element.
*/
public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
if (state==STATE_INNER)
throw new SAXException("Unexpected element "+qName);
if (state==STATE_OUTER)
{
if ((namespaceURI!=null) && (namespaceURI.equals(NS)))
{
if (!localName.equals(TEXT))
throw new SAXException("Unknown element "+qName);
}
else
{
contentHandler.startElement(namespaceURI, localName, qName, atts);
return;
}
}
state = STATE_INNER;
System.out.println("start processing");
// ======================= Start Text Document =======================
current.clear();
current.push(new TerminalStackNode('\u0000', automaton.first, null));
next.clear();
}
/**
* Receive notification of character data.
*/
public void characters(char[] text, int textstart, int textlength)
throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
if (state==STATE_OUTER)
{
contentHandler.characters(text, textstart, textlength);
return;
}
System.out.println("process text \""+(new String(text, textstart, textlength))+"\"");
for (int position = textstart; position<(textstart+textlength); position++)
{
System.out.println("\n===================================\nProcess "+text[position]);
if (current.isEmpty())
throw new IllegalStateException("Parsing process is aborted");
printStates();
//if (current.size()>maxActiveStates)
// throw new IllegalStateException("Processor occupied too many states");
/* ============================ Reduce =================================== */
System.out.println("Count of states : "+current.size());
if ((log!=null) && (log.isDebugEnabled()))
log.debug("------- check reduce actions ---------");
int watchdog = 0;
while (!current.isEmpty())
{
printStates();
//if (watchdog++ > 220)
// throw new IllegalStateException("overflow");
//printStates();
StackNode stackNode = current.pop();
if (stackNode.state.getShiftAction(text[position])!=null)
;
next.push(stackNode);
LookaheadReduceAction[] reduceActions = stackNode.state.getLookaheadReduceActions();
for (int i = 0; i<reduceActions.length; i++)
if (reduceActions[i].contains(text[position]))
{
LookaheadReduceAction reduceAction = reduceActions[i];
if (reduceAction.length==0)
{
GotoAction gotoAction = stackNode.state.getGotoAction(reduceAction);
if (gotoAction!=null)
{
if ((log!=null) && (log.isDebugEnabled()))
log.debug("State "+automaton.indexOf(stackNode.state)+" "+reduceAction);
if (gotoAction.state==stackNode.state)
System.out.println("node rejected because states are equal");
else
current.push(new DefinitionStackNode(reduceAction, 0, null, null,
gotoAction.state, stackNode));
}
}
else
{
StackNode second = stackNode;
for (int j = 0; j<second.ancestors.length; j++)
{
StackNode first = second.ancestors[j];
for (int k = 0; k<first.ancestors.length; k++)
{
StackNode previousStackNode = first.ancestors[k];
GotoAction gotoAction = previousStackNode.state.getGotoAction(reduceAction);
if (gotoAction!=null)
{
if ((log!=null) && (log.isDebugEnabled()))
log.debug("State "+automaton.indexOf(stackNode.state)+" "+reduceAction);
current.push(new DefinitionStackNode(reduceAction, 0, first, second,
gotoAction.state, previousStackNode));
}
}
}
}
}
}
swapStacks();
printStates();
/* ==================================== Shift =================================== */
//System.out.println("Count of states : "+current.size());
if ((log!=null) && (log.isDebugEnabled()))
log.debug("------- check shift actions ---------");
while (!current.isEmpty())
{
//printStates();
StackNode stackNode = current.pop();
ShiftAction shiftAction = stackNode.state.getShiftAction(text[position]);
if (shiftAction!=null)
{
if ((log!=null) && (log.isDebugEnabled()))
log.debug( /*"State "+state+*/
" shift character '"+text[position]+"'");
next.push(new TerminalStackNode(text[position], shiftAction.state, stackNode));
}
}
if (next.isEmpty())
throw new IllegalArgumentException("Character '"+text[position]+"' is not expected");
swapStacks();
System.out.println("------- finished check actions ---------");
printStates();
}
}
/**
* Receive notification of the end of an element.
*/
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
if (state==STATE_OUTER)
contentHandler.endElement(namespaceURI, localName, qName);
if (state==STATE_INNER)
{
if ((namespaceURI!=null) && (namespaceURI.equals(NS)))
{
if (!localName.equals(TEXT))
throw new SAXException("Unknown element "+qName);
}
else
throw new SAXException("Unexpected element "+qName);
}
System.out.println("end processing");
// ======================= End Text Document =======================
System.out.println("\n===================================\nProcess EOF");
while (!current.isEmpty())
{
printStates();
StackNode stackNode = current.pop();
ReduceAction[] reduceActions = stackNode.state.getReduceActions();
for (int i = 0; i<reduceActions.length; i++)
{
ReduceAction reduceAction = reduceActions[i];
if (reduceAction.length==0)
{
GotoAction gotoAction = stackNode.state.getGotoAction(reduceAction);
if ((automaton.first==stackNode.state) &&
(grammar.getStartSymbol().equals(reduceAction.symbol)))
{
if ((log!=null) && (log.isDebugEnabled()))
log.debug("State "+automaton.indexOf(stackNode.state)+" accept");
next.push(new DefinitionStackNode(reduceAction, 0, null, null, null, stackNode));
}
else
{
if ((log!=null) && (log.isDebugEnabled()))
log.debug("State "+automaton.indexOf(stackNode.state)+" "+reduceAction);
if (gotoAction.state==stackNode.state)
System.out.println("node rejected because states are equal");
else
current.push(new DefinitionStackNode(reduceAction, 0, null, null, gotoAction.state,
stackNode));
}
}
else
{
StackNode second = stackNode;
//System.out.println("second="+automaton.indexOf(second.state));
for (int j = 0; j<second.ancestors.length; j++)
{
StackNode first = second.ancestors[j];
//System.out.println("first="+automaton.indexOf(first.state));
//if (second.ancestors.length>1)
// System.out.println("nodes="+second.toCanonicalString(automaton));
for (int k = 0; k<first.ancestors.length; k++)
{
StackNode previousStackNode = first.ancestors[k];
GotoAction gotoAction = previousStackNode.state.getGotoAction(reduceAction);
//System.out.println("j="+j+" k="+k);
if ((automaton.first==previousStackNode.state) &&
(grammar.getStartSymbol().equals(reduceAction.symbol)))
{
if ((log!=null) && (log.isDebugEnabled()))
log.debug("State "+automaton.indexOf(stackNode.state)+" accept");
next.push(new DefinitionStackNode(reduceAction, 0, first, second, null,
previousStackNode));
}
else
{
if ((log!=null) && (log.isDebugEnabled()))
log.debug("State "+automaton.indexOf(stackNode.state)+" "+reduceAction);
current.push(new DefinitionStackNode(reduceAction, 0, first, second,
gotoAction.state, previousStackNode));
//System.out.println("origin="+automaton.indexOf(previousStackNode.state));
}
}
}
}
}
}
if (log.isDebugEnabled())
log.debug("Parser found "+next.size()+" alternatives");
System.out.println();
contentHandler.startPrefixMapping("", NS_OUTPUT);
contentHandler.startElement(NS_OUTPUT, OUTPUT, OUTPUT, new AttributesImpl());
int index = 1;
while (!next.isEmpty())
{
StackNode node = next.pop();
node.toXML(contentHandler);
index++;
}
if (next.size()>1)
log.warn("ExtendedGrammar is ambig, found "+next.size()+" alternative trees");
contentHandler.endElement(NS_OUTPUT, OUTPUT, OUTPUT);
contentHandler.endPrefixMapping("");
state = STATE_OUTER;
}
/**
* Receive notification of ignorable whitespace in element content.
*/
public void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
if (state==STATE_OUTER)
contentHandler.ignorableWhitespace(ch, start, length);
}
/**
* Begin the scope of a prefix-URI Namespace mapping.
*/
public void startPrefixMapping(String prefix, String uri)
throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
contentHandler.startPrefixMapping(prefix, uri);
}
/**
* End the scope of a prefix-URI mapping.
*/
public void endPrefixMapping(String prefix) throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
contentHandler.endPrefixMapping(prefix);
}
/**
* Receive notification of a processing instruction.
*/
public void processingInstruction(String target, String data)
throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
if (state==STATE_OUTER)
contentHandler.processingInstruction(target, data);
}
/**
* Receive notification of a skipped entity.
*/
public void skippedEntity(String name) throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
if (state==STATE_OUTER)
contentHandler.skippedEntity(name);
}
/**
* Receive notification of the end of a document.
*/
public void endDocument() throws SAXException
{
locatorImpl.setLineNumber(locator.getLineNumber());
locatorImpl.setColumnNumber(locator.getColumnNumber());
if (state==STATE_OUTER)
contentHandler.endDocument();
}
/**
* Report the start of DTD declarations, if any.
*/
public void startDTD(String name, String publicId, String systemId)
throws SAXException
{
if (lexicalHandler!=null)
lexicalHandler.startDTD(name, publicId, systemId);
}
/**
* Report the end of DTD declarations.
*/
public void endDTD() throws SAXException
{
if (lexicalHandler!=null)
lexicalHandler.endDTD();
}
/**
* Report the beginning of an entity.
*/
public void startEntity(String name) throws SAXException
{
if (lexicalHandler!=null)
lexicalHandler.startEntity(name);
}
/**
* Report the end of an entity.
*/
public void endEntity(String name) throws SAXException
{
if (lexicalHandler!=null)
lexicalHandler.endEntity(name);
}
/**
* Report the start of a CDATA section.
*/
public void startCDATA() throws SAXException
{
if (lexicalHandler!=null)
lexicalHandler.startCDATA();
}
/**
* Report the end of a CDATA section.
*/
public void endCDATA() throws SAXException
{
if (lexicalHandler!=null)
lexicalHandler.endCDATA();
}
/**
* Report an XML comment anywhere in the document.
*/
public void comment(char[] ch, int start, int len) throws SAXException
{
if (lexicalHandler!=null)
lexicalHandler.comment(ch, start, len);
}
private void printStates()
{
System.out.println("Current states:");
System.out.println(current.toCanonicalString());
System.out.println("Next states:");
System.out.println(next.toCanonicalString());
}
private void swapStacks()
{
StackNodeSet dummy = next;
next = current;
current = dummy;
next.clear();
}
}