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; } }