package com.sap.ide.cts.parser.incremental;
import static com.sap.furcas.runtime.textblocks.modifcation.TbChangeUtil.addToBlockAt;
import org.eclipse.emf.ecore.EObject;
import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate;
import com.sap.furcas.metamodel.FURCAS.textblocks.AbstractToken;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextblocksFactory;
import com.sap.furcas.metamodel.FURCAS.textblocks.Version;
import com.sap.furcas.runtime.common.exceptions.DeferredModelElementCreationException;
import com.sap.furcas.runtime.common.interfaces.IModelElementProxy;
import com.sap.furcas.runtime.parser.textblocks.ModelElementFromTextBlocksFactory;
import com.sap.furcas.runtime.parser.textblocks.TextBlockFactory;
import com.sap.furcas.runtime.parser.textblocks.observer.TextBlockProxy;
import com.sap.furcas.runtime.parser.textblocks.observer.TokenRelocationUtil;
import com.sap.furcas.runtime.tcs.TcsUtil;
import com.sap.furcas.runtime.textblocks.TbNavigationUtil;
import com.sap.furcas.runtime.textblocks.TbUtil;
import com.sap.ide.cts.parser.incremental.TextBlockReuseStrategy.ReuseType;
import com.sap.ide.cts.parser.incremental.TextBlockReuseStrategy.TbBean;
public class ReuseAwareTextBlockFactoryImpl implements TextBlockFactory {
private final TextblocksFactory textblocksFactory;
private TextBlockReuseStrategy reuseStrategy;
private final ModelElementFromTextBlocksFactory modelElementFactory;
private ReferenceHandler referenceHandler;
public ReuseAwareTextBlockFactoryImpl(TextblocksFactory textblocksPackage, TextBlockReuseStrategy tbReuseStrategy,
ModelElementFromTextBlocksFactory modelElementFactory) {
super();
this.textblocksFactory = textblocksPackage;
this.reuseStrategy = tbReuseStrategy;
this.modelElementFactory = modelElementFactory;
}
@Override
public TextBlock createBlock() {
TextBlock textBlock = textblocksFactory.createTextBlock();
// TODO: check versioning for incremental parsing and adapt
// correspondingly here
textBlock.setVersion(Version.CURRENT);
textBlock.setOffsetRelative(true);
textBlock.setComplete(true); // default
textBlock.setOffset(0);
return textBlock;
}
/**
* Method implemented for interface {@link ITextBlockCreator#createNewTextBlock(TextBlockProxy)}
*/
@Override
public TextBlock createNewTextBlock(TextBlockProxy proxy, TextBlock parent) throws DeferredModelElementCreationException {
// createModelElements(proxy);
return instantiateBlockAndMoveTokens(proxy, parent);
}
/**
* Instantiates a new {@link TextBlock} for the {@link TextBlockProxy} while moving all tokens that were captured there into
* the new block. Furthermore it calls itself recursively to add new blocks for its subBlock Proxies.
*
* @param newVersion
* The Proxy to instatiate
* @param parent
* The parent textblock
* @return the newly instantiated {@link TextBlock} for the given proxy.
*/
private TextBlock instantiateBlockAndMoveTokens(TextBlockProxy newVersion, TextBlock parent)
throws DeferredModelElementCreationException {
TextBlock tb = this.createBlock();
;
tb.setType(newVersion.getTemplate());
tb.setSequenceElement(newVersion.getSequenceElement());
tb.getParentAltChoices().addAll(newVersion.getAlternativeChoices());
tb.getAdditionalTemplates().addAll(newVersion.getAdditionalTemplates());
int endIndex = 0;
for (Object subNode : newVersion.getSubNodes()) {
if (subNode instanceof TextBlockProxy) {
if (((TextBlockProxy) subNode).getSubNodes().size() > 0) {
// do not create textBlocks for proxies that have no content
// this may be the case for example with rules for elements
// that have no correspondence in the concrete syntax and
// have been generated by rules containing only property
// inits.
TextBlock reuseCandidate = IncrementalParsingUtil.getOriginalVersion((TextBlockProxy) subNode, parent);
TbBean subBlock = reuseStrategy.reuseTextBlock(reuseCandidate, (TextBlockProxy) subNode);
if (subBlock.reuseType.equals(ReuseType.COMPLETE)) {
AbstractToken firstToken = TbNavigationUtil.firstToken(subBlock.textBlock);
TokenRelocationUtil.updateTextBlockLocationAfterRemoval(firstToken.getParent());
TokenRelocationUtil.updateParentsAscendingAfterRemoval(firstToken.getParent());
int lengthDiff = TbUtil.getAbsoluteOffset(subBlock.textBlock) - TbUtil.getAbsoluteOffset(firstToken);
subBlock.textBlock.setOffset(TbUtil.getAbsoluteOffset(firstToken));
subBlock.textBlock.setOffsetRelative(false);
subBlock.textBlock.setLength(subBlock.textBlock.getLength() + lengthDiff);
// the tb is moved to a new parent, therefore
// all the feature that referred from the parent to this TB
// needs to be unset
if (parent != null) {
referenceHandler.unsetFeature(parent, subBlock.textBlock);
subBlock.textBlock.setParent(null);
}
}
addToBlockAt(tb, endIndex++, subBlock.textBlock);
}
} else if (subNode instanceof AbstractToken) {
TokenRelocationUtil.relocateToken((AbstractToken) subNode, endIndex++, tb);
reuseStrategy.notifyTokenReuse((AbstractToken) subNode);
}
}
tb.getCorrespondingModelElements().addAll(modelElementFactory.createModelElementsFromTextBlock(newVersion));
// Add all elements in the context to the textblock
for (Object elementInContext : newVersion.getContextElements()) {
if (elementInContext instanceof IModelElementProxy) {
if (((IModelElementProxy) elementInContext).getRealObject() == null) {
throw new DeferredModelElementCreationException("ModelElementProxy in context was not resolved",
elementInContext);
}
tb.getElementsInContext().add((EObject) ((IModelElementProxy) elementInContext).getRealObject());
} else if (elementInContext instanceof EObject) {
tb.getElementsInContext().add((EObject) elementInContext);
}
}
// if the template has specified the addToContext property we need to
// add the corresponding element
// to the context
if (newVersion.getTemplate() instanceof ClassTemplate) {
if (((ClassTemplate) newVersion.getTemplate()).isIsAddToContext()) {
// move up to the first parent textblock that has the context
// property set
TextBlock loopParent = parent;
while (loopParent != null) {
if (TcsUtil.isContext(loopParent.getType())) {
loopParent.getElementsInContext().addAll(tb.getCorrespondingModelElements());
break;
}
loopParent = loopParent.getParent();
}
}
}
return tb;
}
public void setTextBlocksReuseStrategy(TextBlockReuseStrategy textBlockReuseStrategyImpl) {
reuseStrategy = textBlockReuseStrategyImpl;
}
public void setReferenceHandler(ReferenceHandler referenceHandler) {
this.referenceHandler = referenceHandler;
}
}