package com.sap.ide.cts.parser.incremental;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.Lexer;
import org.antlr.runtime.Token;
import org.eclipse.emf.ecore.EObject;
import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.ForeachPredicatePropertyInit;
import com.sap.furcas.metamodel.FURCAS.TCS.Template;
import com.sap.furcas.metamodel.FURCAS.textblocks.AbstractToken;
import com.sap.furcas.metamodel.FURCAS.textblocks.Bostoken;
import com.sap.furcas.metamodel.FURCAS.textblocks.DocumentNode;
import com.sap.furcas.metamodel.FURCAS.textblocks.ForEachExecution;
import com.sap.furcas.metamodel.FURCAS.textblocks.LexedToken;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock;
import com.sap.furcas.runtime.common.exceptions.ModelAdapterException;
import com.sap.furcas.runtime.parser.impl.DelayedReference;
import com.sap.furcas.runtime.parser.impl.DelayedReferencesHelper;
import com.sap.furcas.runtime.parser.textblocks.observer.ParserTextBlocksHandler;
import com.sap.furcas.runtime.parser.textblocks.observer.TextBlockProxy;
import com.sap.furcas.runtime.textblocks.TbNavigationUtil;
import com.sap.furcas.runtime.textblocks.modifcation.TbMarkingUtil;
import com.sap.furcas.runtime.textblocks.shortprettyprint.ShortPrettyPrinter;
import com.sap.ide.cts.parser.incremental.antlr.ANTLRLexerAdapter;
public class MappingRecoveringTextBlocksValidator {
private final IncrementalParserFacade parserFacade;
private final ShortPrettyPrinter shortPrettyPrinter;
public MappingRecoveringTextBlocksValidator(IncrementalParserFacade parserFacade) {
this.parserFacade = parserFacade;
this.shortPrettyPrinter = new ShortPrettyPrinter(parserFacade.getModelElementInvestigator());
}
/**
* Ensures that the token ids stored in the TextBlocks model are still in
* sync with those from the generated parser.<p>
*
* If only a token Id changed and lexing is still possible without errors,
* the ids are updated correspondingly. If lexing without errors is not
* possible, this routing will mark the effect region for later reparsing.
*/
public void checkAndMigrateTokenIds(TextBlock rootBlock) {
AbstractToken tok = (AbstractToken) rootBlock.getSubNodes().get(0);
// BOS token does not have to be checked. It also is not necessary to
// check if the tb model is in its empty initial state
// this is indicated by the only token having an id of -1
Lexer antlrLexer = ((ANTLRLexerAdapter) parserFacade.getLexer()).getANTLRLexer();
CharStream originalStream = antlrLexer.getCharStream();
try {
while (!TbMarkingUtil.isEOS(tok = TbNavigationUtil.nextToken(tok)) && tok != null && tok.getType() != 0) {
String value = shortPrettyPrinter.resynchronizeToEditableState(tok);
if (!TbMarkingUtil.isEOS(tok)) {
AbstractToken nextToken = TbNavigationUtil.nextToken(tok);
value += shortPrettyPrinter.resynchronizeToEditableState(nextToken);
}
antlrLexer.setCharStream(new ANTLRStringStream(value));
Token lexerToken = antlrLexer.nextToken();
if (lexerToken.getType() != tok.getType()) {
// token id may have changed
// This works because lexer and parser are already connected
if (parserFacade.getErrors().size() > 0) {
// Ok this means not only have the token ids changed
// but also the lexer rules do not work for this token
// anymore. Markin the token will force re-lexing.
TbMarkingUtil.mark(tok);
} else {
tok.setType(lexerToken.getType());
}
}
}
} finally {
antlrLexer.setCharStream(originalStream);
}
}
public boolean hasBrokenMapping(TextBlock rootBlock) {
return rootBlock.getType() == null;
}
/**
* FIXME: Recover {@link ForeachPredicatePropertyInit#getInjectorActionsBlockReference()}
* from broken mapping!
*
*/
public void recoverMappingLink(TextBlock existingRoot, ClassTemplate rootTemplate)
throws TextBlockMappingRecoveringFailedException {
ParserTextBlocksHandler parserTextBlocksHandler = parserFacade.createParserTextBlocksHandler();
parserFacade.getParser().setObserver(parserTextBlocksHandler);
parserFacade.getIncrementalLexer().setCurrentTokenForParser(existingRoot.getTokens().get(0));
// Ensure no model elements get created only proxies should be created
boolean originalResolveProxiesValue = parserFacade.getParser().isResolveProxies();
parserFacade.getIncrementalParser().prepareForParsing(existingRoot, parserTextBlocksHandler);
parserFacade.getParser().setResolveProxies(false);
try {
existingRoot.setType(rootTemplate);
RecoverMappingLinkComand command = new RecoverMappingLinkComand(parserFacade, existingRoot, parserTextBlocksHandler);
command.runRecovery();
if (command.hasFailed()) {
throw new TextBlockMappingRecoveringFailedException("Unable to recover mapping.");
}
if (parserFacade.getErrors().size() > 1) {
throw new TextBlockMappingRecoveringFailedException(
"Cannot recover textblock to mapping link due to the following parse errors:" + parserFacade.getErrors());
}
} catch (Exception e) {
throw new TextBlockMappingRecoveringFailedException(e,
"Cannot recover textblock to mapping link due to the following errors:" + e.getMessage());
} finally {
parserFacade.getParser().setResolveProxies(originalResolveProxiesValue);
}
}
private static class RecoverMappingLinkComand {
private final TextBlock existingRoot;
private final ParserTextBlocksHandler parserTextBlocksHandler;
private Map<TextBlockProxy, List<DelayedReference>> tBProxy2Reference;
private final IncrementalParserFacade parserFacade;
private boolean failed;
public RecoverMappingLinkComand(IncrementalParserFacade parserFacade, TextBlock existingRoot,
ParserTextBlocksHandler parserTextBlocksHandler) {
this.parserFacade = parserFacade;
this.existingRoot = existingRoot;
this.parserTextBlocksHandler = parserTextBlocksHandler;
}
public void runRecovery() throws Exception {
parserFacade.getIncrementalParser().callBatchParser(existingRoot);
// FIXME: evaluate foreach references to reestablish links where
// templates used in foreachs
// are referenced as additionalTemplates
TextBlockProxy proxy = parserTextBlocksHandler.getCurrentTbProxy();
createTBProxy2ReferenceMap();
recoverMappingLink(existingRoot, proxy);
}
private void createTBProxy2ReferenceMap() {
tBProxy2Reference = new HashMap<TextBlockProxy, List<DelayedReference>>();
for (DelayedReference ref : parserFacade.getParser().getDelayedReferences()) {
if (ref.getType() == DelayedReference.ReferenceType.TYPE_FOREACH_PREDICATE) {
List<DelayedReference> refs = tBProxy2Reference.get(ref.getTextBlock());
if (refs == null) {
refs = new ArrayList<DelayedReference>(3);
tBProxy2Reference.put((TextBlockProxy) ref.getTextBlock(), refs);
}
refs.add(ref);
}
}
}
private void recoverMappingLink(TextBlock textBlock, TextBlockProxy proxy) throws Exception {
if (proxy.getSubNodes().size() != TbNavigationUtil.getSubNodesSize(textBlock)) {
if (TbNavigationUtil.getSubNodeAt(textBlock, 0) instanceof Bostoken) {
if (proxy.getSubNodes().size() + 2 != TbNavigationUtil.getSubNodesSize(textBlock)) {
failed = true;
return;
}
} else {
failed = true;
return;
}
}
textBlock.setSequenceElement(proxy.getSequenceElement());
Template t = proxy.getTemplate();
if (t == null) {
failed = true;
return;
}
textBlock.setType(t);
textBlock.getAdditionalTemplates().addAll(proxy.getAdditionalTemplates());
if (textBlock.getForEachExecutions().size() > 0) {
recoverForEachContext(textBlock, proxy);
}
int i = 0;
for (DocumentNode n : TbNavigationUtil.getSubNodes(textBlock)) {
if (n instanceof TextBlock) {
TextBlockProxy subNodeProxy = (TextBlockProxy) proxy.getSubNodes().get(i);
recoverMappingLink((TextBlock) n, subNodeProxy);
}
if (n instanceof LexedToken && ((LexedToken) n).getSequenceElement() == null) {
((LexedToken) n).setSequenceElement(proxy.getSequenceElement());
}
if (!(n instanceof Bostoken)) {
i++;
}
}
}
private void recoverForEachContext(TextBlock textBlock, TextBlockProxy proxy) throws ModelAdapterException {
for (ForEachExecution fec : textBlock.getForEachExecutions()) {
for (EObject ro : textBlock.getCorrespondingModelElements()) {
if (fec.getSourceModelElement().equals(ro)) {
for (DelayedReference ref : tBProxy2Reference.get(proxy)) {
Collection<?> result = DelayedReferencesHelper.evaluateForeachOcl(ro, ref,
parserFacade.getModelAdapter(), ro);
if (result.contains(fec.getContextElement())) {
fec.setForeachPedicatePropertyInit((ForeachPredicatePropertyInit) ref.getQueryElement());
textBlock.getAdditionalTemplates().add(
fec.getForeachPedicatePropertyInit().getInjectorActionsBlock().getParentTemplate());
}
}
}
}
}
}
public boolean hasFailed() {
return failed;
}
}
}