/******************************************************************************* * Copyright (c) 2000, 2013 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.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.Scanner; import org.eclipse.jface.text.IDocument; import org.eclipse.text.edits.TextEdit; /** * Umbrella owner and abstract syntax tree node factory. * An <code>AST</code> instance serves as the common owner of any number of * AST nodes, and as the factory for creating new AST nodes owned by that * instance. * <p> * Abstract syntax trees may be hand constructed by clients, using the * <code>new<i>TYPE</i></code> factory methods to create new nodes, and the * various <code>set<i>CHILD</i></code> methods * (see {@link org.eclipse.jdt.core.dom.ASTNode ASTNode} and its subclasses) * to connect them together. * </p> * <p> * Each AST node belongs to a unique AST instance, called the owning AST. * The children of an AST node always have the same owner as their parent node. * If a node from one AST is to be added to a different AST, the subtree must * be cloned first to ensures that the added nodes have the correct owning AST. * </p> * <p> * There can be any number of AST nodes owned by a single AST instance that are * unparented. Each of these nodes is the root of a separate little tree of nodes. * The method <code>ASTNode.getRoot()</code> navigates from any node to the root * of the tree that it is contained in. Ordinarily, an AST instance has one main * tree (rooted at a <code>CompilationUnit</code>), with newly-created nodes appearing * as additional roots until they are parented somewhere under the main tree. * One can navigate from any node to its AST instance, but not conversely. * </p> * <p> * The class {@link ASTParser} parses a string * containing a Java source code and returns an abstract syntax tree * for it. The resulting nodes carry source ranges relating the node back to * the original source characters. * </p> * <p> * Compilation units created by <code>ASTParser</code> from a * source document can be serialized after arbitrary modifications * with minimal loss of original formatting. Here is an example: * <pre> * Document doc = new Document("import java.util.List;\nclass X {}\n"); * ASTParser parser = ASTParser.newParser(AST.JLS3); * parser.setSource(doc.get().toCharArray()); * CompilationUnit cu = (CompilationUnit) parser.createAST(null); * cu.recordModifications(); * AST ast = cu.getAST(); * ImportDeclaration id = ast.newImportDeclaration(); * id.setName(ast.newName(new String[] {"java", "util", "Set"}); * cu.imports().add(id); // add import declaration at end * TextEdit edits = cu.rewrite(document, null); * UndoEdit undo = edits.apply(document); * </pre> * See also {@link org.eclipse.jdt.core.dom.rewrite.ASTRewrite} for * an alternative way to describe and serialize changes to a * read-only AST. * </p> * <p> * Clients may create instances of this class using {@link #newAST(int)}, * but this class is not intended to be subclassed. * </p> * * @see ASTParser * @see ASTNode * @since 2.0 * @noinstantiate This class is not intended to be instantiated by clients. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public final class AST { /** * new Class[] {AST.class} * @since 3.0 */ private static final Class[] AST_CLASS = new Class[] {AST.class}; /** * Constant for indicating the AST API that handles JLS2. * <p> * This API is capable of handling all constructs * in the Java language as described in the Java Language * Specification, Second Edition (JLS2). * JLS2 is a superset of all earlier versions of the * Java language, and the JLS2 API can be used to manipulate * programs written in all versions of the Java language * up to and including J2SE 1.4. * </p> * * @since 3.0 * @deprecated Clients should use the {@link #JLS8} AST API instead. */ public static final int JLS2 = 2; /** * Internal synonym for {@link #JLS2}. Use to alleviate * deprecation warnings. * @since 3.1 */ /*package*/ static final int JLS2_INTERNAL = JLS2; /** * Constant for indicating the AST API that handles JLS3. * <p> * This API is capable of handling all constructs in the * Java language as described in the Java Language * Specification, Third Edition (JLS3). * JLS3 is a superset of all earlier versions of the * Java language, and the JLS3 API can be used to manipulate * programs written in all versions of the Java language * up to and including J2SE 5 (aka JDK 1.5). * </p> * * @since 3.1 * @deprecated Clients should use the {@link #JLS8} AST API instead. */ public static final int JLS3 = 3; /** * Internal synonym for {@link #JLS3}. Use to alleviate * deprecation warnings. * @since 3.8 */ /*package*/ static final int JLS3_INTERNAL = JLS3; /** * Constant for indicating the AST API that handles JLS4 (aka JLS7). * <p> * This API is capable of handling all constructs in the * Java language as described in the Java Language * Specification, Java SE 7 Edition (JLS7) as specified by JSR336. * JLS4 is a superset of all earlier versions of the * Java language, and the JLS4 API can be used to manipulate * programs written in all versions of the Java language * up to and including Java SE 7 (aka JDK 1.7). * </p> * * @since 3.7.1 * @deprecated Clients should use the {@link #JLS8} AST API instead. */ public static final int JLS4 = 4; /** * Internal synonym for {@link #JLS4}. Use to alleviate * deprecation warnings. * @since 3.10 */ /*package*/ static final int JLS4_INTERNAL = JLS4; /** * Constant for indicating the AST API that handles JLS8. * <p> * This API is capable of handling all constructs in the * Java language as described in the Java Language * Specification, Java SE 8 Edition (JLS8) as specified by JSR337. * JLS8 is a superset of all earlier versions of the * Java language, and the JLS8 API can be used to manipulate * programs written in all versions of the Java language * up to and including Java SE 8 (aka JDK 1.8). * </p> * * @since 3.10 */ public static final int JLS8 = 8; /* * Must not collide with a value for ICompilationUnit constants */ static final int RESOLVED_BINDINGS = 0x80000000; /** * Internal method. * <p> * This method converts the given internal compiler AST for the given source string * into a compilation unit. This method is not intended to be called by clients. * </p> * * @param level the API level; one of the <code>JLS*</code> level constants * @param compilationUnitDeclaration an internal AST node for a compilation unit declaration * @param source the string of the Java compilation unit * @param options compiler options * @param workingCopy the working copy that the AST is created from * @param monitor the progress monitor used to report progress and request cancellation, * or <code>null</code> if none * @param isResolved whether the given compilation unit declaration is resolved * @return the compilation unit node * @deprecated Use org.eclipse.jdt.core.dom.AST.convertCompilationUnit(int, CompilationUnitDeclaration, Map, boolean, CompilationUnit, int, IProgressMonitor) instead * @noreference This method is not intended to be referenced by clients. */ public static CompilationUnit convertCompilationUnit( int level, org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration compilationUnitDeclaration, char[] source, Map options, boolean isResolved, org.eclipse.jdt.internal.core.CompilationUnit workingCopy, int reconcileFlags, IProgressMonitor monitor) { return null; } /** * Internal method. * <p> * This method converts the given internal compiler AST for the given source string * into a compilation unit. This method is not intended to be called by clients. * </p> * * @param level the API level; one of the <code>JLS*</code> level constants * @param compilationUnitDeclaration an internal AST node for a compilation unit declaration * @param options compiler options * @param workingCopy the working copy that the AST is created from * @param monitor the progress monitor used to report progress and request cancellation, * or <code>null</code> if none * @param isResolved whether the given compilation unit declaration is resolved * @return the compilation unit node * @since 3.4 * @noreference This method is not intended to be referenced by clients. */ public static CompilationUnit convertCompilationUnit( int level, org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration compilationUnitDeclaration, Map options, boolean isResolved, org.eclipse.jdt.internal.core.CompilationUnit workingCopy, int reconcileFlags, IProgressMonitor monitor) { ASTConverter converter = new ASTConverter(options, isResolved, monitor); AST ast = AST.newAST(level); int savedDefaultNodeFlag = ast.getDefaultNodeFlag(); ast.setDefaultNodeFlag(ASTNode.ORIGINAL); BindingResolver resolver = null; if (isResolved) { resolver = new DefaultBindingResolver(compilationUnitDeclaration.scope, workingCopy.owner, new DefaultBindingResolver.BindingTables(), false, true); ((DefaultBindingResolver) resolver).isRecoveringBindings = (reconcileFlags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0; ast.setFlag(AST.RESOLVED_BINDINGS); } else { resolver = new BindingResolver(); } ast.setFlag(reconcileFlags); ast.setBindingResolver(resolver); converter.setAST(ast); CompilationUnit unit = converter.convert(compilationUnitDeclaration, workingCopy.getContents()); unit.setLineEndTable(compilationUnitDeclaration.compilationResult.getLineSeparatorPositions()); unit.setTypeRoot(workingCopy.originalFromClone()); ast.setDefaultNodeFlag(savedDefaultNodeFlag); return unit; } /** * Creates a new Java abstract syntax tree * (AST) following the specified set of API rules. * <p> * Clients should use this method specifying {@link #JLS8} as the * AST level in all cases, even when dealing with source of earlier JDK versions like 1.3 or 1.4. * </p> * * @param level the API level; one of the <code>JLS*</code> level constants * @return new AST instance following the specified set of API rules. * @exception IllegalArgumentException if: * <ul> * <li>the API level is not one of the <code>JLS*</code> level constants</li> * </ul> * @since 3.0 */ public static AST newAST(int level) { return new AST(level); } /** * Parses the given string as a Java compilation unit and creates and * returns a corresponding abstract syntax tree. * <p> * The returned compilation unit node is the root node of a new AST. * Each node in the subtree carries source range(s) information relating back * to positions in the given source string (the given source string itself * is not remembered with the AST). * The source range usually begins at the first character of the first token * corresponding to the node; leading whitespace and comments are <b>not</b> * included. The source range usually extends through the last character of * the last token corresponding to the node; trailing whitespace and * comments are <b>not</b> included. There are a handful of exceptions * (including compilation units and the various body declarations); the * specification for these node type spells out the details. * Source ranges nest properly: the source range for a child is always * within the source range of its parent, and the source ranges of sibling * nodes never overlap. * If a syntax error is detected while parsing, the relevant node(s) of the * tree will be flagged as <code>MALFORMED</code>. * </p> * <p> * This method does not compute binding information; all <code>resolveBinding</code> * methods applied to nodes of the resulting AST return <code>null</code>. * </p> * * @param source the string to be parsed as a Java compilation unit * @return the compilation unit node * @see ASTNode#getFlags() * @see ASTNode#MALFORMED * @see ASTNode#getStartPosition() * @see ASTNode#getLength() * @since 2.0 * @deprecated Use {@link ASTParser} instead. */ public static CompilationUnit parseCompilationUnit(char[] source) { if (source == null) { throw new IllegalArgumentException(); } ASTParser c = ASTParser.newParser(AST.JLS2); c.setSource(source); ASTNode result = c.createAST(null); return (CompilationUnit) result; } /** * Parses the given string as the hypothetical contents of the named * compilation unit and creates and returns a corresponding abstract syntax tree. * <p> * The returned compilation unit node is the root node of a new AST. * Each node in the subtree carries source range(s) information relating back * to positions in the given source string (the given source string itself * is not remembered with the AST). * The source range usually begins at the first character of the first token * corresponding to the node; leading whitespace and comments are <b>not</b> * included. The source range usually extends through the last character of * the last token corresponding to the node; trailing whitespace and * comments are <b>not</b> included. There are a handful of exceptions * (including compilation units and the various body declarations); the * specification for these node type spells out the details. * Source ranges nest properly: the source range for a child is always * within the source range of its parent, and the source ranges of sibling * nodes never overlap. * If a syntax error is detected while parsing, the relevant node(s) of the * tree will be flagged as <code>MALFORMED</code>. * </p> * <p> * If the given project is not <code>null</code>, the various names * and types appearing in the compilation unit can be resolved to "bindings" * by calling the <code>resolveBinding</code> methods. These bindings * draw connections between the different parts of a program, and * generally afford a more powerful vantage point for clients who wish to * analyze a program's structure more deeply. These bindings come at a * considerable cost in both time and space, however, and should not be * requested frivolously. The additional space is not reclaimed until the * AST, all its nodes, and all its bindings become garbage. So it is very * important to not retain any of these objects longer than absolutely * necessary. Bindings are resolved at the time the AST is created. Subsequent * modifications to the AST do not affect the bindings returned by * <code>resolveBinding</code> methods in any way; these methods return the * same binding as before the AST was modified (including modifications * that rearrange subtrees by reparenting nodes). * If the given project is <code>null</code>, the analysis * does not go beyond parsing and building the tree, and all * <code>resolveBinding</code> methods return <code>null</code> from the * outset. * </p> * <p> * The name of the compilation unit must be supplied for resolving bindings. * This name should be suffixed by a dot ('.') followed by one of the * {@link JavaCore#getJavaLikeExtensions() Java-like extensions} * and match the name of the main * (public) class or interface declared in the source. For example, if the source * declares a public class named "Foo", the name of the compilation can be * "Foo.java". For the purposes of resolving bindings, types declared in the * source string hide types by the same name available through the classpath * of the given project. * </p> * * @param source the string to be parsed as a Java compilation unit * @param unitName the name of the compilation unit that would contain the source * string, or <code>null</code> if <code>javaProject</code> is also <code>null</code> * @param project the Java project used to resolve names, or * <code>null</code> if bindings are not resolved * @return the compilation unit node * @see ASTNode#getFlags() * @see ASTNode#MALFORMED * @see ASTNode#getStartPosition() * @see ASTNode#getLength() * @since 2.0 * @deprecated Use {@link ASTParser} instead. */ public static CompilationUnit parseCompilationUnit( char[] source, String unitName, IJavaProject project) { if (source == null) { throw new IllegalArgumentException(); } ASTParser astParser = ASTParser.newParser(AST.JLS2); astParser.setSource(source); astParser.setUnitName(unitName); astParser.setProject(project); astParser.setResolveBindings(project != null); ASTNode result = astParser.createAST(null); return (CompilationUnit) result; } /** * Parses the source string corresponding to the given Java class file * element and creates and returns a corresponding abstract syntax tree. * The source string is obtained from the Java model element using * <code>IClassFile.getSource()</code>, and is only available for a class * files with attached source. * <p> * The returned compilation unit node is the root node of a new AST. * Each node in the subtree carries source range(s) information relating back * to positions in the source string (the source string is not remembered * with the AST). * The source range usually begins at the first character of the first token * corresponding to the node; leading whitespace and comments are <b>not</b> * included. The source range usually extends through the last character of * the last token corresponding to the node; trailing whitespace and * comments are <b>not</b> included. There are a handful of exceptions * (including compilation units and the various body declarations); the * specification for these node type spells out the details. * Source ranges nest properly: the source range for a child is always * within the source range of its parent, and the source ranges of sibling * nodes never overlap. * If a syntax error is detected while parsing, the relevant node(s) of the * tree will be flagged as <code>MALFORMED</code>. * </p> * <p> * If <code>resolveBindings</code> is <code>true</code>, the various names * and types appearing in the compilation unit can be resolved to "bindings" * by calling the <code>resolveBinding</code> methods. These bindings * draw connections between the different parts of a program, and * generally afford a more powerful vantage point for clients who wish to * analyze a program's structure more deeply. These bindings come at a * considerable cost in both time and space, however, and should not be * requested frivolously. The additional space is not reclaimed until the * AST, all its nodes, and all its bindings become garbage. So it is very * important to not retain any of these objects longer than absolutely * necessary. Bindings are resolved at the time the AST is created. Subsequent * modifications to the AST do not affect the bindings returned by * <code>resolveBinding</code> methods in any way; these methods return the * same binding as before the AST was modified (including modifications * that rearrange subtrees by reparenting nodes). * If <code>resolveBindings</code> is <code>false</code>, the analysis * does not go beyond parsing and building the tree, and all * <code>resolveBinding</code> methods return <code>null</code> from the * outset. * </p> * * @param classFile the Java model class file whose corresponding source code is to be parsed * @param resolveBindings <code>true</code> if bindings are wanted, * and <code>false</code> if bindings are not of interest * @return the compilation unit node * @exception IllegalArgumentException if the given Java element does not * exist or if its source string cannot be obtained * @see ASTNode#getFlags() * @see ASTNode#MALFORMED * @see ASTNode#getStartPosition() * @see ASTNode#getLength() * @since 2.1 * @deprecated Use {@link ASTParser} instead. */ public static CompilationUnit parseCompilationUnit( IClassFile classFile, boolean resolveBindings) { if (classFile == null) { throw new IllegalArgumentException(); } try { ASTParser c = ASTParser.newParser(AST.JLS2); c.setSource(classFile); c.setResolveBindings(resolveBindings); ASTNode result = c.createAST(null); return (CompilationUnit) result; } catch (IllegalStateException e) { // convert ASTParser's complaints into old form throw new IllegalArgumentException(); } } /** * Parses the source string of the given Java model compilation unit element * and creates and returns a corresponding abstract syntax tree. The source * string is obtained from the Java model element using * <code>ICompilationUnit.getSource()</code>. * <p> * The returned compilation unit node is the root node of a new AST. * Each node in the subtree carries source range(s) information relating back * to positions in the source string (the source string is not remembered * with the AST). * The source range usually begins at the first character of the first token * corresponding to the node; leading whitespace and comments are <b>not</b> * included. The source range usually extends through the last character of * the last token corresponding to the node; trailing whitespace and * comments are <b>not</b> included. There are a handful of exceptions * (including compilation units and the various body declarations); the * specification for these node type spells out the details. * Source ranges nest properly: the source range for a child is always * within the source range of its parent, and the source ranges of sibling * nodes never overlap. * If a syntax error is detected while parsing, the relevant node(s) of the * tree will be flagged as <code>MALFORMED</code>. * </p> * <p> * If <code>resolveBindings</code> is <code>true</code>, the various names * and types appearing in the compilation unit can be resolved to "bindings" * by calling the <code>resolveBinding</code> methods. These bindings * draw connections between the different parts of a program, and * generally afford a more powerful vantage point for clients who wish to * analyze a program's structure more deeply. These bindings come at a * considerable cost in both time and space, however, and should not be * requested frivolously. The additional space is not reclaimed until the * AST, all its nodes, and all its bindings become garbage. So it is very * important to not retain any of these objects longer than absolutely * necessary. Bindings are resolved at the time the AST is created. Subsequent * modifications to the AST do not affect the bindings returned by * <code>resolveBinding</code> methods in any way; these methods return the * same binding as before the AST was modified (including modifications * that rearrange subtrees by reparenting nodes). * If <code>resolveBindings</code> is <code>false</code>, the analysis * does not go beyond parsing and building the tree, and all * <code>resolveBinding</code> methods return <code>null</code> from the * outset. * </p> * * @param unit the Java model compilation unit whose source code is to be parsed * @param resolveBindings <code>true</code> if bindings are wanted, * and <code>false</code> if bindings are not of interest * @return the compilation unit node * @exception IllegalArgumentException if the given Java element does not * exist or if its source string cannot be obtained * @see ASTNode#getFlags() * @see ASTNode#MALFORMED * @see ASTNode#getStartPosition() * @see ASTNode#getLength() * @since 2.0 * @deprecated Use {@link ASTParser} instead. */ public static CompilationUnit parseCompilationUnit( ICompilationUnit unit, boolean resolveBindings) { try { ASTParser c = ASTParser.newParser(AST.JLS2); c.setSource(unit); c.setResolveBindings(resolveBindings); ASTNode result = c.createAST(null); return (CompilationUnit) result; } catch (IllegalStateException e) { // convert ASTParser's complaints into old form throw new IllegalArgumentException(); } } /** * Level of AST API supported by this AST. * @since 3.0 */ int apiLevel; /** * Tag bit value. This represents internal state of the tree. */ private int bits; /** * Default value of <code>flag<code> when a new node is created. */ private int defaultNodeFlag = 0; /** * When disableEvents > 0, events are not reported and * the modification count stays fixed. * <p> * This mechanism is used in lazy initialization of a node * to prevent events from being reported for the modification * of the node as well as for the creation of the missing child. * </p> * @since 3.0 */ private int disableEvents = 0; /** * The event handler for this AST. * Initially an event handler that does not nothing. * @since 3.0 */ private NodeEventHandler eventHandler = new NodeEventHandler(); /** * Internal object unique to the AST instance. Readers must synchronize on * this object when the modifying instance fields. * @since 3.0 */ private final Object internalASTLock = new Object(); /** * Internal modification count; initially 0; increases monotonically * <b>by one or more</b> as the AST is successively modified. */ private long modificationCount = 0; /** * Internal original modification count; value is equals to <code> * modificationCount</code> at the end of the parse (<code>ASTParser * </code>). If this ast is not created with a parser then value is 0. * @since 3.0 */ private long originalModificationCount = 0; /** * The binding resolver for this AST. Initially a binding resolver that * does not resolve names at all. */ private BindingResolver resolver = new BindingResolver(); /** * Internal ast rewriter used to record ast modification when record mode is enabled. */ InternalASTRewrite rewriter; /** * Java Scanner used to validate preconditions for the creation of specific nodes * like CharacterLiteral, NumberLiteral, StringLiteral or SimpleName. */ Scanner scanner; /** * new Object[] {this} * @since 3.0 */ private final Object[] THIS_AST= new Object[] {this}; /** * Creates a new, empty abstract syntax tree using default options. * * @see JavaCore#getDefaultOptions() * @deprecated Clients should port their code to use the new JLS4 AST API and call * {@link #newAST(int) AST.newAST(AST.JLS4)} instead of using this constructor. */ public AST() { this(JavaCore.getDefaultOptions()); } /** * Creates a new Java abstract syntax tree * (AST) following the specified set of API rules. * * @param level the API level; one of the <code>JLS*</code> level constants * @since 3.0 */ private AST(int level) { switch(level) { case JLS2_INTERNAL : case JLS3_INTERNAL : this.apiLevel = level; // initialize a scanner this.scanner = new Scanner( true /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, ClassFileConstants.JDK1_5 /*complianceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); break; case JLS4_INTERNAL : this.apiLevel = level; // initialize a scanner this.scanner = new Scanner( true /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_7 /*sourceLevel*/, ClassFileConstants.JDK1_7 /*complianceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); break; case JLS8 : this.apiLevel = level; // initialize a scanner this.scanner = new Scanner( true /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_8 /*sourceLevel*/, ClassFileConstants.JDK1_8 /*complianceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); break; default: throw new IllegalArgumentException("Unsupported JLS level"); //$NON-NLS-1$ } } /** * Creates a new, empty abstract syntax tree using the given options. * <p> * Following option keys are significant: * <ul> * <li><code>"org.eclipse.jdt.core.compiler.source"</code> - * indicates source compatibility mode (as per <code>JavaCore</code>); * <code>"1.3"</code> means the source code is as per JDK 1.3; * <code>"1.4"</code> means the source code is as per JDK 1.4 * (<code>"assert"</code> is now a keyword); * <code>"1.5"</code> means the source code is as per JDK 1.5 * (<code>"enum"</code> is now a keyword); * <code>"1.7"</code> means the source code is as per JDK 1.7; * additional legal values may be added later. </li> * </ul> * Options other than the above are ignored. * </p> * * @param options the table of options (key type: <code>String</code>; * value type: <code>String</code>) * @see JavaCore#getDefaultOptions() * @deprecated Clients should port their code to use the new JLS4 AST API and call * {@link #newAST(int) AST.newAST(AST.JLS4)} instead of using this constructor. */ public AST(Map options) { this(JLS2); Object sourceLevelOption = options.get(JavaCore.COMPILER_SOURCE); long sourceLevel = ClassFileConstants.JDK1_3; if (JavaCore.VERSION_1_4.equals(sourceLevelOption)) { sourceLevel = ClassFileConstants.JDK1_4; } else if (JavaCore.VERSION_1_5.equals(sourceLevelOption)) { sourceLevel = ClassFileConstants.JDK1_5; } else if (JavaCore.VERSION_1_7.equals(sourceLevelOption)) { sourceLevel = ClassFileConstants.JDK1_7; } Object complianceLevelOption = options.get(JavaCore.COMPILER_COMPLIANCE); long complianceLevel = ClassFileConstants.JDK1_3; if (JavaCore.VERSION_1_4.equals(complianceLevelOption)) { complianceLevel = ClassFileConstants.JDK1_4; } else if (JavaCore.VERSION_1_5.equals(complianceLevelOption)) { complianceLevel = ClassFileConstants.JDK1_5; } else if (JavaCore.VERSION_1_7.equals(complianceLevelOption)) { complianceLevel = ClassFileConstants.JDK1_7; } // override scanner if 1.4 or 1.5 asked for this.scanner = new Scanner( true /*comment*/, true /*whitespace*/, false /*nls*/, sourceLevel /*sourceLevel*/, complianceLevel /*complianceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); } /** * Return the API level supported by this AST. * * @return level the API level; one of the <code>JLS*</code> level constants * declared on <code>AST</code>; assume this set is open-ended * @since 3.0 */ public int apiLevel() { return this.apiLevel; } /** * Creates an unparented node of the given node class * (non-abstract subclass of {@link ASTNode}). * * @param nodeClass AST node class * @return a new unparented node owned by this AST * @exception IllegalArgumentException if <code>nodeClass</code> is * <code>null</code> or is not a concrete node type class * or is not supported for this AST's API level * @since 3.0 */ public ASTNode createInstance(Class nodeClass) { if (nodeClass == null) { throw new IllegalArgumentException(); } try { // invoke constructor with signature Foo(AST) Constructor c = nodeClass.getDeclaredConstructor(AST_CLASS); Object result = c.newInstance(this.THIS_AST); return (ASTNode) result; } catch (NoSuchMethodException e) { // all AST node classes have a Foo(AST) constructor // therefore nodeClass is not legit throw new IllegalArgumentException(); } catch (InstantiationException e) { // all concrete AST node classes can be instantiated // therefore nodeClass is not legit throw new IllegalArgumentException(); } catch (IllegalAccessException e) { // all AST node classes have an accessible Foo(AST) constructor // therefore nodeClass is not legit throw new IllegalArgumentException(); } catch (InvocationTargetException e) { // concrete AST node classes do not die in the constructor // therefore nodeClass is not legit IllegalArgumentException iae = new IllegalArgumentException(); iae.initCause(e.getCause()); throw iae; } } /** * Creates an unparented node of the given node type. * This convenience method is equivalent to: * <pre> * createInstance(ASTNode.nodeClassForType(nodeType)) * </pre> * * @param nodeType AST node type, one of the node type * constants declared on {@link ASTNode} * @return a new unparented node owned by this AST * @exception IllegalArgumentException if <code>nodeType</code> is * not a legal AST node type or if it's not supported for this AST's API level * @since 3.0 */ public ASTNode createInstance(int nodeType) { // nodeClassForType throws IllegalArgumentException if nodeType is bogus Class nodeClass = ASTNode.nodeClassForType(nodeType); return createInstance(nodeClass); } /** * Disable events. * This method is thread-safe for AST readers. * * @see #reenableEvents() * @since 3.0 */ final void disableEvents() { synchronized (this.internalASTLock) { // guard against concurrent access by another reader this.disableEvents++; } // while disableEvents > 0 no events will be reported, and mod count will stay fixed } /** * Returns the binding resolver for this AST. * * @return the binding resolver for this AST */ BindingResolver getBindingResolver() { return this.resolver; } /** * Returns default node flags of new nodes of this AST. * * @return the default node flags of new nodes of this AST * @since 3.0 */ int getDefaultNodeFlag() { return this.defaultNodeFlag; } /** * Returns the event handler for this AST. * * @return the event handler for this AST * @since 3.0 */ NodeEventHandler getEventHandler() { return this.eventHandler; } /** * Returns true if the ast tree was created with bindings recovery, false otherwise * * @return true if the ast tree was created with bindings recovery, false otherwise * @since 3.3 */ public boolean hasBindingsRecovery() { return (this.bits & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0; } /** * Returns true if the ast tree was created with bindings, false otherwise * * @return true if the ast tree was created with bindings, false otherwise * @since 3.3 */ public boolean hasResolvedBindings() { return (this.bits & RESOLVED_BINDINGS) != 0; } /** * Returns true if the ast tree was created with statements recovery, false otherwise * * @return true if the ast tree was created with statements recovery, false otherwise * @since 3.3 */ public boolean hasStatementsRecovery() { return (this.bits & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0; } /* (omit javadoc for this method) * This method is a copy of setName(String[]) that doesn't do any validation. */ Name internalNewName(String[] identifiers) { int count = identifiers.length; if (count == 0) { throw new IllegalArgumentException(); } final SimpleName simpleName = new SimpleName(this); simpleName.internalSetIdentifier(identifiers[0]); Name result = simpleName; for (int i = 1; i < count; i++) { SimpleName name = new SimpleName(this); name.internalSetIdentifier(identifiers[i]); result = newQualifiedName(result, name); } return result; } /** * Returns the modification count for this AST. The modification count * is a non-negative value that increases (by 1 or perhaps by more) as * this AST or its nodes are changed. The initial value is unspecified. * <p> * The following things count as modifying an AST: * <ul> * <li>creating a new node owned by this AST,</li> * <li>adding a child to a node owned by this AST,</li> * <li>removing a child from a node owned by this AST,</li> * <li>setting a non-node attribute of a node owned by this AST.</li> * </ul> * </p> * Operations which do not entail creating or modifying existing nodes * do not increase the modification count. * <p> * N.B. This method may be called several times in the course * of a single client operation. The only promise is that the modification * count increases monotonically as the AST or its nodes change; there is * no promise that a modifying operation increases the count by exactly 1. * </p> * * @return the current value (non-negative) of the modification counter of * this AST */ public long modificationCount() { return this.modificationCount; } /** * Indicates that this AST is about to be modified. * <p> * The following things count as modifying an AST: * <ul> * <li>creating a new node owned by this AST</li> * <li>adding a child to a node owned by this AST</li> * <li>removing a child from a node owned by this AST</li> * <li>setting a non-node attribute of a node owned by this AST</li>. * </ul> * </p> * <p> * N.B. This method may be called several times in the course * of a single client operation. * </p> */ void modifying() { // when this method is called during lazy init, events are disabled // and the modification count will not be increased if (this.disableEvents > 0) { return; } // increase the modification count this.modificationCount++; } /** * A local method to workaround calling deprecated method in array type. * @deprecated */ private void setArrayComponentType(ArrayType arrayType, Type type) { arrayType.setComponentType(type); } /** * Creates and returns a new unparented annotation type declaration * node for an unspecified, but legal, name; no modifiers; no javadoc; * and an empty list of member declarations. * * @return a new unparented annotation type declaration node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public AnnotationTypeDeclaration newAnnotationTypeDeclaration() { AnnotationTypeDeclaration result = new AnnotationTypeDeclaration(this); return result; } /** * Creates and returns a new unparented annotation type * member declaration node for an unspecified, but legal, * member name and type; no modifiers; no javadoc; * and no default value. * * @return a new unparented annotation type member declaration node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public AnnotationTypeMemberDeclaration newAnnotationTypeMemberDeclaration() { AnnotationTypeMemberDeclaration result = new AnnotationTypeMemberDeclaration(this); return result; } /** * Creates and returns a new unparented anonymous class declaration * node owned by this AST. By default, the body declaration list is empty. * * @return a new unparented anonymous class declaration node */ public AnonymousClassDeclaration newAnonymousClassDeclaration() { AnonymousClassDeclaration result = new AnonymousClassDeclaration(this); return result; } /** * Creates and returns a new unparented array access expression node * owned by this AST. By default, the array and index expression are * both unspecified (but legal). * * @return a new unparented array access expression node */ public ArrayAccess newArrayAccess() { ArrayAccess result = new ArrayAccess(this); return result; } /** * Creates and returns a new unparented array creation expression node * owned by this AST. By default, the array type is an unspecified * 1-dimensional array, the list of dimensions is empty, and there is no * array initializer. * <p> * Examples: * <code> * <pre> * // new String[len] * ArrayCreation ac1 = ast.newArrayCreation(); * ac1.setType( * ast.newArrayType( * ast.newSimpleType(ast.newSimpleName("String")))); * ac1.dimensions().add(ast.newSimpleName("len")); * * // new double[7][24][] * ArrayCreation ac2 = ast.newArrayCreation(); * ac2.setType( * ast.newArrayType( * ast.newPrimitiveType(PrimitiveType.DOUBLE), 3)); * ac2.dimensions().add(ast.newNumberLiteral("7")); * ac2.dimensions().add(ast.newNumberLiteral("24")); * * // new int[] {1, 2} * ArrayCreation ac3 = ast.newArrayCreation(); * ac3.setType( * ast.newArrayType( * ast.newPrimitiveType(PrimitiveType.INT))); * ArrayInitializer ai = ast.newArrayInitializer(); * ac3.setInitializer(ai); * ai.expressions().add(ast.newNumberLiteral("1")); * ai.expressions().add(ast.newNumberLiteral("2")); * </pre> * </code> * </p> * * @return a new unparented array creation expression node */ public ArrayCreation newArrayCreation() { ArrayCreation result = new ArrayCreation(this); return result; } /** * Creates and returns a new unparented array initializer node * owned by this AST. By default, the initializer has no expressions. * * @return a new unparented array initializer node */ public ArrayInitializer newArrayInitializer() { ArrayInitializer result = new ArrayInitializer(this); return result; } /** * Creates and returns a new unparented array type node with the given * element type, which cannot be an array type for API levels JLS8 and later. * By default, the array type has one non-annotated dimension. * <p> * For JLS4 and before, the given component type may be another array type. * * @param elementType element type for API level JLS8 and later, or the * component type (possibly another array type) for levels less than JLS8 * @return a new unparented array type node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * <li>API level is JLS8 or later and type is an array type</li> * </ul> */ public ArrayType newArrayType(Type elementType) { ArrayType result; if (this.apiLevel < AST.JLS8) { result = new ArrayType(this); setArrayComponentType(result, elementType); return result; } if (elementType.isArrayType()) { throw new IllegalArgumentException(); } result = new ArrayType(this); result.setElementType(elementType); return result; } /** * Creates and returns a new unparented array type node with the given * element type and number of dimensions. * <p> * For JLS4 and before, the element type passed in can be an array type, but in that case, the * element type of the result will not be the same as what was passed in. * For JLS4 and before, the dimensions cannot be 0. * </p> * * @param elementType the element type (cannot be an array type for JLS8 and later) * @param dimensions the number of dimensions, a non-negative number * @return a new unparented array type node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * <li>the element type is null</li> * <li>the number of dimensions is lower than 0 (for JLS4 and before: lower than 1)</li> * <li>the number of dimensions is greater than 255</li> * <li>for levels from JLS8 and later, if the element type is an array type </li> * </ul> */ public ArrayType newArrayType(Type elementType, int dimensions) { if (elementType == null) { throw new IllegalArgumentException(); } if (dimensions < 0 || dimensions > 255) { // max as per Java VM spec throw new IllegalArgumentException(); } ArrayType result; if (this.apiLevel < AST.JLS8) { if (dimensions < 1) { throw new IllegalArgumentException(); } result = new ArrayType(this); setArrayComponentType(result, elementType); for (int i = 2; i <= dimensions; i++) { result = newArrayType(result); } return result; } //level >= JLS8 if (elementType.isArrayType()) { throw new IllegalArgumentException(); } result = new ArrayType(this, 0); result.setElementType(elementType); for (int i = 0; i < dimensions; ++i) { result.dimensions().add(new Dimension(this)); } return result; } /** * Creates a new unparented assert statement node owned by this AST. * By default, the first expression is unspecified, but legal, and has no * message expression. * * @return a new unparented assert statement node */ public AssertStatement newAssertStatement() { return new AssertStatement(this); } /** * Creates and returns a new unparented assignment expression node * owned by this AST. By default, the assignment operator is "=" and * the left and right hand side expressions are unspecified, but * legal, names. * * @return a new unparented assignment expression node */ public Assignment newAssignment() { Assignment result = new Assignment(this); return result; } /** * Creates an unparented block node owned by this AST, for an empty list * of statements. * * @return a new unparented, empty block node */ public Block newBlock() { return new Block(this); } /** * Creates and returns a new block comment placeholder node. * <p> * Note that this node type is used to recording the source * range where a comment was found in the source string. * These comment nodes are normally found (only) in * {@linkplain CompilationUnit#getCommentList() * the comment table} for parsed compilation units. * </p> * * @return a new unparented block comment node * @since 3.0 */ public BlockComment newBlockComment() { BlockComment result = new BlockComment(this); return result; } /** * Creates and returns a new unparented boolean literal node. * <p> * For example, the assignment expression <code>foo = true</code> * is generated by the following snippet: * <code> * <pre> * Assignment e= ast.newAssignment(); * e.setLeftHandSide(ast.newSimpleName("foo")); * e.setRightHandSide(ast.newBooleanLiteral(true)); * </pre> * </code> * </p> * * @param value the boolean value * @return a new unparented boolean literal node */ public BooleanLiteral newBooleanLiteral(boolean value) { BooleanLiteral result = new BooleanLiteral(this); result.setBooleanValue(value); return result; } /** * Creates an unparented break statement node owned by this AST. * The break statement has no label. * * @return a new unparented break statement node */ public BreakStatement newBreakStatement() { return new BreakStatement(this); } /** * Creates and returns a new unparented cast expression node * owned by this AST. By default, the type and expression are unspecified * (but legal). * * @return a new unparented cast expression node */ public CastExpression newCastExpression() { CastExpression result = new CastExpression(this); return result; } /** * Creates a new unparented catch clause node owned by this AST. * By default, the catch clause declares an unspecified, but legal, * exception declaration and has an empty block. * * @return a new unparented catch clause node */ public CatchClause newCatchClause() { return new CatchClause(this); } /** * Creates and returns a new unparented character literal node. * Initially the node has an unspecified character literal. * * @return a new unparented character literal node */ public CharacterLiteral newCharacterLiteral() { return new CharacterLiteral(this); } /** * Creates and returns a new unparented class instance creation * ("new") expression node owned by this AST. By default, * there is no qualifying expression, no type parameters, * an unspecified (but legal) type name, an empty list of * arguments, and does not declare an anonymous class declaration. * * @return a new unparented class instance creation expression node */ public ClassInstanceCreation newClassInstanceCreation() { ClassInstanceCreation result = new ClassInstanceCreation(this); return result; } //=============================== DECLARATIONS =========================== /** * Creates an unparented compilation unit node owned by this AST. * The compilation unit initially has no package declaration, no * import declarations, and no type declarations. * * @return the new unparented compilation unit node */ public CompilationUnit newCompilationUnit() { return new CompilationUnit(this); } /** * Creates and returns a new unparented conditional expression node * owned by this AST. By default, the condition and both expressions * are unspecified (but legal). * * @return a new unparented array conditional expression node */ public ConditionalExpression newConditionalExpression() { ConditionalExpression result = new ConditionalExpression(this); return result; } /** * Creates an unparented alternate constructor ("this(...);") invocation * statement node owned by this AST. By default, the lists of arguments * and type arguments are both empty. * <p> * Note that this type of node is a Statement, whereas a regular * method invocation is an Expression. The only valid use of these * statements are as the first statement of a constructor body. * </p> * * @return a new unparented alternate constructor invocation statement node */ public ConstructorInvocation newConstructorInvocation() { ConstructorInvocation result = new ConstructorInvocation(this); return result; } /** * Creates an unparented continue statement node owned by this AST. * The continue statement has no label. * * @return a new unparented continue statement node */ public ContinueStatement newContinueStatement() { return new ContinueStatement(this); } /** * Creates an unparented creation reference node owned by this AST. * By default, the type is unspecified (but legal), and there are no type arguments. * * @return a new unparented creation reference expression node * @exception UnsupportedOperationException if this operation is used in a JLS2, JLS3 or JLS4 AST * @since 3.10 */ public CreationReference newCreationReference() { CreationReference result = new CreationReference(this); return result; } /** * Creates a new unparented do statement node owned by this AST. * By default, the expression is unspecified (but legal), and * the body statement is an empty block. * * @return a new unparented do statement node */ public DoStatement newDoStatement() { return new DoStatement(this); } /** * Creates a new unparented empty statement node owned by this AST. * * @return a new unparented empty statement node */ public EmptyStatement newEmptyStatement() { return new EmptyStatement(this); } /** * Creates a new unparented enhanced for statement node owned by this AST. * By default, the paramter and expression are unspecified * but legal subtrees, and the body is an empty block. * * @return a new unparented throw statement node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public EnhancedForStatement newEnhancedForStatement() { return new EnhancedForStatement(this); } /** * Creates an unparented enum constant declaration node owned by this AST. * The name of the constant is an unspecified, but legal, name; * no doc comment; no modifiers or annotations; no arguments; * and does not declare an anonymous class. * * @return a new unparented enum constant declaration node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public EnumConstantDeclaration newEnumConstantDeclaration() { EnumConstantDeclaration result = new EnumConstantDeclaration(this); return result; } /** * Creates an unparented enum declaration node owned by this AST. * The name of the enum is an unspecified, but legal, name; * no doc comment; no modifiers or annotations; * no superinterfaces; and empty lists of enum constants * and body declarations. * * @return a new unparented enum declaration node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public EnumDeclaration newEnumDeclaration() { EnumDeclaration result = new EnumDeclaration(this); return result; } /** * Creates an unparented expression method reference node owned by this AST. * By default, the expression and method name are unspecified (but legal), * and there are no type arguments. * * @return a new unparented expression method reference expression node * @exception UnsupportedOperationException if this operation is used in a JLS2, JLS3 or JLS4 AST * @since 3.10 */ public ExpressionMethodReference newExpressionMethodReference() { ExpressionMethodReference result = new ExpressionMethodReference(this); return result; } /** * Creates a new unparented expression statement node owned by this AST, * for the given expression. * <p> * This method can be used to convert an expression * (<code>Expression</code>) into a statement (<code>Type</code>) * by wrapping it. Note, however, that the result is only legal for * limited expression types, including method invocations, assignments, * and increment/decrement operations. * </p> * * @param expression the expression * @return a new unparented statement node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> */ public ExpressionStatement newExpressionStatement(Expression expression) { ExpressionStatement result = new ExpressionStatement(this); result.setExpression(expression); return result; } /** * Creates and returns a new unparented annotatable dimension node * (Supported only in JLS8 level). * * @return a new unparented annotatable dimension node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> * @exception UnsupportedOperationException if this operation is used * in a JLS2, JLS3 or JLS4 AST * @since 3.10 */ public Dimension newDimension() { Dimension result = new Dimension(this); return result; } /** * Creates and returns a new unparented field access expression node * owned by this AST. By default, the expression and field are both * unspecified, but legal, names. * * @return a new unparented field access expression node */ public FieldAccess newFieldAccess() { FieldAccess result = new FieldAccess(this); return result; } /** * Creates a new unparented field declaration node owned by this AST, * for the given variable declaration fragment. By default, there are no * modifiers, no doc comment, and the base type is unspecified * (but legal). * <p> * This method can be used to wrap a variable declaration fragment * (<code>VariableDeclarationFragment</code>) into a field declaration * suitable for inclusion in the body of a type declaration * (<code>FieldDeclaration</code> implements <code>BodyDeclaration</code>). * Additional variable declaration fragments can be added afterwards. * </p> * * @param fragment the variable declaration fragment * @return a new unparented field declaration node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * <li>the given fragment is null</li> * </ul> */ public FieldDeclaration newFieldDeclaration(VariableDeclarationFragment fragment) { if (fragment == null) { throw new IllegalArgumentException(); } FieldDeclaration result = new FieldDeclaration(this); result.fragments().add(fragment); return result; } /** * Creates a new unparented for statement node owned by this AST. * By default, there are no initializers, no condition expression, * no updaters, and the body is an empty block. * * @return a new unparented for statement node */ public ForStatement newForStatement() { return new ForStatement(this); } /** * Creates a new unparented if statement node owned by this AST. * By default, the expression is unspecified (but legal), * the then statement is an empty block, and there is no else statement. * * @return a new unparented if statement node */ public IfStatement newIfStatement() { return new IfStatement(this); } /** * Creates an unparented import declaration node owned by this AST. * The import declaration initially contains a single-type import * of a type with an unspecified name. * * @return the new unparented import declaration node */ public ImportDeclaration newImportDeclaration() { ImportDeclaration result = new ImportDeclaration(this); return result; } /** * Creates and returns a new unparented infix expression node * owned by this AST. By default, the operator and left and right * operand are unspecified (but legal), and there are no extended * operands. * * @return a new unparented infix expression node */ public InfixExpression newInfixExpression() { InfixExpression result = new InfixExpression(this); return result; } /** * Creates an unparented initializer node owned by this AST, with an * empty block. By default, the initializer has no modifiers and * an empty block. * * @return a new unparented initializer node */ public Initializer newInitializer() { Initializer result = new Initializer(this); return result; } /** * Creates and returns a new unparented instanceof expression node * owned by this AST. By default, the operator and left and right * operand are unspecified (but legal). * * @return a new unparented instanceof expression node */ public InstanceofExpression newInstanceofExpression() { InstanceofExpression result = new InstanceofExpression(this); return result; } /** * Creates and returns a new doc comment node. * Initially the new node has an empty list of tag elements * (and, for backwards compatability, an unspecified, but legal, * doc comment string) * * @return a new unparented doc comment node */ public Javadoc newJavadoc() { Javadoc result = new Javadoc(this); return result; } /** * Creates a new unparented labeled statement node owned by this AST. * By default, the label and statement are both unspecified, but legal. * * @return a new unparented labeled statement node */ public LabeledStatement newLabeledStatement() { return new LabeledStatement(this); } /** * Creates an unparented lambda expression node owned by this AST. * By default, the new lambda expression has parentheses enabled, contains an empty argument * list, and the body is an empty block. * * @return a new unparented lambda expression node * @exception UnsupportedOperationException if this operation is used in a JLS2, JLS3 or JLS4 AST * @since 3.10 */ public LambdaExpression newLambdaExpression() { LambdaExpression result = new LambdaExpression(this); return result; } /** * Creates and returns a new line comment placeholder node. * <p> * Note that this node type is used to recording the source * range where a comment was found in the source string. * These comment nodes are normally found (only) in * {@linkplain CompilationUnit#getCommentList() * the comment table} for parsed compilation units. * </p> * * @return a new unparented line comment node * @since 3.0 */ public LineComment newLineComment() { LineComment result = new LineComment(this); return result; } /** * Creates and returns a new unparented marker annotation node with * an unspecified type name. * * @return a new unparented marker annotation node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public MarkerAnnotation newMarkerAnnotation() { MarkerAnnotation result = new MarkerAnnotation(this); return result; } /** * Creates and returns a new member reference node. * Initially the new node has no qualifier name and * an unspecified, but legal, member name. * <p> * Note that this node type is used only inside doc comments * ({@link Javadoc}). * </p> * * @return a new unparented member reference node * @since 3.0 */ public MemberRef newMemberRef() { MemberRef result = new MemberRef(this); return result; } //=============================== COMMENTS =========================== /** * Creates and returns a new unparented member value pair node with * an unspecified member name and value. * * @return a new unparented member value pair node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public MemberValuePair newMemberValuePair() { MemberValuePair result = new MemberValuePair(this); return result; } /** * Creates an unparented method declaration node owned by this AST. * By default, the declaration is for a method of an unspecified, but * legal, name; no modifiers; no doc comment; no parameters; return * type void; no extra array dimensions; no thrown exceptions; and no * body (as opposed to an empty body). * <p> * To create a constructor, use this method and then call * <code>MethodDeclaration.setConstructor(true)</code> and * <code>MethodDeclaration.setName(className)</code>. * </p> * * @return a new unparented method declaration node */ public MethodDeclaration newMethodDeclaration() { MethodDeclaration result = new MethodDeclaration(this); result.setConstructor(false); return result; } /** * Creates an unparented method invocation expression node owned by this * AST. By default, the name of the method is unspecified (but legal) * there is no receiver expression, no type arguments, and the list of * arguments is empty. * * @return a new unparented method invocation expression node */ public MethodInvocation newMethodInvocation() { MethodInvocation result = new MethodInvocation(this); return result; } /** * Creates and returns a new method reference node. * Initially the new node has no qualifier name, * an unspecified, but legal, method name, and an * empty parameter list. * <p> * Note that this node type is used only inside doc comments * ({@link Javadoc Javadoc}). * </p> * * @return a new unparented method reference node * @since 3.0 */ public MethodRef newMethodRef() { MethodRef result = new MethodRef(this); return result; } /** * Creates and returns a new method reference node. * Initially the new node has an unspecified, but legal, * type, not variable arity, and no parameter name. * <p> * Note that this node type is used only inside doc comments * ({@link Javadoc}). * </p> * * @return a new unparented method reference parameter node * @since 3.0 */ public MethodRefParameter newMethodRefParameter() { MethodRefParameter result = new MethodRefParameter(this); return result; } /** * Creates and returns a new unparented modifier node for the given * modifier. * * @param keyword one of the modifier keyword constants * @return a new unparented modifier node * @exception IllegalArgumentException if the primitive type code is invalid * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public Modifier newModifier(Modifier.ModifierKeyword keyword) { Modifier result = new Modifier(this); result.setKeyword(keyword); return result; } /** * Creates and returns a list of new unparented modifier nodes * for the given modifier flags. When multiple modifiers are * requested, the modifier nodes will appear in the following order: * <pre> public protected private * abstract default static final synchronized native strictfp transient volatile</pre> * <p> * This order is consistent with the recommendations in JLS8 ("*Modifier:" rules in chapters 8 and 9). * </p> * * @param flags bitwise or of modifier flags declared on {@link Modifier} * @return a possibly empty list of new unparented modifier nodes * (element type <code>Modifier</code>) * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public List newModifiers(int flags) { if (this.apiLevel == AST.JLS2) { unsupportedIn2(); } List result = new ArrayList(3); // 3 modifiers is more than average if (Modifier.isPublic(flags)) { result.add(newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); } if (Modifier.isProtected(flags)) { result.add(newModifier(Modifier.ModifierKeyword.PROTECTED_KEYWORD)); } if (Modifier.isPrivate(flags)) { result.add(newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD)); } if (Modifier.isAbstract(flags)) { result.add(newModifier(Modifier.ModifierKeyword.ABSTRACT_KEYWORD)); } if (Modifier.isDefault(flags)) { result.add(newModifier(Modifier.ModifierKeyword.DEFAULT_KEYWORD)); } if (Modifier.isStatic(flags)) { result.add(newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD)); } if (Modifier.isFinal(flags)) { result.add(newModifier(Modifier.ModifierKeyword.FINAL_KEYWORD)); } if (Modifier.isSynchronized(flags)) { result.add(newModifier(Modifier.ModifierKeyword.SYNCHRONIZED_KEYWORD)); } if (Modifier.isNative(flags)) { result.add(newModifier(Modifier.ModifierKeyword.NATIVE_KEYWORD)); } if (Modifier.isStrictfp(flags)) { result.add(newModifier(Modifier.ModifierKeyword.STRICTFP_KEYWORD)); } if (Modifier.isTransient(flags)) { result.add(newModifier(Modifier.ModifierKeyword.TRANSIENT_KEYWORD)); } if (Modifier.isVolatile(flags)) { result.add(newModifier(Modifier.ModifierKeyword.VOLATILE_KEYWORD)); } return result; } /** * Creates and returns a new unparented name node for the given name. * The name string must consist of 1 or more name segments separated * by single dots '.'. Returns a {@link QualifiedName} if the name has * dots, and a {@link SimpleName} otherwise. Each of the name * segments should be legal Java identifiers (this constraint may or may * not be enforced), and there must be at least one name segment. * The string must not contains white space, '<', '>', * '[', ']', or other any other characters that are not * part of the Java identifiers or separating '.'s. * * @param qualifiedName string consisting of 1 or more name segments, * each of which is a legal Java identifier, separated by single dots '.' * @return a new unparented name node * @exception IllegalArgumentException if: * <ul> * <li>the string is empty</li> * <li>the string begins or ends in a '.'</li> * <li>the string has adjacent '.'s</li> * <li>the segments between the '.'s are not valid Java identifiers</li> * </ul> * @since 3.1 */ public Name newName(String qualifiedName) { StringTokenizer t = new StringTokenizer(qualifiedName, ".", true); //$NON-NLS-1$ Name result = null; // balance is # of name tokens - # of period tokens seen so far // initially 0; finally 1; should never drop < 0 or > 1 int balance = 0; while(t.hasMoreTokens()) { String s = t.nextToken(); if (s.indexOf('.') >= 0) { // this is a delimiter if (s.length() > 1) { // too many dots in a row throw new IllegalArgumentException(); } balance--; if (balance < 0) { throw new IllegalArgumentException(); } } else { // this is an identifier segment balance++; SimpleName name = newSimpleName(s); if (result == null) { result = name; } else { result = newQualifiedName(result, name); } } } if (balance != 1) { throw new IllegalArgumentException(); } return result; } /** * Creates and returns a new unparented name node for the given name * segments. Returns a simple name if there is only one name segment, and * a qualified name if there are multiple name segments. Each of the name * segments should be legal Java identifiers (this constraint may or may * not be enforced), and there must be at least one name segment. * * @param identifiers a list of 1 or more name segments, each of which * is a legal Java identifier * @return a new unparented name node * @exception IllegalArgumentException if: * <ul> * <li>the identifier is invalid</li> * <li>the list of identifiers is empty</li> * </ul> */ public Name newName(String[] identifiers) { // update internalSetName(String[] if changed int count = identifiers.length; if (count == 0) { throw new IllegalArgumentException(); } Name result = newSimpleName(identifiers[0]); for (int i = 1; i < count; i++) { SimpleName name = newSimpleName(identifiers[i]); result = newQualifiedName(result, name); } return result; } /** * Creates and returns a new unparented name qualified type node with * the given qualifier and name. * * @param qualifier the name qualifier name node * @param name the simple name being qualified * @return a new unparented qualified type node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> * @exception UnsupportedOperationException if this operation is used in * a JLS2, JLS3 and JLS4 AST * @since 3.10 */ public NameQualifiedType newNameQualifiedType(Name qualifier, SimpleName name) { NameQualifiedType result = new NameQualifiedType(this); result.setQualifier(qualifier); result.setName(name); return result; } /** * Creates and returns a new unparented normal annotation node with * an unspecified type name and an empty list of member value * pairs. * * @return a new unparented normal annotation node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public NormalAnnotation newNormalAnnotation() { NormalAnnotation result = new NormalAnnotation(this); return result; } /** * Creates and returns a new unparented null literal node. * * @return a new unparented null literal node */ public NullLiteral newNullLiteral() { return new NullLiteral(this); } /** * Creates and returns a new unparented number literal node. * Initially the number literal token is <code>"0"</code>. * * @return a new unparented number literal node */ public NumberLiteral newNumberLiteral() { NumberLiteral result = new NumberLiteral(this); return result; } /** * Creates and returns a new unparented number literal node. * * @param literal the token for the numeric literal as it would * appear in Java source code * @return a new unparented number literal node * @exception IllegalArgumentException if the literal is null */ public NumberLiteral newNumberLiteral(String literal) { if (literal == null) { throw new IllegalArgumentException(); } NumberLiteral result = new NumberLiteral(this); result.setToken(literal); return result; } /** * Creates an unparented package declaration node owned by this AST. * The package declaration initially declares a package with an * unspecified name. * * @return the new unparented package declaration node */ public PackageDeclaration newPackageDeclaration() { PackageDeclaration result = new PackageDeclaration(this); return result; } /** * Creates and returns a new unparented parameterized type node with the * given type and an empty list of type arguments. * * @param type the type that is parameterized * @return a new unparented parameterized type node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public ParameterizedType newParameterizedType(Type type) { ParameterizedType result = new ParameterizedType(this); result.setType(type); return result; } /** * Creates and returns a new unparented parenthesized expression node * owned by this AST. By default, the expression is unspecified (but legal). * * @return a new unparented parenthesized expression node */ public ParenthesizedExpression newParenthesizedExpression() { ParenthesizedExpression result = new ParenthesizedExpression(this); return result; } /** * Creates and returns a new unparented postfix expression node * owned by this AST. By default, the operator and operand are * unspecified (but legal). * * @return a new unparented postfix expression node */ public PostfixExpression newPostfixExpression() { PostfixExpression result = new PostfixExpression(this); return result; } /** * Creates and returns a new unparented prefix expression node * owned by this AST. By default, the operator and operand are * unspecified (but legal). * * @return a new unparented prefix expression node */ public PrefixExpression newPrefixExpression() { PrefixExpression result = new PrefixExpression(this); return result; } /** * Creates and returns a new unparented primitive type node with the given * type code. * * @param typeCode one of the primitive type code constants declared in * <code>PrimitiveType</code> * @return a new unparented primitive type node * @exception IllegalArgumentException if the primitive type code is invalid */ public PrimitiveType newPrimitiveType(PrimitiveType.Code typeCode) { PrimitiveType result = new PrimitiveType(this); result.setPrimitiveTypeCode(typeCode); return result; } /** * Creates and returns a new unparented qualified name node for the given * qualifier and simple name child node. * * @param qualifier the qualifier name node * @param name the simple name being qualified * @return a new unparented qualified name node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> */ public QualifiedName newQualifiedName( Name qualifier, SimpleName name) { QualifiedName result = new QualifiedName(this); result.setQualifier(qualifier); result.setName(name); return result; } /** * Creates and returns a new unparented qualified type node with * the given qualifier type and name. * * @param qualifier the qualifier type node * @param name the simple name being qualified * @return a new unparented qualified type node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public QualifiedType newQualifiedType(Type qualifier, SimpleName name) { QualifiedType result = new QualifiedType(this); result.setQualifier(qualifier); result.setName(name); return result; } /** * Creates a new unparented return statement node owned by this AST. * By default, the return statement has no expression. * * @return a new unparented return statement node */ public ReturnStatement newReturnStatement() { return new ReturnStatement(this); } //=============================== NAMES =========================== /** * Creates and returns a new unparented simple name node for the given * identifier. The identifier should be a legal Java identifier, but not * a keyword, boolean literal ("true", "false") or null literal ("null"). * * @param identifier the identifier * @return a new unparented simple name node * @exception IllegalArgumentException if the identifier is invalid */ public SimpleName newSimpleName(String identifier) { if (identifier == null) { throw new IllegalArgumentException(); } SimpleName result = new SimpleName(this); result.setIdentifier(identifier); return result; } //=============================== TYPES =========================== /** * Creates and returns a new unparented simple type node with the given * type name. * <p> * This method can be used to convert a name (<code>Name</code>) into a * type (<code>Type</code>) by wrapping it. * </p> * * @param typeName the name of the class or interface * @return a new unparented simple type node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> */ public SimpleType newSimpleType(Name typeName) { SimpleType result = new SimpleType(this); result.setName(typeName); return result; } /** * Creates and returns a new unparented single member annotation node with * an unspecified type name and value. * * @return a new unparented single member annotation node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public SingleMemberAnnotation newSingleMemberAnnotation() { SingleMemberAnnotation result = new SingleMemberAnnotation(this); return result; } /** * Creates an unparented single variable declaration node owned by this AST. * By default, the declaration is for a variable with an unspecified, but * legal, name and type; no modifiers; no array dimensions after the * variable; no initializer; not variable arity. * * @return a new unparented single variable declaration node */ public SingleVariableDeclaration newSingleVariableDeclaration() { SingleVariableDeclaration result = new SingleVariableDeclaration(this); return result; } //=============================== EXPRESSIONS =========================== /** * Creates and returns a new unparented string literal node for * the empty string literal. * * @return a new unparented string literal node */ public StringLiteral newStringLiteral() { return new StringLiteral(this); } /** * Creates an unparented alternate super constructor ("super(...);") * invocation statement node owned by this AST. By default, there is no * qualifier, no type arguments, and the list of arguments is empty. * <p> * Note that this type of node is a Statement, whereas a regular * super method invocation is an Expression. The only valid use of these * statements are as the first statement of a constructor body. * </p> * * @return a new unparented super constructor invocation statement node */ public SuperConstructorInvocation newSuperConstructorInvocation() { SuperConstructorInvocation result = new SuperConstructorInvocation(this); return result; } /** * Creates and returns a new unparented super field access expression node * owned by this AST. By default, the expression and field are both * unspecified, but legal, names. * * @return a new unparented super field access expression node */ public SuperFieldAccess newSuperFieldAccess() { SuperFieldAccess result = new SuperFieldAccess(this); return result; } /** * Creates an unparented "super" method invocation expression node owned by * this AST. By default, the name of the method is unspecified (but legal), * there is no qualifier, no type arguments, and the list of arguments is empty. * * @return a new unparented "super" method invocation * expression node */ public SuperMethodInvocation newSuperMethodInvocation() { SuperMethodInvocation result = new SuperMethodInvocation(this); return result; } /** * Creates and returns a new unparented super method reference node owned by * this AST. By default, the name of the method is unspecified (but legal), * and there is no qualifier and no type arguments. * * @return a new unparented super method reference node * @since 3.10 */ public SuperMethodReference newSuperMethodReference() { SuperMethodReference result = new SuperMethodReference(this); return result; } /** * Creates a new unparented switch case statement node owned by * this AST. By default, the expression is unspecified, but legal. * * @return a new unparented switch case node */ public SwitchCase newSwitchCase() { return new SwitchCase(this); } /** * Creates a new unparented switch statement node owned by this AST. * By default, the expression is unspecified, but legal, and there are * no statements or switch cases. * * @return a new unparented labeled statement node */ public SwitchStatement newSwitchStatement() { return new SwitchStatement(this); } /** * Creates a new unparented synchronized statement node owned by this AST. * By default, the expression is unspecified, but legal, and the body is * an empty block. * * @return a new unparented synchronized statement node */ public SynchronizedStatement newSynchronizedStatement() { return new SynchronizedStatement(this); } /** * Creates and returns a new tag element node. * Initially the new node has no tag name and an empty list of fragments. * <p> * Note that this node type is used only inside doc comments * ({@link Javadoc}). * </p> * * @return a new unparented tag element node * @since 3.0 */ public TagElement newTagElement() { TagElement result = new TagElement(this); return result; } /** * Creates and returns a new text element node. * Initially the new node has an empty text string. * <p> * Note that this node type is used only inside doc comments * ({@link Javadoc Javadoc}). * </p> * * @return a new unparented text element node * @since 3.0 */ public TextElement newTextElement() { TextElement result = new TextElement(this); return result; } /** * Creates and returns a new unparented "this" expression node * owned by this AST. By default, there is no qualifier. * * @return a new unparented "this" expression node */ public ThisExpression newThisExpression() { ThisExpression result = new ThisExpression(this); return result; } /** * Creates a new unparented throw statement node owned by this AST. * By default, the expression is unspecified, but legal. * * @return a new unparented throw statement node */ public ThrowStatement newThrowStatement() { return new ThrowStatement(this); } /** * Creates a new unparented try statement node owned by this AST. * By default, the try statement has no resources, an empty block, no catch * clauses, and no finally block. * * @return a new unparented try statement node */ public TryStatement newTryStatement() { return new TryStatement(this); } /** * Creates an unparented class declaration node owned by this AST. * The name of the class is an unspecified, but legal, name; * no modifiers; no doc comment; no superclass or superinterfaces; * and an empty class body. * <p> * To create an interface, use this method and then call * <code>TypeDeclaration.setInterface(true)</code>. * </p> * * @return a new unparented type declaration node */ public TypeDeclaration newTypeDeclaration() { TypeDeclaration result = new TypeDeclaration(this); result.setInterface(false); return result; } /** * Creates a new unparented local type declaration statement node * owned by this AST, for the given type declaration. * <p> * This method can be used to convert any kind of type declaration * (<code>AbstractTypeDeclaration</code>) into a statement * (<code>Statement</code>) by wrapping it. * </p> * * @param decl the type declaration * @return a new unparented local type declaration statement node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> * @since 3.0 */ public TypeDeclarationStatement newTypeDeclarationStatement(AbstractTypeDeclaration decl) { TypeDeclarationStatement result = new TypeDeclarationStatement(this); if (this.apiLevel == AST.JLS2) { result.internalSetTypeDeclaration((TypeDeclaration) decl); } if (this.apiLevel >= AST.JLS3) { result.setDeclaration(decl); } return result; } /** * Creates a new unparented local type declaration statement node * owned by this AST, for the given type declaration. * <p> * This method can be used to convert a type declaration * (<code>TypeDeclaration</code>) into a statement * (<code>Statement</code>) by wrapping it. * </p> * * @param decl the type declaration * @return a new unparented local type declaration statement node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * </ul> */ public TypeDeclarationStatement newTypeDeclarationStatement(TypeDeclaration decl) { TypeDeclarationStatement result = new TypeDeclarationStatement(this); result.setDeclaration(decl); return result; } /** * Creates and returns a new unparented type literal expression node * owned by this AST. By default, the type is unspecified (but legal). * * @return a new unparented type literal node */ public TypeLiteral newTypeLiteral() { TypeLiteral result = new TypeLiteral(this); return result; } /** * Creates an unparented type method reference node owned by this AST. * By default, the type and method name are unspecified (but legal), * and there are no type arguments. * * @return a new unparented type method reference node * @exception UnsupportedOperationException if this operation is used in a JLS2, JLS3 or JLS4 AST * @since 3.10 */ public TypeMethodReference newTypeMethodReference() { TypeMethodReference result = new TypeMethodReference(this); return result; } /** * Creates and returns a new unparented type parameter type node with an * unspecified type variable name and an empty list of type bounds. * * @return a new unparented type parameter node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public TypeParameter newTypeParameter() { TypeParameter result = new TypeParameter(this); return result; } /** * Creates a new unparented union type node owned by this AST. * By default, the union type has no types. * * @return a new unparented UnionType node * @exception UnsupportedOperationException if this operation is used in * a JLS2 or JLS3 AST * @since 3.7.1 */ public UnionType newUnionType() { return new UnionType(this); } /** * Creates a new unparented intersection type node owned by this AST. * By default, the intersection type has no types. * * @return a new unparented IntersectionType node * @exception UnsupportedOperationException if this operation is used in * a JLS2, JLS3 or JLS4 AST * @since 3.10 */ public IntersectionType newIntersectionType() { return new IntersectionType(this); } /** * Creates a new unparented local variable declaration expression node * owned by this AST, for the given variable declaration fragment. By * default, there are no modifiers and the base type is unspecified * (but legal). * <p> * This method can be used to convert a variable declaration fragment * (<code>VariableDeclarationFragment</code>) into an expression * (<code>Expression</code>) by wrapping it. Additional variable * declaration fragments can be added afterwards. * </p> * * @param fragment the first variable declaration fragment * @return a new unparented variable declaration expression node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * <li>the given fragment is null</li> * </ul> */ public VariableDeclarationExpression newVariableDeclarationExpression(VariableDeclarationFragment fragment) { if (fragment == null) { throw new IllegalArgumentException(); } VariableDeclarationExpression result = new VariableDeclarationExpression(this); result.fragments().add(fragment); return result; } /** * Creates an unparented variable declaration fragment node owned by this * AST. By default, the fragment is for a variable with an unspecified, but * legal, name; no extra array dimensions; and no initializer. * * @return a new unparented variable declaration fragment node */ public VariableDeclarationFragment newVariableDeclarationFragment() { VariableDeclarationFragment result = new VariableDeclarationFragment(this); return result; } //=============================== STATEMENTS =========================== /** * Creates a new unparented local variable declaration statement node * owned by this AST, for the given variable declaration fragment. * By default, there are no modifiers and the base type is unspecified * (but legal). * <p> * This method can be used to convert a variable declaration fragment * (<code>VariableDeclarationFragment</code>) into a statement * (<code>Statement</code>) by wrapping it. Additional variable * declaration fragments can be added afterwards. * </p> * * @param fragment the variable declaration fragment * @return a new unparented variable declaration statement node * @exception IllegalArgumentException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * <li>the variable declaration fragment is null</li> * </ul> */ public VariableDeclarationStatement newVariableDeclarationStatement(VariableDeclarationFragment fragment) { if (fragment == null) { throw new IllegalArgumentException(); } VariableDeclarationStatement result = new VariableDeclarationStatement(this); result.fragments().add(fragment); return result; } /** * Creates a new unparented while statement node owned by this AST. * By default, the expression is unspecified (but legal), and * the body statement is an empty block. * * @return a new unparented while statement node */ public WhileStatement newWhileStatement() { return new WhileStatement(this); } /** * Creates and returns a new unparented wildcard type node with no * type bound. * * @return a new unparented wildcard type node * @exception UnsupportedOperationException if this operation is used in * a JLS2 AST * @since 3.1 */ public WildcardType newWildcardType() { WildcardType result = new WildcardType(this); return result; } /** * Reports that the given node has just gained a child. * * @param node the node that was modified * @param child the node that was added as a child * @param property the child or child list property descriptor * @since 3.0 */ void postAddChildEvent(ASTNode node, ASTNode child, StructuralPropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE ADD]"); return; } else { disableEvents(); } } try { this.eventHandler.postAddChildEvent(node, child, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has already been changed } finally { reenableEvents(); } } /** * Reports that the given node has just been cloned. * * @param node the node that was cloned * @param clone the clone of <code>node</code> * @since 3.0 */ void postCloneNodeEvent(ASTNode node, ASTNode clone) { synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE CLONE]"); return; } else { disableEvents(); } } try { this.eventHandler.postCloneNodeEvent(node, clone); // N.B. even if event handler blows up, the AST is not // corrupted since node has already been changed } finally { reenableEvents(); } } /** * Reports that the given node jsut lost a child. * * @param node the node that was modified * @param child the child node that was removed * @param property the child or child list property descriptor * @since 3.0 */ void postRemoveChildEvent(ASTNode node, ASTNode child, StructuralPropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE DEL]"); return; } else { disableEvents(); } } try { this.eventHandler.postRemoveChildEvent(node, child, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has not been changed yet } finally { reenableEvents(); } } /** * Reports that the given node has just had a child replaced. * * @param node the node modified * @param child the child removed * @param newChild the replacement child * @param property the child or child list property descriptor * @since 3.0 */ void postReplaceChildEvent(ASTNode node, ASTNode child, ASTNode newChild, StructuralPropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE REP]"); return; } else { disableEvents(); } } try { this.eventHandler.postReplaceChildEvent(node, child, newChild, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has not been changed yet } finally { reenableEvents(); } } /** * Reports that the given node has just changed the value of a * non-child property. * * @param node the node that was modified * @param property the property descriptor * @since 3.0 */ void postValueChangeEvent(ASTNode node, SimplePropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE CHANGE]"); return; } else { disableEvents(); } } try { this.eventHandler.postValueChangeEvent(node, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has already been changed } finally { reenableEvents(); } } /** * Reports that the given node is about to gain a child. * * @param node the node that to be modified * @param child the node that to be added as a child * @param property the child or child list property descriptor * @since 3.0 */ void preAddChildEvent(ASTNode node, ASTNode child, StructuralPropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE ADD]"); return; } else { disableEvents(); } } try { this.eventHandler.preAddChildEvent(node, child, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has already been changed } finally { reenableEvents(); } } /** * Reports that the given node is about to be cloned. * * @param node the node to be cloned * @since 3.0 */ void preCloneNodeEvent(ASTNode node) { synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE CLONE]"); return; } else { disableEvents(); } } try { this.eventHandler.preCloneNodeEvent(node); // N.B. even if event handler blows up, the AST is not // corrupted since node has already been changed } finally { reenableEvents(); } } /** * Reports that the given node is about to lose a child. * * @param node the node about to be modified * @param child the node about to be removed * @param property the child or child list property descriptor * @since 3.0 */ void preRemoveChildEvent(ASTNode node, ASTNode child, StructuralPropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE DEL]"); return; } else { disableEvents(); } } try { this.eventHandler.preRemoveChildEvent(node, child, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has not been changed yet } finally { reenableEvents(); } } /** * Reports that the given node is about have a child replaced. * * @param node the node about to be modified * @param child the child node about to be removed * @param newChild the replacement child * @param property the child or child list property descriptor * @since 3.0 */ void preReplaceChildEvent(ASTNode node, ASTNode child, ASTNode newChild, StructuralPropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE REP]"); return; } else { disableEvents(); } } try { this.eventHandler.preReplaceChildEvent(node, child, newChild, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has not been changed yet } finally { reenableEvents(); } } /** * Reports that the given node is about to change the value of a * non-child property. * * @param node the node to be modified * @param property the property descriptor * @since 3.0 */ void preValueChangeEvent(ASTNode node, SimplePropertyDescriptor property) { // IMPORTANT: this method is called by readers during lazy init synchronized (this.internalASTLock) { // guard against concurrent access by a reader doing lazy init if (this.disableEvents > 0) { // doing lazy init OR already processing an event // System.out.println("[BOUNCE CHANGE]"); return; } else { disableEvents(); } } try { this.eventHandler.preValueChangeEvent(node, property); // N.B. even if event handler blows up, the AST is not // corrupted since node has already been changed } finally { reenableEvents(); } } /** * Enables the recording of changes to the given 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 the 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 if recording has already been enabled, * or if <code>root</code> is not owned by this AST * @see CompilationUnit#recordModifications() * @since 3.0 */ void recordModifications(CompilationUnit root) { if(this.modificationCount != this.originalModificationCount) { throw new IllegalArgumentException("AST is already modified"); //$NON-NLS-1$ } else if(this.rewriter != null) { throw new IllegalArgumentException("AST modifications are already recorded"); //$NON-NLS-1$ } else if((root.getFlags() & ASTNode.PROTECT) != 0) { throw new IllegalArgumentException("Root node is unmodifiable"); //$NON-NLS-1$ } else if(root.getAST() != this) { throw new IllegalArgumentException("Root node is not owned by this ast"); //$NON-NLS-1$ } this.rewriter = new InternalASTRewrite(root); setEventHandler(this.rewriter); } //=============================== ANNOTATIONS ==================== /** * Reenable events. * This method is thread-safe for AST readers. * * @see #disableEvents() * @since 3.0 */ final void reenableEvents() { synchronized (this.internalASTLock) { // guard against concurrent access by another reader this.disableEvents--; } } /** * Returns the type binding for a "well known" type. * <p> * Note that bindings are generally unavailable unless requested when the * AST is being built. * </p> * <p> * The following type names are supported: * <ul> * <li><code>"boolean"</code></li> * <li><code>"byte"</code></li> * <li><code>"char"</code></li> * <li><code>"double"</code></li> * <li><code>"float"</code></li> * <li><code>"int"</code></li> * <li><code>"long"</code></li> * <li><code>"short"</code></li> * <li><code>"void"</code></li> * <li><code>"java.lang.AssertionError"</code> (since 3.7)</li> * <li><code>"java.lang.Boolean"</code> (since 3.1)</li> * <li><code>"java.lang.Byte"</code> (since 3.1)</li> * <li><code>"java.lang.Character"</code> (since 3.1)</li> * <li><code>"java.lang.Class"</code></li> * <li><code>"java.lang.Cloneable"</code></li> * <li><code>"java.lang.Double"</code> (since 3.1)</li> * <li><code>"java.lang.Error"</code></li> * <li><code>"java.lang.Exception"</code></li> * <li><code>"java.lang.Float"</code> (since 3.1)</li> * <li><code>"java.lang.Integer"</code> (since 3.1)</li> * <li><code>"java.lang.Long"</code> (since 3.1)</li> * <li><code>"java.lang.Object"</code></li> * <li><code>"java.lang.RuntimeException"</code></li> * <li><code>"java.lang.Short"</code> (since 3.1)</li> * <li><code>"java.lang.String"</code></li> * <li><code>"java.lang.StringBuffer"</code></li> * <li><code>"java.lang.Throwable"</code></li> * <li><code>"java.lang.Void"</code> (since 3.1)</li> * <li><code>"java.io.Serializable"</code></li> * </ul> * </p> * * @param name the name of a well known type * @return the corresponding type binding, or <code>null</code> if the * named type is not considered well known or if no binding can be found * for it */ public ITypeBinding resolveWellKnownType(String name) { if (name == null) { return null; } return getBindingResolver().resolveWellKnownType(name); } /** * Converts all modifications recorded into an object * representing the corresponding text edits to the * given document containing the original source * code for the compilation unit that gave rise to * this AST. * * @param document original document containing source code * for the 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 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 CompilationUnit#rewrite(IDocument, Map) * @since 3.0 */ TextEdit rewrite(IDocument document, Map options) { if (document == null) { throw new IllegalArgumentException(); } if (this.rewriter == null) { throw new IllegalStateException("Modifications record is not enabled"); //$NON-NLS-1$ } return this.rewriter.rewriteAST(document, options); } /** * Sets the binding resolver for this AST. * * @param resolver the new binding resolver for this AST */ void setBindingResolver(BindingResolver resolver) { if (resolver == null) { throw new IllegalArgumentException(); } this.resolver = resolver; } /** * Sets default node flags of new nodes of this AST. * * @param flag node flags of new nodes of this AST * @since 3.0 */ void setDefaultNodeFlag(int flag) { this.defaultNodeFlag = flag; } /** * Sets the event handler for this AST. * * @param eventHandler the event handler for this AST * @since 3.0 */ void setEventHandler(NodeEventHandler eventHandler) { if (this.eventHandler == null) { throw new IllegalArgumentException(); } this.eventHandler = eventHandler; } void setFlag(int newValue) { this.bits |= newValue; } /** * Set <code>originalModificationCount</code> to the current modification count * * @since 3.0 */ void setOriginalModificationCount(long count) { this.originalModificationCount = count; } /** * Checks that this AST operation is only used when * building level JLS2 ASTs. * @exception UnsupportedOperationException * @since 3.0 */ void supportedOnlyIn2() { if (this.apiLevel != AST.JLS2) { throw new UnsupportedOperationException("Operation not supported in JLS2 AST"); //$NON-NLS-1$ } } /** * Checks that this AST operation is not used when * building level JLS2 ASTs. * @exception UnsupportedOperationException * @since 3.0 */ void unsupportedIn2() { if (this.apiLevel == AST.JLS2) { throw new UnsupportedOperationException("Operation not supported in JLS2 AST"); //$NON-NLS-1$ } } }