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