blob: 7ba3ac5de441d8ea08d8f07d241516d4fea6ffb5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
* bug "inline method - doesn't handle implicit cast" (see
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
*******************************************************************************/
package org.eclipse.jdt.internal.corext.dom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.IScanner;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.internal.corext.Assert;
import org.eclipse.jdt.internal.corext.SourceRange;
import org.eclipse.jdt.internal.corext.textmanipulation.TextBuffer;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.ui.JavaPlugin;
public class ASTNodes {
public static final int NODE_ONLY= 0;
public static final int INCLUDE_FIRST_PARENT= 1;
public static final int INCLUDE_ALL_PARENTS= 2;
public static final int WARINING= 1 << 0;
public static final int ERROR= 1 << 1;
public static final int PROBLEMS= WARINING | ERROR;
private static final Message[] EMPTY_MESSAGES= new Message[0];
private static final IProblem[] EMPTY_PROBLEMS= new IProblem[0];
private static final int CLEAR_VISIBILITY= ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
private static class ChildrenCollector extends GenericVisitor {
public List result;
public ChildrenCollector() {
result= null;
}
protected boolean visitNode(ASTNode node) {
if (result == null) { // first visitNode: on the node's parent: do nothing, return true
result= new ArrayList();
return true;
}
result.add(node);
return false;
}
}
private static class ListFinder extends ASTVisitor {
public List result;
private ASTNode fNode;
public ListFinder(ASTNode node) {
fNode= node;
}
public boolean visit(AnonymousClassDeclaration node) {
test(node.bodyDeclarations());
return false;
}
public boolean visit(ArrayCreation node) {
test(node.dimensions());
return false;
}
public boolean visit(ArrayInitializer node) {
test(node.expressions());
return false;
}
public boolean visit(Block node) {
test(node.statements());
return false;
}
public boolean visit(ClassInstanceCreation node) {
test(node.arguments());
return false;
}
public boolean visit(CompilationUnit node) {
test(node.imports());
test(node.types());
return false;
}
public boolean visit(ConstructorInvocation node) {
test(node.arguments());
return false;
}
public boolean visit(FieldDeclaration node) {
test(node.fragments());
return false;
}
public boolean visit(ForStatement node) {
test(node.initializers());
test(node.updaters());
return false;
}
public boolean visit(MethodDeclaration node) {
test(node.parameters());
test(node.thrownExceptions());
return false;
}
public boolean visit(MethodInvocation node) {
test(node.arguments());
return false;
}
public boolean visit(SuperConstructorInvocation node) {
test(node.arguments());
return false;
}
public boolean visit(SuperMethodInvocation node) {
test(node.arguments());
return false;
}
public boolean visit(SwitchStatement node) {
test(node.statements());
return false;
}
public boolean visit(TryStatement node) {
test(node.catchClauses());
return false;
}
public boolean visit(TypeDeclaration node) {
test(node.bodyDeclarations());
test(node.superInterfaces());
return false;
}
public boolean visit(VariableDeclarationExpression node) {
test(node.fragments());
return false;
}
public boolean visit(VariableDeclarationStatement node) {
test(node.fragments());
return false;
}
private void test(List nodes) {
if (nodes.contains(fNode)) {
result= nodes;
}
}
}
private ASTNodes() {
// no instance;
}
public static String asString(ASTNode node) {
ASTFlattener flattener= new ASTFlattener();
node.accept(flattener);
return flattener.getResult();
}
public static String asFormattedString(ASTNode node, int indent, String lineDelim) {
String unformatted= asString(node);
TextEdit edit= CodeFormatterUtil.format2(node, unformatted, indent, lineDelim, null);
if (edit != null) {
return CodeFormatterUtil.evaluateFormatterEdit(unformatted, edit, null);
}
return unformatted; // unknown node
}
/**
* Returns the list that contains the given ASTNode. If the node
* isn't part of any list, <code>null</code> is returned.
*
* @param node the node in question
* @return the list that contains the node or <code>null</code>
*/
public static List getContainingList(ASTNode node) {
if (node.getParent() == null)
return null;
ListFinder finder= new ListFinder(node);
node.getParent().accept(finder);
return finder.result;
}
/**
* Returns a list of the direct chidrens of a node. The siblings are ordered by start offset.
* @param node
* @return
*/
public static List getChildren(ASTNode node) {
ChildrenCollector visitor= new ChildrenCollector();
node.accept(visitor);
return visitor.result;
}
/**
* Returns the element type. This is a convenience method that returns its
* argument if it is a simple type and the element type if the parameter is an array type.
* @param type The type to get the element type from.
* @return The element type of the type or the type itself.
*/
public static Type getElementType(Type type) {
if (! type.isArrayType())
return type;
return ((ArrayType)type).getElementType();
}
public static ASTNode findDeclaration(IBinding binding, ASTNode root) {
root= root.getRoot();
if (root instanceof CompilationUnit) {
return ((CompilationUnit)root).findDeclaringNode(binding);
}
return null;
}
public static VariableDeclaration findVariableDeclaration(IVariableBinding binding, ASTNode root) {
if (binding.isField())
return null;
ASTNode result= findDeclaration(binding, root);
if (result instanceof VariableDeclaration)
return (VariableDeclaration)result;
return null;
}
/**
* Returns the type node for the given declaration. The returned node
* is a copy and is owned by a different AST. The returned node contains
* any extra dimensions.
* @param ast The AST to create the resulting type with.
* @param declaration The variable declaration to get the type from
* @return A new type node created with the given AST.
*/
public static Type getType(AST ast, VariableDeclaration declaration) {
Type type= null;
if (declaration instanceof SingleVariableDeclaration) {
type= ((SingleVariableDeclaration)declaration).getType();
} else if (declaration instanceof VariableDeclarationFragment) {
ASTNode parent= ((VariableDeclarationFragment)declaration).getParent();
if (parent instanceof VariableDeclarationExpression)
type= ((VariableDeclarationExpression)parent).getType();
else if (parent instanceof VariableDeclarationStatement)
type= ((VariableDeclarationStatement)parent).getType();
}
if (type == null)
return null;
type= (Type) ASTNode.copySubtree(ast, type);
int extraDim= 0;
if (declaration.getNodeType() == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
extraDim= ((VariableDeclarationFragment)declaration).getExtraDimensions();
} else if (declaration.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION) {
extraDim= ((SingleVariableDeclaration)declaration).getExtraDimensions();
}
for (int i= 0; i < extraDim; i++) {
type= ast.newArrayType(type);
}
return type;
}
public static int getModifiers(VariableDeclaration declaration) {
Assert.isNotNull(declaration);
if (declaration instanceof SingleVariableDeclaration) {
return ((SingleVariableDeclaration)declaration).getModifiers();
} else if (declaration instanceof VariableDeclarationFragment) {
ASTNode parent= declaration.getParent();
if (parent instanceof VariableDeclarationExpression)
return ((VariableDeclarationExpression)parent).getModifiers();
else if (parent instanceof VariableDeclarationStatement)
return ((VariableDeclarationStatement)parent).getModifiers();
}
return 0;
}
public static boolean isSingleDeclaration(VariableDeclaration declaration) {
Assert.isNotNull(declaration);
if (declaration instanceof SingleVariableDeclaration) {
return true;
} else if (declaration instanceof VariableDeclarationFragment) {
ASTNode parent= declaration.getParent();
if (parent instanceof VariableDeclarationExpression)
return ((VariableDeclarationExpression)parent).fragments().size() == 1;
else if (parent instanceof VariableDeclarationStatement)
return ((VariableDeclarationStatement)parent).fragments().size() == 1;
}
return false;
}
public static boolean isLiteral(Expression expression) {
int type= expression.getNodeType();
return type == ASTNode.BOOLEAN_LITERAL || type == ASTNode.CHARACTER_LITERAL || type == ASTNode.NULL_LITERAL ||
type == ASTNode.NUMBER_LITERAL || type == ASTNode.STRING_LITERAL || type == ASTNode.TYPE_LITERAL;
}
public static boolean isLabel(SimpleName name) {
int parentType= name.getParent().getNodeType();
return parentType == ASTNode.LABELED_STATEMENT ||
parentType == ASTNode.BREAK_STATEMENT || parentType != ASTNode.CONTINUE_STATEMENT;
}
public static boolean isStatic(BodyDeclaration declaration) {
switch(declaration.getNodeType()) {
case ASTNode.FIELD_DECLARATION:
return Modifier.isStatic(((FieldDeclaration)declaration).getModifiers());
case ASTNode.METHOD_DECLARATION:
return Modifier.isStatic(((MethodDeclaration)declaration).getModifiers());
case ASTNode.TYPE_DECLARATION:
return Modifier.isStatic(((TypeDeclaration)declaration).getModifiers());
case ASTNode.INITIALIZER:
return Modifier.isStatic(((Initializer)declaration).getModifiers());
}
return false;
}
public static String getTypeName(Type type) {
final StringBuffer buffer= new StringBuffer();
ASTVisitor visitor= new ASTVisitor() {
public boolean visit(PrimitiveType node) {
buffer.append(node.getPrimitiveTypeCode().toString());
return false;
}
public boolean visit(SimpleName node) {
buffer.append(node.getIdentifier());
return false;
}
public boolean visit(QualifiedName node) {
buffer.append(node.getName().getIdentifier());
return false;
}
public void endVisit(ArrayType node) {
buffer.append("[]"); //$NON-NLS-1$
}
};
type.accept(visitor);
return buffer.toString();
}
public static InfixExpression.Operator convertToInfixOperator(Assignment.Operator operator) {
if (operator.equals(Assignment.Operator.PLUS_ASSIGN))
return InfixExpression.Operator.PLUS;
if (operator.equals(Assignment.Operator.MINUS_ASSIGN))
return InfixExpression.Operator.MINUS;
if (operator.equals(Assignment.Operator.TIMES_ASSIGN))
return InfixExpression.Operator.TIMES;
if (operator.equals(Assignment.Operator.DIVIDE_ASSIGN))
return InfixExpression.Operator.DIVIDE;
if (operator.equals(Assignment.Operator.BIT_AND_ASSIGN))
return InfixExpression.Operator.AND;
if (operator.equals(Assignment.Operator.BIT_OR_ASSIGN))
return InfixExpression.Operator.OR;
if (operator.equals(Assignment.Operator.BIT_XOR_ASSIGN))
return InfixExpression.Operator.XOR;
if (operator.equals(Assignment.Operator.REMAINDER_ASSIGN))
return InfixExpression.Operator.REMAINDER;
if (operator.equals(Assignment.Operator.LEFT_SHIFT_ASSIGN))
return InfixExpression.Operator.LEFT_SHIFT;
if (operator.equals(Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN))
return InfixExpression.Operator.RIGHT_SHIFT_SIGNED;
if (operator.equals(Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN))
return InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED;
Assert.isTrue(false, "Cannot convert assignment operator"); //$NON-NLS-1$
return null;
}
public static boolean needsParentheses(Expression expression) {
int type= expression.getNodeType();
return type == ASTNode.INFIX_EXPRESSION || type == ASTNode.CONDITIONAL_EXPRESSION ||
type == ASTNode.PREFIX_EXPRESSION || type == ASTNode.POSTFIX_EXPRESSION ||
type == ASTNode.CAST_EXPRESSION;
}
public static boolean substituteMustBeParenthesized(Expression substitute, Expression location) {
if (!needsParentheses(substitute))
return false;
ASTNode parent= location.getParent();
if (parent instanceof VariableDeclarationFragment){
VariableDeclarationFragment vdf= (VariableDeclarationFragment)parent;
if (vdf.getInitializer().equals(location))
return false;
} else if (parent instanceof MethodInvocation){
MethodInvocation mi= (MethodInvocation)parent;
if (mi.arguments().contains(location))
return false;
} else if (parent instanceof ReturnStatement)
return false;
return true;
}
public static ASTNode getParent(ASTNode node, Class parentClass) {
do {
node= node.getParent();
} while (node != null && !parentClass.isInstance(node));
return node;
}
public static ASTNode getParent(ASTNode node, int nodeType) {
do {
node= node.getParent();
} while (node != null && node.getNodeType() != nodeType);
return node;
}
public static boolean isParent(ASTNode node, ASTNode parent) {
Assert.isNotNull(parent);
do {
node= node.getParent();
if (node == parent)
return true;
} while (node != null);
return false;
}
public static int getExclusiveEnd(ASTNode node){
return node.getStartPosition() + node.getLength();
}
public static int getInclusiveEnd(ASTNode node){
return node.getStartPosition() + node.getLength() - 1;
}
public static IMethodBinding getMethodBinding(Name node) {
IBinding binding= node.resolveBinding();
if (binding instanceof IMethodBinding)
return (IMethodBinding)binding;
return null;
}
public static IVariableBinding getVariableBinding(Name node) {
IBinding binding= node.resolveBinding();
if (binding instanceof IVariableBinding)
return (IVariableBinding)binding;
return null;
}
public static IVariableBinding getLocalVariableBinding(Name node) {
IVariableBinding result= getVariableBinding(node);
if (result == null || result.isField())
return null;
return result;
}
public static IVariableBinding getFieldBinding(Name node) {
IVariableBinding result= getVariableBinding(node);
if (result == null || !result.isField())
return null;
return result;
}
public static ITypeBinding getTypeBinding(Name node) {
IBinding binding= node.resolveBinding();
if (binding instanceof ITypeBinding)
return (ITypeBinding)binding;
return null;
}
/**
* Returns the receiver's type binding of the given method invocation.
*
* @param invocation method invocation to resolve type of
* @return the type binding of the receiver
*/
public static ITypeBinding getReceiverTypeBinding(MethodInvocation invocation) {
ITypeBinding result= null;
Expression exp= invocation.getExpression();
if(exp != null) {
return exp.resolveTypeBinding();
}
else {
TypeDeclaration type= (TypeDeclaration)getParent(invocation, ASTNode.TYPE_DECLARATION);
if (type != null)
return type.resolveBinding();
}
return result;
}
public static ITypeBinding getDeclaringType(BodyDeclaration declaration) {
ASTNode node= declaration;
while(node != null) {
switch(node.getNodeType()) {
case ASTNode.TYPE_DECLARATION:
return ((TypeDeclaration)node).resolveBinding();
case ASTNode.ANONYMOUS_CLASS_DECLARATION:
return ((AnonymousClassDeclaration)node).resolveBinding();
}
node= node.getParent();
}
return null;
}
/**
* Expands the range of the node passed in <code>nodes</code> to cover all comments
* determined by <code>start</code> and <code>length</code> in the given text buffer.
* @param nodes
* @param buffer
* @param start
* @param length
* @throws CoreException
*/
public static void expandRange(ASTNode[] nodes, TextBuffer buffer, int start, int length) throws CoreException {
IScanner scanner= ToolFactory.createScanner(true, false, false, false);
scanner.setSource(buffer.getContent(start, length).toCharArray());
TokenScanner tokenizer= new TokenScanner(scanner);
ASTNode node= nodes[0];
int pos= tokenizer.getNextStartOffset(0, false);
int newStart= start + pos;
if (newStart < node.getStartPosition())
node.setSourceRange(newStart, node.getLength() + node.getStartPosition() - newStart);
node= nodes[nodes.length - 1];
int scannerStart= node.getStartPosition() + node.getLength() - start;
pos= scannerStart;
try {
while (true) {
pos= tokenizer.getNextEndOffset(pos, false);
}
} catch (CoreException e) {
}
node.setSourceRange(node.getStartPosition(), node.getLength() + pos - scannerStart);
}
public static IProblem[] getProblems(ASTNode node, int scope, int severity) {
ASTNode root= node.getRoot();
if (!(root instanceof CompilationUnit))
return EMPTY_PROBLEMS;
IProblem[] problems= ((CompilationUnit)root).getProblems();
if (root == node)
return problems;
final int iterations= computeIterations(scope);
List result= new ArrayList(5);
for (int i= 0; i < problems.length; i++) {
IProblem problem= problems[i];
boolean consider= false;
if ((severity & PROBLEMS) == PROBLEMS)
consider= true;
else if ((severity & WARINING) != 0)
consider= problem.isWarning();
else if ((severity & ERROR) != 0)
consider= problem.isError();
if (consider) {
ASTNode temp= node;
int count= iterations;
do {
int nodeOffset= temp.getStartPosition();
int problemOffset= problem.getSourceStart();
if (nodeOffset <= problemOffset && problemOffset < nodeOffset + temp.getLength()) {
result.add(problem);
count= 0;
} else {
count--;
}
} while ((temp= temp.getParent()) != null && count > 0);
}
}
return (IProblem[]) result.toArray(new IProblem[result.size()]);
}
public static Message[] getMessages(ASTNode node, int flags) {
ASTNode root= node.getRoot();
if (!(root instanceof CompilationUnit))
return EMPTY_MESSAGES;
Message[] messages= ((CompilationUnit)root).getMessages();
if (root == node)
return messages;
final int iterations= computeIterations(flags);
List result= new ArrayList(5);
for (int i= 0; i < messages.length; i++) {
Message message= messages[i];
ASTNode temp= node;
int count= iterations;
do {
int nodeOffset= temp.getStartPosition();
int messageOffset= message.getStartPosition();
if (nodeOffset <= messageOffset && messageOffset < nodeOffset + temp.getLength()) {
result.add(message);
count= 0;
} else {
count--;
}
} while ((temp= temp.getParent()) != null && count > 0);
}
return (Message[]) result.toArray(new Message[result.size()]);
}
private static int computeIterations(int flags) {
switch (flags) {
case NODE_ONLY:
return 1;
case INCLUDE_ALL_PARENTS:
return Integer.MAX_VALUE;
case INCLUDE_FIRST_PARENT:
return 2;
default:
return 1;
}
}
public static final String NODE_RANGE_PROPERTY= "noderange"; //$NON-NLS-1$
public static int getExtendedOffset(ASTNode node) {
ISourceRange range= (ISourceRange) node.getProperty(NODE_RANGE_PROPERTY);
if (range != null) {
return range.getOffset();
}
return node.getStartPosition();
}
public static int getExtendedEnd(ASTNode node) {
ISourceRange range= (ISourceRange) node.getProperty(NODE_RANGE_PROPERTY);
if (range != null) {
return range.getOffset() + range.getLength();
}
return node.getStartPosition() + node.getLength();
}
public static int getExtendedLength(ASTNode node) {
ISourceRange range= (ISourceRange) node.getProperty(NODE_RANGE_PROPERTY);
if (range != null) {
return range.getLength();
}
return node.getLength();
}
/**
* Annotates all node that have extended ranges with a ISourceRange in
* the NODE_RANGE_PROPERTY property
* @param node A node from the tree. The root compilation unit mist be reachable
* through this node.
* @param scanner The scanner initialized to the source corresponding to the node.
*/
public static void annotateExtraRanges(ASTNode node, TokenScanner scanner) {
ASTNode astRoot= node.getRoot();
// annotate the full cu (preliminary solution until the code goes to jdt.core.
if (astRoot.getProperty(NODE_RANGE_PROPERTY) != null) {
return;
}
// mark the cu with a rnage so we know it's already annotated
astRoot.setProperty(NODE_RANGE_PROPERTY, new SourceRange(node.getStartPosition(), node.getLength()));
doExtraRangesForChildren(astRoot, scanner);
}
private static void doExtraRangesForChildren(ASTNode node, TokenScanner scanner) {
List children= ASTNodes.getChildren(node);
int lastChild= children.size() - 1;
int lastPos= node.getStartPosition() + node.getLength();
int endOfLast= node.getStartPosition();
for (int i= 0; i <= lastChild; i++) {
ASTNode curr= (ASTNode) children.get(i);
if (curr.getStartPosition() != -1) {
int beginOfNext= getNextExistingOffset(children, i, lastPos);
endOfLast= annotateNode(curr, endOfLast, beginOfNext, scanner);
doExtraRangesForChildren(curr, scanner);
}
}
}
/* workaround, deals with finding positions in modified ASTs. */
private static int getNextExistingOffset(List children, int idx, int def) {
for (int i= idx + 1; i < children.size(); i++) {
ASTNode curr= (ASTNode) children.get(i);
if (curr.getStartPosition() != -1) {
return curr.getStartPosition();
}
}
return def;
}
private static int annotateNode(ASTNode node, int prevEnd, int nextStart, TokenScanner scanner) {
int tokenStart= node.getStartPosition();
int tokenLength= node.getLength();
try {
int start= scanner.getTokenCommentStart(prevEnd, tokenStart);
int length= scanner.getTokenCommentEnd(tokenStart + tokenLength, nextStart) - start;
if (start != tokenStart || length != tokenLength) {
node.setProperty(NODE_RANGE_PROPERTY, new SourceRange(start, length));
}
return start + length;
} catch (CoreException e) {
JavaPlugin.log(e);
// log, no extra range annotated
}
return tokenStart + tokenLength;
}
/**
* Computes the insertion index to be used to add the given member to the
* the list <code>container</code>.
* @param member the member to add
* @param container a list containing objects of type <code>BodyDeclaration</code>
* @return the insertion index to be used
*/
public static int getInsertionIndex(BodyDeclaration member, List container) {
return getInsertionIndex(container, member.getNodeType(), isStatic(member));
}
/**
* Computes the insertion index to be used to add a member of type <code>
* memberType</code> to the the list <code>container</code>.
*
* @param memberType the type of the member to be added. Valid values are: <code>
* ASTNode.TYPE_DECLARATION</code>, <code>ASTNode.INITIALIZER</code>, <code>
* ASTNode.FIELD_DECLARATION<code> and <code>ASTNode.METHOD_DECLARATION</code>.
* @param container a list containing objects of type <code>BodyDeclaration</code>
* @param isStatic
* @return the insertion index to be used
*/
private static int getInsertionIndex(List container, int memberType, boolean isStatic) {
if (memberType == ASTNode.TYPE_DECLARATION || memberType == ASTNode.INITIALIZER)
return 0;
int defaultIndex= container.size();
if (memberType == ASTNode.FIELD_DECLARATION) {
int first= -1;
int last= -1;
int i= 0;
for (Iterator iter= container.iterator(); iter.hasNext(); i++) {
int nodeType= ((BodyDeclaration)iter.next()).getNodeType();
switch (nodeType) {
case ASTNode.FIELD_DECLARATION:
last= i;
break;
case ASTNode.METHOD_DECLARATION:
if (first == -1)
first= i;
break;
}
}
if (last != -1)
return ++last;
if (first != -1)
return first;
return defaultIndex;
}
if (memberType == ASTNode.METHOD_DECLARATION) {
int lastMethod= -1;
int lastStaticMethod= -1;
int firstMethod= -1;
int i= 0;
for (Iterator iter= container.iterator(); iter.hasNext(); i++) {
ASTNode node= (ASTNode)iter.next();
int nodeType= node.getNodeType();
switch (nodeType) {
case ASTNode.METHOD_DECLARATION:
MethodDeclaration declaration= (MethodDeclaration)node;
if (firstMethod == -1)
firstMethod= i;
if (isStatic && Modifier.isStatic(declaration.getModifiers()))
lastStaticMethod= i;
lastMethod= i;
}
}
if (isStatic) {
if (lastStaticMethod != -1)
return ++lastStaticMethod;
else if (firstMethod != 1)
return firstMethod;
} else {
if (lastMethod != -1)
return ++lastMethod;
}
return defaultIndex;
}
return defaultIndex;
}
public static SimpleName getLeftMostSimpleName(QualifiedName name) {
final SimpleName[] result= new SimpleName[1];
ASTVisitor visitor= new ASTVisitor() {
public boolean visit(QualifiedName qualifiedName) {
Name left= qualifiedName.getQualifier();
if (left instanceof SimpleName)
result[0]= (SimpleName)left;
else
left.accept(this);
return false;
}
};
name.accept(visitor);
return result[0];
}
public static int changeVisibility(int modifiers, int visibility) {
return (modifiers & CLEAR_VISIBILITY) | visibility;
}
}