| /******************************************************************************* |
| * Copyright (c) 2003, 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.internal.debug.core.breakpoints; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; |
| import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; |
| import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; |
| import org.eclipse.jdt.core.dom.ArrayAccess; |
| import org.eclipse.jdt.core.dom.ArrayCreation; |
| import org.eclipse.jdt.core.dom.ArrayInitializer; |
| import org.eclipse.jdt.core.dom.ArrayType; |
| import org.eclipse.jdt.core.dom.AssertStatement; |
| import org.eclipse.jdt.core.dom.Assignment; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.BlockComment; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.BooleanLiteral; |
| import org.eclipse.jdt.core.dom.BreakStatement; |
| import org.eclipse.jdt.core.dom.CastExpression; |
| import org.eclipse.jdt.core.dom.CatchClause; |
| import org.eclipse.jdt.core.dom.CharacterLiteral; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.ConditionalExpression; |
| import org.eclipse.jdt.core.dom.ConstructorInvocation; |
| import org.eclipse.jdt.core.dom.ContinueStatement; |
| import org.eclipse.jdt.core.dom.DoStatement; |
| import org.eclipse.jdt.core.dom.EmptyStatement; |
| import org.eclipse.jdt.core.dom.EnhancedForStatement; |
| import org.eclipse.jdt.core.dom.EnumConstantDeclaration; |
| import org.eclipse.jdt.core.dom.EnumDeclaration; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.ExpressionStatement; |
| import org.eclipse.jdt.core.dom.FieldAccess; |
| import org.eclipse.jdt.core.dom.FieldDeclaration; |
| import org.eclipse.jdt.core.dom.ForStatement; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| import org.eclipse.jdt.core.dom.IfStatement; |
| import org.eclipse.jdt.core.dom.ImportDeclaration; |
| import org.eclipse.jdt.core.dom.InfixExpression; |
| import org.eclipse.jdt.core.dom.Initializer; |
| import org.eclipse.jdt.core.dom.InstanceofExpression; |
| import org.eclipse.jdt.core.dom.Javadoc; |
| import org.eclipse.jdt.core.dom.LabeledStatement; |
| import org.eclipse.jdt.core.dom.LineComment; |
| import org.eclipse.jdt.core.dom.MarkerAnnotation; |
| import org.eclipse.jdt.core.dom.MemberRef; |
| import org.eclipse.jdt.core.dom.MemberValuePair; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.MethodInvocation; |
| import org.eclipse.jdt.core.dom.MethodRef; |
| import org.eclipse.jdt.core.dom.MethodRefParameter; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.NormalAnnotation; |
| import org.eclipse.jdt.core.dom.NullLiteral; |
| import org.eclipse.jdt.core.dom.NumberLiteral; |
| import org.eclipse.jdt.core.dom.PackageDeclaration; |
| import org.eclipse.jdt.core.dom.ParameterizedType; |
| import org.eclipse.jdt.core.dom.ParenthesizedExpression; |
| import org.eclipse.jdt.core.dom.PostfixExpression; |
| import org.eclipse.jdt.core.dom.PrefixExpression; |
| import org.eclipse.jdt.core.dom.PrefixExpression.Operator; |
| import org.eclipse.jdt.core.dom.PrimitiveType; |
| import org.eclipse.jdt.core.dom.QualifiedName; |
| import org.eclipse.jdt.core.dom.QualifiedType; |
| import org.eclipse.jdt.core.dom.ReturnStatement; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SimpleType; |
| import org.eclipse.jdt.core.dom.SingleMemberAnnotation; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.StringLiteral; |
| import org.eclipse.jdt.core.dom.SuperConstructorInvocation; |
| import org.eclipse.jdt.core.dom.SuperFieldAccess; |
| import org.eclipse.jdt.core.dom.SuperMethodInvocation; |
| import org.eclipse.jdt.core.dom.SwitchCase; |
| import org.eclipse.jdt.core.dom.SwitchStatement; |
| import org.eclipse.jdt.core.dom.SynchronizedStatement; |
| import org.eclipse.jdt.core.dom.TagElement; |
| import org.eclipse.jdt.core.dom.TextElement; |
| import org.eclipse.jdt.core.dom.ThisExpression; |
| import org.eclipse.jdt.core.dom.ThrowStatement; |
| import org.eclipse.jdt.core.dom.TryStatement; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| import org.eclipse.jdt.core.dom.TypeDeclarationStatement; |
| import org.eclipse.jdt.core.dom.TypeLiteral; |
| import org.eclipse.jdt.core.dom.TypeParameter; |
| import org.eclipse.jdt.core.dom.UnionType; |
| import org.eclipse.jdt.core.dom.VariableDeclarationExpression; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.dom.VariableDeclarationStatement; |
| import org.eclipse.jdt.core.dom.WhileStatement; |
| import org.eclipse.jdt.core.dom.WildcardType; |
| |
| /** |
| * Compute a valid location where to put a breakpoint from an JDOM |
| * CompilationUnit. The result is the first valid location with a line number |
| * greater or equals than the given position. |
| */ |
| public class ValidBreakpointLocationLocator extends ASTVisitor { |
| |
| public static final int LOCATION_NOT_FOUND = 0; |
| public static final int LOCATION_LINE = 1; |
| public static final int LOCATION_METHOD = 2; |
| public static final int LOCATION_FIELD = 3; |
| |
| private CompilationUnit fCompilationUnit; |
| private int fLineNumber; |
| private boolean fBindingsResolved; |
| private boolean fNeedBindings = false; |
| private boolean fBestMatch; |
| |
| private int fLocationType; |
| private boolean fLocationFound; |
| private String fTypeName; |
| private int fLineLocation; |
| private int fMemberOffset; |
| private List<String> fLabels; |
| |
| /** |
| * @param compilationUnit |
| * the JDOM CompilationUnit of the source code. |
| * @param lineNumber |
| * the line number in the source code where to put the |
| * breakpoint. |
| * @param bestMatch |
| * if <code>true</code> look for the best match, otherwise look |
| * only for a valid line |
| */ |
| public ValidBreakpointLocationLocator(CompilationUnit compilationUnit, |
| int lineNumber, boolean bindingsResolved, boolean bestMatch) { |
| fCompilationUnit = compilationUnit; |
| fLineNumber = lineNumber; |
| fBindingsResolved = bindingsResolved; |
| fBestMatch = bestMatch; |
| fLocationFound = false; |
| } |
| |
| /** |
| * Returns whether binding information would be helpful in validating a |
| * breakpoint location. If this locator makes a pass of the tree and |
| * determines that binding information would be helpful but was not |
| * available, this method returns <code>true</code>. |
| * |
| * @return whether binding information would be helpful in validating a |
| * breakpoint location |
| */ |
| public boolean isBindingsRequired() { |
| return fNeedBindings; |
| } |
| |
| /** |
| * Return the type of the valid location found |
| * |
| * @return one of LOCATION_NOT_FOUND, LOCATION_LINE, LOCATION_METHOD or |
| * LOCATION_FIELD |
| */ |
| public int getLocationType() { |
| return fLocationType; |
| } |
| |
| /** |
| * Return of the type where the valid location is. |
| */ |
| public String getFullyQualifiedTypeName() { |
| return fTypeName; |
| } |
| |
| /** |
| * Return the line number of the computed valid location |
| */ |
| public int getLineLocation() { |
| if (fLocationType == LOCATION_NOT_FOUND || fLocationType == LOCATION_METHOD) { |
| return -1; |
| } |
| return fLineLocation; |
| } |
| |
| /** |
| * Return the offset of the member which is the valid location, if the |
| * location type is LOCATION_METHOD or LOCATION_FIELD. |
| */ |
| public int getMemberOffset() { |
| return fMemberOffset; |
| } |
| |
| /** |
| * Compute the name of the type which contains this node. <br> |
| * <br> |
| * Delegates to the old method of computing the type name if bindings are |
| * not available. |
| * |
| * @see #computeTypeName0(ASTNode) |
| * @since 3.6 |
| */ |
| private String computeTypeName(ASTNode node) { |
| AbstractTypeDeclaration type = null; |
| while (!(node instanceof CompilationUnit)) { |
| if (node instanceof AbstractTypeDeclaration) { |
| type = (AbstractTypeDeclaration) node; |
| break; |
| } |
| node = node.getParent(); |
| } |
| if (type != null) { |
| ITypeBinding binding = type.resolveBinding(); |
| if (binding != null) { |
| return binding.getBinaryName(); |
| } |
| } |
| return computeTypeName0(node); |
| } |
| |
| /** |
| * Fall back to compute the type name if bindings are not resolved |
| * |
| * @param node |
| * @return the computed type name |
| */ |
| String computeTypeName0(ASTNode node) { |
| String typeName = null; |
| while (!(node instanceof CompilationUnit)) { |
| if (node instanceof AbstractTypeDeclaration) { |
| String identifier = ((AbstractTypeDeclaration) node).getName() |
| .getIdentifier(); |
| if (typeName == null) { |
| typeName = identifier; |
| } else { |
| typeName = identifier + "$" + typeName; //$NON-NLS-1$ |
| } |
| } |
| node = node.getParent(); |
| } |
| PackageDeclaration packageDecl = ((CompilationUnit) node).getPackage(); |
| String packageIdentifier = ""; //$NON-NLS-1$ |
| if (packageDecl != null) { |
| Name packageName = packageDecl.getName(); |
| while (packageName.isQualifiedName()) { |
| QualifiedName qualifiedName = (QualifiedName) packageName; |
| packageIdentifier = qualifiedName.getName().getIdentifier() |
| + "." + packageIdentifier; //$NON-NLS-1$ |
| packageName = qualifiedName.getQualifier(); |
| } |
| packageIdentifier = ((SimpleName) packageName).getIdentifier() |
| + "." + packageIdentifier; //$NON-NLS-1$ |
| } |
| return packageIdentifier + typeName; |
| } |
| |
| /** |
| * Return <code>true</code> if this node children may contain a valid |
| * location for the breakpoint. |
| * |
| * @param node |
| * the node. |
| * @param isCode |
| * true indicated that the first line of the given node always |
| * contains some executable code, even if split in multiple |
| * lines. |
| */ |
| private boolean visit(ASTNode node, boolean isCode) { |
| // if we already found a correct location |
| // no need to check the element inside. |
| if (fLocationFound) { |
| return false; |
| } |
| int startPosition = node.getStartPosition(); |
| int endLine = lineNumber(startPosition + node.getLength() - 1); |
| // if the position is not in this part of the code |
| // no need to check the element inside. |
| if (endLine < fLineNumber) { |
| return false; |
| } |
| // if the first line of this node always represents some executable code |
| // and the |
| // breakpoint is requested on this line or on a previous line, this is a |
| // valid |
| // location |
| int startLine = lineNumber(startPosition); |
| if (isCode && (fLineNumber <= startLine)) { |
| fLineLocation = startLine; |
| fLocationFound = true; |
| fLocationType = LOCATION_LINE; |
| fTypeName = computeTypeName(node); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean isReplacedByConstantValue(Expression node) { |
| switch (node.getNodeType()) { |
| // literals are constant |
| case ASTNode.BOOLEAN_LITERAL: |
| case ASTNode.CHARACTER_LITERAL: |
| case ASTNode.NUMBER_LITERAL: |
| case ASTNode.STRING_LITERAL: |
| return true; |
| case ASTNode.SIMPLE_NAME: |
| case ASTNode.QUALIFIED_NAME: |
| return isReplacedByConstantValue((Name) node); |
| case ASTNode.FIELD_ACCESS: |
| return isReplacedByConstantValue((FieldAccess) node); |
| case ASTNode.SUPER_FIELD_ACCESS: |
| return isReplacedByConstantValue((SuperFieldAccess) node); |
| case ASTNode.INFIX_EXPRESSION: |
| return isReplacedByConstantValue((InfixExpression) node); |
| case ASTNode.PREFIX_EXPRESSION: |
| return isReplacedByConstantValue((PrefixExpression) node); |
| case ASTNode.CAST_EXPRESSION: |
| return isReplacedByConstantValue(((CastExpression) node) |
| .getExpression()); |
| default: |
| return false; |
| } |
| } |
| |
| private boolean isReplacedByConstantValue(InfixExpression node) { |
| // if all operands are constant value, the expression is replaced by a |
| // constant value |
| if (!(isReplacedByConstantValue(node.getLeftOperand()) && isReplacedByConstantValue(node |
| .getRightOperand()))) { |
| return false; |
| } |
| if (node.hasExtendedOperands()) { |
| for (Iterator<? extends Expression> iter = node.extendedOperands().iterator(); iter.hasNext();) { |
| if (!isReplacedByConstantValue(iter.next())) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| private boolean isReplacedByConstantValue(PrefixExpression node) { |
| // for '-', '+', '~' and '!', if the operand is a constant value, |
| // the expression is replaced by a constant value |
| Operator operator = node.getOperator(); |
| if (operator != PrefixExpression.Operator.INCREMENT |
| && operator != PrefixExpression.Operator.DECREMENT) { |
| return isReplacedByConstantValue(node.getOperand()); |
| } |
| return false; |
| } |
| |
| private boolean isReplacedByConstantValue(Name node) { |
| if (!fBindingsResolved) { |
| fNeedBindings = true; |
| return false; |
| } |
| // if node is a variable with a constant value (static final field) |
| IBinding binding = node.resolveBinding(); |
| if (binding != null && binding.getKind() == IBinding.VARIABLE) { |
| return ((IVariableBinding) binding).getConstantValue() != null; |
| } |
| return false; |
| } |
| |
| private boolean isReplacedByConstantValue(FieldAccess node) { |
| if (!fBindingsResolved) { |
| fNeedBindings = true; |
| return false; |
| } |
| // if the node is 'this.<field>', and the field is static final |
| Expression expression = node.getExpression(); |
| IVariableBinding binding = node.resolveFieldBinding(); |
| if (binding != null |
| && expression.getNodeType() == ASTNode.THIS_EXPRESSION) { |
| return binding.getConstantValue() != null; |
| } |
| return false; |
| } |
| |
| private boolean isReplacedByConstantValue(SuperFieldAccess node) { |
| if (!fBindingsResolved) { |
| fNeedBindings = true; |
| return false; |
| } |
| // if the field is static final |
| IVariableBinding binding = node.resolveFieldBinding(); |
| if (binding != null) { |
| return binding.getConstantValue() != null; |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * AnnotationTypeDeclaration) |
| */ |
| @Override |
| public boolean visit(AnnotationTypeDeclaration node) { |
| if (visit(node, false)) { |
| List<BodyDeclaration> decls = node.bodyDeclarations(); |
| for(BodyDeclaration decl : decls) { |
| decl.accept(this); |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * AnnotationTypeMemberDeclaration) |
| */ |
| @Override |
| public boolean visit(AnnotationTypeMemberDeclaration node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.AnonymousClassDeclaration) |
| */ |
| @Override |
| public boolean visit(AnonymousClassDeclaration node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ArrayAccess) |
| */ |
| @Override |
| public boolean visit(ArrayAccess node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ArrayCreation) |
| */ |
| @Override |
| public boolean visit(ArrayCreation node) { |
| return visit(node, node.getInitializer() == null); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ArrayInitializer) |
| */ |
| @Override |
| public boolean visit(ArrayInitializer node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ArrayType) |
| */ |
| @Override |
| public boolean visit(ArrayType node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.AssertStatement) |
| */ |
| @Override |
| public boolean visit(AssertStatement node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Assignment) |
| */ |
| @Override |
| public boolean visit(Assignment node) { |
| if (visit(node, false)) { |
| // if the left hand side represent a local variable, or a static |
| // field |
| // and the breakpoint was requested on a line before the line where |
| // starts the assignment, set the location to be the first executable |
| // instruction of the right hand side, as it will be the first part |
| // of |
| // this assignment to be executed |
| Expression leftHandSide = node.getLeftHandSide(); |
| if (leftHandSide instanceof Name) { |
| int startLine = lineNumber(node.getStartPosition()); |
| if (fLineNumber < startLine) { |
| if (fBindingsResolved) { |
| IVariableBinding binding = (IVariableBinding) ((Name) leftHandSide) |
| .resolveBinding(); |
| if (binding != null |
| && (!binding.isField() || Modifier |
| .isStatic(binding.getModifiers()))) { |
| node.getRightHandSide().accept(this); |
| } |
| } else { |
| fNeedBindings = true; |
| } |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Block) |
| */ |
| @Override |
| public boolean visit(Block node) { |
| if (visit(node, false)) { |
| if (node.statements().isEmpty() |
| && node.getParent().getNodeType() == ASTNode.METHOD_DECLARATION) { |
| // in case of an empty method, set the breakpoint on the last |
| // line of the empty block. |
| fLineLocation = lineNumber(node.getStartPosition() |
| + node.getLength() - 1); |
| fLocationFound = true; |
| fLocationType = LOCATION_LINE; |
| fTypeName = computeTypeName(node); |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * BlockComment) |
| */ |
| @Override |
| public boolean visit(BlockComment node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.BooleanLiteral) |
| */ |
| @Override |
| public boolean visit(BooleanLiteral node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.BreakStatement) |
| */ |
| @Override |
| public boolean visit(BreakStatement node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CastExpression) |
| */ |
| @Override |
| public boolean visit(CastExpression node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CatchClause) |
| */ |
| @Override |
| public boolean visit(CatchClause node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CharacterLiteral) |
| */ |
| @Override |
| public boolean visit(CharacterLiteral node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ClassInstanceCreation) |
| */ |
| @Override |
| public boolean visit(ClassInstanceCreation node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CompilationUnit) |
| */ |
| @Override |
| public boolean visit(CompilationUnit node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ConditionalExpression) |
| */ |
| @Override |
| public boolean visit(ConditionalExpression node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ConstructorInvocation) |
| */ |
| @Override |
| public boolean visit(ConstructorInvocation node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ContinueStatement) |
| */ |
| @Override |
| public boolean visit(ContinueStatement node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.DoStatement) |
| */ |
| @Override |
| public boolean visit(DoStatement node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.EmptyStatement) |
| */ |
| @Override |
| public boolean visit(EmptyStatement node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * EnhancedForStatement) |
| */ |
| @Override |
| public boolean visit(EnhancedForStatement node) { |
| if (visit(node, false)) { |
| node.getExpression().accept(this); |
| node.getBody().accept(this); |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * EnumConstantDeclaration) |
| */ |
| @Override |
| public boolean visit(EnumConstantDeclaration node) { |
| if (visit(node, false)) { |
| List<Expression> arguments = node.arguments(); |
| for(Expression exp : arguments) { |
| exp.accept(this); |
| } |
| AnonymousClassDeclaration decl = node.getAnonymousClassDeclaration(); |
| if (decl != null) { |
| decl.accept(this); |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * EnumDeclaration) |
| */ |
| @Override |
| public boolean visit(EnumDeclaration node) { |
| if (visit(node, false)) { |
| List<EnumConstantDeclaration> enumConstants = node.enumConstants(); |
| for(EnumConstantDeclaration econst : enumConstants) { |
| econst.accept(this); |
| } |
| List<BodyDeclaration> decls = node.bodyDeclarations(); |
| for(BodyDeclaration body : decls) { |
| body.accept(this); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ExpressionStatement) |
| */ |
| @Override |
| public boolean visit(ExpressionStatement node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldAccess) |
| */ |
| @Override |
| public boolean visit(FieldAccess node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldDeclaration) |
| */ |
| @Override |
| public boolean visit(FieldDeclaration node) { |
| if (visit(node, false)) { |
| if (fBestMatch) { |
| // check if the line contains a single field declaration. |
| List<VariableDeclarationFragment> fragments = node.fragments(); |
| if (fragments.size() == 1) { |
| VariableDeclarationFragment fragment = fragments.get(0); |
| Expression init = fragment.getInitializer(); |
| int offset = fragment.getName().getStartPosition(); |
| int line = lineNumber(offset); |
| if(Flags.isFinal(node.getModifiers())) { |
| if(init != null) { |
| if (line == fLineNumber) { |
| if (isReplacedByConstantValue(init)) { |
| fLocationType = LOCATION_LINE; |
| } else { |
| fLocationType = LOCATION_FIELD; |
| } |
| fMemberOffset = offset; |
| fLineLocation = line; |
| fLocationFound = true; |
| fTypeName = computeTypeName(node); |
| return false; |
| } |
| } |
| else { |
| if (line == fLineNumber) { |
| fMemberOffset = offset; |
| fLineLocation = line; |
| fLocationType = LOCATION_FIELD; |
| fLocationFound = true; |
| fTypeName = computeTypeName(node); |
| return false; |
| } |
| return false; |
| } |
| } |
| else { |
| // check if the breakpoint is to be set on the line which |
| // contains the name of the field |
| if (line == fLineNumber) { |
| fMemberOffset = offset; |
| fLineLocation = line; |
| fLocationType = LOCATION_FIELD; |
| fLocationFound = true; |
| return false; |
| } |
| } |
| } |
| } |
| // visit only the variable declaration fragments, not the variable |
| // names. |
| List<VariableDeclarationFragment> fragments = node.fragments(); |
| for(VariableDeclarationFragment frag : fragments) { |
| frag.accept(this); |
| if(fLocationFound) { |
| break; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ForStatement) |
| */ |
| @Override |
| public boolean visit(ForStatement node) { |
| // in case on a "for(;;)", the breakpoint can be set on the first token |
| // of the node. |
| return visit(node, |
| node.initializers().isEmpty() && node.getExpression() == null |
| && node.updaters().isEmpty()); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.IfStatement) |
| */ |
| @Override |
| public boolean visit(IfStatement node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ImportDeclaration) |
| */ |
| @Override |
| public boolean visit(ImportDeclaration node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.InfixExpression) |
| */ |
| @Override |
| public boolean visit(InfixExpression node) { |
| // if the breakpoint is to be set on a constant operand, the breakpoint |
| // needs to be |
| // set on the first constant operand after the previous non-constant |
| // operand |
| // (or the beginning of the expression, if there is no non-constant |
| // operand before). |
| // ex: foo() + // previous non-constant operand |
| // 1 + // breakpoint set here |
| // 2 // breakpoint asked to be set here |
| if (visit(node, false)) { |
| Expression leftOperand = node.getLeftOperand(); |
| Expression firstConstant = null; |
| if (visit(leftOperand, false)) { |
| leftOperand.accept(this); |
| return false; |
| } |
| if (isReplacedByConstantValue(leftOperand)) { |
| firstConstant = leftOperand; |
| } |
| Expression rightOperand = node.getRightOperand(); |
| if (visit(rightOperand, false)) { |
| if (firstConstant == null |
| || !isReplacedByConstantValue(rightOperand)) { |
| rightOperand.accept(this); |
| return false; |
| } |
| } else { |
| if (isReplacedByConstantValue(rightOperand)) { |
| if (firstConstant == null) { |
| firstConstant = rightOperand; |
| } |
| } else { |
| firstConstant = null; |
| } |
| List<Expression> extendedOperands = node.extendedOperands(); |
| for(Expression exp : extendedOperands) { |
| if (visit(exp, false)) { |
| if (firstConstant == null || !isReplacedByConstantValue(exp)) { |
| exp.accept(this); |
| return false; |
| } |
| break; |
| } |
| if (isReplacedByConstantValue(exp)) { |
| if (firstConstant == null) { |
| firstConstant = exp; |
| } |
| } else { |
| firstConstant = null; |
| } |
| } |
| } |
| if (firstConstant != null) { |
| fLineLocation = lineNumber(firstConstant.getStartPosition()); |
| fLocationFound = true; |
| fLocationType = LOCATION_LINE; |
| fTypeName = computeTypeName(firstConstant); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Initializer) |
| */ |
| @Override |
| public boolean visit(Initializer node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.InstanceofExpression) |
| */ |
| @Override |
| public boolean visit(InstanceofExpression node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Javadoc) |
| */ |
| @Override |
| public boolean visit(Javadoc node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.LabeledStatement) |
| */ |
| @Override |
| public boolean visit(LabeledStatement node) { |
| nestLabel(node.getLabel().getFullyQualifiedName()); |
| return visit(node, false); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom |
| * .LabeledStatement) |
| */ |
| @Override |
| public void endVisit(LabeledStatement node) { |
| popLabel(); |
| super.endVisit(node); |
| } |
| |
| private String getLabel() { |
| if (fLabels == null || fLabels.isEmpty()) { |
| return null; |
| } |
| return fLabels.get(fLabels.size() - 1); |
| } |
| |
| private void nestLabel(String label) { |
| if (fLabels == null) { |
| fLabels = new ArrayList<String>(); |
| } |
| fLabels.add(label); |
| } |
| |
| private void popLabel() { |
| if (fLabels == null || fLabels.isEmpty()) { |
| return; |
| } |
| fLabels.remove(fLabels.size() - 1); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * LineComment) |
| */ |
| @Override |
| public boolean visit(LineComment node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * MarkerAnnotation) |
| */ |
| @Override |
| public boolean visit(MarkerAnnotation node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MemberRef |
| * ) |
| */ |
| @Override |
| public boolean visit(MemberRef node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * MemberValuePair) |
| */ |
| @Override |
| public boolean visit(MemberValuePair node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration) |
| */ |
| @Override |
| public boolean visit(MethodDeclaration node) { |
| if (visit(node, false)) { |
| if (fBestMatch) { |
| // check if we are on the line which contains the method name |
| int nameOffset = node.getName().getStartPosition(); |
| if (lineNumber(nameOffset) == fLineNumber) { |
| fMemberOffset = nameOffset; |
| fLocationType = LOCATION_METHOD; |
| fLocationFound = true; |
| return false; |
| } |
| } |
| // visit only the body |
| Block body = node.getBody(); |
| if (body != null) { // body is null for abstract methods |
| body.accept(this); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodInvocation) |
| */ |
| @Override |
| public boolean visit(MethodInvocation node) { |
| return visit(node, true); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodRef |
| * ) |
| */ |
| @Override |
| public boolean visit(MethodRef node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * MethodRefParameter) |
| */ |
| @Override |
| public boolean visit(MethodRefParameter node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Modifier |
| * ) |
| */ |
| @Override |
| public boolean visit(Modifier node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * NormalAnnotation) |
| */ |
| @Override |
| public boolean visit(NormalAnnotation node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.NullLiteral) |
| */ |
| @Override |
| public boolean visit(NullLiteral node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.NumberLiteral) |
| */ |
| @Override |
| public boolean visit(NumberLiteral node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PackageDeclaration) |
| */ |
| @Override |
| public boolean visit(PackageDeclaration node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * ParameterizedType) |
| */ |
| @Override |
| public boolean visit(ParameterizedType node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ParenthesizedExpression) |
| */ |
| @Override |
| public boolean visit(ParenthesizedExpression node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PostfixExpression) |
| */ |
| @Override |
| public boolean visit(PostfixExpression node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PrefixExpression) |
| */ |
| @Override |
| public boolean visit(PrefixExpression node) { |
| if (visit(node, false)) { |
| if (isReplacedByConstantValue(node)) { |
| fLineLocation = lineNumber(node.getStartPosition()); |
| fLocationFound = true; |
| fLocationType = LOCATION_LINE; |
| fTypeName = computeTypeName(node); |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PrimitiveType) |
| */ |
| @Override |
| public boolean visit(PrimitiveType node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.QualifiedName) |
| */ |
| @Override |
| public boolean visit(QualifiedName node) { |
| visit(node, true); |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * QualifiedType) |
| */ |
| @Override |
| public boolean visit(QualifiedType node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ReturnStatement) |
| */ |
| @Override |
| public boolean visit(ReturnStatement node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SimpleName) |
| */ |
| @Override |
| public boolean visit(SimpleName node) { |
| // the name is only code if its not the current label (if any) |
| return visit(node, !node.getFullyQualifiedName().equals(getLabel())); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SimpleType) |
| */ |
| @Override |
| public boolean visit(SimpleType node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * SingleMemberAnnotation) |
| */ |
| @Override |
| public boolean visit(SingleMemberAnnotation node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SingleVariableDeclaration) |
| */ |
| @Override |
| public boolean visit(SingleVariableDeclaration node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.StringLiteral) |
| */ |
| @Override |
| public boolean visit(StringLiteral node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SuperConstructorInvocation) |
| */ |
| @Override |
| public boolean visit(SuperConstructorInvocation node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SuperFieldAccess) |
| */ |
| @Override |
| public boolean visit(SuperFieldAccess node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SuperMethodInvocation) |
| */ |
| @Override |
| public boolean visit(SuperMethodInvocation node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SwitchCase) |
| */ |
| @Override |
| public boolean visit(SwitchCase node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SwitchStatement) |
| */ |
| @Override |
| public boolean visit(SwitchStatement node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SynchronizedStatement) |
| */ |
| @Override |
| public boolean visit(SynchronizedStatement node) { |
| return visit(node, false); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TagElement |
| * ) |
| */ |
| @Override |
| public boolean visit(TagElement node) { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * TextElement) |
| */ |
| @Override |
| public boolean visit(TextElement node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ThisExpression) |
| */ |
| @Override |
| public boolean visit(ThisExpression node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ThrowStatement) |
| */ |
| @Override |
| public boolean visit(ThrowStatement node) { |
| return visit(node, true); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TryStatement) |
| */ |
| @Override |
| public boolean visit(TryStatement node) { |
| return visit(node, false); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.UnionType |
| * ) |
| */ |
| @Override |
| public boolean visit(UnionType node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclaration) |
| */ |
| @Override |
| public boolean visit(TypeDeclaration node) { |
| if (visit(node, false)) { |
| // visit only the elements of the type declaration |
| List<BodyDeclaration> bodyDeclaration = node.bodyDeclarations(); |
| for(BodyDeclaration body : bodyDeclaration) { |
| body.accept(this); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclarationStatement) |
| */ |
| @Override |
| public boolean visit(TypeDeclarationStatement node) { |
| return visit(node, false); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * TypeParameter) |
| */ |
| @Override |
| public boolean visit(TypeParameter node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeLiteral) |
| */ |
| @Override |
| public boolean visit(TypeLiteral node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.VariableDeclarationExpression) |
| */ |
| @Override |
| public boolean visit(VariableDeclarationExpression node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.VariableDeclarationFragment) |
| */ |
| @Override |
| public boolean visit(VariableDeclarationFragment node) { |
| Expression initializer = node.getInitializer(); |
| if (visit(node, false)) { |
| int offset = node.getName().getStartPosition(); |
| int line = lineNumber(offset); |
| if (initializer != null) { |
| if (fLineNumber == line) { |
| fLineLocation = line; |
| fLocationFound = true; |
| fLocationType = LOCATION_LINE; |
| fTypeName = computeTypeName(node); |
| return false; |
| } |
| initializer.accept(this); |
| } else { |
| // the variable has no initializer |
| // check if the breakpoint is to be set on the line which |
| // contains the name of the field |
| if (line == fLineNumber) { |
| fMemberOffset = offset; |
| fLineLocation = line; |
| fLocationType = LOCATION_FIELD; |
| fTypeName = computeTypeName(node); |
| fLocationFound = true; |
| return false; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private int lineNumber(int offset) { |
| int lineNumber = fCompilationUnit.getLineNumber(offset); |
| return lineNumber < 1 ? 1 : lineNumber; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. |
| * WildcardType) |
| */ |
| @Override |
| public boolean visit(WildcardType node) { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.VariableDeclarationStatement) |
| */ |
| @Override |
| public boolean visit(VariableDeclarationStatement node) { |
| return visit(node, false); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.WhileStatement) |
| */ |
| @Override |
| public boolean visit(WhileStatement node) { |
| return visit(node, false); |
| } |
| |
| } |