blob: e47f2c1e460ab2e8daa759661ca40dfae4960f09 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 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.wst.jsdt.debug.internal.ui.breakpoints;
import java.util.HashMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
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.debug.core.model.IBreakpoint;
import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IEditorStatusLine;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.wst.jsdt.core.IClassFile;
import org.eclipse.wst.jsdt.core.IFunction;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
import org.eclipse.wst.jsdt.core.IMember;
import org.eclipse.wst.jsdt.core.ISourceRange;
import org.eclipse.wst.jsdt.core.IType;
import org.eclipse.wst.jsdt.core.ITypeRoot;
import org.eclipse.wst.jsdt.core.dom.AST;
import org.eclipse.wst.jsdt.core.dom.ASTParser;
import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptBreakpoint;
import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptFunctionBreakpoint;
import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptLineBreakpoint;
import org.eclipse.wst.jsdt.debug.core.model.JavaScriptDebugModel;
import org.eclipse.wst.jsdt.debug.internal.ui.DebugWCManager;
import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
import org.eclipse.wst.jsdt.internal.ui.javaeditor.ASTProvider;
/**
* JavaScript adapter for toggling breakpoints in the JSDT editor
*
* @since 1.0
*/
public class ToggleBreakpointAdapter implements IToggleBreakpointsTargetExtension {
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension#canToggleBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public boolean canToggleBreakpoints(IWorkbenchPart part, ISelection selection) {
return selection instanceof ITextSelection;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleLineBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) {
return selection instanceof ITextSelection;
}
/**
* Toggles a line breakpoint
* @param part
* @param selection
* @param element
*/
void toggleLineBreakpoint(final IWorkbenchPart part, final ITextSelection selection, final IJavaScriptElement element, final int linenumber) {
Job job = new Job("Toggle Line Breakpoints") { //$NON-NLS-1$
protected IStatus run(IProgressMonitor monitor) {
try {
ITextEditor editor = getTextEditor(part);
if(editor != null && part instanceof IEditorPart) {
if(element == null) {
reportToStatusLine(part, Messages.failed_line_bp_no_element);
return Status.CANCEL_STATUS;
}
IResource resource = element.getResource();
if(resource == null) {
reportToStatusLine(part, NLS.bind(Messages.failed_line_bp_no_resource, element.getElementName()));
return Status.CANCEL_STATUS;
}
IBreakpoint bp = lineBreakpointExists(resource, linenumber);
if(bp != null) {
DebugPlugin.getDefault().getBreakpointManager().removeBreakpoint(bp, true);
return Status.OK_STATUS;
}
IDocumentProvider documentProvider = editor.getDocumentProvider();
IDocument document = documentProvider.getDocument(editor.getEditorInput());
int charstart = -1, charend = -1;
try {
IRegion line = document.getLineInformation(linenumber - 1);
charstart = line.getOffset();
charend = charstart + line.getLength();
}
catch (BadLocationException ble) {}
HashMap attributes = new HashMap();
attributes.put(IJavaScriptBreakpoint.TYPE_NAME, getTypeName(element));
attributes.put(IJavaScriptBreakpoint.SCRIPT_PATH, getScriptPath(element));
attributes.put(IJavaScriptBreakpoint.ELEMENT_HANDLE, element.getHandleIdentifier());
JavaScriptDebugModel.createLineBreakpoint(resource, linenumber, charstart, charend, attributes, true);
return Status.OK_STATUS;
}
reportToStatusLine(part, Messages.failed_to_create_line_bp);
return Status.CANCEL_STATUS;
}
catch(CoreException ce) {
return ce.getStatus();
}
}
};
job.setPriority(Job.INTERACTIVE);
job.setSystem(true);
job.schedule();
}
/**
* Callback from {@link JavaScriptHtmlBreakpointProvider}
* @param resource
* @param document
* @param linenumber
* @throws CoreException
*/
void addBreakpoint(IResource resource, IDocument document, int linenumber) throws CoreException {
IBreakpoint bp = lineBreakpointExists(resource, linenumber);
if(bp != null) {
DebugPlugin.getDefault().getBreakpointManager().removeBreakpoint(bp, true);
}
int charstart = -1, charend = -1;
try {
IRegion line = document.getLineInformation(linenumber - 1);
charstart = line.getOffset();
charend = charstart + line.getLength();
}
catch (BadLocationException ble) {}
HashMap attributes = new HashMap();
attributes.put(IJavaScriptBreakpoint.TYPE_NAME, null);
attributes.put(IJavaScriptBreakpoint.SCRIPT_PATH, resource.getFullPath().makeAbsolute().toString());
attributes.put(IJavaScriptBreakpoint.ELEMENT_HANDLE, null);
JavaScriptDebugModel.createLineBreakpoint(resource, linenumber, charstart, charend, attributes, true);
}
/**
* Returns the path to the script in the workspace or the name of the script in the event it is
* and external or virtual script
* @param element
* @return the path to the script
*/
String getScriptPath(IJavaScriptElement element) {
IPath path = element.getPath();
return path.makeAbsolute().toString();
}
/**
* Resolves the type name from the given element
* @param element
* @return
*/
String getTypeName(IJavaScriptElement element) {
switch(element.getElementType()) {
case IJavaScriptElement.TYPE: {
return ((IType)element).getFullyQualifiedName();
}
case IJavaScriptElement.METHOD:
case IJavaScriptElement.FIELD: {
IMember member = (IMember) element;
IType type = member.getDeclaringType();
if(type != null) {
return type.getFullyQualifiedName();
}
}
//$FALL-THROUGH$
default: {
return null;
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleLineBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public void toggleLineBreakpoints(final IWorkbenchPart part, final ISelection selection) throws CoreException {
//do nothing
}
/**
* Resolves the declaring type for the member
* @param member
* @return the declaring type for the member
*/
IType resolveType(IJavaScriptElement element) {
switch(element.getElementType()) {
case IJavaScriptElement.TYPE: {
return (IType) element;
}
case IJavaScriptElement.METHOD:
case IJavaScriptElement.FIELD: {
IMember member = (IMember) element;
IType type = member.getDeclaringType();
if(type == null) {
ITypeRoot root = (ITypeRoot) member.getParent();
type = root.findPrimaryType();
}
return type;
}
default: {
//already a type root
if(element instanceof ITypeRoot) {
return ((ITypeRoot)element).findPrimaryType();
}
}
}
return null;
}
/**
* Resolves the region from the given document and tries to parse the text of the region. Returns the {@link JavaScriptUnit}
* representing the region or <code>null</code> if it could not be computed
*
* @param doc
* @param offset
* @return the {@link JavaScriptUnit} for the region from the document or <code>null</code>
*/
JavaScriptUnit parse(IDocument doc, int offset) {
try {
ITypedRegion region = doc.getPartition(offset);
if(region != null) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
//parser.setKind(ASTParser.K_STATEMENTS);
parser.setSource(doc.get(offset, region.getLength()).toCharArray());
return (JavaScriptUnit)parser.createAST(null);
}
}
catch(BadLocationException ble) {
//do nothing, return null
}
return null;
}
/**
* Returns the resource on which a breakpoint marker should
* be created for the given member. The resource returned is the
* associated file, or workspace root in the case of a binary in
* an external archive.
*
* @param element member in which a breakpoint is being created
* @return resource the resource on which a breakpoint marker
* should be created
*/
IResource getBreakpointResource(IJavaScriptElement element) {
IResource res = element.getResource();
if (res == null) {
res = ResourcesPlugin.getWorkspace().getRoot();
}
else if(!res.getProject().exists()) {
res = ResourcesPlugin.getWorkspace().getRoot();
}
return res;
}
/**
* Returns the {@link ITypeRoot} for the given editor input
* @param input
* @return the {@link ITypeRoot} for the editor input or <code>null</code>
*/
ITypeRoot getTypeRoot(IEditorInput input) {
ITypeRoot root = (ITypeRoot) input.getAdapter(IClassFile.class);
if(root == null) {
root = DebugWCManager.getCompilationUnit(input, false);
}
return root;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleMethodBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) {
return selection instanceof ITextSelection;
}
/**
* Delegate for toggling a method breakpoint
* @param part
* @param element
* @param line
*/
void toggleMethodBreakpoint(final IWorkbenchPart part, final IJavaScriptElement element, final int line) {
Job job = new Job("Toggle Function Breakpoints") { //$NON-NLS-1$
protected IStatus run(IProgressMonitor monitor) {
try {
if(element == null) {
reportToStatusLine(part, Messages.failed_function_bp_no_element);
return Status.CANCEL_STATUS;
}
if(element.getElementType() == IJavaScriptElement.METHOD) {
IFunction method = (IFunction) element;
IResource resource = element.getResource();
if(resource == null) {
reportToStatusLine(part, NLS.bind(Messages.failed_function_bp_no_resource, element.getElementName()));
return Status.CANCEL_STATUS;
}
IBreakpoint bp = methodBreakpointExists(resource, method.getElementName(), method.getSignature());
if(bp != null) {
DebugPlugin.getDefault().getBreakpointManager().removeBreakpoint(bp, true);
return Status.OK_STATUS;
}
ISourceRange range = method.getNameRange();
int start = -1, end = -1;
if(range != null) {
start = range.getOffset();
end = start + range.getLength();
}
HashMap attributes = new HashMap();
//hack if the type is null the member is declared in a top-level type
//nothing else we can do
attributes.put(IJavaScriptBreakpoint.TYPE_NAME, getTypeName(element));
attributes.put(IJavaScriptBreakpoint.SCRIPT_PATH, getScriptPath(element));
attributes.put(IMarker.LINE_NUMBER, new Integer(line));
IJavaScriptFunctionBreakpoint breakpoint = JavaScriptDebugModel.createFunctionBreakpoint(resource, method.getElementName(), method.getSignature(), start, end, attributes, true);
breakpoint.setJavaScriptElementHandle(element.getHandleIdentifier());
return Status.OK_STATUS;
}
reportToStatusLine(part, Messages.failed_to_create_function_bp);
}
catch(CoreException ce) {
return ce.getStatus();
}
return Status.CANCEL_STATUS;
}
};
job.setPriority(Job.INTERACTIVE);
job.setSystem(true);
job.schedule();
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleMethodBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public void toggleMethodBreakpoints(final IWorkbenchPart part, final ISelection selection) throws CoreException {
//do nothing
}
/**
* Sends a friendly message to the status line about why we could not set a breakpoint
* @param part
* @param message
*/
void reportToStatusLine(final IWorkbenchPart part, final String message) {
getStandardDisplay().asyncExec(new Runnable() {
public void run() {
IEditorStatusLine statusLine = (IEditorStatusLine) part.getAdapter(IEditorStatusLine.class);
if (statusLine != null) {
if (message != null) {
statusLine.setMessage(true, message, null);
} else {
statusLine.setMessage(true, null, null);
}
}
}
});
}
/**
* Returns the standard display to be used.
*/
Display getStandardDisplay() {
Display display;
display = Display.getCurrent();
if (display == null) {
display = Display.getDefault();
}
return display;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension#toggleBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public void toggleBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException {
if(selection instanceof ITextSelection) {
ITextEditor textEditor = getTextEditor(part);
if(textEditor == null) {
reportToStatusLine(part, Messages.no_editor_could_be_found);
return;
}
ITypeRoot root = getTypeRoot(textEditor.getEditorInput());
if(root instanceof IJavaScriptUnit) {
IJavaScriptUnit unit = (IJavaScriptUnit) root;
synchronized (unit) {
unit.reconcile(IJavaScriptUnit.NO_AST , false, null, null);
}
}
if(root == null) {
reportToStatusLine(part, Messages.type_root_could_not_be_computed);
return;
}
JavaScriptUnit jsunit = JavaScriptPlugin.getDefault().getASTProvider().getAST(root, ASTProvider.WAIT_YES, null);
BreakpointLocationFinder finder = new BreakpointLocationFinder(jsunit, ((TextSelection)selection).getStartLine()+1, false);
jsunit.accept(finder);
switch(finder.getLocation()) {
case BreakpointLocationFinder.UNKNOWN : {
reportToStatusLine(part, Messages.no_valid_location);
return;
}
case BreakpointLocationFinder.LINE: {
IJavaScriptElement element = root.getElementAt(finder.getOffset());
if(element == null) {
element = root;
}
toggleLineBreakpoint(part, (ITextSelection) selection, element, finder.getLineNumber());
return;
}
case BreakpointLocationFinder.FUNCTION: {
toggleMethodBreakpoint(part, root.getElementAt(finder.getOffset()), finder.getLineNumber());
return;
}
}
}
else {
IStructuredSelection ss = (IStructuredSelection) selection;
Object o = ss.getFirstElement();
if(o instanceof IMember) {
IMember member = (IMember) o;
if(member.getElementType() == IJavaScriptElement.METHOD) {
toggleMethodBreakpoint(part, member, -1);
}
}
}
}
/**
* Returns if a line breakpoint exists on the given line in the given resource
* @param resource
* @param linenumber
* @return true if a line breakpoint exists on the given line in the given resource, false otherwise
*/
IBreakpoint lineBreakpointExists(IResource resource, int linenumber) {
IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(JavaScriptDebugModel.MODEL_ID);
IJavaScriptLineBreakpoint breakpoint = null;
for (int i = 0; i < breakpoints.length; i++) {
if(breakpoints[i] instanceof IJavaScriptLineBreakpoint) {
breakpoint = (IJavaScriptLineBreakpoint) breakpoints[i];
try {
if(IJavaScriptLineBreakpoint.MARKER_ID.equals(breakpoint.getMarker().getType()) &&
resource.equals(breakpoint.getMarker().getResource()) &&
linenumber == breakpoint.getLineNumber()) {
return breakpoint;
}
} catch (CoreException e) {}
}
}
return null;
}
/**
* Returns if a method breakpoint exists with the given name and signature in the given resource
* @param resource
* @param name
* @param signature
* @return true if a method breakpoint exists with the given name and signature in the given resource, false otherwise
*/
IBreakpoint methodBreakpointExists(IResource resource, String name, String signature) {
IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(JavaScriptDebugModel.MODEL_ID);
IJavaScriptBreakpoint breakpoint = null;
for (int i = 0; i < breakpoints.length; i++) {
breakpoint = (IJavaScriptBreakpoint) breakpoints[i];
try {
if(IJavaScriptFunctionBreakpoint.MARKER_ID.equals(breakpoint.getMarker().getType()) &&
resource.equals(breakpoint.getMarker().getResource()) &&
name.equals(breakpoint.getMarker().getAttribute(IJavaScriptFunctionBreakpoint.FUNCTION_NAME)) &&
signature.equals(breakpoint.getMarker().getAttribute(IJavaScriptFunctionBreakpoint.FUNCTION_SIGNAURE))) {
return breakpoint;
}
} catch (CoreException e) {}
}
return null;
}
/**
* Returns the resource associated with the specified editor part
* @param editor the currently active editor part
* @return the corresponding <code>IResource</code> from the editor part
*/
IResource getResource(IEditorPart editor) {
IEditorInput editorInput = editor.getEditorInput();
IResource resource = (IResource) editorInput.getAdapter(IFile.class);
if (resource == null) {
resource = ResourcesPlugin.getWorkspace().getRoot();
}
return resource;
}
/**
* Returns the text editor associated with the given part or <code>null</code>
* if none. In case of a multi-page editor, this method should be used to retrieve
* the correct editor to perform the breakpoint operation on.
*
* @param part workbench part
* @return text editor part or <code>null</code>
*/
ITextEditor getTextEditor(IWorkbenchPart part) {
if (part instanceof ITextEditor) {
return (ITextEditor) part;
}
return (ITextEditor) part.getAdapter(ITextEditor.class);
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleWatchpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleWatchpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
*/
public void toggleWatchpoints(IWorkbenchPart part, ISelection selection) throws CoreException {}
}