package net.sourceforge.phpdt.core.dom; /******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ /** * Java compilation unit AST node type. This is the type of the root of an AST. *

* The source range for this type of node is ordinarily the entire source file, * including leading and trailing whitespace and comments. *

* For JLS2: *
 * CompilationUnit:
 *    [ PackageDeclaration ]
 *        { ImportDeclaration }
 *        { TypeDeclaration | ; }
 * 
* For JLS3, the kinds of type declarations * grew to include enum and annotation type declarations: *
 * CompilationUnit:
 *    [ PackageDeclaration ]
 *        { ImportDeclaration }
 *        { TypeDeclaration | EnumDeclaration | AnnotationTypeDeclaration | ; }
 * 
* * @since 2.0 */ public class CompilationUnit { // /** // * 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$ // // /** // * 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 "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$ // // /** // * A list of property descriptors (element type: // * {@link StructuralPropertyDescriptor}), // * or null if uninitialized. // * @since 3.0 // */ // private static final List PROPERTY_DESCRIPTORS; // // static { // createPropertyList(CompilationUnit.class); // addProperty(PACKAGE_PROPERTY); // addProperty(IMPORTS_PROPERTY); // addProperty(TYPES_PROPERTY); // PROPERTY_DESCRIPTORS = reapPropertyList(); // } // // /** // * 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 // * AST.JLS* 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 table, or null if none; initially // * null. This array is the storage underlying // * the optionalCommentList ArrayList. // * @since 3.0 // */ // Comment[] optionalCommentTable = null; // // /** // * The comment list (element type: Comment, // * or null if none; initially null. // * @since 3.0 // */ // private List optionalCommentList = null; // // /** // * The package declaration, or null if none; initially // * null. // */ // private PackageDeclaration optionalPackageDeclaration = null; // // /** // * The list of import declarations in textual order order; // * initially none (elementType: ImportDeclaration). // */ // private ASTNode.NodeList imports = // new ASTNode.NodeList(IMPORTS_PROPERTY); // // /** // * The list of type declarations in textual order order; // * initially none (elementType: AbstractTypeDeclaration) // */ // private ASTNode.NodeList types = // new ASTNode.NodeList(TYPES_PROPERTY); // // /** // * Line end table. If lineEndTable[i] == p then the // * line number i+1 ends at character position // * p. Except for the last line, the positions are that // * of the last character of the line delimiter. // * For example, the source string A\nB\nC has // * line end table {1, 3} (if \n is one character). // */ // private int[] lineEndTable = new int[0]; // // /** // * 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]; // // /** // * Messages reported by the compiler during parsing or name resolution. // */ // private Message[] messages; // // /** // * Problems reported by the compiler during parsing or name resolution. // */ // private IProblem[] problems = EMPTY_PROBLEMS; // // /** // * The comment mapper, or null in none; // * initially null. // * @since 3.0 // */ // private DefaultCommentMapper commentMapper = null; // // /** // * Sets the line end table for this compilation unit. // * If lineEndTable[i] == p then line number i+1 // * ends at character position p. Except for the last line, the // * positions are that of (the last character of) the line delimiter. // * For example, the source string A\nB\nC 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; // } // // /** // * 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. // *

// * N.B. This constructor is package-private; all subclasses must be // * declared in the same package; clients are unable to declare // * additional subclasses. // *

// * // * @param ast the AST that is to own this node // */ // CompilationUnit(AST ast) { // super(ast); // } // // /* (omit javadoc for this method) // * Method declared on ASTNode. // * @since 3.0 // */ // final List internalStructuralPropertiesForType(int apiLevel) { // return propertyDescriptors(apiLevel); // } // // /* (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. // */ // 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 int getNodeType0() { // return COMPILATION_UNIT; // } // // /* (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(this.getStartPosition(), this.getLength()); // result.setPackage( // (PackageDeclaration) ASTNode.copySubtree(target, getPackage())); // result.imports().addAll(ASTNode.copySubtrees(target, imports())); // result.types().addAll(ASTNode.copySubtrees(target, types())); // return result; // } // // /* (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. // */ // 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); // } // // /** // * Returns the node for the package declaration of this compilation // * unit, or null if this compilation unit is in the // * default package. // * // * @return the package declaration node, or null if none // */ // public PackageDeclaration getPackage() { // return this.optionalPackageDeclaration; // } // // /** // * 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 // * null if this compilation unit does not have a package // * declaration (that is in the default package) // * @exception IllegalArgumentException if: // * // */ // public void setPackage(PackageDeclaration pkgDecl) { // ASTNode oldChild = this.optionalPackageDeclaration; // preReplaceChild(oldChild, pkgDecl, PACKAGE_PROPERTY); // this.optionalPackageDeclaration = pkgDecl; // postReplaceChild(oldChild, pkgDecl, PACKAGE_PROPERTY); // } // // /** // * 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: ImportDeclaration) // */ // public List imports() { // return this.imports; // } // // /** // * Returns the live list of nodes for the top-level type declarations of this // * compilation unit, in order of appearance. // *

// * Note that in JLS3, the types may include both enum declarations // * and annotation type declarations introduced in J2SE 1.5. // * For JLS2, the elements are always TypeDeclaration. // *

// * // * @return the live list of top-level type declaration // * nodes (elementType: AbstractTypeDeclaration) // */ // public List types() { // return this.types; // } // // /** // * Finds the corresponding AST node in the given compilation unit from // * which the given binding originated. Returns null if the // * binding does not correspond to any node in this compilation unit. // * This method always returns null if bindings were not requested // * when this AST was built. // *

// * The following table indicates the expected node type for the various // * different kinds of bindings: // *

// *

// *

// * Each call to {@link ASTParser#createAST(IProgressMonitor)} with a request for bindings // * gives rise to separate universe of binding objects. This method always returns // * null when the binding object comes from a different AST. // * Use findDeclaringNode(binding.getKey()) when the binding comes // * from a different AST. // *

// * // * @param binding the binding // * @return the corresponding node where the given binding is declared, // * or null 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 // * null if the corresponding node cannot be determined. // * This method always returns null if bindings were not requested // * when this AST was built. // *

// * The following table indicates the expected node type for the various // * different kinds of binding keys: // *

// *

// *

// * Note that as explained in {@link IBinding#getKey() IBinding.getkey} // * there may be no keys for finding the declaring node for local variables, // * local or anonymous classes, etc. // *

// * // * @param key the binding key, or null // * @return the corresponding node where a binding with the given // * key is declared, or null if the key is null // * 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 the internal comment mapper. // * // * @return the comment mapper, or null if none. // * @since 3.0 // */ // DefaultCommentMapper getCommentMapper() { // return this.commentMapper; // } // // /** // * 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); // } // // /** // * 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 -1 // * if no source position information is recorded for this node // * @see #getExtendedLength(ASTNode) // * @since 3.0 // */ // public int getExtendedStartPosition(ASTNode node) { // if (this.commentMapper == null) { // return -1; // } else { // return this.commentMapper.getExtendedStartPosition(node); // } // } // // /** // * 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 0 // * if no source position information is recorded for this node // * @see #getExtendedStartPosition(ASTNode) // * @since 3.0 // */ // public int getExtendedLength(ASTNode node) { // if (this.commentMapper == null) { // return 0; // } else { // return this.commentMapper.getExtendedLength(node); // } // } // // /** // * 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 class A\n{\n} 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 1 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 // * @see ASTParser // */ // public int lineNumber(int position) { // int length = lineEndTable.length; // if (length == 0) { // // no line number info // return 1; // } // int low = 0; // if (position <= lineEndTable[low]) { // // position illegal or before the first line delimiter // return 1; // } // // assert position > lineEndTable[low+1] && low == 0 // int hi = length - 1; // if (position > 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) / 2; // // assert 0 <= low < mid < hi <= length - 1 // if (position <= 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 // } // } // // /** // * 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. // *

// * 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 getProblems to get // * compiler problem objects. // *

// * // * @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(); // messages[i] = new Message(problem.getMessage(), start, end - start + 1); // } // } // } // return this.messages; // } // // /** // * 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. // *

// * Simple clients that do little more than log the messages or display // * them to the user should probably call getMessages instead. // *

// * // * @return the list of detailed problem objects, possibly empty // * @see #getMessages() // * @see ASTParser // * @since 2.1 // */ // public IProblem[] getProblems() { // return this.problems; // } // // /** // * 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; // } // // /** // * Returns a list of the comments encountered while parsing // * this compilation unit. // *

// * 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. // *

// * 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 null, 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. // *

// *

// * 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. // *

// *

// * Clients cannot modify the resulting list. // *

// * // * @return an unmodifiable list of comments in increasing order of source // * start position, or null if comment information // * for this compilation unit is not available // * @see ASTParser // * @since 3.0 // */ // public List getCommentList() { // return this.optionalCommentList; // } // // /** // * 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 null 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); // } // } // // // /* (omit javadoc for this method) // * Method declared on ASTNode. // */ // void appendDebugString(StringBuffer buffer) { // buffer.append("CompilationUnit"); //$NON-NLS-1$ // // include the type names // buffer.append("["); //$NON-NLS-1$ // for (Iterator it = types().iterator(); it.hasNext(); ) { // AbstractTypeDeclaration d = (AbstractTypeDeclaration) it.next(); // buffer.append(d.getName().getIdentifier()); // if (it.hasNext()) { // buffer.append(","); //$NON-NLS-1$ // } // } // buffer.append("]"); //$NON-NLS-1$ // } // // /* (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; // } // // /* (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; // } // // /** // * Enables the recording of changes to this compilation // * unit and its descendents. The compilation unit must have // * been created by ASTParser 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 rewrite 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. // *

// * The compilation unit must have been created by // * ASTParser from the source code string in the // * given document, and recording must have been turned // * on with a prior call to recordModifications // * while the AST was still in its original state. // *

// *

// * 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. // *

// * // * @param document original document containing source code // * for this compilation unit // * @param options the table of formatter options // * (key type: String; value type: String); // * or null to use the standard global options // * {@link 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 // * null or does not correspond to this AST // * @exception IllegalStateException if recordModifications // * was not called to enable recording // * @see #recordModifications() // * @since 3.0 // */ // public TextEdit rewrite(IDocument document, Map options) { // return getAST().rewrite(document, options); // } }