/*******************************************************************************
* 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.runtime.parser.incremental.testbase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
import org.eclipse.ocl.ecore.opposites.DefaultOppositeEndFinder;
import org.junit.Before;
import com.sap.furcas.metamodel.FURCAS.textblocks.AbstractToken;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock;
import com.sap.furcas.metamodel.FURCAS.textblocks.Version;
import com.sap.furcas.parsergenerator.TCSSyntaxContainerBean;
import com.sap.furcas.runtime.parser.incremental.ClassLookupImpl;
import com.sap.furcas.runtime.parser.testbase.ClassLookup;
import com.sap.furcas.runtime.parser.testbase.MockPartitionAssignmentHandler;
import com.sap.furcas.runtime.referenceresolving.SyntaxRegistry;
import com.sap.furcas.runtime.textblocks.model.TextBlocksModel;
import com.sap.furcas.runtime.textblocks.modifcation.TbChangeUtil;
import com.sap.furcas.runtime.textblocks.modifcation.TbVersionUtil;
import com.sap.furcas.runtime.textblocks.testutils.EMFTextBlocksModelElementFactory;
import com.sap.furcas.runtime.textblocks.testutils.TestSourceTextBlockCreator;
import com.sap.furcas.runtime.textblocks.testutils.TextBlocksModelElementFactory;
import com.sap.furcas.runtime.textblocks.validation.TbValidationUtil;
import com.sap.furcas.test.testutils.ResourceTestHelper;
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;
public abstract class IncrementalParserBasedTest extends GeneratedParserAndFactoryBasedTest {
public static class ParsingResult {
public final EObject oldRoot;
public final EObject newRoot;
public final TextBlock newResultBlock;
public ParsingResult(EObject oldRoot, EObject newRoot, TextBlock newResultBlock) {
this.oldRoot = oldRoot;
this.newRoot = newRoot;
this.newResultBlock = newResultBlock;
}
}
protected static IncrementalParserFacade incrementalParserFacade;
protected static Resource transientParsingResource;
protected static ResourceSet resourceSet;
protected static SyntaxRegistry syntaxRegistry;
protected TextBlocksModel model;
protected static void setupParser(String languageName, File syntaxDefFile, File... metamodels) throws Exception {
setupParser(languageName, syntaxDefFile, new ClassLookupImpl(), /*useModelAdapters*/ false, metamodels);
}
protected static void setupParser(String languageName, File syntaxDefFile, boolean useModelUpdaters, File... metamodels) throws Exception {
setupParser(languageName, syntaxDefFile, new ClassLookupImpl(), useModelUpdaters, metamodels);
}
protected static void setupParser(String languageName, File syntaxDefFile, ClassLookup classLookup, File... metamodels) throws Exception {
setupParser(languageName, syntaxDefFile, classLookup, /*useModelAdapters*/ false, metamodels);
}
protected static void setupParser(String languageName, File syntaxDefFile, ClassLookup classLookup, boolean useModelUpdaters, File... metamodels) throws Exception {
GeneratedParserAndFactoryTestConfiguration testConfig = new GeneratedParserAndFactoryTestConfiguration(languageName, syntaxDefFile, metamodels);
resourceSet = testConfig.getSourceConfiguration().getResourceSet();
TCSSyntaxContainerBean syntaxBean = parseSyntax(testConfig);
transientParsingResource = ResourceTestHelper.createTransientResource(resourceSet);
incrementalParserFacade = generateParserAndParserFactoryForLanguage(syntaxBean, testConfig,
resourceSet, new MockPartitionAssignmentHandler(transientParsingResource), classLookup);
ECrossReferenceAdapter crossRefAdapter = new ECrossReferenceAdapter();
resourceSet.eAdapters().add(crossRefAdapter);
crossRefAdapter.setTarget(resourceSet);
if (useModelUpdaters) {
syntaxRegistry = new SyntaxRegistry();
syntaxRegistry.registerSyntaxForIncrementalEvaluation(incrementalParserFacade.getParserScope().getSyntax(),
DefaultOppositeEndFinder.getInstance(), /* progress monitor */null, incrementalParserFacade.getParserFactory());
syntaxRegistry.registerAllLoadedSyntaxesTriggerManagers(resourceSet);
}
}
@Before
public void setupTextBlocksModel() throws Exception {
TextBlocksModelElementFactory modelFactory = new EMFTextBlocksModelElementFactory();
AbstractToken emptyToken = modelFactory.createToken("");
TextBlock rootBlock = TestSourceTextBlockCreator.initialiseTextBlocksWithContentToken(modelFactory, emptyToken);
model = new TextBlocksModel(rootBlock);
// delete model elements and textblocks of the previous test run.
transientParsingResource.getContents().clear();
}
/**
* Parses with best effort. Use TB Version to descide whether parsing was successful.
* This method is modeled after the implementation of the editor ParseCommand.
*/
protected ParsingResult triggerParser() {
EObject oldRoot = IncrementalParserFacade.getParsingResult(model.getRoot());
TextBlock blockWithUnparsedEdits = TbVersionUtil.getOtherVersion(model.getRoot(), Version.PREVIOUS);
String preParseContent = model.get(0, model.getLength());
TextBlock resultBlock = null;
try {
TextBlock newBlock = incrementalParserFacade.parseIncrementally(blockWithUnparsedEdits);
// Both lexing and parsing were successfull. Make a new REFERENCE version.
resultBlock = (TextBlock) TbChangeUtil.cleanUp(newBlock);
model.setRootTextBlock(resultBlock);
} catch (SemanticParserException e) {
if (e.getComponentThatFailed() == Component.LEXICAL_ANALYSIS) {
resultBlock = TbVersionUtil.getOtherVersion(model.getRoot(), Version.PREVIOUS);
} else {
resultBlock = TbVersionUtil.getOtherVersion(model.getRoot(), Version.CURRENT);
}
} catch (RuntimeException e) {
throw e;
}
String postParseContent = model.get(0, model.getLength());
assertEquals("Textual representation must not be changed", preParseContent, postParseContent);
assertPartitioning();
TbValidationUtil.assertTextBlockConsistencyRecursive(resultBlock);
TbValidationUtil.assertCacheIsUpToDate(resultBlock);
EObject newRoot = IncrementalParserFacade.getParsingResult(resultBlock);
return new ParsingResult(oldRoot, newRoot, resultBlock);
}
/**
* Assert that we have correctly partitioned the parsed model into resources.
* Makes the assumption that the resource is cleared before each parse run.
*/
protected void assertPartitioning() {
int numTextBlocksInReferenceVersion = 0;
int numTextBlocksInPreviousVersion = 0;
int numTextBlocksInCurrentVersion = 0;
int numModelElements = 0;
// iter over top-level elements
for (EObject object : transientParsingResource.getContents()) {
if (object instanceof TextBlock) {
switch (((TextBlock) object).getVersion()) {
case REFERENCE: numTextBlocksInReferenceVersion++; break;
case PREVIOUS: numTextBlocksInPreviousVersion++; break;
case CURRENT: numTextBlocksInCurrentVersion++; break;
}
} else if (object instanceof AbstractToken) {
fail("Tokens must always be owned by TextBlocks");
} else {
numModelElements++;
}
}
assertTrue("There may not be more than one textblock of each version. Seen " + numTextBlocksInReferenceVersion
+ " REFERENCE, " + numTextBlocksInPreviousVersion + " PREVIOUS, " + numTextBlocksInCurrentVersion + " CURRENT.",
numTextBlocksInReferenceVersion <= 1 && numTextBlocksInCurrentVersion <=1 && numTextBlocksInPreviousVersion <= 1);
// Only one is edited at a time. If more are seen than either syntax or partitioning is buggy.
assertTrue("May not be more than one model element in the resource. Seen " + numModelElements, numModelElements <= 1);
}
}