processing.mode.java.debug.LineBreakpoint Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-mode Show documentation
Show all versions of java-mode Show documentation
Processing is a programming language, development environment, and online community.
This Java Mode package contains the Java mode for Processing IDE.
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-16 The Processing Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.debug;
import java.util.List;
import processing.app.Messages;
import processing.mode.java.Debugger;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Location;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.request.BreakpointRequest;
/**
* Model/Controller of a line breakpoint. Can be set before or while debugging.
* Adds a highlight using the debuggers view ({@link DebugEditor}).
*/
public class LineBreakpoint implements ClassLoadListener {
protected Debugger dbg; // the debugger
protected LineID line; // the line this breakpoint is set on
protected BreakpointRequest bpr; // the request on the VM's event request manager
protected String className;
/**
* Create a {@link LineBreakpoint}. If in a debug session, will try to
* immediately set the breakpoint. If not in a debug session or the
* corresponding class is not yet loaded the breakpoint will activate on
* class load.
*
* @param line the line id to create the breakpoint on
* @param dbg the {@link Debugger}
*/
public LineBreakpoint(LineID line, Debugger dbg) {
this.line = line;
line.startTracking(dbg.getEditor().getTab(line.fileName()).getDocument());
this.dbg = dbg;
this.className = className();
set(); // activate the breakpoint (show highlight, attach if debugger is running)
Messages.log("LBP Created " + toString() + " class: " + this.className);
}
/**
* Create a {@link LineBreakpoint} on a line in the current tab.
* @param lineIdx the line index of the current tab to create the breakpoint
*/
// TODO: remove and replace by {@link #LineBreakpoint(LineID line, Debugger dbg)}
public LineBreakpoint(int lineIdx, Debugger dbg) {
this(dbg.getEditor().getLineIDInCurrentTab(lineIdx), dbg);
}
/**
* Get the line id this breakpoint is on.
*/
public LineID lineID() {
return line;
}
/**
* Test if this breakpoint is on a certain line.
*
* @param testLine the line id to test
* @return true if this breakpoint is on the given line
*/
public boolean isOnLine(LineID testLine) {
return line.equals(testLine);
}
/**
* Attach this breakpoint to the VM. Creates and enables a
* {@link BreakpointRequest}. VM needs to be paused.
*
* @param theClass class to attach to
* @return true on success
*/
protected boolean attach(ReferenceType theClass) {
if (theClass == null || className == null ||
!className.equals(parseTopLevelClassName(theClass.name()))) {
return false;
}
log("trying to attach: " + line.fileName + ":" + line.lineIdx + " to " + theClass.name());
if (!dbg.isPaused()) {
log("can't attach breakpoint, debugger not paused");
return false;
}
// find line in java space
LineID javaLine = dbg.sketchToJavaLine(line);
if (javaLine == null) {
log("couldn't find line " + line + " in the java code");
return false;
}
try {
log("BPs of class: " + theClass + ", line " + (javaLine.lineIdx() + 1));
List locations = theClass.locationsOfLine(javaLine.lineIdx() + 1);
if (locations.isEmpty()) {
log("no location found for line " + line + " -> " + javaLine);
return false;
}
// use first found location
bpr = dbg.vm().eventRequestManager().createBreakpointRequest(locations.get(0));
bpr.enable();
log("attached breakpoint to " + line + " -> " + javaLine);
return true;
} catch (AbsentInformationException ex) {
Messages.loge(null, ex);
}
return false;
}
protected boolean isAttached() {
return bpr != null;
}
/**
* Detach this breakpoint from the VM. Deletes the
* {@link BreakpointRequest}.
*/
public void detach() {
if (bpr != null) {
try {
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
} catch (VMDisconnectedException ignore) { }
bpr = null;
}
}
/**
* Set this breakpoint. Adds the line highlight. If Debugger is paused
* also attaches the breakpoint by calling {@link #attach()}.
*/
protected void set() {
dbg.addClassLoadListener(this); // class may not yet be loaded
dbg.getEditor().addBreakpointedLine(line);
if (className != null && dbg.isPaused()) { // debugging right now, try to attach
for (ReferenceType rt : dbg.getClasses()) {
// try to attach to all top level or nested classes
if (attach(rt)) break;
}
}
if (dbg.getEditor().isInCurrentTab(line)) {
dbg.getEditor().getSketch().setModified(true);
}
}
/**
* Remove this breakpoint. Clears the highlight and detaches
* the breakpoint if the debugger is paused.
*/
public void remove() {
dbg.removeClassLoadListener(this);
//System.out.println("removing " + line.lineIdx());
dbg.getEditor().removeBreakpointedLine(line.lineIdx());
if (dbg.isPaused()) {
// immediately remove the breakpoint
detach();
}
line.stopTracking();
if (dbg.getEditor().isInCurrentTab(line)) {
dbg.getEditor().getSketch().setModified(true);
}
}
@Override
public String toString() {
return line.toString();
}
/**
* Get the name of the class this breakpoint belongs to. Needed for
* fetching the right location to create a breakpoint request.
* @return the class name
*/
protected String className() {
if (line.fileName().endsWith(".pde")) {
// standard tab
return dbg.getEditor().getSketch().getName();
}
if (line.fileName().endsWith(".java")) {
// pure java tab
return line.fileName().substring(0, line.fileName().lastIndexOf(".java"));
}
return null;
}
/**
* Event handler called when a class is loaded in the debugger. Causes the
* breakpoint to be attached, if its class was loaded.
*
* @param theClass the class that was just loaded.
*/
@Override
public void classLoaded(ReferenceType theClass) {
if (!isAttached()) {
// try to attach
attach(theClass);
}
}
static public String parseTopLevelClassName(String name) {
// Get rid of nested class name
int dollar = name.indexOf('$');
return (dollar == -1) ? name : name.substring(0, dollar);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
private void log(String msg, Object... args) {
if (args != null && args.length != 0) {
Messages.logf(getClass().getName() + " " + msg, args);
} else {
Messages.log(getClass().getName() + " " + msg);
}
}
}