/******************************************************************************* * Copyright (c) 2008, 2014 Wind River Systems, Inc. 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: * Markus Schorn - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTImplicitName; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNodeSelector; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion; import org.eclipse.cdt.internal.core.dom.parser.ASTNodeSpecification.Relation; import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver; /** * Class to support searching for nodes by file offsets. * @since 5.0 */ public class ASTNodeSelector implements IASTNodeSelector { private final ASTTranslationUnit fTu; private final ILocationResolver fLocationResolver; private String fFilePath; private final boolean fIsValid; public ASTNodeSelector(ASTTranslationUnit tu, ILocationResolver locationResolver, String filePath) { fTu= tu; fLocationResolver= locationResolver; fFilePath= filePath; fIsValid= verify(); } private boolean verify() { if (fLocationResolver != null) { if (fFilePath == null) { fFilePath= fLocationResolver.getTranslationUnitPath(); } return true; } return false; } private <T extends IASTNode> T findNode(int offsetInFile, int lengthInFile, Relation relation, Class<T> requiredClass) { return findNode(offsetInFile, lengthInFile, relation, requiredClass, false); } private <T extends IASTNode> T findNode(int offsetInFile, int lengthInFile, Relation relation, Class<T> requiredClass, boolean searchInExpansion) { if (!fIsValid) { return null; } if (lengthInFile < 0) { throw new IllegalArgumentException("Length cannot be less than zero."); //$NON-NLS-1$ } int sequenceLength; int altSequenceNumber= -1; int sequenceNumber= fLocationResolver.getSequenceNumberForFileOffset(fFilePath, offsetInFile); if (sequenceNumber < 0) { return null; } if (lengthInFile > 0) { sequenceLength= fLocationResolver.getSequenceNumberForFileOffset(fFilePath, offsetInFile + lengthInFile - 1) + 1 - sequenceNumber; } else { sequenceLength= 0; if (offsetInFile > 0) { altSequenceNumber= fLocationResolver.getSequenceNumberForFileOffset(fFilePath, offsetInFile - 1); if (altSequenceNumber + 1 == sequenceNumber) { altSequenceNumber= -1; } else { // we are on a context boundary and we need to check the variant to the left and // the one to the right sequenceLength= 1; } } } final ASTNodeSpecification<T> nodeSpec= new ASTNodeSpecification<T>(relation, requiredClass, offsetInFile, lengthInFile); nodeSpec.setRangeInSequence(sequenceNumber, sequenceLength, false); nodeSpec.setSearchInExpansion(searchInExpansion); getNode(nodeSpec); if (altSequenceNumber != -1) { nodeSpec.setRangeInSequence(altSequenceNumber, sequenceLength, true); getNode(nodeSpec); } return nodeSpec.getBestNode(); } private <T extends IASTNode> T getNode(ASTNodeSpecification<T> nodeSpec) { fLocationResolver.findPreprocessorNode(nodeSpec); if (!nodeSpec.requiresClass(IASTPreprocessorMacroExpansion.class)) { // adjust sequence number for search in the expansion of macros int seqbegin= nodeSpec.getSequenceStart(); int seqend= nodeSpec.getSequenceEnd(); IASTPreprocessorMacroExpansion expansion= nodeSpec.findLeadingMacroExpansion(this); if (expansion != null) { IASTFileLocation floc= expansion.getFileLocation(); seqbegin= fLocationResolver.getSequenceNumberForFileOffset(fFilePath, floc.getNodeOffset() + floc.getNodeLength() - 1) + 1; } expansion= nodeSpec.findTrailingMacroExpansion(this); if (expansion != null) { IASTFileLocation floc= expansion.getFileLocation(); seqend= fLocationResolver.getSequenceNumberForFileOffset(fFilePath, floc.getNodeOffset() + floc.getNodeLength()); } nodeSpec.setRangeInSequence(seqbegin, seqend - seqbegin); FindNodeForOffsetAction nodeFinder= new FindNodeForOffsetAction(nodeSpec); fTu.accept(nodeFinder); } return nodeSpec.getBestNode(); } @Override public IASTNode findFirstContainedNode(int offset, int length) { return findNode(offset, length, Relation.FIRST_CONTAINED, IASTNode.class); } @Override public IASTNode findNode(int offset, int length) { return findNode(offset, length, Relation.EXACT_MATCH, IASTNode.class); } @Override public IASTNode findEnclosingNode(int offset, int length) { return findNode(offset, length, Relation.ENCLOSING, IASTNode.class); } @Override public IASTNode findStrictlyEnclosingNode(int offset, int length) { return findNode(offset, length, Relation.STRICTLY_ENCLOSING, IASTNode.class); } @Override public IASTNode findFirstContainedNodeInExpansion(int offset, int length) { return findNode(offset, length, Relation.FIRST_CONTAINED, IASTNode.class, true); } @Override public IASTNode findNodeInExpansion(int offset, int length) { return findNode(offset, length, Relation.EXACT_MATCH, IASTNode.class, true); } @Override public IASTNode findEnclosingNodeInExpansion(int offset, int length) { return findNode(offset, length, Relation.ENCLOSING, IASTNode.class, true); } @Override public IASTName findFirstContainedName(int offset, int length) { return findNode(offset, length, Relation.FIRST_CONTAINED, IASTName.class); } @Override public IASTName findName(int offset, int length) { return findNode(offset, length, Relation.EXACT_MATCH, IASTName.class); } @Override public IASTName findEnclosingName(int offset, int length) { return findNode(offset, length, Relation.ENCLOSING, IASTName.class); } @Override public IASTImplicitName findImplicitName(int offset, int length) { return findNode(offset, length, Relation.EXACT_MATCH, IASTImplicitName.class); } @Override public IASTImplicitName findEnclosingImplicitName(int offset, int length) { return findNode(offset, length, Relation.ENCLOSING, IASTImplicitName.class); } @Override public IASTPreprocessorMacroExpansion findEnclosingMacroExpansion(int offset, int length) { return findNode(offset, length, Relation.ENCLOSING, IASTPreprocessorMacroExpansion.class); } }