/******************************************************************************* * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.core.dom; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.jdt.internal.core.dom.NaiveASTFlattener; /** * Abstract superclass of all Abstract Syntax Tree (AST) node types. * <p> * An AST node represents a Java source code construct, such * as a name, type, expression, statement, or declaration. * </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 ensure that the added nodes have the correct owning AST. * </p> * <p> * When an AST node is part of an AST, it has a unique parent node. * Clients can navigate upwards, from child to parent, as well as downwards, * from parent to child. Newly created nodes are unparented. When an * unparented node is set as a child of a node (using a * <code>set<i>CHILD</i></code> method), its parent link is set automatically * and the parent link of the former child is set to <code>null</code>. * For nodes with properties that include a list of children (for example, * <code>Block</code> whose <code>statements</code> property is a list * of statements), adding or removing an element to/for the list property * automatically updates the parent links. These lists support the * <code>List.set</code> method; however, the constraint that the same * node cannot appear more than once means that this method cannot be used * to swap elements without first removing the node. * </p> * <p> * ASTs must not contain cycles. All operations that could create a cycle * detect this possibility and fail. * </p> * <p> * ASTs do not contain "holes" (missing subtrees). If a node is required to * have a certain property, a syntactically plausible initial value is * always supplied. * </p> * <p> * The hierarchy of AST node types has some convenient groupings marked * by abstract superclasses: * <ul> * <li>expressions - <code>Expression</code></li> * <li>names - <code>Name</code> (a sub-kind of expression)</li> * <li>statements - <code>Statement</code></li> * <li>types - <code>Type</code></li> * <li>type body declarations - <code>BodyDeclaration</code></li> * </ul> * </p> * <p> * Abstract syntax trees may be hand constructed by clients, using the * <code>new<i>TYPE</i></code> factory methods (see <code>AST</code>) to * create new nodes, and the various <code>set<i>CHILD</i></code> methods * to connect them together. * </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. The source range covers the construct * as a whole. * </p> * <p> * Each AST node carries bit flags, which may convey additional information about * the node. For instance, the parser uses a flag to indicate a syntax error. * Newly created nodes have no flags set. * </p> * <p> * Each AST node is capable of carrying an open-ended collection of * client-defined properties. Newly created nodes have none. * <code>getProperty</code> and <code>setProperty</code> are used to access * these properties. * </p> * <p> * AST nodes are thread-safe for readers provided there are no active writers. * If one thread is modifying an AST, including creating new nodes or cloning * existing ones, it is <b>not</b> safe for another thread to read, visit, * write, create, or clone <em>any</em> of the nodes on the same AST. * When synchronization is required, consider using the common AST * object that owns the node; that is, use * <code>synchronize (node.getAST()) {...}</code>. * </p> * <p> * ASTs also support the visitor pattern; see the class <code>ASTVisitor</code> * for details. The <code>NodeFinder</code> class can be used to find a specific * node inside a tree. * </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. See * {@link CompilationUnit#recordModifications()} for details. * 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> * * @see ASTParser * @see ASTVisitor * @see NodeFinder * @since 2.0 * @noextend This class is not intended to be subclassed by clients. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public abstract class ASTNode { /* * ATTENTION: When doing anything to the ASTNode hierarchy, do not try to * reinvent the wheel. * * Look out for precedents with * - the same structural property type * - for child node properties: the same optionality (can be null / lazy initialization blurbs and impl.) * - the same declaring node type kind (abstract supertype or concrete type) * - a similar history (added in JLSx API, below JLSx only, replaced by {@link #xx}) * ..., and copy what was done there. Most of the code and * Javadoc in this package should look like it was created by a code generator. * * In subclasses of ASTNode, order properties by order of occurrence in source. * In general classes that list all AST node types, order alphabetically. */ /* * INSTRUCTIONS FOR ADDING NEW CONCRETE AST NODE TYPES * * There are several things that need to be changed when a * new concrete AST node type (call it "FooBar") is added: * * 1. Create the FooBar AST node type class. * The most effective way to do this is to copy a similar * existing concrete node class to get a template that * includes all the framework methods that must be implemented. * * 2. Add node type constant ASTNode.FOO_BAR. * Node constants are numbered consecutively. Add the * constant after the existing ones. * * 3. Add entry to ASTNode.nodeClassForType(int). * * 4. Add AST.newFooBar() factory method. * * 5. Add ASTVisitor.visit(FooBar) and endVisit(FooBar) methods. Same for DefaultASTVisitor. * * 6. Add ASTMatcher.match(FooBar,Object) method. * * 7. Ensure that SimpleName.isDeclaration() covers FooBar * nodes if required. * * 8. Add NaiveASTFlattener.visit(FooBar) method to illustrate * how these nodes should be serialized. * * 9. Update the AST test suites (ASTVisitorTest, etc.) * * The next steps are to update AST.parse* to start generating * the new type of nodes, and ASTRewrite to serialize them back out. */ /* * INSTRUCTIONS FOR ADDING A NEW PROPERTY TO AN AST NODE TYPE * * For concrete node types, use e.g. properties of SimpleName or ClassInstanceCreation * as templates: * * 1. Copy/paste the field, property descriptor, and getter/setter. * * 2. Adjust everything to the new property name and type. In the field's * Javadoc, properly document default value, initialization, and applicable * API levels. * * 3. Add/remove @since tags as necessary. * * 4. Search for references to the members in the template, and add similar * references in corresponding places for the new property. * * * For abstract node types, use AbstractTypeDeclaration as a template: * * 1. Same steps as above, but take extra care to copy and adjust the * *internal*() methods as well. * * 2. Search for references to the members in the template, and add similar * references in corresponding places for the new property (e.g. property * descriptor in each leaf type). */ /* * INSTRUCTIONS FOR REPLACING/DEPRECATING A PROPERTY OF AN AST NODE * * To replace a simple property with a child list property, see e.g. how * SingleVariableDeclaration replaced MODIFIERS_PROPERTY with * MODIFIERS2_PROPERTY. * * 1. Reuse the old property id. * * 2. Deprecate all references to the old property, except for the old * getter, which should compute the value from the new property in * later API levels. * * To completely replace a property, see how ClassInstanceCreation replaced * NAME_PROPERTY with TYPE_PROPERTY. */ /** * Node type constant indicating a node of type * <code>AnonymousClassDeclaration</code>. * @see AnonymousClassDeclaration */ public static final int ANONYMOUS_CLASS_DECLARATION = 1; /** * Node type constant indicating a node of type * <code>ArrayAccess</code>. * @see ArrayAccess */ public static final int ARRAY_ACCESS = 2; /** * Node type constant indicating a node of type * <code>ArrayCreation</code>. * @see ArrayCreation */ public static final int ARRAY_CREATION = 3; /** * Node type constant indicating a node of type * <code>ArrayInitializer</code>. * @see ArrayInitializer */ public static final int ARRAY_INITIALIZER = 4; /** * Node type constant indicating a node of type * <code>ArrayType</code>. * @see ArrayType */ public static final int ARRAY_TYPE = 5; /** * Node type constant indicating a node of type * <code>AssertStatement</code>. * @see AssertStatement */ public static final int ASSERT_STATEMENT = 6; /** * Node type constant indicating a node of type * <code>Assignment</code>. * @see Assignment */ public static final int ASSIGNMENT = 7; /** * Node type constant indicating a node of type * <code>Block</code>. * @see Block */ public static final int BLOCK = 8; /** * Node type constant indicating a node of type * <code>BooleanLiteral</code>. * @see BooleanLiteral */ public static final int BOOLEAN_LITERAL = 9; /** * Node type constant indicating a node of type * <code>BreakStatement</code>. * @see BreakStatement */ public static final int BREAK_STATEMENT = 10; /** * Node type constant indicating a node of type * <code>CastExpression</code>. * @see CastExpression */ public static final int CAST_EXPRESSION = 11; /** * Node type constant indicating a node of type * <code>CatchClause</code>. * @see CatchClause */ public static final int CATCH_CLAUSE = 12; /** * Node type constant indicating a node of type * <code>CharacterLiteral</code>. * @see CharacterLiteral */ public static final int CHARACTER_LITERAL = 13; /** * Node type constant indicating a node of type * <code>ClassInstanceCreation</code>. * @see ClassInstanceCreation */ public static final int CLASS_INSTANCE_CREATION = 14; /** * Node type constant indicating a node of type * <code>CompilationUnit</code>. * @see CompilationUnit */ public static final int COMPILATION_UNIT = 15; /** * Node type constant indicating a node of type * <code>ConditionalExpression</code>. * @see ConditionalExpression */ public static final int CONDITIONAL_EXPRESSION = 16; /** * Node type constant indicating a node of type * <code>ConstructorInvocation</code>. * @see ConstructorInvocation */ public static final int CONSTRUCTOR_INVOCATION = 17; /** * Node type constant indicating a node of type * <code>ContinueStatement</code>. * @see ContinueStatement */ public static final int CONTINUE_STATEMENT = 18; /** * Node type constant indicating a node of type * <code>DoStatement</code>. * @see DoStatement */ public static final int DO_STATEMENT = 19; /** * Node type constant indicating a node of type * <code>EmptyStatement</code>. * @see EmptyStatement */ public static final int EMPTY_STATEMENT = 20; /** * Node type constant indicating a node of type * <code>ExpressionStatement</code>. * @see ExpressionStatement */ public static final int EXPRESSION_STATEMENT = 21; /** * Node type constant indicating a node of type * <code>FieldAccess</code>. * @see FieldAccess */ public static final int FIELD_ACCESS = 22; /** * Node type constant indicating a node of type * <code>FieldDeclaration</code>. * @see FieldDeclaration */ public static final int FIELD_DECLARATION = 23; /** * Node type constant indicating a node of type * <code>ForStatement</code>. * @see ForStatement */ public static final int FOR_STATEMENT = 24; /** * Node type constant indicating a node of type * <code>IfStatement</code>. * @see IfStatement */ public static final int IF_STATEMENT = 25; /** * Node type constant indicating a node of type * <code>ImportDeclaration</code>. * @see ImportDeclaration */ public static final int IMPORT_DECLARATION = 26; /** * Node type constant indicating a node of type * <code>InfixExpression</code>. * @see InfixExpression */ public static final int INFIX_EXPRESSION = 27; /** * Node type constant indicating a node of type * <code>Initializer</code>. * @see Initializer */ public static final int INITIALIZER = 28; /** * Node type constant indicating a node of type * <code>Javadoc</code>. * @see Javadoc */ public static final int JAVADOC = 29; /** * Node type constant indicating a node of type * <code>LabeledStatement</code>. * @see LabeledStatement */ public static final int LABELED_STATEMENT = 30; /** * Node type constant indicating a node of type * <code>MethodDeclaration</code>. * @see MethodDeclaration */ public static final int METHOD_DECLARATION = 31; /** * Node type constant indicating a node of type * <code>MethodInvocation</code>. * @see MethodInvocation */ public static final int METHOD_INVOCATION = 32; /** * Node type constant indicating a node of type * <code>NullLiteral</code>. * @see NullLiteral */ public static final int NULL_LITERAL = 33; /** * Node type constant indicating a node of type * <code>NumberLiteral</code>. * @see NumberLiteral */ public static final int NUMBER_LITERAL = 34; /** * Node type constant indicating a node of type * <code>PackageDeclaration</code>. * @see PackageDeclaration */ public static final int PACKAGE_DECLARATION = 35; /** * Node type constant indicating a node of type * <code>ParenthesizedExpression</code>. * @see ParenthesizedExpression */ public static final int PARENTHESIZED_EXPRESSION = 36; /** * Node type constant indicating a node of type * <code>PostfixExpression</code>. * @see PostfixExpression */ public static final int POSTFIX_EXPRESSION = 37; /** * Node type constant indicating a node of type * <code>PrefixExpression</code>. * @see PrefixExpression */ public static final int PREFIX_EXPRESSION = 38; /** * Node type constant indicating a node of type * <code>PrimitiveType</code>. * @see PrimitiveType */ public static final int PRIMITIVE_TYPE = 39; /** * Node type constant indicating a node of type * <code>QualifiedName</code>. * @see QualifiedName */ public static final int QUALIFIED_NAME = 40; /** * Node type constant indicating a node of type * <code>ReturnStatement</code>. * @see ReturnStatement */ public static final int RETURN_STATEMENT = 41; /** * Node type constant indicating a node of type * <code>SimpleName</code>. * @see SimpleName */ public static final int SIMPLE_NAME = 42; /** * Node type constant indicating a node of type * <code>SimpleType</code>. * @see SimpleType */ public static final int SIMPLE_TYPE = 43; /** * Node type constant indicating a node of type * <code>SingleVariableDeclaration</code>. * @see SingleVariableDeclaration */ public static final int SINGLE_VARIABLE_DECLARATION = 44; /** * Node type constant indicating a node of type * <code>StringLiteral</code>. * @see StringLiteral */ public static final int STRING_LITERAL = 45; /** * Node type constant indicating a node of type * <code>SuperConstructorInvocation</code>. * @see SuperConstructorInvocation */ public static final int SUPER_CONSTRUCTOR_INVOCATION = 46; /** * Node type constant indicating a node of type * <code>SuperFieldAccess</code>. * @see SuperFieldAccess */ public static final int SUPER_FIELD_ACCESS = 47; /** * Node type constant indicating a node of type * <code>SuperMethodInvocation</code>. * @see SuperMethodInvocation */ public static final int SUPER_METHOD_INVOCATION = 48; /** * Node type constant indicating a node of type * <code>SwitchCase</code>. * @see SwitchCase */ public static final int SWITCH_CASE = 49; /** * Node type constant indicating a node of type * <code>SwitchStatement</code>. * @see SwitchStatement */ public static final int SWITCH_STATEMENT = 50; /** * Node type constant indicating a node of type * <code>SynchronizedStatement</code>. * @see SynchronizedStatement */ public static final int SYNCHRONIZED_STATEMENT = 51; /** * Node type constant indicating a node of type * <code>ThisExpression</code>. * @see ThisExpression */ public static final int THIS_EXPRESSION = 52; /** * Node type constant indicating a node of type * <code>ThrowStatement</code>. * @see ThrowStatement */ public static final int THROW_STATEMENT = 53; /** * Node type constant indicating a node of type * <code>TryStatement</code>. * @see TryStatement */ public static final int TRY_STATEMENT = 54; /** * Node type constant indicating a node of type * <code>TypeDeclaration</code>. * @see TypeDeclaration */ public static final int TYPE_DECLARATION = 55; /** * Node type constant indicating a node of type * <code>TypeDeclarationStatement</code>. * @see TypeDeclarationStatement */ public static final int TYPE_DECLARATION_STATEMENT = 56; /** * Node type constant indicating a node of type * <code>TypeLiteral</code>. * @see TypeLiteral */ public static final int TYPE_LITERAL = 57; /** * Node type constant indicating a node of type * <code>VariableDeclarationExpression</code>. * @see VariableDeclarationExpression */ public static final int VARIABLE_DECLARATION_EXPRESSION = 58; /** * Node type constant indicating a node of type * <code>VariableDeclarationFragment</code>. * @see VariableDeclarationFragment */ public static final int VARIABLE_DECLARATION_FRAGMENT = 59; /** * Node type constant indicating a node of type * <code>VariableDeclarationStatement</code>. * @see VariableDeclarationStatement */ public static final int VARIABLE_DECLARATION_STATEMENT = 60; /** * Node type constant indicating a node of type * <code>WhileStatement</code>. * @see WhileStatement */ public static final int WHILE_STATEMENT = 61; /** * Node type constant indicating a node of type * <code>InstanceofExpression</code>. * @see InstanceofExpression */ public static final int INSTANCEOF_EXPRESSION = 62; /** * Node type constant indicating a node of type * <code>LineComment</code>. * @see LineComment * @since 3.0 */ public static final int LINE_COMMENT = 63; /** * Node type constant indicating a node of type * <code>BlockComment</code>. * @see BlockComment * @since 3.0 */ public static final int BLOCK_COMMENT = 64; /** * Node type constant indicating a node of type * <code>TagElement</code>. * @see TagElement * @since 3.0 */ public static final int TAG_ELEMENT = 65; /** * Node type constant indicating a node of type * <code>TextElement</code>. * @see TextElement * @since 3.0 */ public static final int TEXT_ELEMENT = 66; /** * Node type constant indicating a node of type * <code>MemberRef</code>. * @see MemberRef * @since 3.0 */ public static final int MEMBER_REF = 67; /** * Node type constant indicating a node of type * <code>MethodRef</code>. * @see MethodRef * @since 3.0 */ public static final int METHOD_REF = 68; /** * Node type constant indicating a node of type * <code>MethodRefParameter</code>. * @see MethodRefParameter * @since 3.0 */ public static final int METHOD_REF_PARAMETER = 69; /** * Node type constant indicating a node of type * <code>EnhancedForStatement</code>. * @see EnhancedForStatement * @since 3.1 */ public static final int ENHANCED_FOR_STATEMENT = 70; /** * Node type constant indicating a node of type * <code>EnumDeclaration</code>. * @see EnumDeclaration * @since 3.1 */ public static final int ENUM_DECLARATION = 71; /** * Node type constant indicating a node of type * <code>EnumConstantDeclaration</code>. * @see EnumConstantDeclaration * @since 3.1 */ public static final int ENUM_CONSTANT_DECLARATION = 72; /** * Node type constant indicating a node of type * <code>TypeParameter</code>. * @see TypeParameter * @since 3.1 */ public static final int TYPE_PARAMETER = 73; /** * Node type constant indicating a node of type * <code>ParameterizedType</code>. * @see ParameterizedType * @since 3.1 */ public static final int PARAMETERIZED_TYPE = 74; /** * Node type constant indicating a node of type * <code>QualifiedType</code>. * @see QualifiedType * @since 3.1 */ public static final int QUALIFIED_TYPE = 75; /** * Node type constant indicating a node of type * <code>WildcardType</code>. * @see WildcardType * @since 3.1 */ public static final int WILDCARD_TYPE = 76; /** * Node type constant indicating a node of type * <code>NormalAnnotation</code>. * @see NormalAnnotation * @since 3.1 */ public static final int NORMAL_ANNOTATION = 77; /** * Node type constant indicating a node of type * <code>MarkerAnnotation</code>. * @see MarkerAnnotation * @since 3.1 */ public static final int MARKER_ANNOTATION = 78; /** * Node type constant indicating a node of type * <code>SingleMemberAnnotation</code>. * @see SingleMemberAnnotation * @since 3.1 */ public static final int SINGLE_MEMBER_ANNOTATION = 79; /** * Node type constant indicating a node of type * <code>MemberValuePair</code>. * @see MemberValuePair * @since 3.1 */ public static final int MEMBER_VALUE_PAIR = 80; /** * Node type constant indicating a node of type * <code>AnnotationTypeDeclaration</code>. * @see AnnotationTypeDeclaration * @since 3.1 */ public static final int ANNOTATION_TYPE_DECLARATION = 81; /** * Node type constant indicating a node of type * <code>AnnotationTypeMemberDeclaration</code>. * @see AnnotationTypeMemberDeclaration * @since 3.1 */ public static final int ANNOTATION_TYPE_MEMBER_DECLARATION = 82; /** * Node type constant indicating a node of type * <code>Modifier</code>. * @see Modifier * @since 3.1 */ public static final int MODIFIER = 83; /** * Node type constant indicating a node of type * <code>UnionType</code>. * @see UnionType * @since 3.7.1 */ public static final int UNION_TYPE = 84; /** * Node type constant indicating a node of type * <code>Dimension</code>. * * @see Dimension * @since 3.10 */ public static final int DIMENSION = 85; /** * Node type constant indicating a node of type * <code>LambdaExpression</code>. * @see LambdaExpression * @since 3.10 */ public static final int LAMBDA_EXPRESSION = 86; /** * Node type constant indicating a node of type * <code>IntersectionType</code>. * * @see IntersectionType * @since 3.10 */ public static final int INTERSECTION_TYPE = 87; /** * Node type constant indicating a node of type * <code>NameQualifiedType</code>. * @see NameQualifiedType * @since 3.10 */ public static final int NAME_QUALIFIED_TYPE = 88; /** * Node type constant indicating a node of type * <code>CreationReference</code>. * @see CreationReference * @since 3.10 */ public static final int CREATION_REFERENCE = 89; /** * Node type constant indicating a node of type * <code>ExpressionMethodReference</code>. * @see ExpressionMethodReference * @since 3.10 */ public static final int EXPRESSION_METHOD_REFERENCE = 90; /** * Node type constant indicating a node of type * <code>SuperMethhodReference</code>. * @see SuperMethodReference * @since 3.10 */ public static final int SUPER_METHOD_REFERENCE = 91; /** * Node type constant indicating a node of type * <code>TypeMethodReference</code>. * @see TypeMethodReference * @since 3.10 */ public static final int TYPE_METHOD_REFERENCE = 92; /** * Returns the node class for the corresponding node type. * * @param nodeType AST node type * @return the corresponding <code>ASTNode</code> subclass * @exception IllegalArgumentException if <code>nodeType</code> is * not a legal AST node type * @see #getNodeType() * @since 3.0 */ public static Class nodeClassForType(int nodeType) { switch (nodeType) { case ANNOTATION_TYPE_DECLARATION : return AnnotationTypeDeclaration.class; case ANNOTATION_TYPE_MEMBER_DECLARATION : return AnnotationTypeMemberDeclaration.class; case ANONYMOUS_CLASS_DECLARATION : return AnonymousClassDeclaration.class; case ARRAY_ACCESS : return ArrayAccess.class; case ARRAY_CREATION : return ArrayCreation.class; case ARRAY_INITIALIZER : return ArrayInitializer.class; case ARRAY_TYPE : return ArrayType.class; case ASSERT_STATEMENT : return AssertStatement.class; case ASSIGNMENT : return Assignment.class; case BLOCK : return Block.class; case BLOCK_COMMENT : return BlockComment.class; case BOOLEAN_LITERAL : return BooleanLiteral.class; case BREAK_STATEMENT : return BreakStatement.class; case CAST_EXPRESSION : return CastExpression.class; case CATCH_CLAUSE : return CatchClause.class; case CHARACTER_LITERAL : return CharacterLiteral.class; case CLASS_INSTANCE_CREATION : return ClassInstanceCreation.class; case COMPILATION_UNIT : return CompilationUnit.class; case CONDITIONAL_EXPRESSION : return ConditionalExpression.class; case CONSTRUCTOR_INVOCATION : return ConstructorInvocation.class; case CONTINUE_STATEMENT : return ContinueStatement.class; case CREATION_REFERENCE : return CreationReference.class; case DIMENSION: return Dimension.class; case DO_STATEMENT : return DoStatement.class; case EMPTY_STATEMENT : return EmptyStatement.class; case ENHANCED_FOR_STATEMENT : return EnhancedForStatement.class; case ENUM_CONSTANT_DECLARATION : return EnumConstantDeclaration.class; case ENUM_DECLARATION : return EnumDeclaration.class; case EXPRESSION_METHOD_REFERENCE : return ExpressionMethodReference.class; case EXPRESSION_STATEMENT : return ExpressionStatement.class; case FIELD_ACCESS : return FieldAccess.class; case FIELD_DECLARATION : return FieldDeclaration.class; case FOR_STATEMENT : return ForStatement.class; case IF_STATEMENT : return IfStatement.class; case IMPORT_DECLARATION : return ImportDeclaration.class; case INFIX_EXPRESSION : return InfixExpression.class; case INITIALIZER : return Initializer.class; case INSTANCEOF_EXPRESSION : return InstanceofExpression.class; case INTERSECTION_TYPE: return IntersectionType.class; case JAVADOC : return Javadoc.class; case LABELED_STATEMENT : return LabeledStatement.class; case LAMBDA_EXPRESSION : return LambdaExpression.class; case LINE_COMMENT : return LineComment.class; case MARKER_ANNOTATION : return MarkerAnnotation.class; case MEMBER_REF : return MemberRef.class; case MEMBER_VALUE_PAIR : return MemberValuePair.class; case METHOD_DECLARATION : return MethodDeclaration.class; case METHOD_INVOCATION : return MethodInvocation.class; case METHOD_REF : return MethodRef.class; case METHOD_REF_PARAMETER : return MethodRefParameter.class; case MODIFIER : return Modifier.class; case NAME_QUALIFIED_TYPE : return NameQualifiedType.class; case NORMAL_ANNOTATION : return NormalAnnotation.class; case NULL_LITERAL : return NullLiteral.class; case NUMBER_LITERAL : return NumberLiteral.class; case PACKAGE_DECLARATION : return PackageDeclaration.class; case PARAMETERIZED_TYPE : return ParameterizedType.class; case PARENTHESIZED_EXPRESSION : return ParenthesizedExpression.class; case POSTFIX_EXPRESSION : return PostfixExpression.class; case PREFIX_EXPRESSION : return PrefixExpression.class; case PRIMITIVE_TYPE : return PrimitiveType.class; case QUALIFIED_NAME : return QualifiedName.class; case QUALIFIED_TYPE : return QualifiedType.class; case RETURN_STATEMENT : return ReturnStatement.class; case SIMPLE_NAME : return SimpleName.class; case SIMPLE_TYPE : return SimpleType.class; case SINGLE_MEMBER_ANNOTATION : return SingleMemberAnnotation.class; case SINGLE_VARIABLE_DECLARATION : return SingleVariableDeclaration.class; case STRING_LITERAL : return StringLiteral.class; case SUPER_CONSTRUCTOR_INVOCATION : return SuperConstructorInvocation.class; case SUPER_FIELD_ACCESS : return SuperFieldAccess.class; case SUPER_METHOD_INVOCATION : return SuperMethodInvocation.class; case SUPER_METHOD_REFERENCE : return SuperMethodReference.class; case SWITCH_CASE: return SwitchCase.class; case SWITCH_STATEMENT : return SwitchStatement.class; case SYNCHRONIZED_STATEMENT : return SynchronizedStatement.class; case TAG_ELEMENT : return TagElement.class; case TEXT_ELEMENT : return TextElement.class; case THIS_EXPRESSION : return ThisExpression.class; case THROW_STATEMENT : return ThrowStatement.class; case TRY_STATEMENT : return TryStatement.class; case TYPE_DECLARATION : return TypeDeclaration.class; case TYPE_DECLARATION_STATEMENT : return TypeDeclarationStatement.class; case TYPE_METHOD_REFERENCE : return TypeMethodReference.class; case TYPE_LITERAL : return TypeLiteral.class; case TYPE_PARAMETER : return TypeParameter.class; case UNION_TYPE : return UnionType.class; case VARIABLE_DECLARATION_EXPRESSION : return VariableDeclarationExpression.class; case VARIABLE_DECLARATION_FRAGMENT : return VariableDeclarationFragment.class; case VARIABLE_DECLARATION_STATEMENT : return VariableDeclarationStatement.class; case WHILE_STATEMENT : return WhileStatement.class; case WILDCARD_TYPE : return WildcardType.class; } throw new IllegalArgumentException(); } /** * Owning AST. * <p> * N.B. This is a private field, but declared as package-visible * for more efficient access from inner classes. * </p> */ final AST ast; /** * Parent AST node, or <code>null</code> if this node is a root. * Initially <code>null</code>. */ private ASTNode parent = null; /** * An unmodifiable empty map (used to implement <code>properties()</code>). */ private static final Map UNMODIFIABLE_EMPTY_MAP = Collections.unmodifiableMap(new HashMap(1)); /** * Primary field used in representing node properties efficiently. * If <code>null</code>, this node has no properties. * If a {@link String}, this is the name of this node's sole property, * and <code>property2</code> contains its value. * If a {@link Map}, this is the table of property name-value * mappings; <code>property2</code>, if non-null is its unmodifiable * equivalent. * Initially <code>null</code>. * * @see #property2 */ private Object property1 = null; /** * Auxiliary field used in representing node properties efficiently. * * @see #property1 */ private Object property2 = null; /** * A character index into the original source string, * or <code>-1</code> if no source position information is available * for this node; <code>-1</code> by default. */ private int startPosition = -1; /** * A character length, or <code>0</code> if no source position * information is recorded for this node; <code>0</code> by default. */ private int length = 0; /** * Flag constant (bit mask, value 1) indicating that there is something * not quite right with this AST node. * <p> * The standard parser (<code>ASTParser</code>) sets this * flag on a node to indicate a syntax error detected in the vicinity. * </p> */ public static final int MALFORMED = 1; /** * Flag constant (bit mask, value 2) indicating that this is a node * that was created by the parser (as opposed to one created by another * party). * <p> * The standard parser (<code>ASTParser</code>) sets this * flag on the nodes it creates. * </p> * @since 3.0 */ public static final int ORIGINAL = 2; /** * Flag constant (bit mask, value 4) indicating that this node * is unmodifiable. When a node is marked unmodifiable, the * following operations result in a runtime exception: * <ul> * <li>Change a simple property of this node.</li> * <li>Add or remove a child node from this node.</li> * <li>Parent (or reparent) this node.</li> * </ul> * <p> * The standard parser (<code>ASTParser</code>) does not set * this flag on the nodes it creates. However, clients may set * this flag on a node to prevent further modification of the * its structural properties. * </p> * @since 3.0 */ public static final int PROTECT = 4; /** * Flag constant (bit mask, value 8) indicating that this node * or a part of this node is recovered from source that contains * a syntax error detected in the vicinity. * <p> * The standard parser (<code>ASTParser</code>) sets this * flag on a node to indicate a recovered node. * </p> * @since 3.2 */ public static final int RECOVERED = 8; /** * int containing the node type in the top 16 bits and * flags in the bottom 16 bits; none set by default. * <p> * N.B. This is a private field, but declared as package-visible * for more efficient access from inner classes. * </p> * * @see #MALFORMED */ int typeAndFlags = 0; /** * Property of parent in which this node is a child, or <code>null</code> * if this node is a root. Initially <code>null</code>. * * @see #getLocationInParent * @since 3.0 */ private StructuralPropertyDescriptor location = null; /** Internal convenience constant indicating that there is definite risk of cycles. * @see ChildPropertyDescriptor#cycleRisk() * @see ChildListPropertyDescriptor#cycleRisk() * @since 3.0 */ static final boolean CYCLE_RISK = true; /** Internal convenience constant indicating that there is no risk of cycles. * @see ChildPropertyDescriptor#cycleRisk() * @see ChildListPropertyDescriptor#cycleRisk() * @since 3.0 */ static final boolean NO_CYCLE_RISK = false; /** Internal convenience constant indicating that a structural property is mandatory. * @since 3.0 */ static final boolean MANDATORY = true; /** Internal convenience constant indicating that a structural property is optional. * @since 3.0 */ static final boolean OPTIONAL = false; /** * A specialized implementation of a list of ASTNodes. The * implementation is based on an ArrayList. */ class NodeList extends AbstractList { /** * The underlying list in which the nodes of this list are * stored (element type: {@link ASTNode}). * <p> * Be stingy on storage - assume that list will be empty. * </p> * <p> * This field declared default visibility (rather than private) * so that accesses from <code>NodeList.Cursor</code> do not require * a synthetic accessor method. * </p> */ ArrayList store = new ArrayList(0); /** * The property descriptor for this list. */ ChildListPropertyDescriptor propertyDescriptor; /** * A cursor for iterating over the elements of the list. * Does not lose its position if the list is changed during * the iteration. */ class Cursor implements Iterator { /** * The position of the cursor between elements. If the value * is N, then the cursor sits between the element at positions * N-1 and N. Initially just before the first element of the * list. */ private int position = 0; /* (non-Javadoc) * Method declared on <code>Iterator</code>. */ public boolean hasNext() { return this.position < NodeList.this.store.size(); } /* (non-Javadoc) * Method declared on <code>Iterator</code>. */ public Object next() { Object result = NodeList.this.store.get(this.position); this.position++; return result; } /* (non-Javadoc) * Method declared on <code>Iterator</code>. */ public void remove() { throw new UnsupportedOperationException(); } /** * Adjusts this cursor to accommodate an add/remove at the given * index. * * @param index the position at which the element was added * or removed * @param delta +1 for add, and -1 for remove */ void update(int index, int delta) { if (this.position > index) { // the cursor has passed the added or removed element this.position += delta; } } } /** * A list of currently active cursors (element type: * {@link Cursor}), or <code>null</code> if there are no * active cursors. * <p> * It is important for storage considerations to maintain the * null-means-empty invariant; otherwise, every NodeList instance * will waste a lot of space. A cursor is needed only for the duration * of a visit to the child nodes. Under normal circumstances, only a * single cursor is needed; multiple cursors are only required if there * are multiple visits going on at the same time. * </p> */ private List cursors = null; /** * Creates a new empty list of nodes owned by this node. * This node will be the common parent of all nodes added to * this list. * * @param property the property descriptor * @since 3.0 */ NodeList(ChildListPropertyDescriptor property) { super(); this.propertyDescriptor = property; } /* (non-javadoc) * @see java.util.AbstractCollection#size() */ public int size() { return this.store.size(); } /* (non-javadoc) * @see AbstractList#get(int) */ public Object get(int index) { return this.store.get(index); } /* (non-javadoc) * @see List#set(int, java.lang.Object) */ public Object set(int index, Object element) { if (element == null) { throw new IllegalArgumentException(); } if ((ASTNode.this.typeAndFlags & PROTECT) != 0) { // this node is protected => cannot gain or lose children throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } // delink old child from parent, and link new child to parent ASTNode newChild = (ASTNode) element; ASTNode oldChild = (ASTNode) this.store.get(index); if (oldChild == newChild) { return oldChild; } if ((oldChild.typeAndFlags & PROTECT) != 0) { // old child is protected => cannot be unparented throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } ASTNode.checkNewChild(ASTNode.this, newChild, this.propertyDescriptor.cycleRisk, this.propertyDescriptor.elementType); ASTNode.this.ast.preReplaceChildEvent(ASTNode.this, oldChild, newChild, this.propertyDescriptor); Object result = this.store.set(index, newChild); // n.b. setParent will call ast.modifying() oldChild.setParent(null, null); newChild.setParent(ASTNode.this, this.propertyDescriptor); ASTNode.this.ast.postReplaceChildEvent(ASTNode.this, oldChild, newChild, this.propertyDescriptor); return result; } /* (non-javadoc) * @see List#add(int, java.lang.Object) */ public void add(int index, Object element) { if (element == null) { throw new IllegalArgumentException(); } if ((ASTNode.this.typeAndFlags & PROTECT) != 0) { // this node is protected => cannot gain or lose children throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } // link new child to parent ASTNode newChild = (ASTNode) element; ASTNode.checkNewChild(ASTNode.this, newChild, this.propertyDescriptor.cycleRisk, this.propertyDescriptor.elementType); ASTNode.this.ast.preAddChildEvent(ASTNode.this, newChild, this.propertyDescriptor); this.store.add(index, element); updateCursors(index, +1); // n.b. setParent will call ast.modifying() newChild.setParent(ASTNode.this, this.propertyDescriptor); ASTNode.this.ast.postAddChildEvent(ASTNode.this, newChild, this.propertyDescriptor); } /* (non-javadoc) * @see List#remove(int) */ public Object remove(int index) { if ((ASTNode.this.typeAndFlags & PROTECT) != 0) { // this node is protected => cannot gain or lose children throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } // delink old child from parent ASTNode oldChild = (ASTNode) this.store.get(index); if ((oldChild.typeAndFlags & PROTECT) != 0) { // old child is protected => cannot be unparented throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } ASTNode.this.ast.preRemoveChildEvent(ASTNode.this, oldChild, this.propertyDescriptor); // n.b. setParent will call ast.modifying() oldChild.setParent(null, null); Object result = this.store.remove(index); updateCursors(index, -1); ASTNode.this.ast.postRemoveChildEvent(ASTNode.this, oldChild, this.propertyDescriptor); return result; } /** * Allocate a cursor to use for a visit. The client must call * <code>releaseCursor</code> when done. * <p> * This method is internally synchronized on this NodeList. * It is thread-safe to create a cursor. * </p> * * @return a new cursor positioned before the first element * of the list */ Cursor newCursor() { synchronized (this) { // serialize cursor management on this NodeList if (this.cursors == null) { // convert null to empty list this.cursors = new ArrayList(1); } Cursor result = new Cursor(); this.cursors.add(result); return result; } } /** * Releases the given cursor at the end of a visit. * <p> * This method is internally synchronized on this NodeList. * It is thread-safe to release a cursor. * </p> * * @param cursor the cursor */ void releaseCursor(Cursor cursor) { synchronized (this) { // serialize cursor management on this NodeList this.cursors.remove(cursor); if (this.cursors.isEmpty()) { // important: convert empty list back to null // otherwise the node will hang on to needless junk this.cursors = null; } } } /** * Adjusts all cursors to accommodate an add/remove at the given * index. * <p> * This method is only used when the list is being modified. * The AST is not thread-safe if any of the clients are modifying it. * </p> * * @param index the position at which the element was added * or removed * @param delta +1 for add, and -1 for remove */ private synchronized void updateCursors(int index, int delta) { if (this.cursors == null) { // there are no cursors to worry about return; } for (Iterator it = this.cursors.iterator(); it.hasNext(); ) { Cursor c = (Cursor) it.next(); c.update(index, delta); } } /** * Returns an estimate of the memory footprint of this node list * instance in bytes. * <ul> * <li>1 object header for the NodeList instance</li> * <li>5 4-byte fields of the NodeList instance</li> * <li>0 for cursors since null unless walk in progress</li> * <li>1 object header for the ArrayList instance</li> * <li>2 4-byte fields of the ArrayList instance</li> * <li>1 object header for an Object[] instance</li> * <li>4 bytes in array for each element</li> * </ul> * * @return the size of this node list in bytes */ int memSize() { int result = HEADERS + 5 * 4; result += HEADERS + 2 * 4; result += HEADERS + 4 * size(); return result; } /** * Returns an estimate of the memory footprint in bytes of this node * list and all its subtrees. * * @return the size of this list of subtrees in bytes */ int listSize() { int result = memSize(); for (Iterator it = iterator(); it.hasNext(); ) { ASTNode child = (ASTNode) it.next(); result += child.treeSize(); } return result; } } /** * Creates a new AST node owned by the given AST. Once established, * the relationship between an AST node and its owning AST does not change * over the lifetime of the node. The new node has no parent node, * and no properties. * <p> * N.B. This constructor is package-private; all subclasses my be * declared in the same package; clients are unable to declare * additional subclasses. * </p> * * @param ast the AST that is to own this node */ ASTNode(AST ast) { if (ast == null) { throw new IllegalArgumentException(); } this.ast = ast; setNodeType(getNodeType0()); setFlags(ast.getDefaultNodeFlag()); // setFlags calls modifying(); } /** * Returns this node's AST. * <p> * Note that the relationship between an AST node and its owing AST does * not change over the lifetime of a node. * </p> * * @return the AST that owns this node */ public final AST getAST() { return this.ast; } /** * Returns this node's parent node, or <code>null</code> if this is the * root node. * <p> * Note that the relationship between an AST node and its parent node * may change over the lifetime of a node. * </p> * * @return the parent of this node, or <code>null</code> if none */ public final ASTNode getParent() { return this.parent; } /** * Returns the location of this node within its parent, * or <code>null</code> if this is a root node. * <p> * <pre> * ASTNode node = ...; * ASTNode parent = node.getParent(); * StructuralPropertyDescriptor location = node.getLocationInParent(); * assert (parent != null) == (location != null); * if ((location != null) && location.isChildProperty()) * assert parent.getStructuralProperty(location) == node; * if ((location != null) && location.isChildListProperty()) * assert ((List) parent.getStructuralProperty(location)).contains(node); * </pre> * </p> * <p> * Note that the relationship between an AST node and its parent node * may change over the lifetime of a node. * </p> * * @return the location of this node in its parent, * or <code>null</code> if this node has no parent * @since 3.0 */ public final StructuralPropertyDescriptor getLocationInParent() { return this.location; } /** * Returns the root node at or above this node; returns this node if * it is a root. * * @return the root node at or above this node */ public final ASTNode getRoot() { ASTNode candidate = this; while (true) { ASTNode p = candidate.getParent(); if (p == null) { // candidate has no parent - that's the guy return candidate; } candidate = p; } } /** * Returns the value of the given structural property for this node. The value * returned depends on the kind of property: * <ul> * <li>{@link SimplePropertyDescriptor} - the value of the given simple property, * or <code>null</code> if none; primitive values are "boxed"</li> * <li>{@link ChildPropertyDescriptor} - the child node (type <code>ASTNode</code>), * or <code>null</code> if none</li> * <li>{@link ChildListPropertyDescriptor} - the list (element type: {@link ASTNode})</li> * </ul> * * @param property the property * @return the value, or <code>null</code> if none * @exception RuntimeException if this node does not have the given property * @since 3.0 */ public final Object getStructuralProperty(StructuralPropertyDescriptor property) { if (property instanceof SimplePropertyDescriptor) { SimplePropertyDescriptor p = (SimplePropertyDescriptor) property; if (p.getValueType() == int.class) { int result = internalGetSetIntProperty(p, true, 0); return new Integer(result); } else if (p.getValueType() == boolean.class) { boolean result = internalGetSetBooleanProperty(p, true, false); return Boolean.valueOf(result); } else { return internalGetSetObjectProperty(p, true, null); } } if (property instanceof ChildPropertyDescriptor) { return internalGetSetChildProperty((ChildPropertyDescriptor) property, true, null); } if (property instanceof ChildListPropertyDescriptor) { return internalGetChildListProperty((ChildListPropertyDescriptor) property); } throw new IllegalArgumentException(); } /** * Sets the value of the given structural property for this node. The value * passed depends on the kind of property: * <ul> * <li>{@link SimplePropertyDescriptor} - the new value of the given simple property, * or <code>null</code> if none; primitive values are "boxed"</li> * <li>{@link ChildPropertyDescriptor} - the new child node (type <code>ASTNode</code>), * or <code>null</code> if none</li> * <li>{@link ChildListPropertyDescriptor} - not allowed</li> * </ul> * * @param property the property * @param value the property value * @exception RuntimeException if this node does not have the * given property, or if the given property cannot be set * @since 3.0 */ public final void setStructuralProperty(StructuralPropertyDescriptor property, Object value) { if (property instanceof SimplePropertyDescriptor) { SimplePropertyDescriptor p = (SimplePropertyDescriptor) property; if (p.getValueType() == int.class) { int arg = ((Integer) value).intValue(); internalGetSetIntProperty(p, false, arg); return; } else if (p.getValueType() == boolean.class) { boolean arg = ((Boolean) value).booleanValue(); internalGetSetBooleanProperty(p, false, arg); return; } else { if (value == null && p.isMandatory()) { throw new IllegalArgumentException(); } internalGetSetObjectProperty(p, false, value); return; } } if (property instanceof ChildPropertyDescriptor) { ChildPropertyDescriptor p = (ChildPropertyDescriptor) property; ASTNode child = (ASTNode) value; if (child == null && p.isMandatory()) { throw new IllegalArgumentException(); } internalGetSetChildProperty(p, false, child); return; } if (property instanceof ChildListPropertyDescriptor) { throw new IllegalArgumentException("Cannot set the list of child list property"); //$NON-NLS-1$ } } /** * Sets the value of the given int-valued property for this node. * The default implementation of this method throws an exception explaining * that this node does not have such a property. This method should be * extended in subclasses that have at least one simple property whose value * type is int. * * @param property the property * @param get <code>true</code> for a get operation, and * <code>false</code> for a set operation * @param value the new property value; ignored for get operations * @return the value; always returns * <code>0</code> for set operations * @exception RuntimeException if this node does not have the * given property, or if the given value cannot be set as specified * @since 3.0 */ int internalGetSetIntProperty(SimplePropertyDescriptor property, boolean get, int value) { throw new RuntimeException("Node does not have this property"); //$NON-NLS-1$ } /** * Sets the value of the given boolean-valued property for this node. * The default implementation of this method throws an exception explaining * that this node does not have such a property. This method should be * extended in subclasses that have at least one simple property whose value * type is boolean. * * @param property the property * @param get <code>true</code> for a get operation, and * <code>false</code> for a set operation * @param value the new property value; ignored for get operations * @return the value; always returns * <code>false</code> for set operations * @exception RuntimeException if this node does not have the * given property, or if the given value cannot be set as specified * @since 3.0 */ boolean internalGetSetBooleanProperty(SimplePropertyDescriptor property, boolean get, boolean value) { throw new RuntimeException("Node does not have this property"); //$NON-NLS-1$ } /** * Sets the value of the given property for this node. * The default implementation of this method throws an exception explaining * that this node does not have such a property. This method should be * extended in subclasses that have at least one simple property whose value * type is a reference type. * * @param property the property * @param get <code>true</code> for a get operation, and * <code>false</code> for a set operation * @param value the new property value, or <code>null</code> if none; * ignored for get operations * @return the value, or <code>null</code> if none; always returns * <code>null</code> for set operations * @exception RuntimeException if this node does not have the * given property, or if the given value cannot be set as specified * @since 3.0 */ Object internalGetSetObjectProperty(SimplePropertyDescriptor property, boolean get, Object value) { throw new RuntimeException("Node does not have this property"); //$NON-NLS-1$ } /** * Sets the child value of the given property for this node. * The default implementation of this method throws an exception explaining * that this node does not have such a property. This method should be * extended in subclasses that have at least one child property. * * @param property the property * @param get <code>true</code> for a get operation, and * <code>false</code> for a set operation * @param child the new child value, or <code>null</code> if none; * always <code>null</code> for get operations * @return the child, or <code>null</code> if none; always returns * <code>null</code> for set operations * @exception RuntimeException if this node does not have the * given property, or if the given child cannot be set as specified * @since 3.0 */ ASTNode internalGetSetChildProperty(ChildPropertyDescriptor property, boolean get, ASTNode child) { throw new RuntimeException("Node does not have this property"); //$NON-NLS-1$ } /** * Returns the list value of the given property for this node. * The default implementation of this method throws an exception explaining * that this node does not have such a property. This method should be * extended in subclasses that have at least one child list property. * * @param property the property * @return the list (element type: {@link ASTNode}) * @exception RuntimeException if the given node does not have the * given property * @since 3.0 */ List internalGetChildListProperty(ChildListPropertyDescriptor property) { throw new RuntimeException("Node does not have this property"); //$NON-NLS-1$ } /** * Returns a list of structural property descriptors for nodes of the * same type as this node. Clients must not modify the result. * <p> * Note that property descriptors are a meta-level mechanism * for manipulating ASTNodes in a generic way. They are * unrelated to <code>get/setProperty</code>. * </p> * * @return a list of property descriptors (element type: * {@link StructuralPropertyDescriptor}) * @since 3.0 */ public final List structuralPropertiesForType() { return internalStructuralPropertiesForType(this.ast.apiLevel); } /** * Returns a list of property descriptors for this node type. * Clients must not modify the result. This abstract method * must be implemented in each concrete AST node type. * <p> * N.B. This method is package-private, so that the implementations * of this method in each of the concrete AST node types do not * clutter up the API doc. * </p> * * @param apiLevel the API level; one of the <code>AST.JLS*</code> constants * @return a list of property descriptors (element type: * {@link StructuralPropertyDescriptor}) * @since 3.0 */ abstract List internalStructuralPropertiesForType(int apiLevel); /** * Internal helper method that starts the building a list of * property descriptors for the given node type. * * @param nodeClass the class for a concrete node type with n properties * @param propertyList empty list, with capacity for n+1 elements */ static void createPropertyList(Class nodeClass, List propertyList) { // stuff nodeClass at head of list for future ref propertyList.add(nodeClass); } /** * Internal helper method that adding a property descriptor. * * @param property the structural property descriptor * @param propertyList list beginning with the AST node class * followed by accumulated structural property descriptors */ static void addProperty(StructuralPropertyDescriptor property, List propertyList) { Class nodeClass = (Class) propertyList.get(0); if (property.getNodeClass() != nodeClass) { // easily made cut-and-paste mistake throw new RuntimeException("Structural property descriptor has wrong node class!"); //$NON-NLS-1$ } propertyList.add(property); } /** * Internal helper method that completes the building of * a node type's structural property descriptor list. * * @param propertyList list beginning with the AST node class * followed by accumulated structural property descriptors * @return unmodifiable list of structural property descriptors * (element type: {@link StructuralPropertyDescriptor}) */ static List reapPropertyList(List propertyList) { propertyList.remove(0); // remove nodeClass // compact ArrayList a = new ArrayList(propertyList.size()); a.addAll(propertyList); return Collections.unmodifiableList(a); } /** * Checks that this AST operation is not used when * building JLS2 level ASTs. * <p> * Use this method to prevent access to new properties that have been added in JLS3. * </p> * * @exception UnsupportedOperationException if this operation is used in a JLS2 AST * @since 3.0 */ final void unsupportedIn2() { if (this.ast.apiLevel == AST.JLS2_INTERNAL) { throw new UnsupportedOperationException("Operation not supported in JLS2 AST"); //$NON-NLS-1$ } } /** * Checks that this AST operation is not used when * building JLS2 or JLS3 level ASTs. * <p> * Use this method to prevent access to new properties that have been added in JLS4. * </p> * * @exception UnsupportedOperationException if this operation is used in a JLS2 or JLS3 AST * @since 3.7 */ final void unsupportedIn2_3() { if (this.ast.apiLevel <= AST.JLS3_INTERNAL) { throw new UnsupportedOperationException("Operation only supported in JLS4 and later AST"); //$NON-NLS-1$ } } /** * Checks that this AST operation is not used when * building JLS2 or JLS3 or JLS4 level ASTs. * <p> * Use this method to prevent access to new properties that have been added in JLS8. * </p> * * @exception UnsupportedOperationException if this operation is used below JLS8 * @since 3.10 */ final void unsupportedIn2_3_4() { if (this.ast.apiLevel < AST.JLS8) { throw new UnsupportedOperationException("Operation only supported in JLS8 and later AST"); //$NON-NLS-1$ } } /** * Checks that this AST operation is only used when * building JLS2 level ASTs. * <p> * Use this method to prevent access to deprecated properties (deprecated in JLS3). * </p> * * @exception UnsupportedOperationException if this operation is used in an AST later than JLS2 * @since 3.0 */ // In API Javadocs, add: * @deprecated In the JLS3 API, this method is replaced by {@link #replacement()}. final void supportedOnlyIn2() { if (this.ast.apiLevel != AST.JLS2_INTERNAL) { throw new UnsupportedOperationException("Operation only supported in JLS2 AST"); //$NON-NLS-1$ } } /** * Checks that this AST operation is only used when * building JLS2, JLS3 or JLS4 level ASTs. * <p> * Use this method to prevent access to deprecated properties (deprecated in JLS8). * </p> * * @exception UnsupportedOperationException if this operation is used in an AST later than JLS4 * @since 3.10 */ // In API Javadocs, add: * @deprecated In the JLS8 API, this method is replaced by {@link #replacement()}. final void supportedOnlyIn2_3_4() { if (this.ast.apiLevel >= AST.JLS8) { throw new UnsupportedOperationException("Operation only supported in JLS2, JLS3 and JLS4 ASTs"); //$NON-NLS-1$ } } /** * Sets or clears this node's parent node and location. * <p> * Note that this method is package-private. The pointer from a node * to its parent is set implicitly as a side effect of inserting or * removing the node as a child of another node. This method calls * <code>ast.modifying()</code>. * </p> * * @param parent the new parent of this node, or <code>null</code> if none * @param property the location of this node in its parent, * or <code>null</code> if <code>parent</code> is <code>null</code> * @see #getLocationInParent * @see #getParent * @since 3.0 */ final void setParent(ASTNode parent, StructuralPropertyDescriptor property) { this.ast.modifying(); this.parent = parent; this.location = property; } /** * Removes this node from its parent. Has no effect if this node * is unparented. If this node appears as an element of a child list * property of its parent, then this node is removed from the * list using <code>List.remove</code>. * If this node appears as the value of a child property of its * parent, then this node is detached from its parent * by passing <code>null</code> to the appropriate setter method; * this operation fails if this node is in a mandatory property. * * @since 3.0 */ public final void delete() { StructuralPropertyDescriptor p = getLocationInParent(); if (p == null) { // node is unparented return; } if (p.isChildProperty()) { getParent().setStructuralProperty(this.location, null); return; } if (p.isChildListProperty()) { List l = (List) getParent().getStructuralProperty(this.location); l.remove(this); } } /** * Checks whether the given new child node is a node * in a different AST from its parent-to-be, whether it is * already has a parent, whether adding it to its * parent-to-be would create a cycle, and whether the child is of * the right type. The parent-to-be is the enclosing instance. * * @param node the parent-to-be node * @param newChild the new child of the parent * @param cycleCheck <code>true</code> if cycles are possible and need * to be checked, <code>false</code> if cycles are impossible and do * not need to be checked * @param nodeType a type constraint on child nodes, or <code>null</code> * if no special check is required * @exception IllegalArgumentException if: * <ul> * <li>the child is null</li> * <li>the node belongs to a different AST</li> * <li>the child has the incorrect node type</li> * <li>the node already has a parent</li> * <li>a cycle in would be created</li> * </ul> */ static void checkNewChild(ASTNode node, ASTNode newChild, boolean cycleCheck, Class nodeType) { if (newChild.ast != node.ast) { // new child is from a different AST throw new IllegalArgumentException(); } if (newChild.getParent() != null) { // new child currently has a different parent throw new IllegalArgumentException(); } if (cycleCheck && newChild == node.getRoot()) { // inserting new child would create a cycle throw new IllegalArgumentException(); } Class childClass = newChild.getClass(); if (nodeType != null && !nodeType.isAssignableFrom(childClass)) { // new child is not of the right type throw new ClassCastException(); } if ((newChild.typeAndFlags & PROTECT) != 0) { // new child node is protected => cannot be parented throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } } /** * Prelude portion of the "3 step program" for replacing the * old child of this node with another node. * Here is the code pattern found in all AST node subclasses: * <pre> * ASTNode oldChild = this.foo; * preReplaceChild(oldChild, newFoo, FOO_PROPERTY); * this.foo = newFoo; * postReplaceChild(oldChild, newFoo, FOO_PROPERTY); * </pre> * The first part (preReplaceChild) does all the precondition checks, * reports pre-delete events, and changes parent links. * The old child is delinked from its parent (making it a root node), * and the new child node is linked to its parent. The new child node * must be a root node in the same AST as its new parent, and must not * be an ancestor of this node. All three nodes must be * modifiable (not PROTECTED). The replace operation must fail * atomically; so it is crucial that all precondition checks * be done before any linking and delinking happens. * The final part (postReplaceChild )reports post-add events. * <p> * This method calls <code>ast.modifying()</code> for the nodes affected. * </p> * * @param oldChild the old child of this node, or <code>null</code> if * there was no old child to replace * @param newChild the new child of this node, or <code>null</code> if * there is no replacement child * @param property the property descriptor of this node describing * the relationship between node and child * @exception RuntimeException if: * <ul> * <li>the node belongs to a different AST</li> * <li>the node already has a parent</li> * <li>a cycle in would be created</li> * <li>any of the nodes involved are unmodifiable</li> * </ul> * @since 3.0 */ final void preReplaceChild(ASTNode oldChild, ASTNode newChild, ChildPropertyDescriptor property) { if ((this.typeAndFlags & PROTECT) != 0) { // this node is protected => cannot gain or lose children throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } if (newChild != null) { checkNewChild(this, newChild, property.cycleRisk, null); } // delink old child from parent if (oldChild != null) { if ((oldChild.typeAndFlags & PROTECT) != 0) { // old child node is protected => cannot be unparented throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } if (newChild != null) { this.ast.preReplaceChildEvent(this, oldChild, newChild, property); } else { this.ast.preRemoveChildEvent(this, oldChild, property); } oldChild.setParent(null, null); } else { if(newChild != null) { this.ast.preAddChildEvent(this, newChild, property); } } // link new child to parent if (newChild != null) { newChild.setParent(this, property); // cannot notify postAddChildEvent until parent is linked to child too } } /** * Postlude portion of the "3 step program" for replacing the * old child of this node with another node. * See {@link #preReplaceChild(ASTNode, ASTNode, ChildPropertyDescriptor)} * for details. * @since 3.0 */ final void postReplaceChild(ASTNode oldChild, ASTNode newChild, ChildPropertyDescriptor property) { // link new child to parent if (newChild != null) { if (oldChild != null) { this.ast.postReplaceChildEvent(this, oldChild, newChild, property); } else { this.ast.postAddChildEvent(this, newChild, property); } } else { this.ast.postRemoveChildEvent(this, oldChild, property); } } /** * Prelude portion of the "3 step program" for changing the * value of a simple property of this node. * Here is the code pattern found in all AST node subclasses: * <pre> * preValueChange(FOO_PROPERTY); * this.foo = newFoo; * postValueChange(FOO_PROPERTY); * </pre> * The first part (preValueChange) does the precondition check * to make sure the node is modifiable (not PROTECTED). * The change operation must fail atomically; so it is crucial * that the precondition checks are done before the field is * hammered. The final part (postValueChange)reports post-change * events. * <p> * This method calls <code>ast.modifying()</code> for the node affected. * </p> * * @param property the property descriptor of this node * @exception RuntimeException if: * <ul> * <li>this node is unmodifiable</li> * </ul> * @since 3.0 */ final void preValueChange(SimplePropertyDescriptor property) { if ((this.typeAndFlags & PROTECT) != 0) { // this node is protected => cannot change value of properties throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } this.ast.preValueChangeEvent(this, property); this.ast.modifying(); } /** * Postlude portion of the "3 step program" for replacing the * old child of this node with another node. * See {@link #preValueChange(SimplePropertyDescriptor)} for details. * @since 3.0 */ final void postValueChange(SimplePropertyDescriptor property) { this.ast.postValueChangeEvent(this, property); } /** * Ensures that this node is modifiable (that is, not marked PROTECTED). * If successful, calls ast.modifying(). * @exception RuntimeException is not modifiable */ final void checkModifiable() { if ((this.typeAndFlags & PROTECT) != 0) { throw new IllegalArgumentException("AST node cannot be modified"); //$NON-NLS-1$ } this.ast.modifying(); } /** * Begin lazy initialization of this node. * Here is the code pattern found in all AST * node subclasses: * <pre> * if (this.foo == null) { * // lazy init must be thread-safe for readers * synchronized (this) { * if (this.foo == null) { * preLazyInit(); * this.foo = ...; // code to create new node * postLazyInit(this.foo, FOO_PROPERTY); * } * } * } * </pre> * @since 3.0 */ final void preLazyInit() { // IMPORTANT: this method is called by readers // ASTNode.this is locked at this point this.ast.disableEvents(); // will turn events back on in postLasyInit } /** * End lazy initialization of this node. * * @param newChild the new child of this node, or <code>null</code> if * there is no replacement child * @param property the property descriptor of this node describing * the relationship between node and child * @since 3.0 */ final void postLazyInit(ASTNode newChild, ChildPropertyDescriptor property) { // IMPORTANT: this method is called by readers // ASTNode.this is locked at this point // newChild is brand new (so no chance of concurrent access) newChild.setParent(this, property); // turn events back on (they were turned off in corresponding preLazyInit) this.ast.reenableEvents(); } /** * Returns the value of the named property of this node, or <code>null</code> if none. * * @param propertyName the property name * @return the property value, or <code>null</code> if none * @see #setProperty(String,Object) */ public final Object getProperty(String propertyName) { if (propertyName == null) { throw new IllegalArgumentException(); } if (this.property1 == null) { // node has no properties at all return null; } if (this.property1 instanceof String) { // node has only a single property if (propertyName.equals(this.property1)) { return this.property2; } else { return null; } } // otherwise node has table of properties Map m = (Map) this.property1; return m.get(propertyName); } /** * Sets the named property of this node to the given value, * or to <code>null</code> to clear it. * <p> * Clients should employ property names that are sufficiently unique * to avoid inadvertent conflicts with other clients that might also be * setting properties on the same node. * </p> * <p> * Note that modifying a property is not considered a modification to the * AST itself. This is to allow clients to decorate existing nodes with * their own properties without jeopardizing certain things (like the * validity of bindings), which rely on the underlying tree remaining static. * </p> * * @param propertyName the property name * @param data the new property value, or <code>null</code> if none * @see #getProperty(String) * @throws IllegalArgumentException if the given property name is <code>null</code> */ public final void setProperty(String propertyName, Object data) { if (propertyName == null) { throw new IllegalArgumentException(); } // N.B. DO NOT CALL ast.modifying(); if (this.property1 == null) { // node has no properties at all if (data == null) { // we already know this return; } // node gets its fist property this.property1 = propertyName; this.property2 = data; return; } if (this.property1 instanceof String) { // node has only a single property if (propertyName.equals(this.property1)) { // we're in luck if (data == null) { // just deleted last property this.property1 = null; this.property2 = null; } else { this.property2 = data; } return; } if (data == null) { // we already know this return; } // node already has one property - getting its second // convert to more flexible representation Map m = new HashMap(3); m.put(this.property1, this.property2); m.put(propertyName, data); this.property1 = m; this.property2 = null; return; } // node has two or more properties Map m = (Map) this.property1; if (data == null) { m.remove(propertyName); // check for just one property left if (m.size() == 1) { // convert to more efficient representation Map.Entry[] entries = (Map.Entry[]) m.entrySet().toArray(new Map.Entry[1]); this.property1 = entries[0].getKey(); this.property2 = entries[0].getValue(); } return; } else { m.put(propertyName, data); // still has two or more properties return; } } /** * Returns an unmodifiable table of the properties of this node with * non-<code>null</code> values. * * @return the table of property values keyed by property name * (key type: <code>String</code>; value type: <code>Object</code>) */ public final Map properties() { if (this.property1 == null) { // node has no properties at all return UNMODIFIABLE_EMPTY_MAP; } if (this.property1 instanceof String) { // node has a single property return Collections.singletonMap(this.property1, this.property2); } // node has two or more properties if (this.property2 == null) { this.property2 = Collections.unmodifiableMap((Map) this.property1); } // property2 is unmodifiable wrapper for map in property1 return (Map) this.property2; } /** * Returns the flags associated with this node. * <p> * No flags are associated with newly created nodes. * </p> * <p> * The flags are the bitwise-or of individual flags. * The following flags are currently defined: * <ul> * <li>{@link #MALFORMED} - indicates node is syntactically * malformed</li> * <li>{@link #ORIGINAL} - indicates original node * created by ASTParser</li> * <li>{@link #PROTECT} - indicates node is protected * from further modification</li> * <li>{@link #RECOVERED} - indicates node or a part of this node * is recovered from source that contains a syntax error</li> * </ul> * Other bit positions are reserved for future use. * </p> * * @return the bitwise-or of individual flags * @see #setFlags(int) */ public final int getFlags() { return this.typeAndFlags & 0xFFFF; } /** * Sets the flags associated with this node to the given value. * <p> * The flags are the bitwise-or of individual flags. * The following flags are currently defined: * <ul> * <li>{@link #MALFORMED} - indicates node is syntactically * malformed</li> * <li>{@link #ORIGINAL} - indicates original node * created by ASTParser</li> * <li>{@link #PROTECT} - indicates node is protected * from further modification</li> * <li>{@link #RECOVERED} - indicates node or a part of this node * is recovered from source that contains a syntax error</li> * </ul> * Other bit positions are reserved for future use. * </p> * <p> * Note that the flags are <em>not</em> considered a structural * property of the node, and can be changed even if the * node is marked as protected. * </p> * * @param flags the bitwise-or of individual flags * @see #getFlags() */ public final void setFlags(int flags) { this.ast.modifying(); int old = this.typeAndFlags & 0xFFFF0000; this.typeAndFlags = old | (flags & 0xFFFF); } /** * Returns an integer value identifying the type of this concrete AST node. * The values are small positive integers, suitable for use in switch statements. * <p> * For each concrete node type there is a unique node type constant (name * and value). The unique node type constant for a concrete node type such as * <code>CastExpression</code> is <code>ASTNode.CAST_EXPRESSION</code>. * </p> * * @return one of the node type constants */ public final int getNodeType() { return this.typeAndFlags >>> 16; } /** * Sets the integer value identifying the type of this concrete AST node. * The values are small positive integers, suitable for use in switch statements. * * @param nodeType one of the node type constants */ private void setNodeType(int nodeType) { int old = this.typeAndFlags & 0xFFFF0000; this.typeAndFlags = old | (nodeType << 16); } /** * Returns an integer value identifying the type of this concrete AST node. * <p> * This internal method is implemented in each of the * concrete node subclasses. * </p> * * @return one of the node type constants */ abstract int getNodeType0(); /** * The <code>ASTNode</code> implementation of this <code>Object</code> * method uses object identity (==). Use <code>subtreeMatch</code> to * compare two subtrees for equality. * * @param obj {@inheritDoc} * @return {@inheritDoc} * @see #subtreeMatch(ASTMatcher matcher, Object other) */ public final boolean equals(Object obj) { return this == obj; // equivalent to Object.equals } /* * (non-Javadoc) * This makes it consistent with the fact that a equals methods has been provided. * @see java.lang.Object#hashCode() */ public final int hashCode() { return super.hashCode(); } /** * Returns whether the subtree rooted at the given node matches the * given other object as decided by the given matcher. * * @param matcher the matcher * @param other the other object, or <code>null</code> * @return <code>true</code> if the subtree matches, or * <code>false</code> if they do not match */ public final boolean subtreeMatch(ASTMatcher matcher, Object other) { return subtreeMatch0(matcher, other); } /** * Returns whether the subtree rooted at the given node matches the * given other object as decided by the given matcher. * <p> * This internal method is implemented in each of the * concrete node subclasses. * </p> * * @param matcher the matcher * @param other the other object, or <code>null</code> * @return <code>true</code> if the subtree matches, or * <code>false</code> if they do not match */ abstract boolean subtreeMatch0(ASTMatcher matcher, Object other); /** * Returns a deep copy of the subtree of AST nodes rooted at the * given node. The resulting nodes are owned by the given AST, * which may be different from the ASTs of the given node. * Even if the given node has a parent, the result node will be unparented. * <p> * Source range information on the original nodes is automatically copied to the new * nodes. Client properties (<code>properties</code>) are not carried over. * </p> * <p> * The node's <code>AST</code> and the target <code>AST</code> must support * the same API level. * </p> * * @param target the AST that is to own the nodes in the result * @param node the node to copy, or <code>null</code> if none * @return the copied node, or <code>null</code> if <code>node</code> * is <code>null</code> */ public static ASTNode copySubtree(AST target, ASTNode node) { if (node == null) { return null; } if (target == null) { throw new IllegalArgumentException(); } if (target.apiLevel() != node.getAST().apiLevel()) { throw new UnsupportedOperationException(); } ASTNode newNode = node.clone(target); return newNode; } /** * Returns a deep copy of the subtrees of AST nodes rooted at the * given list of nodes. The resulting nodes are owned by the given AST, * which may be different from the ASTs of the nodes in the list. * Even if the nodes in the list have parents, the nodes in the result * will be unparented. * <p> * Source range information on the original nodes is automatically copied to the new * nodes. Client properties (<code>properties</code>) are not carried over. * </p> * * @param target the AST that is to own the nodes in the result * @param nodes the list of nodes to copy * (element type: {@link ASTNode}) * @return the list of copied subtrees * (element type: {@link ASTNode}) */ public static List copySubtrees(AST target, List nodes) { List result = new ArrayList(nodes.size()); for (Iterator it = nodes.iterator(); it.hasNext(); ) { ASTNode oldNode = (ASTNode) it.next(); ASTNode newNode = oldNode.clone(target); result.add(newNode); } return result; } /** * Returns a deep copy of the subtree of AST nodes rooted at this node. * The resulting nodes are owned by the given AST, which may be different * from the AST of this node. Even if this node has a parent, the * result node will be unparented. * <p> * This method reports pre- and post-clone events, and dispatches * to <code>clone0(AST)</code> which is reimplemented in node subclasses. * </p> * * @param target the AST that is to own the nodes in the result * @return the root node of the copies subtree */ final ASTNode clone(AST target) { this.ast.preCloneNodeEvent(this); ASTNode c = clone0(target); this.ast.postCloneNodeEvent(this, c); return c; } /** * Returns a deep copy of the subtree of AST nodes rooted at this node. * The resulting nodes are owned by the given AST, which may be different * from the AST of this node. Even if this node has a parent, the * result node will be unparented. * <p> * This method must be implemented in subclasses. * </p> * <p> * General template for implementation on each concrete ASTNode class: * <pre> * <code> * ConcreteNodeType result = new ConcreteNodeType(target); * result.setSourceRange(getStartPosition(), getLength()); * result.setChildProperty( * (ChildPropertyType) ASTNode.copySubtree(target, getChildProperty())); * result.setSimpleProperty(isSimpleProperty()); * result.childrenProperty().addAll( * ASTNode.copySubtrees(target, childrenProperty())); * return result; * </code> * </pre> * </p> * <p> * This method does not report pre- and post-clone events. * All callers should instead call <code>clone(AST)</code> * to ensure that pre- and post-clone events are reported. * </p> * <p> * N.B. This method is package-private, so that the implementations * of this method in each of the concrete AST node types do not * clutter up the API doc. * </p> * * @param target the AST that is to own the nodes in the result * @return the root node of the copies subtree */ abstract ASTNode clone0(AST target); /** * Accepts the given visitor on a visit of the current node. * * @param visitor the visitor object * @exception IllegalArgumentException if the visitor is null */ public final void accept(ASTVisitor visitor) { if (visitor == null) { throw new IllegalArgumentException(); } // begin with the generic pre-visit if (visitor.preVisit2(this)) { // dynamic dispatch to internal method for type-specific visit/endVisit accept0(visitor); } // end with the generic post-visit visitor.postVisit(this); } /** * Accepts the given visitor on a type-specific visit of the current node. * This method must be implemented in all concrete AST node types. * <p> * General template for implementation on each concrete ASTNode class: * <pre> * <code> * boolean visitChildren = visitor.visit(this); * if (visitChildren) { * // visit children in normal left to right reading order * acceptChild(visitor, getProperty1()); * acceptChildren(visitor, this.rawListProperty); * acceptChild(visitor, getProperty2()); * } * visitor.endVisit(this); * </code> * </pre> * Note that the caller (<code>accept</code>) take cares of invoking * <code>visitor.preVisit(this)</code> and <code>visitor.postVisit(this)</code>. * </p> * * @param visitor the visitor object */ abstract void accept0(ASTVisitor visitor); /** * Accepts the given visitor on a visit of the current node. * <p> * This method should be used by the concrete implementations of * <code>accept0</code> to traverse optional properties. Equivalent * to <code>child.accept(visitor)</code> if <code>child</code> * is not <code>null</code>. * </p> * * @param visitor the visitor object * @param child the child AST node to dispatch too, or <code>null</code> * if none */ final void acceptChild(ASTVisitor visitor, ASTNode child) { if (child == null) { return; } child.accept(visitor); } /** * Accepts the given visitor on a visit of the given live list of * child nodes. * <p> * This method must be used by the concrete implementations of * <code>accept</code> to traverse list-values properties; it * encapsulates the proper handling of on-the-fly changes to the list. * </p> * * @param visitor the visitor object * @param children the child AST node to dispatch too, or <code>null</code> * if none */ final void acceptChildren(ASTVisitor visitor, ASTNode.NodeList children) { // use a cursor to keep track of where we are up to // (the list may be changing under foot) NodeList.Cursor cursor = children.newCursor(); try { while (cursor.hasNext()) { ASTNode child = (ASTNode) cursor.next(); child.accept(visitor); } } finally { children.releaseCursor(cursor); } } /** * Returns the character index into the original source file indicating * where the source fragment corresponding to this node begins. * <p> * The parser supplies useful well-defined source ranges to the nodes it creates. * See {@link ASTParser#setKind(int)} for details * on precisely where source ranges begin and end. * </p> * * @return the 0-based character index, or <code>-1</code> * if no source position information is recorded for this node * @see #getLength() * @see ASTParser */ public final int getStartPosition() { return this.startPosition; } /** * Returns the length in characters of the original source file indicating * where the source fragment corresponding to this node ends. * <p> * The parser supplies useful well-defined source ranges to the nodes it creates. * See {@link ASTParser#setKind(int)} methods for details * on precisely where source ranges begin and end. * </p> * * @return a (possibly 0) length, or <code>0</code> * if no source position information is recorded for this node * @see #getStartPosition() * @see ASTParser */ public final int getLength() { return this.length; } /** * Sets the source range of the original source file where the source * fragment corresponding to this node was found. * <p> * See {@link ASTParser#setKind(int)} for details * on precisely where source ranges are supposed to begin and end. * </p> * * @param startPosition a 0-based character index, * or <code>-1</code> if no source position information is * available for this node * @param length a (possibly 0) length, * or <code>0</code> if no source position information is recorded * for this node * @see #getStartPosition() * @see #getLength() * @see ASTParser */ public final void setSourceRange(int startPosition, int length) { if (startPosition >= 0 && length < 0) { throw new IllegalArgumentException(); } if (startPosition < 0 && length != 0) { throw new IllegalArgumentException(); } // source positions are not considered a structural property // but we protect them nevertheless checkModifiable(); this.startPosition = startPosition; this.length = length; } /** * Returns a string representation of this node suitable for debugging * purposes only. * * @return a debug string */ public final String toString() { StringBuffer buffer = new StringBuffer(); int p = buffer.length(); try { appendDebugString(buffer); } catch (RuntimeException e) { // since debugger sometimes call toString methods, problems can easily happen when // toString is called on an instance that is being initialized buffer.setLength(p); buffer.append("!"); //$NON-NLS-1$ buffer.append(standardToString()); } return buffer.toString(); } /** * Returns the string representation of this node produced by the standard * <code>Object.toString</code> method. * * @return a debug string */ final String standardToString() { return super.toString(); } /** * Appends a debug representation of this node to the given string buffer. * <p> * The <code>ASTNode</code> implementation of this method prints out the entire * subtree. Subclasses may override to provide a more succinct representation. * </p> * * @param buffer the string buffer to append to */ void appendDebugString(StringBuffer buffer) { // print the subtree by default appendPrintString(buffer); } /** * Appends a standard Java source code representation of this subtree to the given * string buffer. * * @param buffer the string buffer to append to */ final void appendPrintString(StringBuffer buffer) { NaiveASTFlattener printer = new NaiveASTFlattener(); accept(printer); buffer.append(printer.getResult()); } /** * Estimate of size of an object header in bytes. */ static final int HEADERS = 12; /** * Approximate base size of an AST node instance in bytes, * including object header and instance fields. * That is, HEADERS + (# instance vars in ASTNode)*4. */ static final int BASE_NODE_SIZE = HEADERS + 7 * 4; /** * Returns an estimate of the memory footprint, in bytes, * of the given string. * * @param string the string to measure, or <code>null</code> * @return the size of this string object in bytes, or * 0 if the string is <code>null</code> * @since 3.0 */ static int stringSize(String string) { int size = 0; if (string != null) { // Strings usually have 4 instance fields, one of which is a char[] size += HEADERS + 4 * 4; // char[] has 2 bytes per character size += HEADERS + 2 * string.length(); } return size; } /** * Returns an estimate of the memory footprint in bytes of the entire * subtree rooted at this node. * * @return the size of this subtree in bytes */ public final int subtreeBytes() { return treeSize(); } /** * Returns an estimate of the memory footprint in bytes of the entire * subtree rooted at this node. * <p> * N.B. This method is package-private, so that the implementations * of this method in each of the concrete AST node types do not * clutter up the API doc. * </p> * * @return the size of this subtree in bytes */ abstract int treeSize(); /** * Returns an estimate of the memory footprint of this node in bytes. * The estimate does not include the space occupied by child nodes. * * @return the size of this node in bytes */ abstract int memSize(); }