/**
*
*/
package com.sap.furcas.runtime.parser.impl;
import org.antlr.runtime.BitSet;
import org.antlr.runtime.IntStream;
import org.antlr.runtime.Parser;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.RecognizerSharedState;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenStream;
import com.sap.furcas.metamodel.FURCAS.TCS.ConcreteSyntax;
import com.sap.furcas.runtime.parser.ANTLR3LocationToken;
import com.sap.furcas.runtime.parser.IParsingObserver;
/**
* A parser suppressing the ANTLR 3.0.1 system.err messages and allowing an observer for the parsing process.
* Made abstract since implementing does not make sense without a generated parser subclass, really.
* Also handles awareness of exceptions in observers, makes sure that the exitTemplateRule is not called if an
* exception has happened, so that the exception does not get swallowed by finallys.
*/
public abstract class ObservablePatchedParser extends Parser {
/**
* @param input
* @param state
*/
public ObservablePatchedParser(TokenStream input, RecognizerSharedState state) {
super(input, state);
}
/**
* @param input
* @param state
*/
public ObservablePatchedParser(TokenStream input) {
super(input);
}
protected IParsingObserver observer;
private boolean exceptionThrown = false;
/**
* Returns the UUID of the {@link ConcreteSyntax} for which the parser was generated.
*
* @return the UUID of the syntax model element.
*/
public String getSyntaxUUID() {
return null;
}
public void setObserver(IParsingObserver newObserver) {
this.observer = newObserver;
}
protected void onEnterTemplateRule(String templateURI) {
setExceptionThrown(false);
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
// in observer: create textBlock in TB context (or root) and link to Template using SyntaxLookup
// enter TB context
observer.notifyEnterRule(templateURI);
}
protected void onElementAddedToContext(Object element) {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyElementAddedToContext(element);
}
protected void onRuleElementCreationCommited(Object modelElement) {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
if (modelElement != null) {
observer.notifyCommittedModelElementCreation(modelElement);
} else {
// TODO notify of failure
observer.notifyCommitModelElementFailed();
}
}
/**
* notifies observer that after parsing, an element has been created for a reference in the text.
* @param modelElement
*/
public void onRuleElementResolvedOutOfContext(Object modelElement, Object contextModelElement,
ANTLR3LocationToken referenceLocation, DelayedReference reference) {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyModelElementResolvedOutOfContext(modelElement, contextModelElement, referenceLocation, reference);
}
/**
* notifies observer whenever a new {@link DelayedReference} is created.
* @param ref
*/
protected void onDelayedReferenceCreated(DelayedReference ref) {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyDelayedReferenceCreated(ref);
}
/**
* notifies observer whenever the parser enters a new InjectorAction element.
* @param ref
*/
protected void _enterInjectorAction() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyEnterInjectorAction();
}
/**
* notifies observer whenever the parser enters a new InjectorAction element.
* @param ref
*/
protected void _exitInjectorAction() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyExitInjectorAction();
}
protected void onErrorInTemplateRule(RecognitionException re) {
if (observer != null || getBacktrackingLevel() > 0) {
// in Observer: leave TB Context
observer.notifyErrorInRule(re);
}
}
/**
* notifies leaving of current creation /resolution context, symmetrical to EnterTemplateRule
*/
protected void onExitTemplateRule() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
if (!isExceptionThrown()) { // if we had an exception, do not call notifyExit to prevent consequent exceptions
// in Observer: leave TB Context
observer.notifyExitRule();
}
}
/**
* called before parsing any sequence element of a TCS sequence
*/
protected void _beforeSeqEl() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyEnterSequenceElement();
}
/**
* called before parsing any sequence element of a TCS sequence
* with the given URI (specified as String)
*/
protected void _beforeSeqEl(String sequenceElementURI) {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyEnterSequenceElement(sequenceElementURI);
}
/**
* called after parsing any sequence element of a TCS sequence
*/
protected void _afterSeqEl() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyExitSequenceElement();
}
/**
* called before parsing one of several alternative elements of a TCS sequence
*/
protected void _enterAlt(int choice) {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyEnterSequenceAlternative(choice);
}
protected void _exitAlt() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyExitSequenceAlternative();
}
/**
* called before parsing a separator sequence element of a multivalued property
*/
protected void _enterSepSeq() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyEnterSeparatorSequence();
}
protected void _exitSepSeq() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyExitSeparatorSequence();
}
/**
* called before the bit of an operatored template which allows recursive brackets "((((...))))"
*/
protected void _enterOpdBrackSeq() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyEnterOperatoredBrackettedSequence();
}
protected void _exitOpdBrackSeq() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyExitOperatoredBrackettedSequence();
}
/**
* notifies entering of a certain operator sequence, meaning e.g. for 3+4 that next should be an integer, a "+" and another integer.
* @param operator symbol used for this sequence
* @param arity 1 or 2
* @param unaryPostfix only for 1-ary operators, allow putting the operator behind the operand
*/
protected void _enterOpSeq(String operator, int arity, boolean isUnaryPostfix) {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyEnterOperatorSequence(operator, arity, isUnaryPostfix);
}
protected void _exitOpSeq() {
if (observer == null || getBacktrackingLevel() > 0) {
return;
}
observer.notifyExitOperatorSequence();
}
// copied from ANTLR, added observer code only
/** Match current input symbol against ttype. Upon error, do one token
* insertion or deletion if possible. You can override to not recover
* here and bail out of the current production to the normal error
* exception catch (at the end of the method) by just throwing
* MismatchedTokenException upon input.LA(1)!=ttype.
*/
@Override
public Object match(IntStream input, int ttype, BitSet follow) throws RecognitionException {
//System.out.println("match "+((TokenStream)input).LT(1));
Object matchedSymbol = getCurrentInputSymbol(input);
if (input.LA(1) == ttype) {
input.consume();
if (observer != null && getBacktrackingLevel() == 0) {
observer.notifyTokenConsume((Token) matchedSymbol);
}
state.errorRecovery = false;
state.failed = false;
return matchedSymbol;
}
if (state.backtracking > 0) {
state.failed = true;
return matchedSymbol;
}
if (observer != null && getBacktrackingLevel() == 0) {
observer.notifyTokenConsumeWithError((Token) matchedSymbol);
}
matchedSymbol = recoverFromMismatchedToken(input, ttype, follow);
return matchedSymbol;
}
public boolean isExceptionThrown() {
return exceptionThrown;
}
public void setExceptionThrown(boolean exceptionThrown) {
this.exceptionThrown = exceptionThrown;
}
@Override
public void reset() {
super.reset();
if (this.observer != null) {
this.observer.reset();
}
}
}