/*******************************************************************************
* Copyright (c) 2000, 2009 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.core.dom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.util.Util;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;
/**
* Java compilation unit AST node type. This is the type of the root of an AST.
* <p>
* The source range for this type of node is ordinarily the entire source file, including leading
* and trailing whitespace and comments.
* </p>
* For JLS2:
*
* <pre>
* CompilationUnit:
* [ PackageDeclaration ]
* { ImportDeclaration }
* { TypeDeclaration | <b>;</b> }
* </pre>
*
* For JLS3, the kinds of type declarations grew to include enum and annotation type declarations:
*
* <pre>
* CompilationUnit:
* [ PackageDeclaration ]
* { ImportDeclaration }
* { TypeDeclaration | EnumDeclaration | AnnotationTypeDeclaration | <b>;</b> }
* </pre>
*
* @since 2.0
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public class CompilationUnit extends ASTNode {
/**
* Canonical empty list of messages.
*/
private static final Message[] EMPTY_MESSAGES= new Message[0];
/**
* Canonical empty list of problems.
*/
private static final IProblem[] EMPTY_PROBLEMS= new IProblem[0];
/**
* The "imports" structural property of this node type.
*
* @since 3.0
*/
public static final ChildListPropertyDescriptor IMPORTS_PROPERTY=
new ChildListPropertyDescriptor(CompilationUnit.class, "imports", ImportDeclaration.class, NO_CYCLE_RISK); //$NON-NLS-1$
/**
* The "package" structural property of this node type.
*
* @since 3.0
*/
public static final ChildPropertyDescriptor PACKAGE_PROPERTY=
new ChildPropertyDescriptor(CompilationUnit.class, "package", PackageDeclaration.class, OPTIONAL, NO_CYCLE_RISK); //$NON-NLS-1$
/**
* A list of property descriptors (element type: {@link StructuralPropertyDescriptor}), or null
* if uninitialized.
*
* @since 3.0
*/
private static final List PROPERTY_DESCRIPTORS;
/**
* The "types" structural property of this node type.
*
* @since 3.0
*/
public static final ChildListPropertyDescriptor TYPES_PROPERTY=
new ChildListPropertyDescriptor(CompilationUnit.class, "types", AbstractTypeDeclaration.class, CYCLE_RISK); //$NON-NLS-1$
static {
List properyList= new ArrayList(4);
createPropertyList(CompilationUnit.class, properyList);
addProperty(PACKAGE_PROPERTY, properyList);
addProperty(IMPORTS_PROPERTY, properyList);
addProperty(TYPES_PROPERTY, properyList);
PROPERTY_DESCRIPTORS= reapPropertyList(properyList);
}
/**
* Returns a list of structural property descriptors for this node type. Clients must not modify
* the result.
*
* @param apiLevel the API level; one of the <code>AST.JLS*</code> constants
*
* @return a list of property descriptors (element type: {@link StructuralPropertyDescriptor})
* @since 3.0
*/
public static List propertyDescriptors(int apiLevel) {
return PROPERTY_DESCRIPTORS;
}
/**
* The comment mapper, or <code>null</code> if none; initially <code>null</code>.
*
* @since 3.0
*/
private DefaultCommentMapper commentMapper= null;
/**
* The Java type root (an <code>org.eclipse.jdt.core.ICompilationUnit</code> or an
* <code>org.eclipse.jdt.core.IClassFile</code>) this compilation unit was created from, or
* <code>null</code> if it was not created from a Java type root.
*/
private ITypeRoot typeRoot= null;
/**
* The list of import declarations in textual order order; initially none (elementType:
* <code>ImportDeclaration</code>).
*/
private ASTNode.NodeList imports=
new ASTNode.NodeList(IMPORTS_PROPERTY);
/**
* Line end table. If <code>lineEndTable[i] == p</code> then the line number <code>i+1</code>
* ends at character position <code>p</code>. Except for the last line, the positions are that
* of the last character of the line delimiter. For example, the source string
* <code>A\nB\nC</code> has line end table {1, 3} (if \n is one character).
*/
private int[] lineEndTable= Util.EMPTY_INT_ARRAY;
/**
* Messages reported by the compiler during parsing or name resolution.
*/
private Message[] messages;
/**
* The comment list (element type: <code>Comment</code>, or <code>null</code> if none; initially
* <code>null</code>.
*
* @since 3.0
*/
private List optionalCommentList= null;
/**
* The comment table, or <code>null</code> if none; initially <code>null</code>. This array is
* the storage underlying the <code>optionalCommentList</code> ArrayList.
*
* @since 3.0
*/
Comment[] optionalCommentTable= null;
/**
* The package declaration, or <code>null</code> if none; initially <code>null</code>.
*/
private PackageDeclaration optionalPackageDeclaration= null;
/**
* Problems reported by the compiler during parsing or name resolution.
*/
private IProblem[] problems= EMPTY_PROBLEMS;
/**
* Internal data used to perform statements recovery.
*/
private Object statementsRecoveryData;
/**
* The list of type declarations in textual order order; initially none (elementType:
* <code>AbstractTypeDeclaration</code>)
*/
private ASTNode.NodeList types=
new ASTNode.NodeList(TYPES_PROPERTY);
/**
* Creates a new AST node for a compilation owned by the given AST. The compilation unit
* initially has no package declaration, no import declarations, and no type declarations.
* <p>
* N.B. This constructor is package-private; all subclasses must be declared in the same
* package; clients are unable to declare additional subclasses.
* </p>
*
* @param ast the AST that is to own this node
*/
CompilationUnit(AST ast) {
super(ast);
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
void accept0(ASTVisitor visitor) {
boolean visitChildren= visitor.visit(this);
if (visitChildren) {
// visit children in normal left to right reading order
acceptChild(visitor, getPackage());
acceptChildren(visitor, this.imports);
acceptChildren(visitor, this.types);
}
visitor.endVisit(this);
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
ASTNode clone0(AST target) {
CompilationUnit result= new CompilationUnit(target);
// n.b do not copy line number table or messages
result.setSourceRange(getStartPosition(), getLength());
result.setPackage(
(PackageDeclaration)ASTNode.copySubtree(target, getPackage()));
result.imports().addAll(ASTNode.copySubtrees(target, imports()));
result.types().addAll(ASTNode.copySubtrees(target, types()));
return result;
}
/**
* Returns the column number corresponding to the given source character position in the
* original source string. Column number are zero-based. Return <code>-1</code> if it is beyond
* the valid range or <code>-2</code> if the column number information is unknown.
*
* @param position a 0-based character position, possibly negative or out of range
* @return the 0-based column number, or <code>-1</code> if the character position does not
* correspond to a source line in the original source file or <code>-2</code> if column
* number information is unknown for this compilation unit
* @see ASTParser
* @since 3.2
*/
public int getColumnNumber(final int position) {
if (this.lineEndTable == null)
return -2;
final int line= getLineNumber(position);
if (line == -1) {
return -1;
}
if (line == 1) {
if (position >= getStartPosition() + getLength())
return -1;
return position;
}
// length is different from 0
int length= this.lineEndTable.length;
// -1 to for one-based to zero-based conversion.
// -1, again, to get previous line.
final int previousLineOffset= this.lineEndTable[line - 2];
// previousLineOffset + 1 is the first character of the current line
final int offsetForLine= previousLineOffset + 1;
final int currentLineEnd= line == length + 1 ? getStartPosition() + getLength() - 1 : this.lineEndTable[line - 1];
if (offsetForLine > currentLineEnd) {
return -1;
} else {
return position - offsetForLine;
}
}
/**
* Finds the corresponding AST node in the given compilation unit from which the given binding
* originated. Returns <code>null</code> if the binding does not correspond to any node in this
* compilation unit. This method always returns <code>null</code> if bindings were not requested
* when this AST was built.
* <p>
* The following table indicates the expected node type for the various different kinds of
* bindings:
* <ul>
* <li>package - a <code>PackageDeclaration</code></li>
* <li>class or interface - a <code>TypeDeclaration</code> or a
* <code>AnonymousClassDeclaration</code> (for anonymous classes)</li>
* <li>primitive type - none</li>
* <li>array type - none</li>
* <li>field - a <code>VariableDeclarationFragment</code> in a <code>FieldDeclaration</code></li>
* <li>local variable - a <code>SingleVariableDeclaration</code>, or a
* <code>VariableDeclarationFragment</code> in a <code>VariableDeclarationStatement</code> or
* <code>VariableDeclarationExpression</code></li>
* <li>method - a <code>MethodDeclaration</code></li>
* <li>constructor - a <code>MethodDeclaration</code></li>
* <li>annotation type - an <code>AnnotationTypeDeclaration</code></li>
* <li>annotation type member - an <code>AnnotationTypeMemberDeclaration</code></li>
* <li>enum type - an <code>EnumDeclaration</code></li>
* <li>enum constant - an <code>EnumConstantDeclaration</code></li>
* <li>type variable - a <code>TypeParameter</code></li>
* <li>capture binding - none</li>
* <li>annotation binding - an <code>Annotation</code></li>
* <li>member value pair binding - an <code>MemberValuePair</code>, or <code>null</code> if it
* represents a default value or a single member value</li>
* </ul>
* For parameterized or raw type bindings, the declaring node is that of the corresponding
* generic type. And for parameterized or raw method bindings, the declaring node is that of the
* corresponding generic method.
* </p>
* <p>
* Each call to {@link ASTParser#createAST(org.eclipse.core.runtime.IProgressMonitor)} with a
* request for bindings gives rise to separate universe of binding objects. This method always
* returns <code>null</code> when the binding object comes from a different AST. Use
* <code>findDeclaringNode(binding.getKey())</code> when the binding comes from a different AST.
* </p>
*
* @param binding the binding
* @return the corresponding node where the given binding is declared, or <code>null</code> if
* the binding does not correspond to a node in this compilation unit or if bindings
* were not requested when this AST was built
* @see #findDeclaringNode(String)
*/
public ASTNode findDeclaringNode(IBinding binding) {
return this.ast.getBindingResolver().findDeclaringNode(binding);
}
/**
* Finds the corresponding AST node in the given compilation unit from which the binding with
* the given key originated. Returns <code>null</code> if the corresponding node cannot be
* determined. This method always returns <code>null</code> if bindings were not requested when
* this AST was built.
* <p>
* The following table indicates the expected node type for the various different kinds of
* binding keys:
* <ul>
* <li></li>
* <li>package - a <code>PackageDeclaration</code></li>
* <li>class or interface - a <code>TypeDeclaration</code> or a
* <code>AnonymousClassDeclaration</code> (for anonymous classes)</li>
* <li>primitive type - none</li>
* <li>array type - none</li>
* <li>field - a <code>VariableDeclarationFragment</code> in a <code>FieldDeclaration</code></li>
* <li>local variable - a <code>SingleVariableDeclaration</code>, or a
* <code>VariableDeclarationFragment</code> in a <code>VariableDeclarationStatement</code> or
* <code>VariableDeclarationExpression</code></li>
* <li>method - a <code>MethodDeclaration</code></li>
* <li>constructor - a <code>MethodDeclaration</code></li>
* <li>annotation type - an <code>AnnotationTypeDeclaration</code></li>
* <li>annotation type member - an <code>AnnotationTypeMemberDeclaration</code></li>
* <li>enum type - an <code>EnumDeclaration</code></li>
* <li>enum constant - an <code>EnumConstantDeclaration</code></li>
* <li>type variable - a <code>TypeParameter</code></li>
* <li>capture binding - none</li>
* </ul>
* For parameterized or raw type bindings, the declaring node is that of the corresponding
* generic type. And for parameterized or raw method bindings, the declaring node is that of the
* corresponding generic method.
* </p>
*
* @param key the binding key, or <code>null</code>
* @return the corresponding node where a binding with the given key is declared, or
* <code>null</code> if the key is <code>null</code> or if the key does not correspond
* to a node in this compilation unit or if bindings were not requested when this AST
* was built
* @see IBinding#getKey()
* @since 2.1
*/
public ASTNode findDeclaringNode(String key) {
return this.ast.getBindingResolver().findDeclaringNode(key);
}
/**
* Returns a list of the comments encountered while parsing this compilation unit.
* <p>
* Since the Java language allows comments to appear most anywhere in the source text, it is
* problematic to locate comments in relation to the structure of an AST. The one exception is
* doc comments which, by convention, immediately precede type, field, and method declarations;
* these comments are located in the AST by {@link BodyDeclaration#getJavadoc
* BodyDeclaration.getJavadoc}. Other comments do not show up in the AST. The table of comments
* is provided for clients that need to find the source ranges of all comments in the original
* source string. It includes entries for comments of all kinds (line, block, and doc), arranged
* in order of increasing source position.
* </p>
* <p>
* Note on comment parenting: The {@link ASTNode#getParent() getParent()} of a doc comment
* associated with a body declaration is the body declaration node; for these comment nodes
* {@link ASTNode#getRoot() getRoot()} will return the compilation unit (assuming an unmodified
* AST) reflecting the fact that these nodes are property located in the AST for the compilation
* unit. However, for other comment nodes, {@link ASTNode#getParent() getParent()} will return
* <code>null</code>, and {@link ASTNode#getRoot() getRoot()} will return the comment node
* itself, indicating that these comment nodes are not directly connected to the AST for the
* compilation unit. The {@link Comment#getAlternateRoot Comment.getAlternateRoot} method
* provides a way to navigate from a comment to its compilation unit.
* </p>
* <p>
* A note on visitors: The only comment nodes that will be visited when visiting a compilation
* unit are the doc comments parented by body declarations. To visit all comments in normal
* reading order, iterate over the comment table and call {@link ASTNode#accept(ASTVisitor)
* accept} on each element.
* </p>
* <p>
* Clients cannot modify the resulting list.
* </p>
*
* @return an unmodifiable list of comments in increasing order of source start position, or
* <code>null</code> if comment information for this compilation unit is not available
* @see ASTParser
* @since 3.0
*/
public List getCommentList() {
return this.optionalCommentList;
}
/**
* Returns the internal comment mapper.
*
* @return the comment mapper, or <code>null</code> if none.
* @since 3.0
*/
DefaultCommentMapper getCommentMapper() {
return this.commentMapper;
}
/**
* Returns the extended source length of the given node. Unlike
* {@link ASTNode#getStartPosition()} and {@link ASTNode#getLength()}, the extended source range
* may include comments and whitespace immediately before or after the normal source range for
* the node.
*
* @param node the node
* @return a (possibly 0) length, or <code>0</code> if no source position information is
* recorded for this node
* @see #getExtendedStartPosition(ASTNode)
* @since 3.0
*/
public int getExtendedLength(ASTNode node) {
if (node == null) {
throw new IllegalArgumentException();
}
if (this.commentMapper == null || node.getAST() != getAST()) {
// fall back: use best info available
return node.getLength();
} else {
return this.commentMapper.getExtendedLength(node);
}
}
/**
* Returns the extended start position of the given node. Unlike
* {@link ASTNode#getStartPosition()} and {@link ASTNode#getLength()}, the extended source range
* may include comments and whitespace immediately before or after the normal source range for
* the node.
*
* @param node the node
* @return the 0-based character index, or <code>-1</code> if no source position information is
* recorded for this node
* @see #getExtendedLength(ASTNode)
* @since 3.0
*/
public int getExtendedStartPosition(ASTNode node) {
if (node == null) {
throw new IllegalArgumentException();
}
if (this.commentMapper == null || node.getAST() != getAST()) {
// fall back: use best info available
return node.getStartPosition();
} else {
return this.commentMapper.getExtendedStartPosition(node);
}
}
/**
* The Java element (an <code>org.eclipse.jdt.core.ICompilationUnit</code> or an
* <code>org.eclipse.jdt.core.IClassFile</code>) this compilation unit was created from, or
* <code>null</code> if it was not created from a Java element.
*
* @return the Java element this compilation unit was created from, or <code>null</code> if none
* @since 3.1
* @see #getTypeRoot()
*/
public IJavaElement getJavaElement() {
return this.typeRoot;
}
/**
* Returns the list of messages reported by the compiler during the parsing or the type checking
* of this compilation unit. This list might be a subset of errors detected and reported by a
* Java compiler.
* <p>
* This list of messages is suitable for simple clients that do little more than log the
* messages or display them to the user. Clients that need further details should call
* <code>getProblems</code> to get compiler problem objects.
* </p>
*
* @return the list of messages, possibly empty
* @see #getProblems()
* @see ASTParser
*/
public Message[] getMessages() {
if (this.messages == null) {
int problemLength= this.problems.length;
if (problemLength == 0) {
this.messages= EMPTY_MESSAGES;
} else {
this.messages= new Message[problemLength];
for (int i= 0; i < problemLength; i++) {
IProblem problem= this.problems[i];
int start= problem.getSourceStart();
int end= problem.getSourceEnd();
this.messages[i]= new Message(problem.getMessage(), start, end - start + 1);
}
}
}
return this.messages;
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
final int getNodeType0() {
return COMPILATION_UNIT;
}
/**
* Returns the node for the package declaration of this compilation unit, or <code>null</code>
* if this compilation unit is in the default package.
*
* @return the package declaration node, or <code>null</code> if none
*/
public PackageDeclaration getPackage() {
return this.optionalPackageDeclaration;
}
/**
* Given a line number and column number, returns the corresponding position in the original
* source string. Returns -2 if no line number information is available for this compilation
* unit. Returns the total size of the source string if <code>line</code> is greater than the
* actual number lines in the unit. Returns -1 if <code>column</code> is less than 0, or the
* position of the last character of the line if <code>column</code> is beyond the legal range,
* or the given line number is less than one.
*
* @param line the one-based line number
* @param column the zero-based column number
* @return the 0-based character position in the source string; <code>-2</code> if line/column
* number information is not known for this compilation unit or <code>-1</code> the
* inputs are not valid
* @since 3.2
*/
public int getPosition(int line, int column) {
if (this.lineEndTable == null)
return -2;
if (line < 1 || column < 0)
return -1;
int length;
if ((length= this.lineEndTable.length) == 0) {
if (line != 1)
return -1;
return column >= getStartPosition() + getLength() ? -1 : column;
}
if (line == 1) {
final int endOfLine= this.lineEndTable[0];
return column > endOfLine ? -1 : column;
} else if (line > length + 1) {
// greater than the number of lines in the source string.
return -1;
}
// -1 to for one-based to zero-based conversion.
// -1, again, to get previous line.
final int previousLineOffset= this.lineEndTable[line - 2];
// previousLineOffset + 1 is the first character of the current line
final int offsetForLine= previousLineOffset + 1;
final int currentLineEnd= line == length + 1 ? getStartPosition() + getLength() - 1 : this.lineEndTable[line - 1];
if ((offsetForLine + column) > currentLineEnd) {
return -1;
} else {
return offsetForLine + column;
}
}
/**
* Returns the list of detailed problem reports noted by the compiler during the parsing or the
* type checking of this compilation unit. This list might be a subset of errors detected and
* reported by a Java compiler.
* <p>
* Simple clients that do little more than log the messages or display them to the user should
* probably call <code>getMessages</code> instead.
* </p>
*
* @return the list of detailed problem objects, possibly empty
* @see #getMessages()
* @see ASTParser
* @since 2.1
*/
public IProblem[] getProblems() {
return this.problems;
}
/**
* Internal method
*
* This method return internal data used to perform statements recovery.
*
* @return internal data used to perform statements recovery.
*
* @noreference This method is not intended to be referenced by clients.
* @since 3.5
*/
public Object getStatementsRecoveryData() {
return this.statementsRecoveryData;
}
/**
* The Java type root (a {@link org.eclipse.jdt.core.ICompilationUnit compilation unit} or a
* {@link org.eclipse.jdt.core.IClassFile class file}) this compilation unit was created from,
* or <code>null</code> if it was not created from a Java type root.
*
* @return the Java type root this compilation unit was created from, or <code>null</code> if
* none
* @since 3.3
*/
public ITypeRoot getTypeRoot() {
return this.typeRoot;
}
/**
* Returns the live list of nodes for the import declarations of this compilation unit, in order
* of appearance.
*
* @return the live list of import declaration nodes (elementType:
* <code>ImportDeclaration</code>)
*/
public List imports() {
return this.imports;
}
/**
* Return the index in the whole comments list {@link #getCommentList() } of the first leading
* comments associated with the given node.
*
* @param node the node
* @return 0-based index of first leading comment or -1 if node has no associated comment before
* its start position.
* @since 3.2
*/
public int firstLeadingCommentIndex(ASTNode node) {
if (node == null) {
throw new IllegalArgumentException();
}
if (this.commentMapper == null || node.getAST() != getAST()) {
return -1;
}
return this.commentMapper.firstLeadingCommentIndex(node);
}
/**
* Return the index in the whole comments list {@link #getCommentList() } of the last trailing
* comments associated with the given node.
*
* @param node the node
* @return 0-based index of last trailing comment or -1 if node has no associated comment after
* its end position.
* @since 3.2
*/
public int lastTrailingCommentIndex(ASTNode node) {
if (node == null) {
throw new IllegalArgumentException();
}
if (this.commentMapper == null || node.getAST() != getAST()) {
return -1;
}
return this.commentMapper.lastTrailingCommentIndex(node);
}
/**
* Initializes the internal comment mapper with the given scanner.
*
* @param scanner the scanner
* @since 3.0
*/
void initCommentMapper(Scanner scanner) {
this.commentMapper= new DefaultCommentMapper(this.optionalCommentTable);
this.commentMapper.initialize(this, scanner);
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
final List internalGetChildListProperty(ChildListPropertyDescriptor property) {
if (property == IMPORTS_PROPERTY) {
return imports();
}
if (property == TYPES_PROPERTY) {
return types();
}
// allow default implementation to flag the error
return super.internalGetChildListProperty(property);
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
final ASTNode internalGetSetChildProperty(ChildPropertyDescriptor property, boolean get, ASTNode child) {
if (property == PACKAGE_PROPERTY) {
if (get) {
return getPackage();
} else {
setPackage((PackageDeclaration)child);
return null;
}
}
// allow default implementation to flag the error
return super.internalGetSetChildProperty(property, get, child);
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
* @since 3.0
*/
final List internalStructuralPropertiesForType(int apiLevel) {
return propertyDescriptors(apiLevel);
}
/**
* Returns the line number corresponding to the given source character position in the original
* source string. The initial line of the compilation unit is numbered 1, and each line extends
* through the last character of the end-of-line delimiter. The very last line extends through
* the end of the source string and has no line delimiter. For example, the source string
* <code>class A\n{\n}</code> has 3 lines corresponding to inclusive character ranges [0,7],
* [8,9], and [10,10]. Returns 1 for a character position that does not correspond to any source
* line, or if no line number information is available for this compilation unit.
*
* @param position a 0-based character position, possibly negative or out of range
* @return the 1-based line number, or <code>1</code> if the character position does not
* correspond to a source line in the original source file or if line number information
* is not known for this compilation unit
* @deprecated Use getLineNumber(int) instead. Be careful to handle the negative values.
* @see ASTParser
* @see #getLineNumber(int)
*/
public int lineNumber(int position) {
int lineNumber= getLineNumber(position);
return lineNumber < 1 ? 1 : lineNumber;
}
/**
* Returns the line number corresponding to the given source character position in the original
* source string. The initial line of the compilation unit is numbered 1, and each line extends
* through the last character of the end-of-line delimiter. The very last line extends through
* the end of the source string and has no line delimiter. For example, the source string
* <code>class A\n{\n}</code> has 3 lines corresponding to inclusive character ranges [0,7],
* [8,9], and [10,10]. Returns -1 for a character position that does not correspond to any
* source line, or -2 if no line number information is available for this compilation unit.
*
* @param position a 0-based character position, possibly negative or out of range
* @return the 1-based line number, or <code>-1</code> if the character position does not
* correspond to a source line in the original source file or <code>-2</code> if line
* number information is not known for this compilation unit
* @see ASTParser
* @since 3.2
*/
public int getLineNumber(int position) {
if (this.lineEndTable == null)
return -2;
int length;
if ((length= this.lineEndTable.length) == 0) {
if (position >= getStartPosition() + getLength()) {
return -1;
}
return 1;
}
int low= 0;
if (position < 0) {
// position illegal
return -1;
}
if (position <= this.lineEndTable[low]) {
// before the first line delimiter
return 1;
}
// assert position > lineEndTable[low+1] && low == 0
int hi= length - 1;
if (position > this.lineEndTable[hi]) {
// position beyond the last line separator
if (position >= getStartPosition() + getLength()) {
// this is beyond the end of the source length
return -1;
} else {
return length + 1;
}
}
// assert lineEndTable[low] < position <= lineEndTable[hi]
// && low == 0 && hi == length - 1 && low < hi
// binary search line end table
while (true) {
// invariant lineEndTable[low] < position <= lineEndTable[hi]
// && 0 <= low < hi <= length - 1
// reducing measure hi - low
if (low + 1 == hi) {
// assert lineEndTable[low] < position <= lineEndTable[low+1]
// position is on line low+1 (line number is low+2)
return low + 2;
}
// assert hi - low >= 2, so average is truly in between
int mid= low + (hi - low) / 2;
// assert 0 <= low < mid < hi <= length - 1
if (position <= this.lineEndTable[mid]) {
// assert lineEndTable[low] < position <= lineEndTable[mid]
// && 0 <= low < mid < hi <= length - 1
hi= mid;
} else {
// position > lineEndTable[mid]
// assert lineEndTable[mid] < position <= lineEndTable[hi]
// && 0 <= low < mid < hi <= length - 1
low= mid;
}
// in both cases, invariant reachieved with reduced measure
}
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
int memSize() {
int size= BASE_NODE_SIZE + 8 * 4;
if (this.lineEndTable != null) {
size+= HEADERS + 4 * this.lineEndTable.length;
}
if (this.optionalCommentTable != null) {
size+= HEADERS + 4 * this.optionalCommentTable.length;
}
// ignore the space taken up by optionalCommentList
return size;
}
/**
* Enables the recording of changes to this compilation unit and its descendents. The
* compilation unit must have been created by <code>ASTParser</code> and still be in its
* original state. Once recording is on, arbitrary changes to the subtree rooted at this
* compilation unit are recorded internally. Once the modification has been completed, call
* <code>rewrite</code> to get an object representing the corresponding edits to the original
* source code string.
*
* @exception IllegalArgumentException if this compilation unit is marked as unmodifiable, or if
* this compilation unit has already been tampered with, or recording has already
* been enabled
* @since 3.0
*/
public void recordModifications() {
getAST().recordModifications(this);
}
/**
* Converts all modifications recorded for this compilation unit into an object representing the
* corresponding text edits to the given document containing the original source code for this
* compilation unit.
* <p>
* The compilation unit must have been created by <code>ASTParser</code> from the source code
* string in the given document, and recording must have been turned on with a prior call to
* <code>recordModifications</code> while the AST was still in its original state.
* </p>
* <p>
* Calling this methods does not discard the modifications on record. Subsequence modifications
* made to the AST are added to the ones already on record. If this method is called again
* later, the resulting text edit object will accurately reflect the net cumulative affect of
* all those changes.
* </p>
*
* @param document original document containing source code for this compilation unit
* @param options the table of formatter options (key type: <code>String</code>; value type:
* <code>String</code>); or <code>null</code> to use the standard global options
* {@link org.eclipse.jdt.core.JavaCore#getOptions() JavaCore.getOptions()}.
* @return text edit object describing the changes to the document corresponding to the recorded
* AST modifications
* @exception IllegalArgumentException if the document passed is <code>null</code> or does not
* correspond to this AST
* @exception IllegalStateException if <code>recordModifications</code> was not called to enable
* recording
* @see #recordModifications()
* @since 3.0
*/
public TextEdit rewrite(IDocument document, Map options) {
return getAST().rewrite(document, options);
}
/**
* Sets the list of the comments encountered while parsing this compilation unit.
*
* @param commentTable a list of comments in increasing order of source start position, or
* <code>null</code> if comment information for this compilation unit is not
* available
* @exception IllegalArgumentException if the comment table is not in increasing order of source
* position
* @see #getCommentList()
* @see ASTParser
* @since 3.0
*/
void setCommentTable(Comment[] commentTable) {
// double check table to ensure that all comments have
// source positions and are in strictly increasing order
if (commentTable == null) {
this.optionalCommentList= null;
this.optionalCommentTable= null;
} else {
int nextAvailablePosition= 0;
for (int i= 0; i < commentTable.length; i++) {
Comment comment= commentTable[i];
if (comment == null) {
throw new IllegalArgumentException();
}
int start= comment.getStartPosition();
int length= comment.getLength();
if (start < 0 || length < 0 || start < nextAvailablePosition) {
throw new IllegalArgumentException();
}
nextAvailablePosition= comment.getStartPosition() + comment.getLength();
}
this.optionalCommentTable= commentTable;
List commentList= Arrays.asList(commentTable);
// protect the list from further modification
this.optionalCommentList= Collections.unmodifiableList(commentList);
}
}
/**
* Sets the Java type root (a {@link org.eclipse.jdt.core.ICompilationUnit compilation unit} or
* a {@link org.eclipse.jdt.core.IClassFile class file}) this compilation unit was created from,
* or <code>null</code> if it was not created from a Java type root.
*
* @param typeRoot the Java type root this compilation unit was created from
*/
void setTypeRoot(ITypeRoot typeRoot) {
this.typeRoot= typeRoot;
}
/**
* Sets the line end table for this compilation unit. If <code>lineEndTable[i] == p</code> then
* line number <code>i+1</code> ends at character position <code>p</code>. Except for the last
* line, the positions are that of (the last character of) the line delimiter. For example, the
* source string <code>A\nB\nC</code> has line end table {1, 3, 4}.
*
* @param lineEndTable the line end table
*/
void setLineEndTable(int[] lineEndTable) {
if (lineEndTable == null) {
throw new NullPointerException();
}
// alternate root is *not* considered a structural property
// but we protect them nevertheless
checkModifiable();
this.lineEndTable= lineEndTable;
}
/**
* Sets or clears the package declaration of this compilation unit node to the given package
* declaration node.
*
* @param pkgDecl the new package declaration node, or <code>null</code> if this compilation
* unit does not have a package declaration (that is in the default package)
* @exception IllegalArgumentException if:
* <ul>
* <li>the node belongs to a different AST</li>
* <li>the node already has a parent</li>
* </ul>
*/
public void setPackage(PackageDeclaration pkgDecl) {
ASTNode oldChild= this.optionalPackageDeclaration;
preReplaceChild(oldChild, pkgDecl, PACKAGE_PROPERTY);
this.optionalPackageDeclaration= pkgDecl;
postReplaceChild(oldChild, pkgDecl, PACKAGE_PROPERTY);
}
/**
* Sets the array of problems reported by the compiler during the parsing or name resolution of
* this compilation unit.
*
* @param problems the list of problems
*/
void setProblems(IProblem[] problems) {
if (problems == null) {
throw new IllegalArgumentException();
}
this.problems= problems;
}
/**
* Internal method
*
* Sets internal data used to perform statements recovery.
*
* @param data
*
* @noreference This method is not intended to be referenced by clients.
* @since 3.5
*/
void setStatementsRecoveryData(Object data) {
this.statementsRecoveryData= data;
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
final boolean subtreeMatch0(ASTMatcher matcher, Object other) {
// dispatch to correct overloaded match method
return matcher.match(this, other);
}
/* (omit javadoc for this method)
* Method declared on ASTNode.
*/
int treeSize() {
int size= memSize();
if (this.optionalPackageDeclaration != null) {
size+= getPackage().treeSize();
}
size+= this.imports.listSize();
size+= this.types.listSize();
// include disconnected comments
if (this.optionalCommentList != null) {
for (int i= 0; i < this.optionalCommentList.size(); i++) {
Comment comment= (Comment)this.optionalCommentList.get(i);
if (comment != null && comment.getParent() == null) {
size+= comment.treeSize();
}
}
}
return size;
}
/**
* Returns the live list of nodes for the top-level type declarations of this compilation unit,
* in order of appearance.
* <p>
* Note that in JLS3, the types may include both enum declarations and annotation type
* declarations introduced in J2SE 5. For JLS2, the elements are always
* <code>TypeDeclaration</code>.
* </p>
*
* @return the live list of top-level type declaration nodes (elementType:
* <code>AbstractTypeDeclaration</code>)
*/
public List types() {
return this.types;
}
}