/******************************************************************************* * Copyright (c) 2005, 2015 IBM Corporation 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: * John Camelon - Initial API and implementation * Markus Schorn (Wind River Systems) * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTImageLocation; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.parser.IToken; import org.eclipse.cdt.core.parser.OffsetLimitReachedException; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.parser.scanner.ILexerLog; import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver; import org.eclipse.cdt.internal.core.parser.scanner.Lexer; import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; import org.eclipse.cdt.internal.core.parser.scanner.Token; /** * Base class for all non-preprocessor nodes in the AST. */ public abstract class ASTNode implements IASTNode { private IASTNode parent; private ASTNodeProperty property; /** * The sequence number of the ast-node as calculated by the location map. * Do not access directly, because getOffset() may be overloaded for lazy calculations. */ private int offset; private int length; private IASTNodeLocation[] locations; private IASTFileLocation fileLocation; private boolean frozen = false; private boolean active = true; @Override public IASTNode getParent() { return parent; } @Override public IASTNode[] getChildren() { ChildCollector collector= new ChildCollector(this); return collector.getChildren(); } @Override public final boolean isFrozen() { return frozen; } @Override public boolean isActive() { return active; } void setIsFrozen() { frozen = true; } public void setInactive() { assertNotFrozen(); active= false; } protected final void assertNotFrozen() throws IllegalStateException { if (frozen) throw new IllegalStateException("Attempt to modify a frozen AST node"); //$NON-NLS-1$ } @Override public void setParent(IASTNode node) { assertNotFrozen(); this.parent = node; } @Override public final ASTNodeProperty getPropertyInParent() { return property; } @Override public void setPropertyInParent(ASTNodeProperty property) { assertNotFrozen(); this.property = property; } public int getOffset() { return offset; } public final int getLength() { return length; } public void setOffset(int offset) { this.offset = offset; this.locations = null; this.fileLocation = null; } public void setLength(int length) { this.length = length; this.locations = null; this.fileLocation = null; } public void setOffsetAndLength(int offset, int length) { this.offset = offset; this.length = length; this.locations = null; this.fileLocation = null; } public void setOffsetAndLength(ASTNode node) { setOffsetAndLength(node.getOffset(), node.getLength()); } @Override public IASTNodeLocation[] getNodeLocations() { if (locations == null) { if (length != 0) { final IASTTranslationUnit tu= getTranslationUnit(); if (tu != null) { ILocationResolver l= tu.getAdapter(ILocationResolver.class); if (l != null) { locations= l.getLocations(getOffset(), length); } } } if (locations == null) locations= IASTNodeLocation.EMPTY_ARRAY; } return locations; } public IASTImageLocation getImageLocation() { final IASTTranslationUnit tu= getTranslationUnit(); if (tu != null) { ILocationResolver l= tu.getAdapter(ILocationResolver.class); if (l != null) { return l.getImageLocation(getOffset(), length); } } return null; } protected char[] getRawSignatureChars() { final IASTNode originalNode = getOriginalNode(); final IASTFileLocation floc= originalNode.getFileLocation(); final IASTTranslationUnit ast = originalNode.getTranslationUnit(); if (floc != null && ast != null) { ILocationResolver lr= ast.getAdapter(ILocationResolver.class); if (lr != null) { return lr.getUnpreprocessedSignature(floc); } } return CharArrayUtils.EMPTY; } @Override public String getRawSignature() { return new String(getRawSignatureChars()); } @Override public String getContainingFilename() { final int offset = getOffset(); if (offset <= 0 && (length == 0 || offset < 0)) { final IASTNode parent = getParent(); if (parent == null) { if (this instanceof IASTTranslationUnit) { return ((IASTTranslationUnit) this).getFilePath(); } return ""; //$NON-NLS-1$ } return parent.getContainingFilename(); } return getTranslationUnit().getContainingFilename(offset); } @Override public IASTFileLocation getFileLocation() { if (fileLocation != null) return fileLocation; final int offset = getOffset(); // Only an empty translation unit should have offset = 0 and length = 0. // Otherwise these values mean the parser failed to set the offset and length. if (offset < 0 || (offset == 0 && length == 0 && !(this instanceof IASTTranslationUnit))) { return null; } IASTTranslationUnit ast = getTranslationUnit(); if (ast != null) { ILocationResolver lr= ast.getAdapter(ILocationResolver.class); if (lr != null) { fileLocation= lr.getMappedFileLocation(offset, length); } else { // Support for old location map fileLocation= ast.flattenLocationsToFile(getNodeLocations()); } } return fileLocation; } @Override public boolean isPartOfTranslationUnitFile() { IASTTranslationUnit ast = getTranslationUnit(); if (ast != null) { ILocationResolver lr= ast.getAdapter(ILocationResolver.class); if (lr != null) { return lr.isPartOfTranslationUnitFile(getOffset()); } } return false; } public boolean isPartOfSourceFile() { IASTTranslationUnit ast = getTranslationUnit(); if (ast != null) { ILocationResolver lr= ast.getAdapter(ILocationResolver.class); if (lr != null) { return lr.isPartOfSourceFile(getOffset()); } } return false; } @Override public IASTTranslationUnit getTranslationUnit() { IASTNode node = this; for (IASTNode p = parent; p != null; p = p.getParent()) { node = p; } return node instanceof IASTTranslationUnit ? (IASTTranslationUnit) node : null; } @Override public boolean accept(ASTVisitor visitor) { return true; } @Override public boolean contains(IASTNode node) { if (node instanceof ASTNode) { ASTNode astNode= (ASTNode) node; final int offset = getOffset(); final int nodeOffset= astNode.getOffset(); return offset <= nodeOffset && nodeOffset + astNode.length <= offset + length; } return false; } @Override public IToken getSyntax() throws ExpansionOverlapsBoundaryException { final int offset = getOffset(); return getSyntax(offset, offset + length, 0); } @Override public IToken getLeadingSyntax() throws ExpansionOverlapsBoundaryException { int left= getBoundary(-1); return getSyntax(left, getOffset(), -1); } @Override public IToken getTrailingSyntax() throws ExpansionOverlapsBoundaryException { int right= getBoundary(1); return getSyntax(getOffset() + length, right, 1); } /** * Compute the sequence number of the boundary of the leading/trailing syntax. */ private int getBoundary(int direction) { ASTNodeSearch visitor= new ASTNodeSearch(this); IASTNode sib= direction < 0 ? visitor.findLeftSibling() : visitor.findRightSibling(); if (sib == null) { direction= -direction; sib= getParent(); } if (sib instanceof ASTNode) { ASTNode astNode= (ASTNode) sib; int offset= astNode.getOffset(); if (direction < 0) { offset += astNode.getLength(); } return offset; } // No parent. throw new UnsupportedOperationException(); } private IToken getSyntax(int fromSequenceNumber, int nextSequenceNumber, int direction) throws ExpansionOverlapsBoundaryException { final IASTTranslationUnit tu= getTranslationUnit(); if (!(tu instanceof ASTNode)) throw new UnsupportedOperationException(); ILocationResolver lr= tu.getAdapter(ILocationResolver.class); if (lr == null) throw new UnsupportedOperationException(); int endSequenceNumber= lr.convertToSequenceEndNumber(nextSequenceNumber); IASTFileLocation total= lr.getMappedFileLocation(fromSequenceNumber, endSequenceNumber - fromSequenceNumber); IASTFileLocation myfloc= getFileLocation(); if (total == null || myfloc == null) throw new UnsupportedOperationException(); if (!total.getFileName().equals(myfloc.getFileName())) throw new ExpansionOverlapsBoundaryException(); if (fromSequenceNumber > 0) { IASTFileLocation fl= lr.getMappedFileLocation(fromSequenceNumber-1, endSequenceNumber - fromSequenceNumber + 1); if (fl.getFileName().equals(total.getFileName()) && fl.getNodeOffset() == total.getNodeOffset()) throw new ExpansionOverlapsBoundaryException(); } if (endSequenceNumber < ((ASTNode) tu).getOffset() + ((ASTNode) tu).getLength()) { IASTFileLocation fl= lr.getMappedFileLocation(fromSequenceNumber, nextSequenceNumber - fromSequenceNumber + 1); if (fl.getFileName().equals(total.getFileName()) && fl.getNodeLength() == total.getNodeLength()) throw new ExpansionOverlapsBoundaryException(); } int adjustment= total.getNodeOffset() - myfloc.getNodeOffset(); if (direction > 0) { adjustment-= myfloc.getNodeLength(); } char[] txt= lr.getUnpreprocessedSignature(total); Lexer lex= new Lexer(txt, tu.getAdapter(LexerOptions.class), ILexerLog.NULL, null); try { Token result= null; Token last= null; while (true) { Token t= lex.nextToken(); switch (t.getType()) { case IToken.tEND_OF_INPUT: return result; case Lexer.tNEWLINE: break; default: int offset= t.getOffset() + adjustment; int endOffset= t.getEndOffset() + adjustment; t.setOffset(offset, endOffset); if (last == null) { result= last= t; } else { last.setNext(t); last= t; } break; } } } catch (OffsetLimitReachedException e) { // Does not happen without using content assist limit. } return null; } protected <T extends ASTNode> T copy(T copy, CopyStyle style) { copy.setOffsetAndLength(this); if (style == CopyStyle.withLocations) { ((ASTNode) copy).setCopyLocation(this); } return copy; } private void setCopyLocation(IASTNode originalNode) { locations = new IASTNodeLocation[] { new ASTCopyLocation(originalNode) }; } @Override public IASTNode getOriginalNode() { IASTNode node = this; while (true) { IASTNodeLocation[] locations = node.getNodeLocations(); if (locations.length == 0 || !(locations[0] instanceof ASTCopyLocation)) break; node = ((ASTCopyLocation) locations[0]).getOriginalNode(); } return node; } /** * If ambiguity resolution is in progress, and processing of this node has been deferred, * process it now. Has no effect if ambiguity resolution is not in progress. */ public void resolvePendingAmbiguities() { ((ASTTranslationUnit) getTranslationUnit()).resolvePendingAmbiguities(this); } /** * Helper method for use in {{@link #accept(ASTVisitor)} methods. * * @param action the visitor to accept * @param nodes the array of nodes accepting the visitor * @return continue on ({@code true}) or quit ({@code false}) */ protected static <T extends IASTNode> boolean acceptByNodes(T[] nodes, ASTVisitor action) { for (T node : nodes) { if (!node.accept(action)) return false; } return true; } }