/*
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.debug.ui;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
import org.eclipse.jdt.debug.core.JDIDebugModel;
import org.eclipse.jdt.groovy.search.VariableScope;
import org.eclipse.jdt.internal.debug.ui.BreakpointUtils;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.debug.ui.actions.ActionMessages;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.texteditor.IEditorStatusLine;
/**
* Job used to verify the position of a breakpoint
* Somewhat based on org.eclipse.jdt.internal.debug.ui.actions.BreakpointLocationVerifierJob
*/
public class BreakpointLocationVerifierJob extends Job {
public final static Object FAMILY = new Object();
/**
* The temporary breakpoint that has been set. Can be <code>null</code> if the callee was not able
* to check if a breakpoint was already set at this position.
*/
private IJavaLineBreakpoint fBreakpoint;
/**
* The number of the line where the breakpoint has been requested.
*/
private int fLineNumber;
/**
* The qualified type name of the class where the temporary breakpoint as been set.
* Can be <code>null</code> if fBreakpoint is null.
*/
private String fTypeName;
/**
* The type in which should be set the breakpoint.
*/
private IType fType;
/**
* The resource in which should be set the breakpoint.
*/
private IResource fResource;
/**
* The status line to use to display errors
*/
private IEditorStatusLine fStatusLine;
@SuppressWarnings("cast")
public BreakpointLocationVerifierJob(IJavaLineBreakpoint breakpoint, int lineNumber, String typeName, IType type, IResource resource, IEditorPart editorPart) {
super(ActionMessages.BreakpointLocationVerifierJob_breakpoint_location);
fBreakpoint = breakpoint;
fLineNumber = lineNumber;
fTypeName = typeName;
fType = type;
fResource = resource;
fStatusLine = (IEditorStatusLine) editorPart.getAdapter(IEditorStatusLine.class);
}
@Override
public IStatus run(IProgressMonitor monitor) {
ICompilationUnit cu = JavaCore.createCompilationUnitFrom((IFile) fResource);
try {
ModuleNode node = null;
if (cu instanceof GroovyCompilationUnit) {
node = ((GroovyCompilationUnit) cu).getModuleNode();
}
if (node == null) {
return new Status(IStatus.WARNING, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, ActionMessages.BreakpointLocationVerifierJob_not_valid_location, null);
}
if (fBreakpoint != null) {
DebugPlugin.getDefault().getBreakpointManager().removeBreakpoint(fBreakpoint, true);
}
ValidBreakpointLocationFinder finder = new ValidBreakpointLocationFinder(fLineNumber);
ASTNode valid = finder.findValidBreakpointLocation(node);
if (valid instanceof MethodNode && ((MethodNode) valid).getNameEnd() > 0) {
createNewMethodBreakpoint((MethodNode) valid, fTypeName);
return new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.OK,
ActionMessages.BreakpointLocationVerifierJob_breakpoint_set, null);
} else if (valid != null) {
createNewLineBreakpoint(valid, fTypeName);
return new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.OK, ActionMessages.BreakpointLocationVerifierJob_breakpoint_set, null);
}
} catch (JavaModelException e) {
} catch (CoreException e) {
}
// Cannot find a valid location
report(ActionMessages.BreakpointLocationVerifierJob_not_valid_location);
return new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.ERROR, ActionMessages.BreakpointLocationVerifierJob_not_valid_location, null);
}
private void createNewMethodBreakpoint(MethodNode node, String typeName) throws CoreException {
Map<String, Object> newAttributes = new HashMap<String, Object>(10);
int start = node.getNameStart();
int end = node.getNameEnd();
if (fType != null) {
IJavaElement elt = fType.getTypeRoot().getElementAt(start);
if (elt != null) {
IMethod method = (IMethod) elt;
BreakpointUtils.addJavaBreakpointAttributesWithMemberDetails(newAttributes, fType, start, end);
BreakpointUtils.addJavaBreakpointAttributes(newAttributes, method);
JDIDebugModel.createMethodBreakpoint(fResource, typeName, node.getName(), createMethodSignature(node), true, false, false, node.getLineNumber(), start, end, 0, true, newAttributes);
}
}
}
private String createMethodSignature(MethodNode node) {
String returnType = createTypeSignatureStr(node.getReturnType());
String[] parameterTypes = new String[node.getParameters().length];
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypes[i] = createTypeSignatureStr(node.getParameters()[i].getType());
}
return Signature.createMethodSignature(parameterTypes, returnType).replace('.', '/');
}
private String createTypeSignatureStr(ClassNode node) {
if (node == null) {
node = VariableScope.OBJECT_CLASS_NODE;
}
String name = node.getName();
if (name.startsWith("[")) {
return name;
} else {
return Signature.createTypeSignature(name, true);
}
}
/**
* Create a new breakpoint at the right position.
*/
private void createNewLineBreakpoint(ASTNode node, String typeName) throws CoreException {
// check to make sure that breakpoint doesn't exist on this line
// line may have moved by the validator
if (JDIDebugModel.lineBreakpointExists(typeName, node.getLineNumber()) != null) {
return;
}
Map<String, Object> newAttributes = new HashMap<String, Object>(10);
int start= node.getStart();
int end= node.getEnd();
if (fType != null) {
BreakpointUtils.addJavaBreakpointAttributesWithMemberDetails(newAttributes, fType, start, end);
}
JDIDebugModel.createLineBreakpoint(fResource, typeName, node.getLineNumber(), start, end, 0, true, newAttributes);
}
protected void report(final String message) {
JDIDebugUIPlugin.getStandardDisplay().asyncExec(new Runnable() {
public void run() {
if (fStatusLine != null) {
fStatusLine.setMessage(true, message, null);
}
if (message != null && JDIDebugUIPlugin.getActiveWorkbenchShell() != null) {
Display.getCurrent().beep();
}
}
});
}
@Override
public boolean belongsTo(Object family) {
return family == FAMILY;
}
}