/******************************************************************************* * 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.imp.services; import java.util.Collection; import java.util.Collections; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.imp.editor.ModelTreeNode; import org.eclipse.imp.parser.ISourcePositionLocator; import com.sap.furcas.ide.editor.imp.FurcasParseController; import com.sap.furcas.metamodel.FURCAS.textblocks.Bostoken; import com.sap.furcas.metamodel.FURCAS.textblocks.DocumentNode; import com.sap.furcas.metamodel.FURCAS.textblocks.Eostoken; import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock; import com.sap.furcas.runtime.textblocks.TbNavigationUtil; import com.sap.furcas.runtime.textblocks.TbUtil; import com.sap.furcas.runtime.textblocks.model.TextBlocksModel; import com.sap.ide.cts.parser.incremental.IncrementalParser.TextBlocksTarjanTreeContentProvider; import com.sap.ide.cts.parser.incremental.util.TarjansLCA; /** * Allows IMP to inspect the "AST" nodes returned by our {@link FurcasParseController}. * In case of the letter these are always {@link DocumentNode}s. * * @author Stephan Erb * */ public class FurcasSourcePositionLocator implements ISourcePositionLocator { @Override public Object findNode(Object astRoot, int offset) { TextBlock rootBlock = (TextBlock) astRoot; TextBlocksModel textBlockModel = new TextBlocksModel(rootBlock); return textBlockModel.getFloorTokenInRoot(offset); } @Override public Object findNode(Object astRoot, int startOffset, int endOffset) { TextBlock rootBlock = (TextBlock) astRoot; TextBlocksModel textBlockModel = new TextBlocksModel(rootBlock); DocumentNode leftMostSelectedNode = textBlockModel.getFloorTokenInRoot(startOffset); DocumentNode rightMostSelectedNode = textBlockModel.getFloorTokenInRoot(endOffset); // Handle special case that exactly a whole textblock is selected. // We use this LCA shortcut as it is (much) faster than #getNodesBetweenAsRootSet. TarjansLCA<DocumentNode> lca = new TarjansLCA<DocumentNode>(new TextBlocksTarjanTreeContentProvider()); DocumentNode commonAncestor = lca.lcaSearch(textBlockModel.getRoot(), leftMostSelectedNode, rightMostSelectedNode); DocumentNode leftMostAncestorNode = TbNavigationUtil.firstToken(commonAncestor); // could be BOS token DocumentNode rightMostAncestorNode = TbNavigationUtil.lastToken(commonAncestor); // could be EOS token // special case handling for omitted BOS/EOS token boolean equalLeftMostToken = leftMostAncestorNode.equals(leftMostSelectedNode) || leftMostAncestorNode instanceof Bostoken && TbNavigationUtil.getNextInSubTree(leftMostAncestorNode).equals(leftMostSelectedNode); boolean equalRightMostToken = rightMostAncestorNode.equals(rightMostSelectedNode) || rightMostAncestorNode instanceof Eostoken && TbNavigationUtil.getPreviousInSubTree(rightMostAncestorNode).equals(rightMostSelectedNode); if (equalLeftMostToken && equalRightMostToken) { return Collections.singletonList(commonAncestor); } else { return textBlockModel.getNodesBetweenAsRootSet(textBlockModel.getRoot(), startOffset, endOffset); } } @Override public int getStartOffset(Object entity) { if (entity instanceof TextBlock) { TextBlock tb = (TextBlock) entity; return TbUtil.getAbsoluteOffsetWithoutBlanks(tb); } else if (entity instanceof DocumentNode) { return TbUtil.getAbsoluteOffset((DocumentNode) entity); } if (entity instanceof ModelTreeNode) { return getStartOffset(((ModelTreeNode) entity).getASTNode()); } throw new AssertionError("Unknown entity type " + entity.getClass()); } @Override public int getEndOffset(Object entity) { if (entity instanceof TextBlock) { TextBlock tb = (TextBlock) entity; return TbUtil.getAbsoluteOffsetWithoutBlanks(tb) + getLength(tb) - 1; } else if (entity instanceof DocumentNode) { DocumentNode node = (DocumentNode) entity; return TbUtil.getAbsoluteOffset(node) + getLength(node) - 1; } if (entity instanceof ModelTreeNode) { return getEndOffset(((ModelTreeNode) entity).getASTNode()); } throw new AssertionError("Unknown entity type " + entity.getClass()); } @Override public int getLength(Object entity) { if (entity instanceof TextBlock) { TextBlock tb = (TextBlock) entity; return TbUtil.getLengthWithoutStartingBlanks(tb); } else if (entity instanceof DocumentNode) { DocumentNode node = (DocumentNode) entity; return node.getLength(); } if (entity instanceof ModelTreeNode) { return getLength(((ModelTreeNode) entity).getASTNode()); } throw new AssertionError("Unknown entity type " + entity.getClass()); } @Override public IPath getPath(Object node) { if (node instanceof DocumentNode) { return new Path(((DocumentNode) node).eResource().getURI().toFileString()); } if (node instanceof ModelTreeNode) { return getPath(((ModelTreeNode) node).getASTNode()); } throw new AssertionError("Unknown entity type " + node.getClass()); } /** * Find the TextBlock representing the given modelElement within the tree under * the given rootBlock. It is assumed that there can only be one. */ public TextBlock findTextBlockOf(TextBlock rootBlock, EObject modelElement, ResourceSet resourceSet) { Collection<TextBlock> result = TbUtil.findTextBlockOf(rootBlock, modelElement, resourceSet); if (result.size() == 0) { return null; } else { assert result.size() == 1; return result.iterator().next(); } } }