/** * */ package com.sap.furcas.runtime.parser.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EmptyStackException; import java.util.HashSet; import java.util.List; import java.util.Stack; import org.antlr.runtime.BitSet; import org.antlr.runtime.CommonToken; import org.antlr.runtime.Lexer; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.RecognizerSharedState; import org.antlr.runtime.Token; import org.antlr.runtime.TokenStream; import org.eclipse.emf.common.util.URI; import com.sap.furcas.runtime.common.exceptions.ModelAdapterException; import com.sap.furcas.runtime.common.implementation.ResolvedModelElementProxy; import com.sap.furcas.runtime.common.interfaces.IModelElementProxy; import com.sap.furcas.runtime.common.interfaces.IRuleName; import com.sap.furcas.runtime.parser.ANTLR3LocationToken; import com.sap.furcas.runtime.parser.IModelInjector; import com.sap.furcas.runtime.parser.IParsingObserver; import com.sap.furcas.runtime.parser.ModelElementCreationException; import com.sap.furcas.runtime.parser.ParsingError; import com.sap.furcas.runtime.parser.TextLocation; import com.sap.furcas.runtime.parser.antlr3.ANTLR3LocationTokenImpl; import com.sap.furcas.runtime.parser.impl.context.ContextManager; /** * Class used as superclass for generated parser/Injectors which allows observing of syntax processing events and token * matching events. Assumes generated parser calls the methods in this class on events. The parser is stateful, separate * parse calls should be separated by resetParser() in between. All non-private methods are final to ensure that in the * unlikely case of a generated parser rule method overwriting a method, this does not compile (would require renaming * of modelelements or patch to name of methods). * * Also see in class comments. */ public abstract class ObservableInjectingParser extends ObservablePatchedParser implements ContextBuilder { /* * The implementation assumes parser rules to follow a certain convention to ensure Model injection works. Rules * which create ModelElements should do so by calling createModelElementProxy(), then set() or setRef() several * times, then commitCreation or discardProxy (which is in this case called by onErrorInTemplateRule). Finally * setDelayedReferencesAfterParsing needs to be called. createModelElementProxy() will create an IModelElementProxy * Object which should have a lifecylce which ends in any case when the rule that created it is finished. * * A Syntax may declare Elements to be contexts for other elements, and the parser keeps track of the context * elements using a Stack of proxies and the ContextManager Class. The context only becomes relevant for Delayed * References with a path referring to the context. Since as explained above ModelElements should be created at the * end of rules, the Stack will only ever contain modelElement proxies. When that context is left, the proxy is * being replaced by the actual ModelElement, in the contextmanager and all delayed references. * * In sum, there are two different concepts for deferred actions, IModelElementProxy defers ModelElement creation to * the end of a rule (when properties have been set), and DelayedReferences defers Reference creation to the end of * the whole parsing process (when ModelElements to be referred to could have been created). */ /** * The reference to the model injector, which allows creating modelElements during parsing. */ private com.sap.furcas.runtime.parser.IModelInjector injector = null; private ContextManager contextManager; /** The delayed reference list. */ private List<DelayedReference> delayedReferenceList; /** * the list of delayed references with referents that have not yet been resolved or created */ private List<DelayedReference> unResolvedDelayedReferenceList; /** * Stack context proxies. Each {@link #addContext(IModelElementProxy, String...)} pushes an element * onto this stack whereas a {@link #leaveContext()} pops one. */ private java.util.Stack<IModelElementProxy> currentContextStack; /** * When a "foreach" predicate is used, the values computed by its OCL expression are assigned to this attribute * before invoking the parse rule for the subtemplate identified by the foreach construct. The parser can then use * it when creating {@link DelayedReference}s such that when the reference uses "#foreach" in its OCL expression, it * can replace the "#foreach" occurrences by "self" and use the element denoted by this attribute as context for the * OCL expression instead. * <p> * * A single element is sufficient, no stack is required, because when a "foreach" predicate reference is evaluated, * while the parser rule is executed, should new "foreach" references be created, they won't be resolved before the * parser has finished. Therefore, no nesting can occur at runtime. * * The value may be of type {@link RefObject}, {@link String}, {@link Boolean} or {@link Number}. */ private Object currentForeachElement; /** * Specifies if proxies should be resolved dureing parsing or if the resolving is triggered explicitely */ private boolean resolveProxies = true; private String languageId; /** * @param input * @param state */ public ObservableInjectingParser(TokenStream input, RecognizerSharedState state) { super(input, state); resetParser(); } /** * @param input * @param state */ public ObservableInjectingParser(TokenStream input) { super(input); resetParser(); } public ContextManager getContextManager() { return contextManager; } public Stack<IModelElementProxy> getCurrentContextStack() { return currentContextStack; } protected Object getCurrentForeachElement() { return currentForeachElement; } public void setCurrentForeachElement(Object currentForeachElement) { this.currentForeachElement = currentForeachElement; } public void initParser(ContextManager manager, Stack<IModelElementProxy> currentContext) { contextManager = manager; currentContextStack = currentContext; } /** * */ public final void resetParser() { contextManager = initContextManager(); currentContextStack = new Stack<IModelElementProxy>(); delayedReferenceList = new ArrayList<DelayedReference>(); unResolvedDelayedReferenceList = new ArrayList<DelayedReference>(); } @Override public void reset() { super.reset(); // clear errors if (injector != null) { injector.getErrorList().clear(); } resetParser(); } /** * util method intended for test mocks to override, to break class dependency * * @return */ protected ContextManager initContextManager() { return new ContextManager(); } protected IModelElementProxy createModelElementProxy(List<String> name, boolean context, boolean addToContext) { return createModelElementProxy(name, context, addToContext, (String[]) null); } /** * * @param name * @param context * @param addToContext * @return */ protected final IModelElementProxy createModelElementProxy(List<String> name, boolean context, boolean addToContext, String... tags) { IModelElementProxy element = new ModelElementProxy(name, true, getANTLRToken(input.LT(1))); // conditionally adding if addToContext==true if (addToContext) { addToCurrentContext(element); } if (context) { // this element is relevant as context for setting // delayed references addContext(element, tags); } return element; } /* * (non-Javadoc) * * @see com.sap.mi.textual.grammar.impl.ContextBuilder#addToContext(com.sap.mi .textual.grammar.IModelElementProxy, * java.lang.String) */ @Override public void addContext(IModelElementProxy element, String... tags) { IModelElementProxy currentContextElement = getCurrentContextElement(); if (currentContextElement != null) { // create context using proxy for now, need to replace the proxy by // real object after // resolution / creation contextManager.addContextChildFor(currentContextElement, element, tags); } else { contextManager.addRootContext(element, tags); } currentContextStack.push(element); } /** * * @param name * @param context * @param addToContext * @return */ protected final IModelElementProxy createReferenceProxy(List<String> name) { IModelElementProxy element = new ModelElementProxy(name, false, input.LT(1)); return element; } /** * * @param me */ @Override public void addToCurrentContext(Object me) { Object currentContext = getCurrentContextElement(); if (currentContext == null) { throw new RuntimeException("Bug in DSL, addToContext called without context."); // TODO: for incremental parsing maybe set context manually before // calling rule } contextManager.addToContext(currentContext, me); } /** * */ public final void leaveContext() { try { List<Object> elementsInContext = contextManager.getElementsInContext(getCurrentContextElement()); if (elementsInContext != null) { for (Object elementInContext : elementsInContext) { onElementAddedToContext(elementInContext); } } currentContextStack.pop(); } catch (EmptyStackException e) { throw new RuntimeException("BUG: leave Context called more often than enterContext."); } } /** * convenience Method creating or resolving proxies having just one token. * * @param object * @param token * @return */ public final Object createOrResolve(Object object, Token token) { if (resolveProxies) { return createOrResolve(object, (ANTLR3LocationToken) token, (ANTLR3LocationToken) token); } else { return object; } } /** * finalizes creation, turns the proxy created at the beginning of a rule into a real Modelelement * * @param object * @param token * @param firstToken * @return */ public final Object createOrResolve(Object object, ANTLR3LocationToken firstToken, ANTLR3LocationToken lastToken) { Object result = null; ModelElementProxy proxy = (ModelElementProxy) object; try { result = injector.createOrResolve(proxy, firstToken, lastToken); } catch (ModelElementCreationException e) { injector.addError(new ParsingError(e.getMessage(), createTextLocationBetween(firstToken, lastToken))); } if (result != null) { contextManager.notifyProxyResolvedWith(proxy, result, getCurrentContextElement()); // now we still have DelayedReferences being set in the context of // this object. ArrayList<DelayedReference> tempResolvedDelayedReferenceList = new ArrayList<DelayedReference>(); for (DelayedReference ref : unResolvedDelayedReferenceList) { // Check if the unresolved reference is trying to resolve some // feature of the // element represented by object: if (ref.getModelElement().equals(object)) { // if so, the resolved object proxy (result) can now be // substituted in the reference ref.setModelElement(result); // if not to be resolved by a context lookup, do a deferred // lookup if (ref.getContextElement() == null) { tempResolvedDelayedReferenceList.add(ref); } // Those with a context lookup will be handled below } else if (ref instanceof SemanticDisambiguateDelayedReference) { tempResolvedDelayedReferenceList.add(ref); } // TODO what if the context-Proxy gets resolved before the // modelElement proxy? Then the DelayedReference would no longer // be in unResolvedDelayedReferenceList and therefore the // modelElement wouldn't be correctly substituted. if (ref.getContextElement() != null && ref.getContextElement().equals(object)) { // now both modelElement and context should be resolved. ref.setContextElement(result); tempResolvedDelayedReferenceList.add(ref); } } for (DelayedReference delayedReference : tempResolvedDelayedReferenceList) { unResolvedDelayedReferenceList.remove(delayedReference); delayedReferenceList.add(delayedReference); } } else { // resolution or creation of object from proxy failed, errors should // have been generated // in other places. ArrayList<DelayedReference> tempObsoleteDelayedReferenceList = new ArrayList<DelayedReference>(); for (DelayedReference ref : unResolvedDelayedReferenceList) { if (ref.getModelElement().equals(object)) { tempObsoleteDelayedReferenceList.add(ref); } if (ref.getContextElement() != null && ref.getContextElement().equals(object)) { tempObsoleteDelayedReferenceList.add(ref); } } for (DelayedReference delayedReference : tempObsoleteDelayedReferenceList) { unResolvedDelayedReferenceList.remove(delayedReference); } } return result; } /** * the method to call to get the root modelElement parsed (or a collection or roots, if allowed by the syntax) * * @return * @throws RecognitionException */ public abstract Object main() throws RecognitionException; /** * Execute a parse run with a dummy ModelInjector that actually does not create any model element. This may be * useful if you only want to do a syntax check without creating model elements at all. * * @return * @throws RecognitionException */ public List<ParsingError> checkSyntaxWithoutInjecting() { // store current Injector to restore it afterwards IModelInjector temp = getInjector(); // The getTokenNames is overwritten in the generated Parser to not // return null. DummyModelInjector dummyModelInjector = new DummyModelInjector(getTokenNames()); setInjector(dummyModelInjector); try { main(); } catch (RecognitionException e) { reportError(e); } setInjector(temp); return dummyModelInjector.getErrorList(); } /** * ANTLR method to report some error. */ @Override public final void displayRecognitionError(String[] tokenNames, RecognitionException e) { injector.reportError(e); } /** * This method is called for modelElements to be created by templates. Since actual creation is delayed until the * end of the rule, the commit method has to check whether the modelElement can be created (or resolved) and or not. * * @param ret * @param firstToken * @param leaveContext * @return */ protected final Object commitCreation(Object ret, Token firstToken, boolean leaveContext) { Object ret2 = ret; ANTLR3LocationToken lastToken = getANTLRToken(input.LT(-1)); if (resolveProxies) { // firstToken will always be null if used with scannerless parsing if (firstToken != null) { ANTLR3LocationToken firstANTLRToken = getANTLRToken(firstToken); ret2 = createOrResolve(ret, firstANTLRToken, lastToken); } else { ret2 = createOrResolve(ret, null, null); } if (ret2 != null && firstToken != null) { this.setLocationAndComment(ret2, firstToken); } else { discardProxy(ret); } } else { if (ret instanceof ModelElementProxy) { ((ModelElementProxy) ret).setLastToken(lastToken); } } if (ret2 != null) { onRuleElementCreationCommited(ret2); } if (leaveContext) { leaveContext(); } return ret2; } /** * @param firstToken * @return */ private static ANTLR3LocationToken getANTLRToken(Token firstToken) { if (firstToken == null) { return null; } if (firstToken instanceof ANTLR3LocationToken) { return (ANTLR3LocationToken) firstToken; } else { CommonToken castToken = (CommonToken) firstToken; ANTLR3LocationToken convertedToken = new ANTLR3LocationTokenImpl(castToken.getType(), castToken.getText()); convertedToken.setChannel(castToken.getChannel()); convertedToken.setCharPositionInLine(castToken.getCharPositionInLine()); convertedToken.setLine(castToken.getLine()); convertedToken.setTokenIndex(castToken.getTokenIndex()); convertedToken.setStartIndex(castToken.getStartIndex()); convertedToken.setStopIndex(castToken.getStopIndex()); return convertedToken; } } /** * sets Location of modelElement using the firstToken parameter as start and the token before the current token of * the input stream as last token. Stream = {..., first, x, y, z, last, current, ...} -> Location = {first, x, y, z, * last} * * @param ret * @param firstTokenInBlock */ public final void setLocationAndComment(Object ret, org.antlr.runtime.Token firstTokenInBlock) { // get last token in current textBlock (meaning there may be n tokens // between first and lastToken) ANTLR3LocationToken lastTokenInBlock = (ANTLR3LocationToken) input.LT(-1); TextLocation location = createTextLocationBetween(firstTokenInBlock, lastTokenInBlock); injector.setLocation(ret, location); injector.setCommentsBefore(ret, input, firstTokenInBlock); if (lastTokenInBlock != null) { injector.setCommentsAfter(ret, input, lastTokenInBlock); } } /** * creates a Textlocation between two tokens including the first char of the first token and the last char of the * last token. Assumes last comes after first. * * @param firstTokenInBlock * @param lastTokenInBlock * @return */ protected static TextLocation createTextLocationBetween(org.antlr.runtime.Token firstTokenInBlock, ANTLR3LocationToken lastTokenInBlock) { int startIndex = ((org.antlr.runtime.CommonToken) firstTokenInBlock).getStartIndex(); int line = firstTokenInBlock.getLine(); int column = (firstTokenInBlock.getCharPositionInLine() + 1); // fallbacks if last token is null int stopIndex = startIndex; int endLine = line; int endColumn = column; if (lastTokenInBlock != null) { ANTLR3LocationToken locToken = lastTokenInBlock; stopIndex = locToken.getStopIndex() + 1; endLine = locToken.getEndLine(); endColumn = (locToken.getEndColumn() + 1); } TextLocation location = new TextLocation(startIndex, stopIndex, line, column, endLine, endColumn); return location; } /** * * @return */ public final IModelInjector getInjector() { return injector; } /** * * @param ei */ public final void setInjector(IModelInjector ei) { this.injector = ei; if (input != null && input.getTokenSource() instanceof Lexer) { try { input.getTokenSource().getClass().getField("ei").set(input.getTokenSource(), ei); } catch (IllegalArgumentException e) { throw new RuntimeException("Lexer does not support model injector!!"); } catch (SecurityException e) { throw new RuntimeException("Lexer does not support model injector!!"); } catch (IllegalAccessException e) { throw new RuntimeException("Lexer does not support model injector!!"); } catch (NoSuchFieldException e) { throw new RuntimeException("Lexer does not support model injector!!"); } } } /** * * @param re * @param modelElementOrProxy */ protected final void onErrorInTemplateRule(RecognitionException re, Object modelElementOrProxy) { if (modelElementOrProxy != null && modelElementOrProxy instanceof IModelElementProxy) { // can not resolve or create ModelElement, discardProxy(modelElementOrProxy); if (getCurrentContextElement() == modelElementOrProxy) { leaveContext(); } } super.onErrorInTemplateRule(re); } /** * util method to build list of Strings, to be used in generated Parsers to build qualified names. * * @param entries * @return */ protected static List<String> list(String... entries) { List<String> list = Arrays.asList(entries); return list; } /** * notifies that within a template rule, a token mismatch was encountered, meaning that the temporary modelElement * proxy cannot be instantiated. * * @param re * @param ret */ protected final void handleExceptionInTemplateRule(Exception e, Token firstTokenInBlock, Object proxy) { if (e instanceof RecognitionException) { RecognitionException re = (RecognitionException) e; reportError(re); recover(input, re); onErrorInTemplateRule(re, proxy); } else if (e instanceof RuntimeException) { // can only really be // RuntimeException thrown // by observer setExceptionThrown(true); throw (RuntimeException) e; } else { // should never happen throw new RuntimeException("BUG: Unexpected Exception", e); } } /** * Returns an (unmodifiable) list of still unresolved references. May be interesting to clients who triggered a * parser run and want to display, e.g., to a user if there are still unresolved references and what they are. */ public List<DelayedReference> getUnresolvedReferences() { // first ensure that all newly resolvable elements are moved to the // delayedReferenceList replaceResolvedProxies(); return Collections.unmodifiableList(delayedReferenceList); } /** * Clients may call this to prevent a reference from being resolved. * * @param ref */ public void removeUnresolvedReference(DelayedReference ref) { delayedReferenceList.remove(ref); } /** * parsing may generate references which can only be resolved after parsing, this method needs to be called to tell * the parser to use the elements parsed so far (and elements in other contexts if any) to resolve any open * references. This is done using a kind of fixpoint iteration where references are tried to resolve until no new * reference is successfully resolved. * * @return <tt>true</tt> if all unresolved references were resolved, <tt>false</tt> if there are still unresolved * references. A client can obtain those unresolved references by calling {@link #getUnresolvedReferences}. */ public final boolean setDelayedReferencesAfterParsing() { injector.performAdapterDeferredActions(); replaceResolvedProxies(); /* * It is assumed here that at this point of time, the code structure guarantees that the list of delayed * References does not contain any IModelElementproxy elements, neither does the ContextManager contain any * IModelElementProxy object. */ if (injector.getErrorList().size() != 0) { return delayedReferenceList.isEmpty() && unResolvedDelayedReferenceList.isEmpty(); } do { // if in the resolving part some new references are created they must be evaluated as well delayedReferenceList.addAll(unResolvedDelayedReferenceList); unResolvedDelayedReferenceList.clear(); boolean resolvedNewReference = true; Collection<DelayedReference> resolvedReferences = new HashSet<DelayedReference>(); // try to resolve references as long as there are new references resolved // this is done because the actual optimal ordering is not known at this point while (fixPointIterationHasNotReachedAStableStage(resolvedNewReference)) { resolvedNewReference = false; // clear the error list as the errors might get resolved within // the next iteration if not they will be added again anyways injector.getErrorList().clear(); for (DelayedReference reference : delayedReferenceList) { if (reference.getAutoCreate() != null && resolvedReferences.contains(reference)) { continue; // make sure to create elements only once. } if (resolveDelayedRef(resolvedReferences, reference)) { resolvedNewReference = true; } } } // We've reached a stable stage for the currently active references and don't have to check them again. delayedReferenceList.removeAll(resolvedReferences); } while (hasMoreUnresolvedReferences()); return delayedReferenceList.size() == 0; } protected boolean hasMoreUnresolvedReferences() { return !unResolvedDelayedReferenceList.isEmpty(); } protected boolean fixPointIterationHasNotReachedAStableStage(boolean resolvedNewReference) { return !delayedReferenceList.isEmpty() && resolvedNewReference; } protected boolean resolveDelayedRef(Collection<DelayedReference> resolvedReferences, DelayedReference reference) { try { Collection<ParsingError> errorList = new ArrayList<ParsingError>(injector.getErrorList()); Object valueBefore = getReferenceValue(reference); boolean resolvedSuccessfully = injector.resolveReference(reference, contextManager, this); if (resolvedSuccessfully) { // log which references could be resolved. Keep them around // so that they can be tried to resolve again if necessary. resolvedReferences.add(reference); onRuleElementResolvedOutOfContext(reference.getRealValue(), getModelElement(reference), reference.getToken(), reference); Object valueAfter = getReferenceValue(reference); if (valueHasChanged(valueBefore, valueAfter)) { return true; } } else if (reference.isOptional()) { restoreErrorList(errorList); } } catch (ModelElementCreationException e) { getInjector().addError(new ParsingError(e.getMessage(), reference.getToken())); } catch (ModelAdapterException e) { getInjector().addError(new ParsingError(e.getMessage(), reference.getToken())); } return false; } private Object getModelElement(DelayedReference reference) { return (reference.getModelElement() instanceof IModelElementProxy) ? ((IModelElementProxy) reference .getModelElement()).getRealObject() : reference.getModelElement(); } private Object getReferenceValue(DelayedReference reference) throws ModelAdapterException { return injector.getModelAdapter().get(reference.getModelElement(), reference.getPropertyName()); } private boolean valueHasChanged(Object valueBefore, Object valueAfter) { if ((valueBefore == null && valueAfter != null) || (valueBefore != null && valueAfter == null)) { return true; } if (valueBefore == null && valueAfter == null) { return false; } else if (valueBefore instanceof Collection && valueAfter instanceof Collection) { Collection<?> collectionBefore = (Collection<?>) valueBefore; Collection<?> collectionAfter = (Collection<?>) valueAfter; return !collectionBefore.containsAll(collectionAfter) || !collectionAfter.containsAll(collectionBefore); } else { return !valueBefore.equals(valueAfter); } } private void restoreErrorList(Collection<ParsingError> errorList) { injector.getErrorList().clear(); injector.getErrorList().addAll(errorList); } /** * The {@link #unResolvedDelayedReferenceList} list may still contain some {@link DelayedReference}s that haven't * been copied over to {@link #delayedReferenceList} because they still contain proxies as their * {@link DelayedReference#getModelElement() modelElement} or as their {@link DelayedReference#getContextElement() * context element}. However, those proxies may be resolved, e.g., because they always were (see, e.g., * {@link ResolvedModelElementProxy}). For those proxies, no * {@link #createOrResolve(Object, ANTLR3LocationToken, ANTLR3LocationToken)} will ever be called. Therefore, this * method needs to be called at the beginning of {@link #setDelayedReferencesAfterParsing()} in order to replace the * remaining proxies by the real objects they represent. */ private void replaceResolvedProxies() { List<DelayedReference> tempResolvedDelayedReferenceList = new ArrayList<DelayedReference>(); for (DelayedReference ref : unResolvedDelayedReferenceList) { if (ref.getModelElement() instanceof IModelElementProxy && ((IModelElementProxy) ref.getModelElement()).getRealObject() != null) { contextManager.notifyProxyResolvedWith((IModelElementProxy) ref.getModelElement(), ((IModelElementProxy) ref.getModelElement()).getRealObject(), null); ref.setModelElement(((IModelElementProxy) ref.getModelElement()).getRealObject()); // if not to be resolved by a context lookup, do a deferred // lookup if (ref.getContextElement() == null) { tempResolvedDelayedReferenceList.add(ref); } // Those with a context lookup will be handled below } if (ref.getContextElement() != null && ref.getContextElement() instanceof IModelElementProxy && ((IModelElementProxy) ref.getContextElement()).getRealObject() != null) { contextManager.notifyProxyResolvedWith((IModelElementProxy) ref.getContextElement(), ((IModelElementProxy) ref.getContextElement()).getRealObject(), null); ref.setContextElement(((IModelElementProxy) ref.getContextElement()).getRealObject()); tempResolvedDelayedReferenceList.add(ref); } } for (DelayedReference delayedReference : tempResolvedDelayedReferenceList) { unResolvedDelayedReferenceList.remove(delayedReference); delayedReferenceList.add(delayedReference); } } /** * * @param modelElement * @param featurename * @param value */ public final void setProperty(Object modelElement, String featurename, Object value) { injector.set(modelElement, featurename, value); } public final void setParent(Object modelElement, Object parent, String propertyName) { if (modelElement instanceof ModelElementProxy) { ModelElementProxy proxy = ((ModelElementProxy) modelElement); proxy.setParent(parent); proxy.setParentPropertyName(propertyName); } } public final void setSemDisambiguate(Object proxy, Object opTemplateLefthand, String opName, Object element, List<SemanticDisambRuleData> preds, boolean hasContext, ANTLR3LocationToken firstToken) { ANTLR3LocationToken lastToken = (ANTLR3LocationToken) input.LT(-1); SemanticDisambiguateDelayedReference ref = new SemanticDisambiguateDelayedReference(getCurrentContextElement(), proxy, element, opTemplateLefthand, opName, preds, lastToken, firstToken, hasContext, true); unResolvedDelayedReferenceList.add(ref); } /** * * @param modelElement * @param propertyName * @param valueTypeName * @param keyName * @param keyValue * @param lookIn * @param autoCreate * @param createAs * @param importContext * @param createIn */ public final void setRef(Object modelElement, String propertyName, List<String> valueTypeName, String keyName, Object keyValue, String lookIn, String autoCreate, List<String> createAs, boolean importContext, String createIn) { // if (keyValue == null) return; ANTLR3LocationToken lastToken = (ANTLR3LocationToken) input.LT(-1); ContextLookupDelayedReference ref = new ContextLookupDelayedReference(getCurrentContextElement(), getCurrentForeachElement(), modelElement, propertyName, valueTypeName, keyName, keyValue, lookIn, autoCreate, createAs, importContext, createIn, false, lastToken); onDelayedReferenceCreated(ref); unResolvedDelayedReferenceList.add(ref); } /** * * @param modelElement * @param propertyName * @param valueTypeName * @param keyName * @param keyValue * @param lookIn * @param autoCreate * @param createAs * @param importContext * @param createIn */ public final void setRef(Object modelElement, String propertyName, List<String> valueTypeName, String keyName, Object keyValue, String lookIn, String autoCreate, List<String> createAs, boolean importContext, String createIn, boolean isOptional) { // if (keyValue == null) return; ANTLR3LocationToken lastToken = (ANTLR3LocationToken) input.LT(-1); ContextLookupDelayedReference ref = new ContextLookupDelayedReference(getCurrentContextElement(), getCurrentForeachElement(), modelElement, propertyName, valueTypeName, keyName, keyValue, lookIn, autoCreate, createAs, importContext, createIn, isOptional, lastToken); onDelayedReferenceCreated(ref); unResolvedDelayedReferenceList.add(ref); } /** * @return */ private IModelElementProxy getCurrentContextElement() { if (currentContextStack.size() > 0) { return currentContextStack.peek(); } return null; } /** * * @param proxy */ public void discardProxy(Object proxy) { contextManager.discardProxy(proxy, getCurrentContextElement()); } public final void setOclRef(Object object, String propertyName, String keyName, Object keyValue, String query, String propInitURI, ModelUpdaterRegistry modelUpdaterRegistry) { setOclRef(object, propertyName, keyName, keyValue, query, /* optional */ false, propInitURI, modelUpdaterRegistry); } /** * * @param object * @param propertyName * @param keyName * @param keyValue * @param query */ public final void setOclRef(Object object, String propertyName, String keyName, Object keyValue, String query, boolean optional, String propInitURI, ModelUpdaterRegistry modelUpdaterRegistry) { ANTLR3LocationToken lastToken = (ANTLR3LocationToken) input.LT(-1); // TODO use propInitURI for DelayedReference construction to obtain Triggerable from SyntaxRegistry if (modelUpdaterRegistry != null) { ModelUpdater modelUpdater = modelUpdaterRegistry.getModelUpdater(URI.createURI(propInitURI)); } DefaultLookupDelayedReference ref = new DefaultLookupDelayedReference(getCurrentContextElement(), getCurrentForeachElement(), object, propertyName, keyName, keyValue, query, optional, lastToken); onDelayedReferenceCreated(ref); unResolvedDelayedReferenceList.add(ref); } /** * * @param object * @param propertyName * @param mode * @param query */ public final void setPredicateRef(Object object, String propertyName, String mode, String query, List<PredicateSemantic> preds, IRuleName ruleNameFinder, boolean hasContext, String propInitURI, ModelUpdaterRegistry modelUpdaterRegistry) { ANTLR3LocationToken lastToken = (ANTLR3LocationToken) input.LT(-1); // TODO use propInitURI for DelayedReference construction to obtain Triggerable from SyntaxRegistry if (modelUpdaterRegistry != null) { ModelUpdater modelUpdater = modelUpdaterRegistry.getModelUpdater(URI.createURI(propInitURI)); } ForeachDelayedReference ref = new ForeachDelayedReference(getCurrentContextElement(), object, propertyName, query, mode, preds, ruleNameFinder, lastToken, hasContext, /* isOptional: ForEach is always considered optional as * error reporting will be done based on metamodel constraints. */ true); unResolvedDelayedReferenceList.add(ref); onDelayedReferenceCreated(ref); } /** * * @param enumName * @param literalValue * @return */ protected final Object createEnumLiteral(List<String> enumName, String literalValue) { return injector.createEnumLiteral(enumName, literalValue); } @Override public void pushFollow(BitSet fset) { super.pushFollow(fset); } public void popFollow() { state._fsp--; } /** * This redefinition assumes that if an empty FOLLOWS set is on the follows stack anywhere other than at the bottm, * it should be ignored instead of taking it as authoritative because it doesn't contain End-of-Rule. */ @Override protected BitSet combineFollows(boolean exact) { int top = state._fsp; BitSet followSet = new BitSet(); for (int i = top; i >= 0; i--) { BitSet localFollowSet = state.following[i]; /* * System.out.println("local follow depth "+i+"="+ localFollowSet.toString(getTokenNames())+")"); */ followSet.orInPlace(localFollowSet); if (exact) { // can we see end of rule? if (localFollowSet.member(Token.EOR_TOKEN_TYPE)) { // Only leave EOR in set if at top (start rule); this lets // us know if we have to include follow(start rule); i.e., // EOF if (i > 0) { followSet.remove(Token.EOR_TOKEN_TYPE); } } else { // can't see end of rule, quit if (!localFollowSet.isNil()) { break; } } } } return followSet; } protected void checkFollows() { /* * if (state.backtracking > 0 && !state.failed) { BitSet follows = combineFollows(true); state.failed = * !(follows.member(input.LA(1)) || (input.LA(1) == -1 && follows .member(Token.EOR_TOKEN_TYPE))); } */ } public String toString(BitSet fset) { StringBuilder result = new StringBuilder(); boolean first = true; for (int i : fset.toArray()) { if (!first) { result.append(", "); } first = false; result.append(getTokenNames()[i]); } return result.toString(); } /** * @return the resolveProxies */ public boolean isResolveProxies() { return resolveProxies; } /** * @param resolveProxies * the resolveProxies to set */ public void setResolveProxies(boolean resolveProxies) { this.resolveProxies = resolveProxies; } public void setLanguageId(String id) { languageId = id; } public String getLanguageId() { return languageId; } public IParsingObserver getObserver() { return observer; } public List<DelayedReference> getDelayedReferences() { return unResolvedDelayedReferenceList; } }