/*******************************************************************************
* Copyright (c) 2011 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
******************************************************************************/
package com.sap.furcas.ide.editor.commands;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.imp.parser.IMessageHandler;
import org.eclipse.swt.widgets.Display;
import com.sap.furcas.ide.editor.document.CtsDocument;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock;
import com.sap.furcas.metamodel.FURCAS.textblocks.Version;
import com.sap.furcas.runtime.parser.ParsingError;
import com.sap.furcas.runtime.textblocks.modifcation.TbChangeUtil;
import com.sap.furcas.runtime.textblocks.modifcation.TbVersionUtil;
import com.sap.furcas.runtime.textblocks.shortprettyprint.ShortPrettyPrinter;
import com.sap.ide.cts.parser.errorhandling.SemanticParserException;
import com.sap.ide.cts.parser.errorhandling.SemanticParserException.Component;
import com.sap.ide.cts.parser.incremental.IncrementalParserFacade;
/**
* Command which controlls the execution of the incremental parser.
* It is designed to properly synchronize with both the editing domain
* and the content of the editor.
*
* @author Stephan Erb
*/
public class ParseCommand extends RecordingCommand {
// TODO: Looks like that for proper undo/redo all modifications have to be
// wrapped into CompositeEMFOperation, so that after redo we can restore
// the old content of the CtsDocument. This would mean steps 1) and 3)
// have to individual operations.
// Stephan Erb, 28.05.2011
private final CtsDocument document;
private final IncrementalParserFacade parserFacade;
private TextBlock result;
private final IMessageHandler handler;
private boolean wasEffective;
private final ShortPrettyPrinter shortPrettyPrinter;
private final IProgressMonitor monitor;
private final TransactionalEditingDomain domain;
public ParseCommand(TransactionalEditingDomain domain, CtsDocument document, IncrementalParserFacade parserFacade, IMessageHandler handler, IProgressMonitor monitor) {
super(domain, "Parse document");
this.domain = domain;
this.document = document;
this.parserFacade = parserFacade;
this.handler = handler;
this.monitor = monitor;
this.result = document.getRootBlock();
this.shortPrettyPrinter = new ShortPrettyPrinter(parserFacade.getModelElementInvestigator());
}
@Override
protected void doExecute() {
wasEffective = false;
// 1) Write all buffered user edits to the underlying
// textblocks model. Synchronize to prevent concurrent
// editing from within the UI thread.
boolean contentChanged = document.flushUserEditsToTextBlocskModel();
if (!contentChanged) {
// parser was triggered by IMP, but there are no changes that require reparsing.
return;
}
// 2) Run the parser. This creates a new TextBlocks model
// Parsing runs in the background only.
TextBlock blockWithUnparsedEdits = TbVersionUtil.getOtherVersion(document.getRootBlock(), Version.PREVIOUS);
result = parse(blockWithUnparsedEdits);
// Succeded without an exception. The command will not be rolled back and the
// model changes will remain in effect
wasEffective = true;
if (result == null || Version.REFERENCE != result.getVersion()) {
return;
}
// 3) Merge all user edits that happened while step 2 was running.
// Afterwards, run the short pretty printer to update all tokens according
// to domain model changes. The order is essential here, because otherwise
// the offsets of the user's text edits are already invalid.
document.setRootBlock(result);
Runnable privRunnable = domain.createPrivilegedRunnable(new Runnable() {
@Override
public void run() {
document.refreshContentFromTextBlocksModelChanges(shortPrettyPrinter);
}
});
Display.getDefault().syncExec(privRunnable);
}
private TextBlock parse(TextBlock oldBlock) {
try {
TextBlock newBlock = parserFacade.parseIncrementally(oldBlock, monitor);
if (monitor.isCanceled()) {
return null;
} else {
// Both lexing and parsing were successfull. Make a new REFERENCE version.
return (TextBlock) TbChangeUtil.cleanUp(newBlock);
}
} catch (SemanticParserException e) {
handleParseException(e);
// We can use the created textblock as an intermediate result, as
// it is sufficient for services such as the token colorer.
if (e.getComponentThatFailed() == Component.LEXICAL_ANALYSIS) {
return TbVersionUtil.getOtherVersion(oldBlock, Version.PREVIOUS);
} else {
return TbVersionUtil.getOtherVersion(oldBlock, Version.CURRENT);
}
}
}
private void handleParseException(SemanticParserException parserException) {
for (ParsingError ex : parserException.getIssuesList()) {
handler.handleSimpleMessage(ex.getMessage(), ex.getIndex(), ex.getStopIndex(),
ex.getPosition(), ex.getEndPosition(), ex.getLine(), ex.getEndLine());
}
}
public TextBlock getParsingResult() {
return result;
}
/**
* True if some actual work was done. False if the command did not change anything.
*/
public boolean wasEffective() {
return wasEffective;
}
}