blob: a98eec2c67df49014420a1ba197fbd4d34f854cb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 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 java.util.Map;
import java.util.Stack;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IBreakpointImportParticipant;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.debug.core.IJavaWatchpoint;
/**
* Default implementation covering the import of all platform Java breakpoints
*
* @since 3.5
*/
public class JavaBreakpointImportParticipant implements IBreakpointImportParticipant {
class BreakpointVerifier extends ASTVisitor {
final int TYPE = 0;
final int METHOD = 1;
final int FIELD = 2;
String fTypename = null;
String fName = null;
String fSignature = null;
IBreakpoint fBreakpoint = null;
CompilationUnit fUnit = null;
Stack fTypeNameStack = null;
/**
* Constructor
* @param breakpoint
* @param unit
*/
public BreakpointVerifier(IBreakpoint breakpoint, CompilationUnit unit) {
fTypename = getBreakpointTypeName(breakpoint);
fName = getMemberName(breakpoint);
fSignature = getMemberSignature(breakpoint);
fBreakpoint = breakpoint;
fUnit = unit;
fTypeNameStack = new Stack();
}
/**
* Returns the value of the {@link JavaBreakpoint#TYPE_NAME} attribute from the breakpoint or <code>null</code>
* @param breakpoint
* @return the value of the type name attribute
*/
String getBreakpointTypeName(IBreakpoint breakpoint) {
return breakpoint.getMarker().getAttribute(JavaBreakpoint.TYPE_NAME, null);
}
/**
* Returns the name of the member from the breakpoint attributes. The name will be one of
* (1) {@link JavaWatchpoint#FIELD_NAME}, if the breakpoint is a watchpoint, or
* (2) {@link JavaMethodBreakpoint#METHOD_NAME} if the breakpoint is a method or method entry breakpoint
* (3) <code>null</code> if there is no member name
* @param breakpoint
* @return the member name or <code>null</code>
*/
String getMemberName(IBreakpoint breakpoint) {
if(breakpoint instanceof IJavaWatchpoint) {
return breakpoint.getMarker().getAttribute(JavaWatchpoint.FIELD_NAME, null);
}
return breakpoint.getMarker().getAttribute(JavaMethodBreakpoint.METHOD_NAME, null);
}
/**
* Returns the signature of the member, defined with the {@link JavaMethodBreakpoint#METHOD_SIGNATURE} attribute,
* or <code>null</code>
* @param breakpoint
* @return the signature of the member or <code>null</code>
*/
String getMemberSignature(IBreakpoint breakpoint){
return breakpoint.getMarker().getAttribute(JavaMethodBreakpoint.METHOD_SIGNATURE, null);
}
/**
* Returns the fully qualified name of the enclosing type for the given node
* @param node
* @return the fully qualified name of the enclosing type
*/
private String getTypeName(ASTNode node) {
return getTypeName(node, new StringBuffer());
}
/**
* Constructs the qualified name of the enclosing parent type
* @param node the node to get the parent name for
* @param buffer the buffer to write the name into
* @return the fully qualified name of the parent
*/
private String getTypeName(ASTNode node, StringBuffer buffer) {
switch(node.getNodeType()) {
case ASTNode.COMPILATION_UNIT : {
CompilationUnit unit = (CompilationUnit) node;
PackageDeclaration packageDeclaration = unit.getPackage();
if (packageDeclaration != null) {
buffer.insert(0, '.');
buffer.insert(0, packageDeclaration.getName().getFullyQualifiedName());
}
return String.valueOf(buffer);
}
default : {
if (node instanceof AbstractTypeDeclaration) {
AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) node;
if (typeDeclaration.isPackageMemberTypeDeclaration()) {
buffer.insert(0, typeDeclaration.getName().getIdentifier());
}
else {
buffer.insert(0, typeDeclaration.getName().getFullyQualifiedName());
buffer.insert(0, '$');
}
}
}
}
return getTypeName(node.getParent(), buffer);
}
/**
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclaration)
*/
public boolean visit(TypeDeclaration node) {
return doTypeVisit(node);
}
/**
* @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.TypeDeclaration)
*/
public void endVisit(TypeDeclaration node) {
doEndTypeVisit();
}
/**
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.EnumDeclaration)
*/
public boolean visit(EnumDeclaration node) {
return doTypeVisit(node);
}
/**
* @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.EnumDeclaration)
*/
public void endVisit(EnumDeclaration node) {
doEndTypeVisit();
}
/**
* Cleans up after a type visit has ended
*/
private void doEndTypeVisit() {
if(!fTypeNameStack.isEmpty()) {
fTypeNameStack.pop();
}
}
/**
* Visits the type node and return if children should be visited
* @param node
* @return true if child nodes should be visited false otherwise
*/
private boolean doTypeVisit(AbstractTypeDeclaration node) {
SimpleName name = node.getName();
String typename = getTypeName(node);
fTypeNameStack.push(typename);
if(!fTypename.startsWith(typename)) {
//we are examining the wrong type stop and process other types
return false;
}
if(fBreakpoint instanceof JavaClassPrepareBreakpoint &&
name != null &&
typename.equals(fTypename)) {
int charstart = name.getStartPosition();
IMarker marker = fBreakpoint.getMarker();
try {
marker.setAttribute(IMarker.CHAR_START, charstart);
marker.setAttribute(IMarker.CHAR_END, charstart + name.getLength());
}
catch(CoreException ce) {}
//found the node we were looking for, do not visit children
return false;
}
return fTypename.indexOf('$') > -1 || name != null;
}
/**
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldDeclaration)
*/
public boolean visit(FieldDeclaration node) {
if(!fTypename.equals(fTypeNameStack.peek())) {
return false;
}
List fragments = node.fragments();
VariableDeclarationFragment fragment = null;
SimpleName name = null;
IMarker marker = fBreakpoint.getMarker();
int currentstart = marker.getAttribute(IMarker.CHAR_START, -1);
for(Iterator iter = fragments.iterator(); iter.hasNext();) {
fragment = (VariableDeclarationFragment) iter.next();
name = fragment.getName();
if(name != null && name.getFullyQualifiedName().equals(fName)) {
//found field update the charstart / charend
int charstart = name.getStartPosition();
if(currentstart != charstart) {
try {
marker.setAttribute(IMarker.CHAR_START, charstart);
marker.setAttribute(IMarker.CHAR_END, charstart + name.getLength());
}
catch(CoreException ce) {}
}
}
}
return false;
}
/**
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration)
*/
public boolean visit(MethodDeclaration node) {
SimpleName name = node.getName();
if(!fTypename.equals(fTypeNameStack.peek())) {
return false;
}
if(name != null && name.getFullyQualifiedName().equals(fName)) {
String sig = getMethodSignatureFromNode(node);
if(sig != null) {
sig = sig.replaceAll("\\.", "/"); //$NON-NLS-1$ //$NON-NLS-2$
if(sig.equals(fSignature)) {
IMarker marker = fBreakpoint.getMarker();
int currentstart = marker.getAttribute(IMarker.CHAR_START, -1);
int charstart = name.getStartPosition();
if(currentstart != charstart) {
try {
marker.setAttribute(IMarker.CHAR_START, charstart);
marker.setAttribute(IMarker.CHAR_END, charstart + name.getLength());
}
catch(CoreException ce) {}
}
}
}
}
return false;
}
/**
* Creates a method signature from a specified {@link MethodDeclaration}
* @param node
* @return the signature for the given method node or <code>null</code>
*/
private String getMethodSignatureFromNode(MethodDeclaration node) {
Assert.isNotNull(node);
List params = node.parameters();
List rparams = getParametersTypeNames(params);
if(rparams.size() == params.size()) {
if(!node.isConstructor()) {
Type returnType = node.getReturnType2();
if (returnType != null) {
String rtype = getTypeSignature(returnType);
if(rtype != null) {
return Signature.createMethodSignature((String[]) rparams.toArray(new String[rparams.size()]), rtype);
}
}
}
else {
StringBuffer buffer = new StringBuffer();
buffer.append("<init>"); //$NON-NLS-1$
collectSyntheticParam(node, rparams);
buffer.append(Signature.createMethodSignature((String[]) rparams.toArray(new String[rparams.size()]), Signature.SIG_VOID));
return buffer.toString();
}
}
return null;
}
/**
* Returns the listing of the signatures of the parameters passed in
* @param rawparams
* @return a listing of signatures for the specified parameters
*/
private List getParametersTypeNames(List rawparams) {
List rparams = new ArrayList(rawparams.size());
SingleVariableDeclaration param = null;
String pname = null;
for(Iterator iter = rawparams.iterator(); iter.hasNext();) {
param = (SingleVariableDeclaration) iter.next();
pname = getTypeSignature(param.getType());
if(pname != null) {
rparams.add(pname);
}
}
return rparams;
}
/**
* Processes the signature for the given {@link Type}
* @param type the type to process
* @return the signature for the type or <code>null</code> if one could not be
* derived
*/
private String getTypeSignature(Type type) {
ITypeBinding binding = type.resolveBinding();
if(binding == null) {
return null;
}
switch(type.getNodeType()) {
case ASTNode.PRIMITIVE_TYPE:
case ASTNode.QUALIFIED_TYPE:
case ASTNode.SIMPLE_TYPE: {
return Signature.createTypeSignature(binding.getQualifiedName(), true);
}
case ASTNode.ARRAY_TYPE: {
ArrayType a = (ArrayType) type;
return Signature.createArraySignature(getTypeSignature(a.getElementType()), a.getDimensions());
}
case ASTNode.PARAMETERIZED_TYPE: {
//we don't need to care about the other scoping types only the base type
return getTypeSignature(((ParameterizedType) type).getType());
}
}
return null;
}
/**
* Collects the synthetic parameter of the fully qualified name of the enclosing context for a constructor of an inner type
* @param method the constructor declaration
* @param rparams the listing of parameters to add to
*/
private void collectSyntheticParam(final MethodDeclaration method, List rparams) {
Assert.isNotNull(method);
if(isInTopLevelType(method)) {
return;
}
ASTNode parent = method.getParent();
StringBuffer name = new StringBuffer();
while(parent != null) {
parent = parent.getParent();
if(parent instanceof AbstractTypeDeclaration) {
AbstractTypeDeclaration type = (AbstractTypeDeclaration) parent;
name.insert(0, type.getName().getFullyQualifiedName());
if(type.isMemberTypeDeclaration()) {
name.insert(0, '$');
}
continue;
}
if(parent instanceof CompilationUnit) {
CompilationUnit cunit = (CompilationUnit) parent;
PackageDeclaration pdec = cunit.getPackage();
if(pdec != null) {
name.insert(0, '.');
name.insert(0, cunit.getPackage().getName().getFullyQualifiedName());
}
}
}
name.insert(0, "L"); //$NON-NLS-1$
name.append(';');
if(name.length() > 2) {
rparams.add(0, name.toString());
}
}
/**
* Determines if the given {@link MethodDeclaration} is present in a top level type
* @param method
* @return
*/
private boolean isInTopLevelType(final MethodDeclaration method) {
TypeDeclaration type = (TypeDeclaration) method.getParent();
return type != null && type.isPackageMemberTypeDeclaration();
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IBreakpointImportParticipant#matches(java.util.Map, org.eclipse.debug.core.model.IBreakpoint)
*/
public boolean matches(Map attributes, IBreakpoint breakpoint) throws CoreException {
if(attributes == null || breakpoint == null) {
return false;
}
String type = (String) attributes.get("type"); //$NON-NLS-1$
if(type == null) {
return false;
}
if(!breakpoint.getMarker().getType().equals(type)) {
return false;
}
if(breakpoint instanceof JavaClassPrepareBreakpoint) {
return matchesClassBreakpoint(attributes, (JavaClassPrepareBreakpoint) breakpoint);
}
if(breakpoint instanceof JavaExceptionBreakpoint) {
return matchesExceptionBreakpoint(attributes, (JavaExceptionBreakpoint) breakpoint);
}
if(breakpoint instanceof JavaMethodBreakpoint) {
return matchesMethodBreakpoint(attributes, (JavaMethodBreakpoint) breakpoint);
}
if(breakpoint instanceof JavaMethodEntryBreakpoint) {
return matchesMethodEntryBreakpoint(attributes, (JavaMethodEntryBreakpoint) breakpoint);
}
if(breakpoint instanceof JavaWatchpoint){
return matchesWatchpoint(attributes, (JavaWatchpoint) breakpoint);
}
if(breakpoint instanceof JavaStratumLineBreakpoint) {
return matchesStratumLineBreakpoint(attributes, (JavaStratumLineBreakpoint) breakpoint);
}
if(breakpoint instanceof JavaPatternBreakpoint) {
return matchesPatternBreakpoint(attributes, (JavaPatternBreakpoint) breakpoint);
}
if(breakpoint instanceof JavaTargetPatternBreakpoint){
return matchesTargetPatternBreakpoint(attributes, (JavaTargetPatternBreakpoint) breakpoint);
}
if(breakpoint instanceof JavaLineBreakpoint) {
return matchesLineBreakpoint(attributes, (JavaLineBreakpoint) breakpoint);
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IBreakpointImportParticipant#verify(org.eclipse.debug.core.model.IBreakpoint)
*/
public void verify(IBreakpoint breakpoint) throws CoreException {
IResource resource = breakpoint.getMarker().getResource();
CompilationUnit unit = null;
if(resource != null && resource.getType() == IResource.FILE) {
ICompilationUnit cunit = JavaCore.createCompilationUnitFrom((IFile) resource);
if(cunit != null) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(cunit);
parser.setResolveBindings(true);
unit = (CompilationUnit) parser.createAST(new NullProgressMonitor());
}
}
if(unit != null) {
if(breakpoint instanceof JavaClassPrepareBreakpoint ||
breakpoint instanceof JavaWatchpoint ||
breakpoint instanceof JavaMethodEntryBreakpoint ||
breakpoint instanceof JavaMethodBreakpoint) {
unit.accept(new BreakpointVerifier(breakpoint, unit));
}
else if(breakpoint instanceof JavaLineBreakpoint) {
JavaLineBreakpoint bp = (JavaLineBreakpoint) breakpoint;
//line breakpoint use the ValidBreakpointLocationLocator to (re)place it
int currentline = bp.getLineNumber();
ValidBreakpointLocationLocator locator = new ValidBreakpointLocationLocator(unit, currentline, true, true);
unit.accept(locator);
int newline = locator.getLineLocation();
if(locator.getLocationType() == ValidBreakpointLocationLocator.LOCATION_LINE) {
if(currentline != newline) {
bp.getMarker().setAttribute(IMarker.LINE_NUMBER, newline);
}
}
}
}
}
/**
* Compares two attributes in a <code>null</code> safe way
* @param attr1 the first attribute
* @param attr2 the second attribute
* @return true if the attributes are equal, false otherwise. If both attributes are <code>null</code>
* they are considered to be equal
*/
private boolean attributesEqual(Object attr1, Object attr2) {
if(attr1 == null) {
return attr2 == null;
}
return attr1.equals(attr2);
}
/**
* Returns if the given map of attributes matches the given line breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesLineBreakpoint(Map attributes, JavaLineBreakpoint breakpoint) throws CoreException {
Integer line = (Integer) attributes.get(IMarker.LINE_NUMBER);
return breakpoint.getLineNumber() == (line == null ? -1 : line.intValue()) &&
attributesEqual(breakpoint.getTypeName(), attributes.get(JavaBreakpoint.TYPE_NAME));
}
/**
* Returns if the given map of attributes matches the given class prepare breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesClassBreakpoint(Map attributes, JavaClassPrepareBreakpoint breakpoint) throws CoreException {
Integer type = (Integer) attributes.get(JavaClassPrepareBreakpoint.MEMBER_TYPE);
return attributesEqual(breakpoint.getTypeName(), attributes.get(JavaBreakpoint.TYPE_NAME)) &&
breakpoint.getMemberType() == (type == null ? -1 : type.intValue());
}
/**
* Returns if the given map of attributes matches the given exception breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesExceptionBreakpoint(Map attributes, JavaExceptionBreakpoint breakpoint) throws CoreException {
return attributesEqual(breakpoint.getTypeName(), attributes.get(JavaBreakpoint.TYPE_NAME));
}
/**
* Returns if the given map of attributes matches the given method breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesMethodBreakpoint(Map attributes, JavaMethodBreakpoint breakpoint) throws CoreException {
return attributesEqual(breakpoint.getTypeName(), attributes.get(JavaBreakpoint.TYPE_NAME)) &&
attributesEqual(breakpoint.getMethodName(), attributes.get(JavaMethodBreakpoint.METHOD_NAME)) &&
attributesEqual(breakpoint.getMethodSignature(), attributes.get(JavaMethodBreakpoint.METHOD_SIGNATURE));
}
/**
* Returns if the given map of attributes matches the given method entry breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesMethodEntryBreakpoint(Map attributes, JavaMethodEntryBreakpoint breakpoint) throws CoreException {
return attributesEqual(breakpoint.getTypeName(), attributes.get(JavaBreakpoint.TYPE_NAME)) &&
attributesEqual(breakpoint.getMethodName(), attributes.get(JavaMethodBreakpoint.METHOD_NAME)) &&
attributesEqual(breakpoint.getMethodSignature(), attributes.get(JavaMethodBreakpoint.METHOD_SIGNATURE));
}
/**
* Returns if the given map of attributes matches the given watchpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the watchpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesWatchpoint(Map attributes, JavaWatchpoint watchpoint) throws CoreException {
return watchpoint.getFieldName().equals(attributes.get(JavaWatchpoint.FIELD_NAME)) &&
attributesEqual(watchpoint.getTypeName(), attributes.get(JavaBreakpoint.TYPE_NAME));
}
/**
* Returns if the given map of attributes matches the given stratum line breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesStratumLineBreakpoint(Map attributes, JavaStratumLineBreakpoint breakpoint) throws CoreException {
Integer line = (Integer) attributes.get(IMarker.LINE_NUMBER);
return breakpoint.getLineNumber() == (line == null ? -1 : line.intValue()) &&
attributesEqual(breakpoint.getSourceName(), attributes.get(JavaLineBreakpoint.SOURCE_NAME)) &&
attributesEqual(breakpoint.getStratum(), attributes.get(JavaStratumLineBreakpoint.STRATUM)) &&
attributesEqual(breakpoint.getSourcePath(), attributes.get(JavaStratumLineBreakpoint.SOURCE_PATH));
}
/**
* Returns if the given map of attributes matches the given pattern breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesPatternBreakpoint(Map attributes, JavaPatternBreakpoint breakpoint) throws CoreException {
Integer line = (Integer) attributes.get(IMarker.LINE_NUMBER);
return breakpoint.getLineNumber() == (line == null ? -1 : line.intValue()) &&
attributesEqual(breakpoint.getSourceName(), attributes.get(JavaLineBreakpoint.SOURCE_NAME)) &&
//TDOD comparing pattern too restrictive??
breakpoint.getPattern().equals(attributes.get(JavaPatternBreakpoint.PATTERN));
}
/**
* Returns if the given map of attributes matches the given target pattern breakpoint
* @param attributes
* @param breakpoint
* @return true if the attributes match the breakpoints' attributes, false otherwise
* @throws CoreException
*/
private boolean matchesTargetPatternBreakpoint(Map attributes, JavaTargetPatternBreakpoint breakpoint) throws CoreException {
Integer line = (Integer) attributes.get(IMarker.LINE_NUMBER);
return breakpoint.getLineNumber() == (line == null ? -1 : line.intValue()) &&
attributesEqual(breakpoint.getTypeName(), attributes.get(JavaBreakpoint.TYPE_NAME)) &&
attributesEqual(breakpoint.getSourceName(), attributes.get(JavaLineBreakpoint.SOURCE_NAME));
}
}