moving to new location

This commit is contained in:
benfry
2012-12-06 20:21:52 +00:00
parent 223ba9f754
commit 08dc2b87e1
31 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.java2;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.Value;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Specialized {@link VariableNode} for representing single fields in an array.
* Overrides {@link #setValue} to properly change the value of the encapsulated
* array field.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class ArrayFieldNode extends VariableNode {
protected ArrayReference array;
protected int index;
/**
* Construct an {@link ArrayFieldNode}.
*
* @param name the name
* @param type the type
* @param value the value
* @param array a reference to the array
* @param index the index inside the array
*/
public ArrayFieldNode(String name, String type, Value value, ArrayReference array, int index) {
super(name, type, value);
this.array = array;
this.index = index;
}
@Override
public void setValue(Value value) {
try {
array.setValue(index, value);
} catch (InvalidTypeException ex) {
Logger.getLogger(ArrayFieldNode.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotLoadedException ex) {
Logger.getLogger(ArrayFieldNode.class.getName()).log(Level.SEVERE, null, ex);
}
this.value = value;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.java2;
import com.sun.jdi.ReferenceType;
/**
* Listener to be notified when a class is loaded in the debugger. Used by
* {@link LineBreakpoint}s to activate themselves as soon as the respective
* class is loaded.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public interface ClassLoadListener {
/**
* Event handler called when a class is loaded.
*
* @param theClass the class
*/
public void classLoaded(ReferenceType theClass);
}

View File

@@ -0,0 +1,367 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import java.io.*;
import java.lang.reflect.Method;
import processing.app.Base;
import processing.app.SketchException;
import processing.core.PApplet;
/**
* Copied from processing.mode.java.Compiler, just added -g switch to generate
* debugging info.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class Compiler extends processing.mode.java.Compiler {
/**
* Compile with ECJ. See http://j.mp/8paifz for documentation.
*
* @return true if successful.
* @throws RunnerException Only if there's a problem. Only then.
*/
// public boolean compile(Sketch sketch,
// File srcFolder,
// File binFolder,
// String primaryClassName,
// String sketchClassPath,
// String bootClassPath) throws RunnerException {
static public boolean compile(DebugBuild build) throws SketchException {
// This will be filled in if anyone gets angry
SketchException exception = null;
boolean success = false;
String baseCommand[] = new String[] {
"-g",
"-Xemacs",
//"-noExit", // not necessary for ecj
"-source", "1.6",
"-target", "1.6",
"-classpath", build.getClassPath(),
"-nowarn", // we're not currently interested in warnings (works in ecj)
"-d", build.getBinFolder().getAbsolutePath() // output the classes in the buildPath
};
//PApplet.println(baseCommand);
// make list of code files that need to be compiled
// String[] sourceFiles = new String[sketch.getCodeCount()];
// int sourceCount = 0;
// sourceFiles[sourceCount++] =
// new File(buildPath, primaryClassName + ".java").getAbsolutePath();
//
// for (SketchCode code : sketch.getCode()) {
// if (code.isExtension("java")) {
// String path = new File(buildPath, code.getFileName()).getAbsolutePath();
// sourceFiles[sourceCount++] = path;
// }
// }
String[] sourceFiles = Base.listFiles(build.getSrcFolder(), false, ".java");
// String[] command = new String[baseCommand.length + sourceFiles.length];
// System.arraycopy(baseCommand, 0, command, 0, baseCommand.length);
// // append each of the files to the command string
// System.arraycopy(sourceFiles, 0, command, baseCommand.length, sourceCount);
String[] command = PApplet.concat(baseCommand, sourceFiles);
//PApplet.println(command);
try {
// Load errors into a local StringBuffer
final StringBuffer errorBuffer = new StringBuffer();
// Create single method dummy writer class to slurp errors from ecj
Writer internalWriter = new Writer() {
public void write(char[] buf, int off, int len) {
errorBuffer.append(buf, off, len);
}
public void flush() { }
public void close() { }
};
// Wrap as a PrintWriter since that's what compile() wants
PrintWriter writer = new PrintWriter(internalWriter);
//result = com.sun.tools.javac.Main.compile(command, writer);
PrintWriter outWriter = new PrintWriter(System.out);
// Version that's not dynamically loaded
//CompilationProgress progress = null;
//success = BatchCompiler.compile(command, outWriter, writer, progress);
// Version that *is* dynamically loaded. First gets the mode class loader
// so that it can grab the compiler JAR files from it.
ClassLoader loader = build.getMode().getJavaModeClassLoader();
//ClassLoader loader = build.getMode().getClassLoader();
try {
Class batchClass =
Class.forName("org.eclipse.jdt.core.compiler.batch.BatchCompiler", false, loader);
Class progressClass =
Class.forName("org.eclipse.jdt.core.compiler.CompilationProgress", false, loader);
Class[] compileArgs =
new Class[] { String[].class, PrintWriter.class, PrintWriter.class, progressClass };
Method compileMethod = batchClass.getMethod("compile", compileArgs);
success = (Boolean)
compileMethod.invoke(null, new Object[] { command, outWriter, writer, null });
} catch (Exception e) {
e.printStackTrace();
throw new SketchException("Unknown error inside the compiler.");
}
// Close out the stream for good measure
writer.flush();
writer.close();
BufferedReader reader =
new BufferedReader(new StringReader(errorBuffer.toString()));
//System.err.println(errorBuffer.toString());
String line = null;
while ((line = reader.readLine()) != null) {
//System.out.println("got line " + line); // debug
// get first line, which contains file name, line number,
// and at least the first line of the error message
String errorFormat = "([\\w\\d_]+.java):(\\d+):\\s*(.*):\\s*(.*)\\s*";
String[] pieces = PApplet.match(line, errorFormat);
//PApplet.println(pieces);
// if it's something unexpected, die and print the mess to the console
if (pieces == null) {
exception = new SketchException("Cannot parse error text: " + line);
exception.hideStackTrace();
// Send out the rest of the error message to the console.
System.err.println(line);
while ((line = reader.readLine()) != null) {
System.err.println(line);
}
break;
}
// translate the java filename and line number into a un-preprocessed
// location inside a source file or tab in the environment.
String dotJavaFilename = pieces[1];
// Line numbers are 1-indexed from javac
int dotJavaLineIndex = PApplet.parseInt(pieces[2]) - 1;
String errorMessage = pieces[4];
exception = build.placeException(errorMessage,
dotJavaFilename,
dotJavaLineIndex);
/*
int codeIndex = 0; //-1;
int codeLine = -1;
// first check to see if it's a .java file
for (int i = 0; i < sketch.getCodeCount(); i++) {
SketchCode code = sketch.getCode(i);
if (code.isExtension("java")) {
if (dotJavaFilename.equals(code.getFileName())) {
codeIndex = i;
codeLine = dotJavaLineIndex;
}
}
}
// if it's not a .java file, codeIndex will still be 0
if (codeIndex == 0) { // main class, figure out which tab
//for (int i = 1; i < sketch.getCodeCount(); i++) {
for (int i = 0; i < sketch.getCodeCount(); i++) {
SketchCode code = sketch.getCode(i);
if (code.isExtension("pde")) {
if (code.getPreprocOffset() <= dotJavaLineIndex) {
codeIndex = i;
//System.out.println("i'm thinkin file " + i);
codeLine = dotJavaLineIndex - code.getPreprocOffset();
}
}
}
}
//System.out.println("code line now " + codeLine);
exception = new RunnerException(errorMessage, codeIndex, codeLine, -1, false);
*/
if (exception == null) {
exception = new SketchException(errorMessage);
}
// for a test case once message parsing is implemented,
// use new Font(...) since that wasn't getting picked up properly.
/*
if (errorMessage.equals("cannot find symbol")) {
handleCannotFindSymbol(reader, exception);
} else if (errorMessage.indexOf("is already defined") != -1) {
reader.readLine(); // repeats the line of code w/ error
int codeColumn = caretColumn(reader.readLine());
exception = new RunnerException(errorMessage,
codeIndex, codeLine, codeColumn);
} else if (errorMessage.startsWith("package") &&
errorMessage.endsWith("does not exist")) {
// Because imports are stripped out and re-added to the 0th line of
// the preprocessed code, codeLine will always be wrong for imports.
exception = new RunnerException("P" + errorMessage.substring(1) +
". You might be missing a library.");
} else {
exception = new RunnerException(errorMessage);
}
*/
if (errorMessage.startsWith("The import ") &&
errorMessage.endsWith("cannot be resolved")) {
// The import poo cannot be resolved
//import poo.shoe.blah.*;
//String what = errorMessage.substring("The import ".length());
String[] m = PApplet.match(errorMessage, "The import (.*) cannot be resolved");
//what = what.substring(0, what.indexOf(' '));
if (m != null) {
// System.out.println("'" + m[1] + "'");
if (m[1].equals("processing.xml")) {
exception.setMessage("processing.xml no longer exists, this code needs to be updated for 2.0.");
System.err.println("The processing.xml library has been replaced " +
"with a new 'XML' class that's built-in.");
handleCrustyCode();
} else {
exception.setMessage("The package " +
"\u201C" + m[1] + "\u201D" +
" does not exist. " +
"You might be missing a library.");
System.err.println("Libraries must be " +
"installed in a folder named 'libraries' " +
"inside the 'sketchbook' folder.");
}
}
// // Actually create the folder and open it for the user
// File sketchbookLibraries = Base.getSketchbookLibrariesFolder();
// if (!sketchbookLibraries.exists()) {
// if (sketchbookLibraries.mkdirs()) {
// Base.openFolder(sketchbookLibraries);
// }
// }
} else if (errorMessage.endsWith("cannot be resolved to a type")) {
// xxx cannot be resolved to a type
//xxx c;
String what = errorMessage.substring(0, errorMessage.indexOf(' '));
if (what.equals("BFont") ||
what.equals("BGraphics") ||
what.equals("BImage")) {
exception.setMessage(what + " has been replaced with P" + what.substring(1));
handleCrustyCode();
} else {
exception.setMessage("Cannot find a class or type " +
"named \u201C" + what + "\u201D");
}
} else if (errorMessage.endsWith("cannot be resolved")) {
// xxx cannot be resolved
//println(xxx);
String what = errorMessage.substring(0, errorMessage.indexOf(' '));
if (what.equals("LINE_LOOP") ||
what.equals("LINE_STRIP")) {
exception.setMessage("LINE_LOOP and LINE_STRIP are not available, " +
"please update your code.");
handleCrustyCode();
} else if (what.equals("framerate")) {
exception.setMessage("framerate should be changed to frameRate.");
handleCrustyCode();
} else if (what.equals("screen")) {
exception.setMessage("Change screen.width and screen.height to " +
"displayWidth and displayHeight.");
handleCrustyCode();
} else if (what.equals("screenWidth") ||
what.equals("screenHeight")) {
exception.setMessage("Change screenWidth and screenHeight to " +
"displayWidth and displayHeight.");
handleCrustyCode();
} else {
exception.setMessage("Cannot find anything " +
"named \u201C" + what + "\u201D");
}
} else if (errorMessage.startsWith("Duplicate")) {
// "Duplicate nested type xxx"
// "Duplicate local variable xxx"
} else {
String[] parts = null;
// The method xxx(String) is undefined for the type Temporary_XXXX_XXXX
//xxx("blah");
// The method xxx(String, int) is undefined for the type Temporary_XXXX_XXXX
//xxx("blah", 34);
// The method xxx(String, int) is undefined for the type PApplet
//PApplet.sub("ding");
String undefined =
"The method (\\S+\\(.*\\)) is undefined for the type (.*)";
parts = PApplet.match(errorMessage, undefined);
if (parts != null) {
if (parts[1].equals("framerate(int)")) {
exception.setMessage("framerate() no longer exists, use frameRate() instead.");
handleCrustyCode();
} else if (parts[1].equals("push()")) {
exception.setMessage("push() no longer exists, use pushMatrix() instead.");
handleCrustyCode();
} else if (parts[1].equals("pop()")) {
exception.setMessage("pop() no longer exists, use popMatrix() instead.");
handleCrustyCode();
} else {
String mess = "The function " + parts[1] + " does not exist.";
exception.setMessage(mess);
}
break;
}
}
if (exception != null) {
// The stack trace just shows that this happened inside the compiler,
// which is a red herring. Don't ever show it for compiler stuff.
exception.hideStackTrace();
break;
}
}
} catch (IOException e) {
String bigSigh = "Error while compiling. (" + e.getMessage() + ")";
exception = new SketchException(bigSigh);
e.printStackTrace();
success = false;
}
// In case there was something else.
if (exception != null) throw exception;
return success;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import java.io.File;
import processing.app.Sketch;
import processing.app.SketchException;
import processing.mode.java.JavaBuild;
/**
* Copied from processing.mode.java.JavaBuild, just changed compiler.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class DebugBuild extends JavaBuild {
public DebugBuild(Sketch sketch) {
super(sketch);
}
/**
* Preprocess and compile sketch. Copied from
* processing.mode.java.JavaBuild, just changed compiler.
*
* @param srcFolder
* @param binFolder
* @param sizeWarning
* @return main class name or null on compile failure
* @throws SketchException
*/
@Override
public String build(File srcFolder, File binFolder, boolean sizeWarning) throws SketchException {
this.srcFolder = srcFolder;
this.binFolder = binFolder;
// Base.openFolder(srcFolder);
// Base.openFolder(binFolder);
// run the preprocessor
String classNameFound = preprocess(srcFolder, sizeWarning);
// compile the program. errors will happen as a RunnerException
// that will bubble up to whomever called build().
// Compiler compiler = new Compiler(this);
// String bootClasses = System.getProperty("sun.boot.class.path");
// if (compiler.compile(this, srcFolder, binFolder, primaryClassName, getClassPath(), bootClasses)) {
if (Compiler.compile(this)) { // use compiler with debug info enabled (-g switch flicked)
sketchClassName = classNameFound;
return classNameFound;
}
return null;
}
public DebugMode getMode() {
return (DebugMode)mode;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import processing.app.Base;
import processing.app.EditorState;
import processing.app.Mode;
import processing.mode.java.JavaMode;
/**
* Debug Mode for Processing. Built on top of JavaMode.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class DebugMode extends JavaMode {
public static final boolean VERBOSE_LOGGING = true;
//public static final boolean VERBOSE_LOGGING = false;
public static final int LOG_SIZE = 512 * 1024; // max log file size (in bytes)
public DebugMode(Base base, File folder) {
super(base, folder);
// use libraries folder from javamode. will make sketches using core libraries work, as well as import libraries and examples menus
// for (Mode m : base.getModeList()) {
// if (m.getClass() == JavaMode.class) {
// JavaMode jMode = (JavaMode) m;
// librariesFolder = jMode.getLibrariesFolder();
// rebuildLibraryList();
// break;
// }
// }
// Fetch examples and reference from java mode
// thx to Manindra (https://github.com/martinleopold/DebugMode/issues/4)
examplesFolder = Base.getContentFile("modes/java/examples");
// https://github.com/martinleopold/DebugMode/issues/6
referenceFolder = Base.getContentFile("modes/java/reference");
// set logging level
Logger globalLogger = Logger.getLogger("");
//Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); // doesn't work on os x
if (VERBOSE_LOGGING) {
globalLogger.setLevel(Level.INFO);
} else {
globalLogger.setLevel(Level.WARNING);
}
// enable logging to file
try {
// settings is writable for built-in modes, mode folder is not writable
File logFolder = Base.getSettingsFile("debug");
if (!logFolder.exists()) {
logFolder.mkdir();
}
File logFile = new File(logFolder, "DebugMode.%g.log");
Handler handler = new FileHandler(logFile.getAbsolutePath(), LOG_SIZE, 10, false);
globalLogger.addHandler(handler);
} catch (IOException ex) {
Logger.getLogger(DebugMode.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(DebugMode.class.getName()).log(Level.SEVERE, null, ex);
}
// output version from manifest file
Package p = DebugMode.class.getPackage();
String titleAndVersion = p.getImplementationTitle() + " (v" + p.getImplementationVersion() + ")";
//System.out.println(titleAndVersion);
Logger.getLogger(DebugMode.class.getName()).log(Level.INFO, titleAndVersion);
}
@Override
public String getTitle() {
return "Experimental";
}
/**
* Create a new editor associated with this mode.
*/
@Override
public processing.app.Editor createEditor(Base base, String path, EditorState state) {
return new DebugEditor(base, path, state, this);
}
/**
* Load a String value from theme.txt
*
* @param attribute the attribute key to load
* @param defaultValue the default value
* @return the attributes value, or the default value if the attribute
* couldn't be loaded
*/
public String loadThemeString(String attribute, String defaultValue) {
String newString = theme.get(attribute);
if (newString != null) {
return newString;
}
Logger.getLogger(DebugMode.class.getName()).log(Level.WARNING, "Error loading String: {0}", attribute);
return defaultValue;
}
/**
* Load a Color value from theme.txt
*
* @param attribute the attribute key to load
* @param defaultValue the default value
* @return the attributes value, or the default value if the attribute
* couldn't be loaded
*/
public Color getThemeColor(String attribute, Color defaultValue) {
Color newColor = theme.getColor(attribute);
if (newColor != null) {
return newColor;
}
System.out.println("error loading color: " + attribute);
Logger.getLogger(DebugMode.class.getName()).log(Level.WARNING, "Error loading Color: {0}", attribute);
return defaultValue;
}
public ClassLoader getJavaModeClassLoader() {
for (Mode m : base.getModeList()) {
if (m.getClass() == JavaMode.class) {
JavaMode jMode = (JavaMode) m;
return jMode.getClassLoader();
}
}
// badness
return null;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import com.sun.jdi.VirtualMachine;
import processing.app.RunnerListener;
import processing.app.SketchException;
import processing.app.exec.StreamRedirectThread;
import processing.mode.java.JavaBuild;
import processing.mode.java.runner.MessageSiphon;
/**
* Runs a {@link JavaBuild}. Launches the build in a new debuggee VM.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class DebugRunner extends processing.mode.java.runner.Runner {
// important inherited fields
// protected VirtualMachine vm;
public DebugRunner(JavaBuild build, RunnerListener listener) throws SketchException {
super(build, listener);
}
/**
* Launch the virtual machine. Simple non-blocking launch. VM starts
* suspended.
*
* @return debuggee VM or null on failure
*/
public VirtualMachine launch() {
String[] machineParamList = getMachineParams();
String[] sketchParamList = getSketchParams();
/*
* System.out.println("vm launch sketch params:"); for (int i=0;
* i<sketchParamList.length; i++) {
* System.out.println(sketchParamList[i]); } System.out.println("vm
* launch machine params:"); for (int i=0; i<machineParamList.length;
* i++) { System.out.println(machineParamList[i]); }
*
*/
vm = launchVirtualMachine(machineParamList, sketchParamList); // will return null on failure
if (vm != null) {
redirectStreams(vm);
}
return vm;
}
/**
* Redirect a VMs output and error streams to System.out and System.err
*
* @param vm the VM
*/
protected void redirectStreams(VirtualMachine vm) {
MessageSiphon ms = new MessageSiphon(vm.process().getErrorStream(), this);
errThread = ms.getThread();
outThread = new StreamRedirectThread("VM output reader", vm.process().getInputStream(), System.out);
errThread.start();
outThread.start();
}
/**
* Additional access to the virtual machine. TODO: may not be needed
*
* @return debugge VM or null if not running
*/
public VirtualMachine vm() {
return vm;
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import processing.app.Base;
import processing.app.Editor;
import processing.mode.java.JavaToolbar;
/**
* Custom toolbar for the editor window. Preserves original button numbers
* ({@link JavaToolbar#RUN}, {@link JavaToolbar#STOP}, {@link JavaToolbar#NEW},
* {@link JavaToolbar#OPEN}, {@link JavaToolbar#SAVE}, {@link JavaToolbar#EXPORT})
* which can be used e.g. in {@link #activate} and
* {@link #deactivate}.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class DebugToolbar extends JavaToolbar {
// preserve original button id's, but re-define so they are accessible
// (they are used by DebugEditor, so they want to be public)
@SuppressWarnings("hiding")
static protected final int RUN = 100; // change this, to be able to get it's name via getTitle()
static protected final int DEBUG = JavaToolbar.RUN;
static protected final int CONTINUE = 101;
static protected final int STEP = 102;
static protected final int TOGGLE_BREAKPOINT = 103;
static protected final int TOGGLE_VAR_INSPECTOR = 104;
@SuppressWarnings("hiding")
static protected final int STOP = JavaToolbar.STOP;
@SuppressWarnings("hiding")
static protected final int NEW = JavaToolbar.NEW;
@SuppressWarnings("hiding")
static protected final int OPEN = JavaToolbar.OPEN;
@SuppressWarnings("hiding")
static protected final int SAVE = JavaToolbar.SAVE;
@SuppressWarnings("hiding")
static protected final int EXPORT = JavaToolbar.EXPORT;
// the sequence of button ids. (this maps button position = index to button ids)
static protected final int[] buttonSequence = {
DEBUG, CONTINUE, STEP, STOP, TOGGLE_BREAKPOINT, TOGGLE_VAR_INSPECTOR,
NEW, OPEN, SAVE, EXPORT
};
public DebugToolbar(Editor editor, Base base) {
super(editor, base);
}
/**
* Initialize buttons. Loads images and adds the buttons to the toolbar.
*/
@Override
public void init() {
Image[][] images = loadImages();
for (int idx = 0; idx < buttonSequence.length; idx++) {
int id = buttonId(idx);
addButton(getTitle(id, false), getTitle(id, true), images[idx], id == NEW || id == TOGGLE_BREAKPOINT);
}
}
/**
* Get the title for a toolbar button. Displayed in the toolbar when
* hovering over a button.
* @param id id of the toolbar button
* @param shift true if shift is pressed
* @return the title
*/
public static String getTitle(int id, boolean shift) {
switch (id) {
case DebugToolbar.RUN:
return JavaToolbar.getTitle(JavaToolbar.RUN, shift);
case STOP:
return JavaToolbar.getTitle(JavaToolbar.STOP, shift);
case NEW:
return JavaToolbar.getTitle(JavaToolbar.NEW, shift);
case OPEN:
return JavaToolbar.getTitle(JavaToolbar.OPEN, shift);
case SAVE:
return JavaToolbar.getTitle(JavaToolbar.SAVE, shift);
case EXPORT:
return JavaToolbar.getTitle(JavaToolbar.EXPORT, shift);
case DEBUG:
if (shift) {
return "Run";
} else {
return "Debug";
}
case CONTINUE:
return "Continue";
case TOGGLE_BREAKPOINT:
return "Toggle Breakpoint";
case STEP:
if (shift) {
return "Step Into";
} else {
return "Step";
}
case TOGGLE_VAR_INSPECTOR:
return "Variable Inspector";
}
return null;
}
/**
* Event handler called when a toolbar button is clicked.
* @param e the mouse event
* @param idx index (i.e. position) of the toolbar button clicked
*/
@Override
public void handlePressed(MouseEvent e, int idx) {
boolean shift = e.isShiftDown();
DebugEditor deditor = (DebugEditor) editor;
int id = buttonId(idx); // convert index/position to button id
switch (id) {
// case DebugToolbar.RUN:
// super.handlePressed(e, JavaToolbar.RUN);
// break;
case STOP:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Stop' toolbar button");
super.handlePressed(e, JavaToolbar.STOP);
break;
case NEW:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'New' toolbar button");
super.handlePressed(e, JavaToolbar.NEW);
break;
case OPEN:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Open' toolbar button");
super.handlePressed(e, JavaToolbar.OPEN);
break;
case SAVE:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Save' toolbar button");
super.handlePressed(e, JavaToolbar.SAVE);
break;
case EXPORT:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Export' toolbar button");
super.handlePressed(e, JavaToolbar.EXPORT);
break;
case DEBUG:
if (shift) {
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Run' toolbar button");
deditor.handleRun();
} else {
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Debug' toolbar button");
deditor.dbg.startDebug();
}
break;
case CONTINUE:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Continue' toolbar button");
deditor.dbg.continueDebug();
break;
case TOGGLE_BREAKPOINT:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Toggle Breakpoint' toolbar button");
deditor.dbg.toggleBreakpoint();
break;
case STEP:
if (shift) {
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Step Into' toolbar button");
deditor.dbg.stepInto();
} else {
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Step' toolbar button");
deditor.dbg.stepOver();
}
break;
// case STEP_INTO:
// deditor.dbg.stepInto();
// break;
// case STEP_OUT:
// deditor.dbg.stepOut();
// break;
case TOGGLE_VAR_INSPECTOR:
Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Variable Inspector' toolbar button");
deditor.toggleVariableInspector();
break;
}
}
/**
* Activate (light up) a button.
* @param id the button id
*/
@Override
public void activate(int id) {
//System.out.println("activate button idx: " + buttonIndex(id));
super.activate(buttonIndex(id));
}
/**
* Set a button to be inactive.
* @param id the button id
*/
@Override
public void deactivate(int id) {
//System.out.println("deactivate button idx: " + buttonIndex(id));
super.deactivate(buttonIndex(id));
}
/**
* Get button position (index) from it's id.
* @param buttonId the button id
* ({@link #RUN}, {@link #DEBUG}, {@link #CONTINUE}), {@link #STEP}, ...)
* @return the button index
*/
protected int buttonIndex(int buttonId) {
for (int i = 0; i < buttonSequence.length; i++) {
if (buttonSequence[i] == buttonId) {
return i;
}
}
return -1;
}
/**
* Get the button id from its position (index).
* @param buttonIdx the button index
* @return the button id
* ({@link #RUN}, {@link #DEBUG}, {@link #CONTINUE}), {@link #STEP}, ...)
*/
protected int buttonId(int buttonIdx) {
return buttonSequence[buttonIdx];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,373 @@
/*
Part of the XQMode project - https://github.com/Manindra29/XQMode
Under Google Summer of Code 2012 -
http://www.google-melange.com/gsoc/homepage/google/gsoc2012
Copyright (C) 2012 Manindra Moharana
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.java2;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import processing.app.Base;
import processing.app.SketchCode;
/**
* The bar on the left of the text area which displays all errors as rectangles. <br>
* <br>
* All errors and warnings of a sketch are drawn on the bar, clicking on one,
* scrolls to the tab and location. Error messages displayed on hover. Markers
* are not in sync with the error line. Similar to eclipse's right error bar
* which displays the overall errors in a document
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class ErrorBar extends JPanel {
/**
* Preferred height of the component
*/
protected int preferredHeight;
/**
* Preferred height of the component
*/
protected int preferredWidth = 12;
/**
* Height of marker
*/
public static final int errorMarkerHeight = 4;
/**
* Color of Error Marker
*/
public Color errorColor = new Color(0xED2630);
/**
* Color of Warning Marker
*/
public Color warningColor = new Color(0xFFC30E);
/**
* Background color of the component
*/
public Color backgroundColor = new Color(0x2C343D);
protected DebugEditor editor;
protected ErrorCheckerService errorCheckerService;
/**
* Stores error markers displayed PER TAB along the error bar.
*/
protected ArrayList<ErrorMarker> errorPoints = new ArrayList<ErrorMarker>();
protected ArrayList<ErrorMarker> errorPointsOld = new ArrayList<ErrorMarker>();
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(backgroundColor);
g.fillRect(0, 0, getWidth(), getHeight());
for (ErrorMarker emarker : errorPoints) {
if (emarker.type == ErrorMarker.Error) {
g.setColor(errorColor);
}
else {
g.setColor(warningColor);
}
g.fillRect(2, emarker.y, (getWidth() - 3), errorMarkerHeight);
}
}
public Dimension getPreferredSize() {
return new Dimension(preferredWidth, preferredHeight);
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public ErrorBar(DebugEditor editor, int height, DebugMode mode) {
this.editor = editor;
this.preferredHeight = height;
this.errorCheckerService = editor.errorCheckerService;
errorColor = mode.getThemeColor("errorbar.errorcolor", errorColor);
warningColor = mode.getThemeColor("errorbar.warningcolor",
warningColor);
backgroundColor = mode.getThemeColor("errorbar.backgroundcolor",
backgroundColor);
addListeners();
}
/**
* Update error markers in the error bar.
*
* @param problems
* - List of problems.
*/
synchronized public void updateErrorPoints(final ArrayList<Problem> problems) {
// NOTE TO SELF: ErrorMarkers are calculated for the present tab only
// Error Marker index in the arraylist is LOCALIZED for current tab.
final int fheight = this.getHeight();
SwingWorker worker = new SwingWorker() {
protected Object doInBackground() throws Exception {
return null;
}
protected void done() {
int bigCount = 0;
int totalLines = 0;
int currentTab = 0;
for (SketchCode sc : editor.getSketch().getCode()) {
if (sc.isExtension("pde")) {
sc.setPreprocOffset(bigCount);
try {
if (editor.getSketch().getCurrentCode().equals(sc)) {
// Adding + 1 to len because \n gets appended for each
// sketchcode extracted during processPDECode()
totalLines = Base.countLines(sc.getDocument().getText(
0, sc.getDocument().getLength())) + 1;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
currentTab++;
}
// System.out.println("Total lines: " + totalLines);
errorPointsOld.clear();
for (ErrorMarker marker : errorPoints) {
errorPointsOld.add(marker);
}
errorPoints.clear();
// Each problem.getSourceLine() will have an extra line added because of
// class declaration in the beginning
for (Problem problem : problems) {
if (problem.tabIndex == currentTab) {
// Ratio of error line to total lines
float y = problem.lineNumber / ((float) totalLines);
// Ratio multiplied by height of the error bar
y *= fheight - 15; // -15 is just a vertical offset
errorPoints.add(new ErrorMarker(problem, (int) y, problem
.isError() ? ErrorMarker.Error : ErrorMarker.Warning));
// System.out.println("Y: " + y);
}
}
repaint();
}
};
try {
worker.execute(); // I eat concurrency bugs for breakfast.
} catch (Exception exp) {
System.out.println("Errorbar update markers is slacking."
+ exp.getMessage());
// e.printStackTrace();
}
}
/**
* Check if new errors have popped up in the sketch since the last check
*
* @return true - if errors have changed
*/
public boolean errorPointsChanged() {
if (errorPointsOld.size() != errorPoints.size()) {
editor.getTextArea().repaint();
// System.out.println("2 Repaint " + System.currentTimeMillis());
return true;
}
else {
for (int i = 0; i < errorPoints.size(); i++) {
if (errorPoints.get(i).y != errorPointsOld.get(i).y) {
editor.getTextArea().repaint();
// System.out.println("3 Repaint " +
// System.currentTimeMillis());
return true;
}
}
}
return false;
}
/**
* Add various mouse listeners.
*/
protected void addListeners() {
this.addMouseListener(new MouseAdapter() {
// Find out which error/warning the user has clicked
// and then scroll to that
@SuppressWarnings("rawtypes")
@Override
public void mouseClicked(final MouseEvent e) {
SwingWorker worker = new SwingWorker() {
protected Object doInBackground() throws Exception {
return null;
}
protected void done() {
for (ErrorMarker eMarker : errorPoints) {
// -2 and +2 are extra allowance, clicks in the
// vicinity of the markers register that way
if (e.getY() >= eMarker.y - 2
&& e.getY() <= eMarker.y + 2
+ errorMarkerHeight) {
int currentTabErrorIndex = errorPoints
.indexOf(eMarker);
// System.out.println("Index: " +
// currentTabErrorIndex);
int currentTab = editor.getSketch()
.getCodeIndex(
editor.getSketch()
.getCurrentCode());
int totalErrorIndex = currentTabErrorIndex;
for (int i = 0; i < errorCheckerService.problemsList
.size(); i++) {
Problem p = errorCheckerService.problemsList
.get(i);
if (p.tabIndex < currentTab)
totalErrorIndex++;
if (p.tabIndex == currentTab)
break;
}
errorCheckerService
.scrollToErrorLine(totalErrorIndex);
}
}
}
};
try {
worker.execute();
} catch (Exception exp) {
System.out.println("Errorbar mouseClicked is slacking."
+ exp.getMessage());
// e.printStackTrace();
}
}
});
// Tooltip on hover
this.addMouseMotionListener(new MouseMotionListener() {
@SuppressWarnings("rawtypes")
@Override
public void mouseMoved(final MouseEvent e) {
// System.out.println(e);
SwingWorker worker = new SwingWorker() {
protected Object doInBackground() throws Exception {
return null;
}
protected void done() {
for (ErrorMarker eMarker : errorPoints) {
if (e.getY() >= eMarker.y - 2
&& e.getY() <= eMarker.y + 2
+ errorMarkerHeight) {
// System.out.println("Index: " +
// errorPoints.indexOf(y));
int currentTab = editor.getSketch()
.getCodeIndex(
editor.getSketch()
.getCurrentCode());
int currentTabErrorCount = 0;
for (int i = 0; i < errorPoints.size(); i++) {
Problem p = errorPoints.get(i).problem;
if (p.tabIndex == currentTab) {
if (currentTabErrorCount == errorPoints
.indexOf(eMarker)) {
// System.out.println("Roger that.");
String msg = (p.isError() ? "Error: "
: "Warning: ")
+ p.message;
setToolTipText(msg);
setCursor(Cursor
.getPredefinedCursor(Cursor.HAND_CURSOR));
return;
} else {
currentTabErrorCount++;
// System.out.println("Still looking..");
}
}
}
}
// Reset cursor and tooltip
else {
setToolTipText("");
setCursor(Cursor
.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
}
};
try {
worker.execute();
} catch (Exception exp) {
System.out
.println("Errorbar mousemoved Worker is slacking."
+ exp.getMessage());
// e.printStackTrace();
}
}
@Override
public void mouseDragged(MouseEvent arg0) {
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
package processing.mode.java2;
/**
* Error markers displayed on the Error Bar.
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class ErrorMarker {
/**
* y co-ordinate of the marker
*/
public int y;
/**
* Type of marker: Error or Warning?
*/
public int type = -1;
/**
* Error Type constant
*/
public static final int Error = 1;
/**
* Warning Type constant
*/
public static final int Warning = 2;
/**
* Problem that the error marker represents
* @see Problem
*/
public Problem problem;
public ErrorMarker(Problem problem, int y, int type) {
this.problem = problem;
this.y = y;
this.type = type;
}
}

View File

@@ -0,0 +1,374 @@
/*
Part of the XQMode project - https://github.com/Manindra29/XQMode
Under Google Summer of Code 2012 -
http://www.google-melange.com/gsoc/homepage/google/gsoc2012
Copyright (C) 2012 Manindra Moharana
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.java2;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.table.TableModel;
import processing.app.Editor;
import processing.app.Toolkit;
/**
* Error Window that displays a tablular list of errors. Clicking on an error
* scrolls to its location in the code.
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class ErrorWindow extends JFrame {
private JPanel contentPane;
/**
* The table displaying the errors
*/
protected XQErrorTable errorTable;
/**
* Scroll pane that contains the Error Table
*/
protected JScrollPane scrollPane;
protected DebugEditor thisEditor;
private JFrame thisErrorWindow;
/**
* Handles the sticky Problem window
*/
private DockTool2Base Docker;
protected ErrorCheckerService errorCheckerService;
/**
* Preps up ErrorWindow
*
* @param editor
* - Editor
* @param ecs - ErrorCheckerService
*/
public ErrorWindow(DebugEditor editor, ErrorCheckerService ecs) {
thisErrorWindow = this;
errorCheckerService = ecs;
thisEditor = editor;
setTitle("Problems");
prepareFrame();
}
/**
* Sets up ErrorWindow
*/
protected void prepareFrame() {
Toolkit.setIcon(this);
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
// Default size: setBounds(100, 100, 458, 160);
setBounds(100, 100, 458, 160); // Yeah, I hardcode such things sometimes. Hate me.
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout(0, 0));
scrollPane = new JScrollPane();
contentPane.add(scrollPane);
errorTable = new XQErrorTable(errorCheckerService);
scrollPane.setViewportView(errorTable);
try {
Docker = new DockTool2Base();
addListeners();
} catch (Exception e) {
System.out.println("addListeners() acted silly.");
e.printStackTrace();
}
if (thisEditor != null) {
setLocation(new Point(thisEditor.getLocation().x
+ thisEditor.getWidth(), thisEditor.getLocation().y));
}
}
/**
* Updates the error table with new data(Table Model). Called from Error
* Checker Service.
*
* @param tableModel
* - Table Model
* @return True - If error table was updated successfully.
*/
synchronized public boolean updateTable(final TableModel tableModel) {
// XQErrorTable handles evrything now
return errorTable.updateTable(tableModel);
}
/**
* Adds various listeners to components of EditorWindow and to the Editor
* window
*/
protected void addListeners() {
if (thisErrorWindow == null)
System.out.println("ERW null");
thisErrorWindow.addComponentListener(new ComponentListener() {
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentResized(ComponentEvent e) {
Docker.tryDocking();
}
@Override
public void componentMoved(ComponentEvent e) {
Docker.tryDocking();
}
@Override
public void componentHidden(ComponentEvent e) {
}
});
thisErrorWindow.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
thisEditor.problemWindowMenuCB.setSelected(false);
}
@Override
public void windowDeiconified(WindowEvent e) {
thisEditor.setExtendedState(Frame.NORMAL);
}
});
if (thisEditor == null) {
System.out.println("Editor null");
return;
}
thisEditor.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
errorCheckerService.pauseThread();
errorCheckerService.stopThread(); // Bye bye thread.
thisErrorWindow.dispose();
}
@Override
public void windowIconified(WindowEvent e) {
thisErrorWindow.setExtendedState(Frame.ICONIFIED);
}
@Override
public void windowDeiconified(WindowEvent e) {
thisErrorWindow.setExtendedState(Frame.NORMAL);
}
});
thisEditor.addComponentListener(new ComponentListener() {
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentResized(ComponentEvent e) {
if (Docker.isDocked()) {
Docker.dock();
} else {
Docker.tryDocking();
}
}
@Override
public void componentMoved(ComponentEvent e) {
if (Docker.isDocked()) {
Docker.dock();
} else {
Docker.tryDocking();
}
}
@Override
public void componentHidden(ComponentEvent e) {
// System.out.println("ed hidden");
}
});
}
/**
* Implements the docking feature of the tool - The frame sticks to the
* editor and once docked, moves along with it as the editor is resized,
* moved, or closed.
*
* This class has been borrowed from Tab Manager tool by Thomas Diewald. It
* has been slightly modified and used here.
*
* @author Thomas Diewald , http://thomasdiewald.com
*/
private class DockTool2Base {
private int docking_border = 0;
private int dock_on_editor_y_offset_ = 0;
private int dock_on_editor_x_offset_ = 0;
// ///////////////////////////////
// ____2____
// | |
// | |
// 0 | editor | 1
// | |
// |_________|
// 3
// ///////////////////////////////
// public void reset() {
// dock_on_editor_y_offset_ = 0;
// dock_on_editor_x_offset_ = 0;
// docking_border = 0;
// }
public boolean isDocked() {
return (docking_border >= 0);
}
private final int MAX_GAP_ = 20;
//
public void tryDocking() {
if (thisEditor == null)
return;
Editor editor = thisEditor;
Frame frame = thisErrorWindow;
int ex = editor.getX();
int ey = editor.getY();
int ew = editor.getWidth();
int eh = editor.getHeight();
int fx = frame.getX();
int fy = frame.getY();
int fw = frame.getWidth();
int fh = frame.getHeight();
if (((fy > ey) && (fy < ey + eh))
|| ((fy + fh > ey) && (fy + fh < ey + eh))) {
int dis_border_left = Math.abs(ex - (fx + fw));
int dis_border_right = Math.abs((ex + ew) - (fx));
if (dis_border_left < MAX_GAP_ || dis_border_right < MAX_GAP_) {
docking_border = (dis_border_left < dis_border_right) ? 0
: 1;
dock_on_editor_y_offset_ = fy - ey;
dock();
return;
}
}
if (((fx > ex) && (fx < ex + ew))
|| ((fx + fw > ey) && (fx + fw < ex + ew))) {
int dis_border_top = Math.abs(ey - (fy + fh));
int dis_border_bot = Math.abs((ey + eh) - (fy));
if (dis_border_top < MAX_GAP_ || dis_border_bot < MAX_GAP_) {
docking_border = (dis_border_top < dis_border_bot) ? 2 : 3;
dock_on_editor_x_offset_ = fx - ex;
dock();
return;
}
}
docking_border = -1;
}
public void dock() {
if (thisEditor == null)
return;
Editor editor = thisEditor;
Frame frame = thisErrorWindow;
int ex = editor.getX();
int ey = editor.getY();
int ew = editor.getWidth();
int eh = editor.getHeight();
// int fx = frame.getX();
// int fy = frame.getY();
int fw = frame.getWidth();
int fh = frame.getHeight();
int x = 0, y = 0;
if (docking_border == -1) {
return;
}
if (docking_border == 0) {
x = ex - fw;
y = ey + dock_on_editor_y_offset_;
}
if (docking_border == 1) {
x = ex + ew;
y = ey + dock_on_editor_y_offset_;
}
if (docking_border == 2) {
x = ex + dock_on_editor_x_offset_;
y = ey - fh;
}
if (docking_border == 3) {
x = ex + dock_on_editor_x_offset_;
y = ey + eh;
}
frame.setLocation(x, y);
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.java2;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.Field;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Specialized {@link VariableNode} for representing fields. Overrides
* {@link #setValue} to properly change the value of the encapsulated field.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class FieldNode extends VariableNode {
protected Field field;
protected ObjectReference obj;
/**
* Construct a {@link FieldNode}.
*
* @param name the name
* @param type the type
* @param value the value
* @param field the field
* @param obj a reference to the object containing the field
*/
public FieldNode(String name, String type, Value value, Field field, ObjectReference obj) {
super(name, type, value);
this.field = field;
this.obj = obj;
}
@Override
public void setValue(Value value) {
try {
obj.setValue(field, value);
} catch (InvalidTypeException ex) {
Logger.getLogger(FieldNode.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotLoadedException ex) {
Logger.getLogger(FieldNode.class.getName()).log(Level.SEVERE, null, ex);
}
this.value = value;
}
}

View File

@@ -0,0 +1,51 @@
/*
Part of the XQMode project - https://github.com/Manindra29/XQMode
Under Google Summer of Code 2012 -
http://www.google-melange.com/gsoc/homepage/google/gsoc2012
Copyright (C) 2012 Manindra Moharana
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.java2;
/**
* Wrapper for import statements
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class ImportStatement {
/**
* Ex: processing.opengl.*, java.util.*
*/
String importName;
/**
* Which tab does it belong to?
*/
int tab;
/**
* Line number(pde code) of the import
*/
int lineNumber;
public ImportStatement(String importName, int tab, int lineNumber) {
this.importName = importName;
this.tab = tab;
this.lineNumber = lineNumber;
}
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.java2;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Location;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.request.BreakpointRequest;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Model/Controller of a line breakpoint. Can be set before or while debugging.
* Adds a highlight using the debuggers view ({@link DebugEditor}).
*
* @author Martin Leopold <m@martinleopold.com>
*/
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 ReferenceType theClass; // the class containing this breakpoint, null when not yet loaded
/**
* 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.editor().getTab(line.fileName()).getDocument());
this.dbg = dbg;
theClass = dbg.getClass(className()); // try to get the class immediately, may return null if not yet loaded
set(); // activate the breakpoint (show highlight, attach if debugger is running)
}
/**
* Create a {@link LineBreakpoint} on a line in the current tab.
*
* @param lineIdx the line index of the current tab to create the breakpoint
* on
* @param dbg the {@link Debugger}
*/
// TODO: remove and replace by {@link #LineBreakpoint(LineID line, Debugger dbg)}
public LineBreakpoint(int lineIdx, Debugger dbg) {
this(dbg.editor().getLineIDInCurrentTab(lineIdx), dbg);
}
/**
* Get the line id this breakpoint is on.
*
* @return the line id
*/
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.
*/
protected void attach() {
if (!dbg.isPaused()) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "can't attach breakpoint, debugger not paused");
return;
}
if (theClass == null) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "can't attach breakpoint, class not loaded: {0}", className());
return;
}
// find line in java space
LineID javaLine = dbg.sketchToJavaLine(line);
if (javaLine == null) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "couldn't find line {0} in the java code", line);
return;
}
try {
List<Location> locations = theClass.locationsOfLine(javaLine.lineIdx() + 1);
if (locations.isEmpty()) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "no location found for line {0} -> {1}", new Object[]{line, javaLine});
return;
}
// use first found location
bpr = dbg.vm().eventRequestManager().createBreakpointRequest(locations.get(0));
bpr.enable();
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.INFO, "attached breakpoint to {0} -> {1}", new Object[]{line, javaLine});
} catch (AbsentInformationException ex) {
Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Detach this breakpoint from the VM. Deletes the
* {@link BreakpointRequest}.
*/
protected void detach() {
if (bpr != null) {
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
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.editor().addBreakpointedLine(line);
if (theClass != null && dbg.isPaused()) { // class is loaded
// immediately activate the breakpoint
attach();
}
if (dbg.editor().isInCurrentTab(line)) {
dbg.editor().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.editor().removeBreakpointedLine(line.lineIdx());
if (dbg.isPaused()) {
// immediately remove the breakpoint
detach();
}
line.stopTracking();
if (dbg.editor().isInCurrentTab(line)) {
dbg.editor().getSketch().setModified(true);
}
}
// public void enable() {
// }
//
// public void disable() {
// }
@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
ReferenceType mainClass = dbg.getMainClass();
if (mainClass == null) {
return null;
}
return dbg.getMainClass().name();
}
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) {
// check if our class is being loaded
if (theClass.name().equals(className())) {
this.theClass = theClass;
attach();
}
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.java2;
import java.awt.Color;
import java.util.HashSet;
import java.util.Set;
/**
* Model/Controller for a highlighted source code line. Implements a custom
* background color and a text based marker placed in the left-hand gutter area.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class LineHighlight implements LineListener {
protected DebugEditor editor; // the view, used for highlighting lines by setting a background color
protected Color bgColor; // the background color for highlighting lines
protected LineID lineID; // the id of the line
protected String marker; //
protected Color markerColor;
protected int priority = 0;
protected static Set<LineHighlight> allHighlights = new HashSet();
protected static boolean isHighestPriority(LineHighlight hl) {
for (LineHighlight check : allHighlights) {
if (check.lineID().equals(hl.lineID()) && check.priority() > hl.priority()) {
return false;
}
}
return true;
}
/**
* Create a {@link LineHighlight}.
*
* @param lineID the line id to highlight
* @param bgColor the background color used for highlighting
* @param editor the {@link DebugEditor}
*/
public LineHighlight(LineID lineID, Color bgColor, DebugEditor editor) {
this.lineID = lineID;
this.bgColor = bgColor;
this.editor = editor;
lineID.addListener(this);
lineID.startTracking(editor.getTab(lineID.fileName()).getDocument()); // TODO: overwrite a previous doc?
paint(); // already checks if on current tab
allHighlights.add(this);
}
public void setPriority(int p) {
this.priority = p;
}
public int priority() {
return priority;
}
/**
* Create a {@link LineHighlight} on the current tab.
*
* @param lineIdx the line index on the current tab to highlight
* @param bgColor the background color used for highlighting
* @param editor the {@link DebugEditor}
*/
// TODO: Remove and replace by {@link #LineHighlight(LineID lineID, Color bgColor, DebugEditor editor)}
public LineHighlight(int lineIdx, Color bgColor, DebugEditor editor) {
this(editor.getLineIDInCurrentTab(lineIdx), bgColor, editor);
}
/**
* Set a text based marker displayed in the left hand gutter area of this
* highlighted line.
*
* @param marker the marker text
*/
public void setMarker(String marker) {
this.marker = marker;
paint();
}
/**
* Set a text based marker displayed in the left hand gutter area of this
* highlighted line. Also use a custom text color.
*
* @param marker the marker text
* @param markerColor the text color
*/
public void setMarker(String marker, Color markerColor) {
this.markerColor = markerColor;
setMarker(marker);
}
/**
* Retrieve the line id of this {@link LineHighlight}.
*
* @return the line id
*/
public LineID lineID() {
return lineID;
}
/**
* Retrieve the color for highlighting this line.
*
* @return the highlight color.
*/
public Color getColor() {
return bgColor;
}
/**
* Test if this highlight is on a certain line.
*
* @param testLine the line to test
* @return true if this highlight is on the given line
*/
public boolean isOnLine(LineID testLine) {
return lineID.equals(testLine);
}
/**
* Event handler for line number changes (due to editing). Will remove the
* highlight from the old line number and repaint it at the new location.
*
* @param line the line that has changed
* @param oldLineIdx the old line index (0-based)
* @param newLineIdx the new line index (0-based)
*/
@Override
public void lineChanged(LineID line, int oldLineIdx, int newLineIdx) {
// clear old line
if (editor.isInCurrentTab(new LineID(line.fileName(), oldLineIdx))) {
editor.textArea().clearLineBgColor(oldLineIdx);
editor.textArea().clearGutterText(oldLineIdx);
}
// paint new line
// but only if it's on top -> fixes current line being hidden by breakpoint moving it down.
// lineChanged events seem to come in inverse order of startTracking the LineID. (and bp is created first...)
if (LineHighlight.isHighestPriority(this)) {
paint();
}
}
/**
* Notify this line highlight that it is no longer used. Call this for
* cleanup before the {@link LineHighlight} is discarded.
*/
public void dispose() {
lineID.removeListener(this);
lineID.stopTracking();
allHighlights.remove(this);
}
/**
* (Re-)paint this line highlight.
*/
public void paint() {
if (editor.isInCurrentTab(lineID)) {
editor.textArea().setLineBgColor(lineID.lineIdx(), bgColor);
if (marker != null) {
if (markerColor != null) {
editor.textArea().setGutterText(lineID.lineIdx(), marker, markerColor);
} else {
editor.textArea().setGutterText(lineID.lineIdx(), marker);
}
}
}
}
/**
* Clear this line highlight.
*/
public void clear() {
if (editor.isInCurrentTab(lineID)) {
editor.textArea().clearLineBgColor(lineID.lineIdx());
editor.textArea().clearGutterText(lineID.lineIdx());
}
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Position;
/**
* Describes an ID for a code line. Comprised of a file name and a (0-based)
* line number. Can track changes to the line number due to text editing by
* attaching a {@link Document}. Registered {@link LineListener}s are notified
* of changes to the line number.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class LineID implements DocumentListener {
protected String fileName; // the filename
protected int lineIdx; // the line number, 0-based
protected Document doc; // the Document to use for line number tracking
protected Position pos; // the Position acquired during line number tracking
protected Set<LineListener> listeners = new HashSet(); // listeners for line number changes
public LineID(String fileName, int lineIdx) {
this.fileName = fileName;
this.lineIdx = lineIdx;
}
/**
* Get the file name of this line.
*
* @return the file name
*/
public String fileName() {
return fileName;
}
/**
* Get the (0-based) line number of this line.
*
* @return the line index (i.e. line number, starting at 0)
*/
public synchronized int lineIdx() {
return lineIdx;
}
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Test whether this {@link LineID} is equal to another object. Two
* {@link LineID}'s are equal when both their fileName and lineNo are equal.
*
* @param obj the object to test for equality
* @return {@code true} if equal
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final LineID other = (LineID) obj;
if ((this.fileName == null) ? (other.fileName != null) : !this.fileName.equals(other.fileName)) {
return false;
}
if (this.lineIdx != other.lineIdx) {
return false;
}
return true;
}
/**
* Output a string representation in the form fileName:lineIdx+1. Note this
* uses a 1-based line number as is customary for human-readable line
* numbers.
*
* @return the string representation of this line ID
*/
@Override
public String toString() {
return fileName + ":" + (lineIdx + 1);
}
// /**
// * Retrieve a copy of this line ID.
// *
// * @return the copy
// */
// @Override
// public LineID clone() {
// return new LineID(fileName, lineIdx);
// }
/**
* Attach a {@link Document} to enable line number tracking when editing.
* The position to track is before the first non-whitespace character on the
* line. Edits happening before that position will cause the line number to
* update accordingly. Multiple {@link #startTracking} calls will replace
* the tracked document. Whoever wants a tracked line should track it and
* add itself as listener if necessary.
* ({@link LineHighlight}, {@link LineBreakpoint})
*
* @param doc the {@link Document} to use for line number tracking
*/
public synchronized void startTracking(Document doc) {
//System.out.println("tracking: " + this);
if (doc == null) {
return; // null arg
}
if (doc == this.doc) {
return; // already tracking that doc
}
try {
Element line = doc.getDefaultRootElement().getElement(lineIdx);
if (line == null) {
return; // line doesn't exist
}
String lineText = doc.getText(line.getStartOffset(), line.getEndOffset() - line.getStartOffset());
// set tracking position at (=before) first non-white space character on line
pos = doc.createPosition(line.getStartOffset() + nonWhiteSpaceOffset(lineText));
this.doc = doc;
doc.addDocumentListener(this);
} catch (BadLocationException ex) {
Logger.getLogger(LineID.class.getName()).log(Level.SEVERE, null, ex);
pos = null;
this.doc = null;
}
}
/**
* Notify this {@link LineID} that it is no longer in use. Will stop
* position tracking. Call this when this {@link LineID} is no longer
* needed.
*/
public synchronized void stopTracking() {
if (doc != null) {
doc.removeDocumentListener(this);
doc = null;
}
}
/**
* Update the tracked position. Will notify listeners if line number has
* changed.
*/
protected synchronized void updatePosition() {
if (doc != null && pos != null) {
// track position
int offset = pos.getOffset();
int oldLineIdx = lineIdx;
lineIdx = doc.getDefaultRootElement().getElementIndex(offset); // offset to lineNo
if (lineIdx != oldLineIdx) {
for (LineListener l : listeners) {
if (l != null) {
l.lineChanged(this, oldLineIdx, lineIdx);
} else {
listeners.remove(l); // remove null listener
}
}
}
}
}
/**
* Add listener to be notified when the line number changes.
*
* @param l the listener to add
*/
public void addListener(LineListener l) {
listeners.add(l);
}
/**
* Remove a listener for line number changes.
*
* @param l the listener to remove
*/
public void removeListener(LineListener l) {
listeners.remove(l);
}
/**
* Calculate the offset of the first non-whitespace character in a string.
*
* @param str the string to examine
* @return offset of first non-whitespace character in str
*/
protected static int nonWhiteSpaceOffset(String str) {
for (int i = 0; i < str.length(); i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return i;
}
}
return str.length();
}
/**
* Called when the {@link Document} registered using {@link #startTracking}
* is edited. This happens when text is inserted or removed.
*
* @param de
*/
protected void editEvent(DocumentEvent de) {
//System.out.println("document edit @ " + de.getOffset());
if (de.getOffset() <= pos.getOffset()) {
updatePosition();
//System.out.println("updating, new line no: " + lineNo);
}
}
/**
* {@link DocumentListener} callback. Called when text is inserted.
*
* @param de
*/
@Override
public void insertUpdate(DocumentEvent de) {
editEvent(de);
}
/**
* {@link DocumentListener} callback. Called when text is removed.
*
* @param de
*/
@Override
public void removeUpdate(DocumentEvent de) {
editEvent(de);
}
/**
* {@link DocumentListener} callback. Called when attributes are changed.
* Not used.
*
* @param de
*/
@Override
public void changedUpdate(DocumentEvent de) {
// not needed.
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.java2;
/**
* A Listener for line number changes.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public interface LineListener {
/**
* Event handler for line number changes (due to editing).
*
* @param line the line that has changed
* @param oldLineIdx the old line index (0-based)
* @param newLineIdx the new line index (0-based)
*/
void lineChanged(LineID line, int oldLineIdx, int newLineIdx);
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.java2;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.StackFrame;
import com.sun.jdi.Value;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Specialized {@link VariableNode} for representing local variables. Overrides
* {@link #setValue} to properly change the value of the encapsulated local
* variable.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class LocalVariableNode extends VariableNode {
protected LocalVariable var;
protected StackFrame frame;
/**
* Construct a {@link LocalVariableNode}.
*
* @param name the name
* @param type the type
* @param value the value
* @param var the local variable
* @param frame the stack frame containing the local variable
*/
public LocalVariableNode(String name, String type, Value value, LocalVariable var, StackFrame frame) {
super(name, type, value);
this.var = var;
this.frame = frame;
}
@Override
public void setValue(Value value) {
try {
frame.setValue(var, value);
} catch (InvalidTypeException ex) {
Logger.getLogger(LocalVariableNode.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotLoadedException ex) {
Logger.getLogger(LocalVariableNode.class.getName()).log(Level.SEVERE, null, ex);
}
this.value = value;
}
}

View File

@@ -0,0 +1,160 @@
/*
Part of the XQMode project - https://github.com/Manindra29/XQMode
Under Google Summer of Code 2012 -
http://www.google-melange.com/gsoc/homepage/google/gsoc2012
Copyright (C) 2012 Manindra Moharana
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.java2;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.core.compiler.IProblem;
/**
* Wrapper class for IProblem.
*
* Stores the tabIndex and line number according to its tab, including the
* original IProblem object
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class Problem {
/**
* The IProblem which is being wrapped
*/
private IProblem iProblem;
/**
* The tab number to which the error belongs to
*/
public int tabIndex;
/**
* Line number(pde code) of the error
*/
public int lineNumber;
/**
* Error Message. Processed form of IProblem.getMessage()
*/
public String message;
public int type;
public static final int ERROR = 1, WARNING = 2;
public Problem(IProblem iProblem, int tabIndex, int lineNumber) {
this.iProblem = iProblem;
if(iProblem.isError()) {
type = ERROR;
}
else if(iProblem.isWarning()) {
type = WARNING;
}
this.tabIndex = tabIndex;
this.lineNumber = lineNumber;
this.message = process(iProblem);
}
public String toString() {
return new String("TAB " + tabIndex + ",LN " + lineNumber + ",PROB: "
+ message);
}
public boolean isError(){
return type == ERROR;
}
public boolean isWarning(){
return type == WARNING;
}
public String getMessage(){
return message;
}
public IProblem getIProblem(){
return iProblem;
}
public void setType(int ProblemType){
if(ProblemType == ERROR)
type = ERROR;
else if(ProblemType == WARNING)
type = WARNING;
else throw new IllegalArgumentException("Illegal Problem type passed to Problem.setType(int)");
}
static Pattern pattern;
static Matcher matcher;
static final String tokenRegExp = "\\b token\\b";
public static String process(IProblem problem) {
return process(problem.getMessage());
}
/**
* Processes error messages and attempts to make them a bit more english like.
* Currently performs:
* <li>Remove all instances of token. "Syntax error on token 'blah', delete this token"
* becomes "Syntax error on 'blah', delete this"
* @param message - The message to be processed
* @return String - The processed message
*/
public static String process(String message) {
// Remove all instances of token
// "Syntax error on token 'blah', delete this token"
pattern = Pattern.compile(tokenRegExp);
matcher = pattern.matcher(message);
message = matcher.replaceAll("");
// Split camel case words into separate words.
// "VaraibleDeclaration" becomes "Variable Declaration"
// But sadly "PApplet" become "P Applet" and so on.
// StringTokenizer st = new StringTokenizer(message);
// String newMessage = "";
// while (st.hasMoreTokens()) {
// String word = st.nextToken();
// newMessage += splitCamelCaseWord(word) + " ";
// }
// message = new String(newMessage);
return message;
}
public static String splitCamelCaseWord(String word) {
String newWord = "";
for (int i = 1; i < word.length(); i++) {
if (Character.isUpperCase(word.charAt(i))) {
// System.out.println(word.substring(0, i) + " "
// + word.substring(i));
newWord += word.substring(0, i) + " ";
word = word.substring(i);
i = 1;
}
}
newWord += word;
// System.out.println(newWord);
return newWord.trim();
}
}

View File

@@ -0,0 +1,335 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.FontMetrics;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.HashMap;
import java.util.Map;
import processing.app.syntax.JEditTextArea;
import processing.app.syntax.TextAreaDefaults;
/**
* Customized text area. Adds support for line background colors.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class TextArea extends JEditTextArea {
protected MouseListener[] mouseListeners; // cached mouselisteners, these are wrapped by MouseHandler
protected DebugEditor editor; // the editor
// line properties
protected Map<Integer, Color> lineColors = new HashMap(); // contains line background colors
// left-hand gutter properties
protected int gutterPadding = 3; // [px] space added to the left and right of gutter chars
protected Color gutterBgColor = new Color(252, 252, 252); // gutter background color
protected Color gutterLineColor = new Color(233, 233, 233); // color of vertical separation line
protected String breakpointMarker = "<>"; // the text marker for highlighting breakpoints in the gutter
protected String currentLineMarker = "->"; // the text marker for highlighting the current line in the gutter
protected Map<Integer, String> gutterText = new HashMap(); // maps line index to gutter text
protected Map<Integer, Color> gutterTextColors = new HashMap(); // maps line index to gutter text color
protected TextAreaPainter customPainter;
public TextArea(TextAreaDefaults defaults, DebugEditor editor) {
super(defaults);
this.editor = editor;
// replace the painter:
// first save listeners, these are package-private in JEditTextArea, so not accessible
ComponentListener[] componentListeners = painter.getComponentListeners();
mouseListeners = painter.getMouseListeners();
MouseMotionListener[] mouseMotionListeners = painter.getMouseMotionListeners();
remove(painter);
// set new painter
customPainter = new TextAreaPainter(this, defaults);
painter = customPainter;
// set listeners
for (ComponentListener cl : componentListeners) {
painter.addComponentListener(cl);
}
for (MouseMotionListener mml : mouseMotionListeners) {
painter.addMouseMotionListener(mml);
}
// use a custom mouse handler instead of directly using mouseListeners
MouseHandler mouseHandler = new MouseHandler();
painter.addMouseListener(mouseHandler);
painter.addMouseMotionListener(mouseHandler);
add(CENTER, painter);
// load settings from theme.txt
DebugMode theme = (DebugMode) editor.getMode();
gutterBgColor = theme.getThemeColor("gutter.bgcolor", gutterBgColor);
gutterLineColor = theme.getThemeColor("gutter.linecolor", gutterLineColor);
gutterPadding = theme.getInteger("gutter.padding");
breakpointMarker = theme.loadThemeString("breakpoint.marker", breakpointMarker);
currentLineMarker = theme.loadThemeString("currentline.marker", currentLineMarker);
}
public void setECSandThemeforTextArea(ErrorCheckerService ecs, DebugMode mode)
{
customPainter.setECSandTheme(ecs, mode);
}
/**
* Retrieve the total width of the gutter area.
*
* @return gutter width in pixels
*/
protected int getGutterWidth() {
FontMetrics fm = painter.getFontMetrics();
// System.out.println("fm: " + (fm == null));
// System.out.println("editor: " + (editor == null));
//System.out.println("BPBPBPBPB: " + (editor.breakpointMarker == null));
int textWidth = Math.max(fm.stringWidth(breakpointMarker), fm.stringWidth(currentLineMarker));
return textWidth + 2 * gutterPadding;
}
/**
* Retrieve the width of margins applied to the left and right of the gutter
* text.
*
* @return margins in pixels
*/
protected int getGutterMargins() {
return gutterPadding;
}
/**
* Set the gutter text of a specific line.
*
* @param lineIdx the line index (0-based)
* @param text the text
*/
public void setGutterText(int lineIdx, String text) {
gutterText.put(lineIdx, text);
painter.invalidateLine(lineIdx);
}
/**
* Set the gutter text and color of a specific line.
*
* @param lineIdx the line index (0-based)
* @param text the text
* @param textColor the text color
*/
public void setGutterText(int lineIdx, String text, Color textColor) {
gutterTextColors.put(lineIdx, textColor);
setGutterText(lineIdx, text);
}
/**
* Clear the gutter text of a specific line.
*
* @param lineIdx the line index (0-based)
*/
public void clearGutterText(int lineIdx) {
gutterText.remove(lineIdx);
painter.invalidateLine(lineIdx);
}
/**
* Clear all gutter text.
*/
public void clearGutterText() {
for (int lineIdx : gutterText.keySet()) {
painter.invalidateLine(lineIdx);
}
gutterText.clear();
}
/**
* Retrieve the gutter text of a specific line.
*
* @param lineIdx the line index (0-based)
* @return the gutter text
*/
public String getGutterText(int lineIdx) {
return gutterText.get(lineIdx);
}
/**
* Retrieve the gutter text color for a specific line.
*
* @param lineIdx the line index
* @return the gutter text color
*/
public Color getGutterTextColor(int lineIdx) {
return gutterTextColors.get(lineIdx);
}
/**
* Set the background color of a line.
*
* @param lineIdx 0-based line number
* @param col the background color to set
*/
public void setLineBgColor(int lineIdx, Color col) {
lineColors.put(lineIdx, col);
painter.invalidateLine(lineIdx);
}
/**
* Clear the background color of a line.
*
* @param lineIdx 0-based line number
*/
public void clearLineBgColor(int lineIdx) {
lineColors.remove(lineIdx);
painter.invalidateLine(lineIdx);
}
/**
* Clear all line background colors.
*/
public void clearLineBgColors() {
for (int lineIdx : lineColors.keySet()) {
painter.invalidateLine(lineIdx);
}
lineColors.clear();
}
/**
* Get a lines background color.
*
* @param lineIdx 0-based line number
* @return the color or null if no color was set for the specified line
*/
public Color getLineBgColor(int lineIdx) {
return lineColors.get(lineIdx);
}
/**
* Convert a character offset to a horizontal pixel position inside the text
* area. Overridden to take gutter width into account.
*
* @param line the 0-based line number
* @param offset the character offset (0 is the first character on a line)
* @return the horizontal position
*/
@Override
public int _offsetToX(int line, int offset) {
return super._offsetToX(line, offset) + getGutterWidth();
}
/**
* Convert a horizontal pixel position to a character offset. Overridden to
* take gutter width into account.
*
* @param line the 0-based line number
* @param x the horizontal pixel position
* @return he character offset (0 is the first character on a line)
*/
@Override
public int xToOffset(int line, int x) {
return super.xToOffset(line, x - getGutterWidth());
}
/**
* Custom mouse handler. Implements double clicking in the gutter area to
* toggle breakpoints, sets default cursor (instead of text cursor) in the
* gutter area.
*/
protected class MouseHandler implements MouseListener, MouseMotionListener {
protected int lastX; // previous horizontal positon of the mouse cursor
@Override
public void mouseClicked(MouseEvent me) {
// forward to standard listeners
for (MouseListener ml : mouseListeners) {
ml.mouseClicked(me);
}
}
@Override
public void mousePressed(MouseEvent me) {
// check if this happened in the gutter area
if (me.getX() < getGutterWidth()) {
if (me.getButton() == MouseEvent.BUTTON1 && me.getClickCount() == 2) {
int line = me.getY() / painter.getFontMetrics().getHeight() + firstLine;
if (line >= 0 && line <= getLineCount() - 1) {
editor.gutterDblClicked(line);
}
}
} else {
// forward to standard listeners
for (MouseListener ml : mouseListeners) {
ml.mousePressed(me);
}
}
}
@Override
public void mouseReleased(MouseEvent me) {
// forward to standard listeners
for (MouseListener ml : mouseListeners) {
ml.mouseReleased(me);
}
}
@Override
public void mouseEntered(MouseEvent me) {
// forward to standard listeners
for (MouseListener ml : mouseListeners) {
ml.mouseEntered(me);
}
}
@Override
public void mouseExited(MouseEvent me) {
// forward to standard listeners
for (MouseListener ml : mouseListeners) {
ml.mouseExited(me);
}
}
@Override
public void mouseDragged(MouseEvent me) {
// No need to forward since the standard MouseMotionListeners are called anyway
// nop
}
@Override
public void mouseMoved(MouseEvent me) {
// No need to forward since the standard MouseMotionListeners are called anyway
if (me.getX() < getGutterWidth()) {
if (lastX >= getGutterWidth()) {
painter.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
} else {
if (lastX < getGutterWidth()) {
painter.setCursor(new Cursor(Cursor.TEXT_CURSOR));
}
}
lastX = me.getX();
}
}
}

View File

@@ -0,0 +1,313 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.text.BadLocationException;
import javax.swing.text.Segment;
import javax.swing.text.Utilities;
import processing.app.syntax.TextAreaDefaults;
import processing.app.syntax.TokenMarker;
/**
* Customized line painter. Adds support for background colors, left hand gutter
* area with background color and text.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class TextAreaPainter extends processing.app.syntax.TextAreaPainter {
protected TextArea ta; // we need the subclassed textarea
protected ErrorCheckerService errorCheckerService;
/**
* Error line underline color
*/
public Color errorColor = new Color(0xED2630);
/**
* Warning line underline color
*/
public Color warningColor = new Color(0xFFC30E);
/**
* Color of Error Marker
*/
public Color errorMarkerColor = new Color(0xED2630);
/**
* Color of Warning Marker
*/
public Color warningMarkerColor = new Color(0xFFC30E);
public TextAreaPainter(TextArea textArea, TextAreaDefaults defaults) {
super(textArea, defaults);
ta = textArea;
}
private void loadTheme(DebugMode mode){
errorColor = mode.getThemeColor("editor.errorcolor", errorColor);
warningColor = mode.getThemeColor("editor.warningcolor",
warningColor);
errorMarkerColor = mode.getThemeColor("editor.errormarkercolor",
errorMarkerColor);
warningMarkerColor = mode.getThemeColor(
"editor.warningmarkercolor", warningMarkerColor);
}
/**
* Paint a line. Paints the gutter (with background color and text) then the
* line (background color and text).
*
* @param gfx the graphics context
* @param tokenMarker
* @param line 0-based line number
* @param x horizontal position
*/
@Override
protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
int line, int x) {
// paint gutter
paintGutterBg(gfx, line, x);
paintLineBgColor(gfx, line, x + ta.getGutterWidth());
paintGutterLine(gfx, line, x);
// paint gutter symbol
paintGutterText(gfx, line, x);
paintErrorLine(gfx, line, x);
super.paintLine(gfx, tokenMarker, line, x + ta.getGutterWidth());
}
/**
* Paint the gutter background (solid color).
*
* @param gfx the graphics context
* @param line 0-based line number
* @param x horizontal position
*/
protected void paintGutterBg(Graphics gfx, int line, int x) {
gfx.setColor(ta.gutterBgColor);
int y = ta.lineToY(line) + fm.getLeading() + fm.getMaxDescent();
gfx.fillRect(0, y, ta.getGutterWidth(), fm.getHeight());
}
/**
* Paint the vertical gutter separator line.
*
* @param gfx the graphics context
* @param line 0-based line number
* @param x horizontal position
*/
protected void paintGutterLine(Graphics gfx, int line, int x) {
int y = ta.lineToY(line) + fm.getLeading() + fm.getMaxDescent();
gfx.setColor(ta.gutterLineColor);
gfx.drawLine(ta.getGutterWidth(), y, ta.getGutterWidth(), y + fm.getHeight());
}
/**
* Paint the gutter text.
*
* @param gfx the graphics context
* @param line 0-based line number
* @param x horizontal position
*/
protected void paintGutterText(Graphics gfx, int line, int x) {
String text = ta.getGutterText(line);
if (text == null) {
return;
}
gfx.setFont(getFont());
Color textColor = ta.getGutterTextColor(line);
if (textColor == null) {
gfx.setColor(getForeground());
} else {
gfx.setColor(textColor);
}
int y = ta.lineToY(line) + fm.getHeight();
// draw 4 times to make it appear bold, displaced 1px to the right, to the bottom and bottom right.
//int len = text.length() > ta.gutterChars ? ta.gutterChars : text.length();
Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins(), y, gfx, this, 0);
Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins() + 1, y, gfx, this, 0);
Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins(), y + 1, gfx, this, 0);
Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins() + 1, y + 1, gfx, this, 0);
}
/**
* Paint the background color of a line.
*
* @param gfx the graphics context
* @param line 0-based line number
* @param x
*/
protected void paintLineBgColor(Graphics gfx, int line, int x) {
int y = ta.lineToY(line);
y += fm.getLeading() + fm.getMaxDescent();
int height = fm.getHeight();
// get the color
Color col = ta.getLineBgColor(line);
//System.out.print("bg line " + line + ": ");
// no need to paint anything
if (col == null) {
//System.out.println("none");
return;
}
// paint line background
gfx.setColor(col);
gfx.fillRect(0, y, getWidth(), height);
}
/**
* Paints the underline for an error/warning line
*
* @param gfx
* the graphics context
* @param tokenMarker
* @param line
* 0-based line number: NOTE
* @param x
*/
private void paintErrorLine(Graphics gfx, int line, int x) {
if (errorCheckerService == null) {
return;
}
if (errorCheckerService.problemsList== null) {
return;
}
boolean notFound = true;
boolean isWarning = false;
// Check if current line contains an error. If it does, find if it's an
// error or warning
for (ErrorMarker emarker : errorCheckerService.getEditor().errorBar.errorPoints) {
if (emarker.problem.lineNumber == line + 1) {
notFound = false;
if (emarker.type == ErrorMarker.Warning) {
isWarning = true;
}
break;
}
}
if (notFound) {
return;
}
// Determine co-ordinates
// System.out.println("Hoff " + ta.getHorizontalOffset() + ", " +
// horizontalAdjustment);
int y = ta.lineToY(line);
y += fm.getLeading() + fm.getMaxDescent();
int height = fm.getHeight();
int start = ta.getLineStartOffset(line);
try {
String linetext = null;
try {
linetext = ta.getDocument().getText(start,
ta.getLineStopOffset(line) - start - 1);
} catch (BadLocationException bl) {
// Error in the import statements or end of code.
// System.out.print("BL caught. " + ta.getLineCount() + " ,"
// + line + " ,");
// System.out.println((ta.getLineStopOffset(line) - start - 1));
return;
}
// Take care of offsets
int aw = fm.stringWidth(trimRight(linetext))
+ ta.getHorizontalOffset(); // apparent width. Whitespaces
// to the left of line + text
// width
int rw = fm.stringWidth(linetext.trim()); // real width
int x1 = 0 + (aw - rw), y1 = y + fm.getHeight() - 2, x2 = x1 + rw;
// Adding offsets for the gutter
x1 += 20;
x2 += 20;
// gfx.fillRect(x1, y, rw, height);
// Let the painting begin!
gfx.setColor(errorMarkerColor);
if (isWarning) {
gfx.setColor(warningMarkerColor);
}
gfx.fillRect(1, y + 2, 3, height - 2);
gfx.setColor(errorColor);
if (isWarning) {
gfx.setColor(warningColor);
}
int xx = x1;
// Draw the jagged lines
while (xx < x2) {
gfx.drawLine(xx, y1, xx + 2, y1 + 1);
xx += 2;
gfx.drawLine(xx, y1 + 1, xx + 2, y1);
xx += 2;
}
} catch (Exception e) {
System.out
.println("Looks like I messed up! XQTextAreaPainter.paintLine() : "
+ e);
//e.printStackTrace();
}
// Won't highlight the line. Select the text instead.
// gfx.setColor(Color.RED);
// gfx.fillRect(2, y, 3, height);
}
/**
* Trims out trailing whitespaces (to the right)
*
* @param string
* @return - String
*/
private String trimRight(String string) {
String newString = "";
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) != ' ') {
newString = string.substring(0, i) + string.trim();
break;
}
}
return newString;
}
public void setECSandTheme(ErrorCheckerService ecs, DebugMode mode){
this.errorCheckerService = ecs;
loadTheme(mode);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import com.sun.jdi.event.EventSet;
/**
* Interface for VM callbacks.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public interface VMEventListener {
/**
* Receive an event from the VM. Events are sent in batches. See
* documentation of EventSet for more information.
*
* @param es Set of events
*/
void vmEvent(EventSet es);
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Reader Thread for VM Events. Constantly monitors a VMs EventQueue for new
* events and forwards them to an VMEventListener.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class VMEventReader extends Thread {
EventQueue eventQueue;
VMEventListener listener;
/**
* Construct a VMEventReader. Needs to be kicked off with start() once
* constructed.
*
* @param eventQueue The queue to read events from. Can be obtained from a
* VirtualMachine via eventQueue().
* @param listener the listener to forward events to.
*/
public VMEventReader(EventQueue eventQueue, VMEventListener listener) {
super("VM Event Thread");
this.eventQueue = eventQueue;
this.listener = listener;
}
@Override
public void run() {
try {
while (true) {
EventSet eventSet = eventQueue.remove();
listener.vmEvent(eventSet);
/*
* for (Event e : eventSet) { System.out.println("VM Event: " +
* e.toString()); }
*/
}
} catch (VMDisconnectedException e) {
Logger.getLogger(VMEventReader.class.getName()).log(Level.INFO, "VMEventReader quit on VM disconnect");
} catch (Exception e) {
Logger.getLogger(VMEventReader.class.getName()).log(Level.SEVERE, "VMEventReader quit", e);
}
}
}

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="scrollPane" alignment="0" pref="400" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="scrollPane" alignment="1" pref="300" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="scrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="org.netbeans.swing.outline.Outline" name="tree">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@@ -0,0 +1,930 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import com.sun.jdi.Value;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultCellEditor;
import javax.swing.GrayFilter;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.table.TableColumn;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.netbeans.swing.outline.DefaultOutlineCellRenderer;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.ExtTreeWillExpandListener;
import org.netbeans.swing.outline.OutlineModel;
import org.netbeans.swing.outline.RenderDataProvider;
import org.netbeans.swing.outline.RowModel;
/**
* Variable Inspector window.
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class VariableInspector extends javax.swing.JFrame {
protected DefaultMutableTreeNode rootNode; // the root node (invisible)
protected DefaultMutableTreeNode builtins; // node for Processing built-in variables
protected DefaultTreeModel treeModel; // data model for the tree column
protected OutlineModel model; // data model for the whole Outline (tree and other columns)
protected List<DefaultMutableTreeNode> callStack; // the call stack
protected List<VariableNode> locals; // current local variables
protected List<VariableNode> thisFields; // all fields of the current this-object
protected List<VariableNode> declaredThisFields; // declared i.e. non-inherited fields of this
protected DebugEditor editor; // the editor
protected Debugger dbg; // the debugger
protected List<TreePath> expandedNodes = new ArrayList(); // list of expanded tree paths. (using list to maintain the order of expansion)
protected boolean p5mode = true; // processing / "advanced" mode flag (currently not used
/**
* Creates new form VariableInspector
*/
public VariableInspector(DebugEditor editor) {
this.editor = editor;
this.dbg = editor.dbg();
initComponents();
// setup Outline
rootNode = new DefaultMutableTreeNode("root");
builtins = new DefaultMutableTreeNode("Processing");
treeModel = new DefaultTreeModel(rootNode); // model for the tree column
model = DefaultOutlineModel.createOutlineModel(treeModel, new VariableRowModel(), true, "Name"); // model for all columns
ExpansionHandler expansionHandler = new ExpansionHandler();
model.getTreePathSupport().addTreeWillExpandListener(expansionHandler);
model.getTreePathSupport().addTreeExpansionListener(expansionHandler);
tree.setModel(model);
tree.setRootVisible(false);
tree.setRenderDataProvider(new OutlineRenderer());
tree.setColumnHidingAllowed(false); // disable visible columns button (shows by default when right scroll bar is visible)
tree.setAutoscrolls(false);
// set custom renderer and editor for value column, since we are using a custom class for values (VariableNode)
TableColumn valueColumn = tree.getColumnModel().getColumn(1);
valueColumn.setCellRenderer(new ValueCellRenderer());
valueColumn.setCellEditor(new ValueCellEditor());
//System.out.println("renderer: " + tree.getDefaultRenderer(String.class).getClass());
//System.out.println("editor: " + tree.getDefaultEditor(String.class).getClass());
callStack = new ArrayList();
locals = new ArrayList();
thisFields = new ArrayList();
declaredThisFields = new ArrayList();
this.setTitle(editor.getSketch().getName());
// for (Entry<Object, Object> entry : UIManager.getDefaults().entrySet()) {
// System.out.println(entry.getKey());
// }
}
@Override
public void setTitle(String title) {
super.setTitle(title + " | Variable Inspector");
}
/**
* Model for a Outline Row (excluding the tree column). Column 0 is "Value".
* Column 1 is "Type". Handles setting and getting values. TODO: Maybe use a
* TableCellRenderer instead of this to also have a different icon based on
* expanded state. See:
* http://kickjava.com/src/org/netbeans/swing/outline/DefaultOutlineCellRenderer.java.htm
*/
protected class VariableRowModel implements RowModel {
protected String[] columnNames = {"Value", "Type"};
protected int[] editableTypes = {VariableNode.TYPE_BOOLEAN, VariableNode.TYPE_FLOAT, VariableNode.TYPE_INTEGER, VariableNode.TYPE_STRING, VariableNode.TYPE_FLOAT, VariableNode.TYPE_DOUBLE, VariableNode.TYPE_LONG, VariableNode.TYPE_SHORT, VariableNode.TYPE_CHAR};
@Override
public int getColumnCount() {
if (p5mode) {
return 1; // only show value in p5 mode
} else {
return 2;
}
}
@Override
public Object getValueFor(Object o, int i) {
if (o instanceof VariableNode) {
VariableNode var = (VariableNode) o;
switch (i) {
case 0:
return var; // will be converted to an appropriate String by ValueCellRenderer
case 1:
return var.getTypeName();
default:
return "";
}
} else {
return "";
}
}
@Override
public Class getColumnClass(int i) {
if (i == 0) {
return VariableNode.class;
}
return String.class;
}
@Override
public boolean isCellEditable(Object o, int i) {
if (i == 0 && o instanceof VariableNode) {
VariableNode var = (VariableNode) o;
//System.out.println("type: " + var.getTypeName());
for (int type : editableTypes) {
if (var.getType() == type) {
return true;
}
}
}
return false;
}
@Override
public void setValueFor(Object o, int i, Object o1) {
VariableNode var = (VariableNode) o;
String stringValue = (String) o1;
Value value = null;
try {
switch (var.getType()) {
case VariableNode.TYPE_INTEGER:
value = dbg.vm().mirrorOf(Integer.parseInt(stringValue));
break;
case VariableNode.TYPE_BOOLEAN:
value = dbg.vm().mirrorOf(Boolean.parseBoolean(stringValue));
break;
case VariableNode.TYPE_FLOAT:
value = dbg.vm().mirrorOf(Float.parseFloat(stringValue));
break;
case VariableNode.TYPE_STRING:
value = dbg.vm().mirrorOf(stringValue);
break;
case VariableNode.TYPE_LONG:
value = dbg.vm().mirrorOf(Long.parseLong(stringValue));
break;
case VariableNode.TYPE_BYTE:
value = dbg.vm().mirrorOf(Byte.parseByte(stringValue));
break;
case VariableNode.TYPE_DOUBLE:
value = dbg.vm().mirrorOf(Double.parseDouble(stringValue));
break;
case VariableNode.TYPE_SHORT:
value = dbg.vm().mirrorOf(Short.parseShort(stringValue));
break;
case VariableNode.TYPE_CHAR:
// TODO: better char support
if (stringValue.length() > 0) {
value = dbg.vm().mirrorOf(stringValue.charAt(0));
}
break;
}
} catch (NumberFormatException ex) {
Logger.getLogger(VariableRowModel.class.getName()).log(Level.INFO, "invalid value entered for {0}: {1}", new Object[]{var.getName(), stringValue});
}
if (value != null) {
var.setValue(value);
Logger.getLogger(VariableRowModel.class.getName()).log(Level.INFO, "new value set: {0}", var.getStringValue());
}
}
@Override
public String getColumnName(int i) {
return columnNames[i];
}
}
/**
* Renderer for the tree portion of the outline component. Handles icons,
* text color and tool tips.
*/
protected class OutlineRenderer implements RenderDataProvider {
protected Icon[][] icons;
protected static final int ICON_SIZE = 16; // icon size (square, size=width=height)
public OutlineRenderer() {
// load icons
icons = loadIcons("theme/var-icons.gif");
}
/**
* Load multiple icons (horizotal) with multiple states (vertical) from
* a single file.
*
* @param fileName file path in the mode folder.
* @return a nested array (first index: icon, second index: state) or
* null if the file wasn't found.
*/
protected ImageIcon[][] loadIcons(String fileName) {
DebugMode mode = editor.mode();
File file = mode.getContentFile(fileName);
if (!file.exists()) {
Logger.getLogger(OutlineRenderer.class.getName()).log(Level.SEVERE, "icon file not found: {0}", file.getAbsolutePath());
return null;
}
Image allIcons = mode.loadImage(fileName);
int cols = allIcons.getWidth(null) / ICON_SIZE;
int rows = allIcons.getHeight(null) / ICON_SIZE;
ImageIcon[][] iconImages = new ImageIcon[cols][rows];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
//Image image = createImage(ICON_SIZE, ICON_SIZE);
Image image = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.drawImage(allIcons, -i * ICON_SIZE, -j * ICON_SIZE, null);
iconImages[i][j] = new ImageIcon(image);
}
}
return iconImages;
}
protected Icon getIcon(int type, int state) {
if (type < 0 || type > icons.length - 1) {
return null;
}
return icons[type][state];
}
protected VariableNode toVariableNode(Object o) {
if (o instanceof VariableNode) {
return (VariableNode) o;
} else {
return null;
}
}
protected Icon toGray(Icon icon) {
if (icon instanceof ImageIcon) {
Image grayImage = GrayFilter.createDisabledImage(((ImageIcon) icon).getImage());
return new ImageIcon(grayImage);
}
// Cannot convert
return icon;
}
@Override
public String getDisplayName(Object o) {
return o.toString(); // VariableNode.toString() returns name; (for sorting)
// VariableNode var = toVariableNode(o);
// if (var != null) {
// return var.getName();
// } else {
// return o.toString();
// }
}
@Override
public boolean isHtmlDisplayName(Object o) {
return false;
}
@Override
public Color getBackground(Object o) {
return null;
}
@Override
public Color getForeground(Object o) {
if (tree.isEnabled()) {
return null; // default
} else {
return Color.GRAY;
}
}
@Override
public String getTooltipText(Object o) {
VariableNode var = toVariableNode(o);
if (var != null) {
return var.getDescription();
} else {
return "";
}
}
@Override
public Icon getIcon(Object o) {
VariableNode var = toVariableNode(o);
if (var != null) {
if (tree.isEnabled()) {
return getIcon(var.getType(), 0);
} else {
return getIcon(var.getType(), 1);
}
} else {
if (o instanceof TreeNode) {
// TreeNode node = (TreeNode) o;
// AbstractLayoutCache layout = tree.getLayoutCache();
UIDefaults defaults = UIManager.getDefaults();
boolean isLeaf = model.isLeaf(o);
Icon icon;
if (isLeaf) {
icon = defaults.getIcon("Tree.leafIcon");
} else {
icon = defaults.getIcon("Tree.closedIcon");
}
if (!tree.isEnabled()) {
return toGray(icon);
}
return icon;
}
}
return null; // use standard icon
//UIManager.getIcon(o);
}
}
// TODO: could probably extend the simpler javax.swing.table.DefaultTableCellRenderer here
/**
* Renderer for the value column. Uses an italic font for null values and
* Object values ("instance of ..."). Uses a gray color when tree is not
* enabled.
*/
protected class ValueCellRenderer extends DefaultOutlineCellRenderer {
public ValueCellRenderer() {
super();
}
protected void setItalic(boolean on) {
if (on) {
setFont(new Font(getFont().getName(), Font.ITALIC, getFont().getSize()));
} else {
setFont(new Font(getFont().getName(), Font.PLAIN, getFont().getSize()));
}
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (!tree.isEnabled()) {
setForeground(Color.GRAY);
} else {
setForeground(Color.BLACK);
}
if (value instanceof VariableNode) {
VariableNode var = (VariableNode) value;
if (var.getValue() == null || var.getType() == VariableNode.TYPE_OBJECT) {
setItalic(true);
} else {
setItalic(false);
}
value = var.getStringValue();
}
setValue(value);
return c;
}
}
/**
* Editor for the value column. Will show an empty string when editing
* String values that are null.
*/
protected class ValueCellEditor extends DefaultCellEditor {
public ValueCellEditor() {
super(new JTextField());
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if (!(value instanceof VariableNode)) {
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
VariableNode var = (VariableNode) value;
if (var.getType() == VariableNode.TYPE_STRING && var.getValue() == null) {
return super.getTableCellEditorComponent(table, "", isSelected, row, column);
} else {
return super.getTableCellEditorComponent(table, var.getStringValue(), isSelected, row, column);
}
}
}
/**
* Handler for expanding and collapsing tree nodes. Implements lazy loading
* of tree data (on expand).
*/
protected class ExpansionHandler implements ExtTreeWillExpandListener, TreeExpansionListener {
@Override
public void treeWillExpand(TreeExpansionEvent tee) throws ExpandVetoException {
//System.out.println("will expand");
Object last = tee.getPath().getLastPathComponent();
if (!(last instanceof VariableNode)) {
return;
}
VariableNode var = (VariableNode) last;
// load children
// if (!dbg.isPaused()) {
// System.out.println("throwing veto");
// //throw new ExpandVetoException(tee, "Debugger busy");
// } else {
var.removeAllChildren(); // TODO: should we only load it once?
// TODO: don't filter in advanced mode
//System.out.println("loading children for: " + var);
// true means include inherited
var.addChildren(filterNodes(dbg.getFields(var.getValue(), 0, true), new ThisFilter()));
// }
}
@Override
public void treeWillCollapse(TreeExpansionEvent tee) throws ExpandVetoException {
//throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void treeExpanded(TreeExpansionEvent tee) {
//System.out.println("expanded: " + tee.getPath());
if (!expandedNodes.contains(tee.getPath())) {
expandedNodes.add(tee.getPath());
}
// TreePath newPath = tee.getPath();
// if (expandedLast != null) {
// // test each node of the path for equality
// for (int i = 0; i < expandedLast.getPathCount(); i++) {
// if (i < newPath.getPathCount()) {
// Object last = expandedLast.getPathComponent(i);
// Object cur = newPath.getPathComponent(i);
// System.out.println(last + " =? " + cur + ": " + last.equals(cur) + "/" + (last.hashCode() == cur.hashCode()));
// }
// }
// }
// System.out.println("path equality: " + newPath.equals(expandedLast));
// expandedLast = newPath;
}
@Override
public void treeCollapsed(TreeExpansionEvent tee) {
//System.out.println("collapsed: " + tee.getPath());
// first remove all children of collapsed path
// this makes sure children do not appear before parents in the list.
// (children can't be expanded before their parents)
List<TreePath> removalList = new ArrayList();
for (TreePath path : expandedNodes) {
if (path.getParentPath().equals(tee.getPath())) {
removalList.add(path);
}
}
for (TreePath path : removalList) {
expandedNodes.remove(path);
}
// remove collapsed path
expandedNodes.remove(tee.getPath());
}
@Override
public void treeExpansionVetoed(TreeExpansionEvent tee, ExpandVetoException eve) {
//System.out.println("expansion vetoed");
// nop
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
scrollPane = new javax.swing.JScrollPane();
tree = new org.netbeans.swing.outline.Outline();
scrollPane.setViewportView(tree);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
// /**
// * @param args the command line arguments
// */
// public static void main(String args[]) {
// /*
// * Set the Nimbus look and feel
// */
// //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
// /*
// * If Nimbus (introduced in Java SE 6) is not available, stay with the
// * default look and feel. For details see
// * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
// */
// try {
// javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
// } catch (ClassNotFoundException ex) {
// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
// } catch (InstantiationException ex) {
// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
// } catch (IllegalAccessException ex) {
// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
// } catch (javax.swing.UnsupportedLookAndFeelException ex) {
// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
// }
// //</editor-fold>
//
// /*
// * Create and display the form
// */
// run(new VariableInspector());
// }
protected static void run(final VariableInspector vi) {
/*
* Create and display the form
*/
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
vi.setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane scrollPane;
protected org.netbeans.swing.outline.Outline tree;
// End of variables declaration//GEN-END:variables
/**
* Access the root node of the tree.
*
* @return the root node
*/
public DefaultMutableTreeNode getRootNode() {
return rootNode;
}
/**
* Unlock the inspector window. Rebuild after this to avoid ... dots in the
* trees labels
*/
public void unlock() {
tree.setEnabled(true);
}
/**
* Lock the inspector window. Cancels open edits.
*/
public void lock() {
if (tree.getCellEditor() != null) {
//tree.getCellEditor().stopCellEditing(); // force quit open edit
tree.getCellEditor().cancelCellEditing(); // cancel an open edit
}
tree.setEnabled(false);
}
/**
* Reset the inspector windows data. Rebuild after this to make changes
* visible.
*/
public void reset() {
rootNode.removeAllChildren();
// clear local data for good measure (in case someone rebuilds)
callStack.clear();
locals.clear();
thisFields.clear();
declaredThisFields.clear();
expandedNodes.clear();
// update
treeModel.nodeStructureChanged(rootNode);
}
// public void setAdvancedMode() {
// p5mode = false;
// }
//
// public void setP5Mode() {
// p5mode = true;
// }
//
// public void toggleMode() {
// if (p5mode) {
// setAdvancedMode();
// } else {
// setP5Mode();
// }
// }
/**
* Update call stack data.
*
* @param nodes a list of nodes that represent the call stack.
* @param title the title to be used when labeling or otherwise grouping
* call stack data.
*/
public void updateCallStack(List<DefaultMutableTreeNode> nodes, String title) {
callStack = nodes;
}
/**
* Update locals data.
*
* @param nodes a list of {@link VariableNode} to be shown as local
* variables in the inspector.
* @param title the title to be used when labeling or otherwise grouping
* locals data.
*/
public void updateLocals(List<VariableNode> nodes, String title) {
locals = nodes;
}
/**
* Update this-fields data.
*
* @param nodes a list of {@link VariableNode}s to be shown as this-fields
* in the inspector.
* @param title the title to be used when labeling or otherwise grouping
* this-fields data.
*/
public void updateThisFields(List<VariableNode> nodes, String title) {
thisFields = nodes;
}
/**
* Update declared (non-inherited) this-fields data.
*
* @param nodes a list of {@link VariableNode}s to be shown as declared
* this-fields in the inspector.
* @param title the title to be used when labeling or otherwise grouping
* declared this-fields data.
*/
public void updateDeclaredThisFields(List<VariableNode> nodes, String title) {
declaredThisFields = nodes;
}
/**
* Rebuild the outline tree from current data. Uses the data provided by
* {@link #updateCallStack}, {@link #updateLocals}, {@link #updateThisFields}
* and {@link #updateDeclaredThisFields}
*/
public void rebuild() {
rootNode.removeAllChildren();
if (p5mode) {
// add all locals to root
addAllNodes(rootNode, locals);
// add non-inherited this fields
addAllNodes(rootNode, filterNodes(declaredThisFields, new LocalHidesThisFilter(locals, LocalHidesThisFilter.MODE_PREFIX)));
// add p5 builtins in a new folder
builtins.removeAllChildren();
addAllNodes(builtins, filterNodes(thisFields, new P5BuiltinsFilter()));
if (builtins.getChildCount() > 0) { // skip builtins in certain situations e.g. in pure java tabs.
rootNode.add(builtins);
}
// notify tree (using model) changed a node and its children
// http://stackoverflow.com/questions/2730851/how-to-update-jtree-elements
// needs to be done before expanding paths!
treeModel.nodeStructureChanged(rootNode);
// handle node expansions
for (TreePath path : expandedNodes) {
//System.out.println("re-expanding: " + path);
path = synthesizePath(path);
if (path != null) {
tree.expandPath(path);
} else {
//System.out.println("couldn't synthesize path");
}
}
// this expansion causes problems when sorted and stepping
//tree.expandPath(new TreePath(new Object[]{rootNode, builtins}));
} else {
// TODO: implement advanced mode here
}
}
/**
* Re-build a {@link TreePath} from a previous path using equals-checks
* starting at the root node. This is used to use paths from previous trees
* for use on the current tree.
*
* @param path the path to synthesize.
* @return the rebuilt path, usable on the current tree.
*/
protected TreePath synthesizePath(TreePath path) {
//System.out.println("synthesizing: " + path);
if (path.getPathCount() == 0 || !rootNode.equals(path.getPathComponent(0))) {
return null;
}
Object[] newPath = new Object[path.getPathCount()];
newPath[0] = rootNode;
TreeNode currentNode = rootNode;
for (int i = 0; i < path.getPathCount() - 1; i++) {
// get next node
for (int j = 0; j < currentNode.getChildCount(); j++) {
TreeNode nextNode = currentNode.getChildAt(j);
if (nextNode.equals(path.getPathComponent(i + 1))) {
currentNode = nextNode;
newPath[i + 1] = nextNode;
//System.out.println("found node " + (i+1) + ": " + nextNode);
break;
}
}
if (newPath[i + 1] == null) {
//System.out.println("didn't find node");
return null;
}
}
return new TreePath(newPath);
}
/**
* Filter a list of nodes using a {@link VariableNodeFilter}.
*
* @param nodes the list of nodes to filter.
* @param filter the filter to be used.
* @return the filtered list.
*/
protected List<VariableNode> filterNodes(List<VariableNode> nodes, VariableNodeFilter filter) {
List<VariableNode> filtered = new ArrayList();
for (VariableNode node : nodes) {
if (filter.accept(node)) {
filtered.add(node);
}
}
return filtered;
}
/**
* Add all nodes in a list to a root node.
*
* @param root the root node to add to.
* @param nodes the list of nodes to add.
*/
protected void addAllNodes(DefaultMutableTreeNode root, List<? extends MutableTreeNode> nodes) {
for (MutableTreeNode node : nodes) {
root.add(node);
}
}
/**
* A filter for {@link VariableNode}s.
*/
public interface VariableNodeFilter {
/**
* Check whether the filter accepts a {@link VariableNode}.
*
* @param var the input node
* @return true when the filter accepts the input node otherwise false.
*/
public boolean accept(VariableNode var);
}
/**
* A {@link VariableNodeFilter} that accepts Processing built-in variable
* names.
*/
public class P5BuiltinsFilter implements VariableNodeFilter {
protected String[] p5Builtins = {
"focused",
"frameCount",
"frameRate",
"height",
"online",
"screen",
"width",
"mouseX",
"mouseY",
"pmouseX",
"pmouseY",
"key",
"keyCode",
"keyPressed"
};
@Override
public boolean accept(VariableNode var) {
return Arrays.asList(p5Builtins).contains(var.getName());
}
}
/**
* A {@link VariableNodeFilter} that rejects implicit this references.
* (Names starting with "this$")
*/
public class ThisFilter implements VariableNodeFilter {
@Override
public boolean accept(VariableNode var) {
return !var.getName().startsWith("this$");
}
}
/**
* A {@link VariableNodeFilter} that either rejects this-fields if hidden by
* a local, or prefixes its name with "this."
*/
public class LocalHidesThisFilter implements VariableNodeFilter {
/**
* Reject a this-field if hidden by a local.
*/
public static final int MODE_HIDE = 0; // don't show hidden this fields
/**
* Prefix a this-fields name with "this." if hidden by a local.
*/
public static final int MODE_PREFIX = 1; // prefix hidden this fields with "this."
@SuppressWarnings("hiding")
protected List<VariableNode> locals;
protected int mode;
/**
* Construct a {@link LocalHidesThisFilter}.
*
* @param locals a list of locals to check against
* @param mode either {@link #MODE_HIDE} or {@link #MODE_PREFIX}
*/
public LocalHidesThisFilter(List<VariableNode> locals, int mode) {
this.locals = locals;
this.mode = mode;
}
@Override
public boolean accept(VariableNode var) {
// check if the same name appears in the list of locals i.e. the local hides the field
for (VariableNode local : locals) {
if (var.getName().equals(local.getName())) {
switch (mode) {
case MODE_PREFIX:
var.setName("this." + var.getName());
return true;
case MODE_HIDE:
return false;
}
}
}
return true;
}
}
}

View File

@@ -0,0 +1,358 @@
/*
* Copyright (C) 2012 Martin Leopold <m@martinleopold.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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.java2;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.StringReference;
import com.sun.jdi.Value;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
/**
* Model for a variable in the variable inspector. Has a type and name and
* optionally a value. Can have sub-variables (as is the case for objects, and
* arrays).
*
* @author Martin Leopold <m@martinleopold.com>
*/
public class VariableNode implements MutableTreeNode {
public static final int TYPE_UNKNOWN = -1;
public static final int TYPE_OBJECT = 0;
public static final int TYPE_ARRAY = 1;
public static final int TYPE_INTEGER = 2;
public static final int TYPE_FLOAT = 3;
public static final int TYPE_BOOLEAN = 4;
public static final int TYPE_CHAR = 5;
public static final int TYPE_STRING = 6;
public static final int TYPE_LONG = 7;
public static final int TYPE_DOUBLE = 8;
public static final int TYPE_BYTE = 9;
public static final int TYPE_SHORT = 10;
public static final int TYPE_VOID = 11;
protected String type;
protected String name;
protected Value value;
protected List<MutableTreeNode> children = new ArrayList();
protected MutableTreeNode parent;
/**
* Construct a {@link VariableNode}.
* @param name the name
* @param type the type
* @param value the value
*/
public VariableNode(String name, String type, Value value) {
this.name = name;
this.type = type;
this.value = value;
}
public void setValue(Value value) {
this.value = value;
}
public Value getValue() {
return value;
}
/**
* Get a String representation of this variable nodes value.
*
* @return a String representing the value.
*/
public String getStringValue() {
String str;
if (value != null) {
if (getType() == TYPE_OBJECT) {
str = "instance of " + type;
} else if (getType() == TYPE_ARRAY) {
//instance of int[5] (id=998) --> instance of int[5]
str = value.toString().substring(0, value.toString().lastIndexOf(" "));
} else if (getType() == TYPE_STRING) {
str = ((StringReference) value).value(); // use original string value (without quotes)
} else {
str = value.toString();
}
} else {
str = "null";
}
return str;
}
public String getTypeName() {
return type;
}
public int getType() {
if (type == null) {
return TYPE_UNKNOWN;
}
if (type.endsWith("[]")) {
return TYPE_ARRAY;
}
if (type.equals("int")) {
return TYPE_INTEGER;
}
if (type.equals("long")) {
return TYPE_LONG;
}
if (type.equals("byte")) {
return TYPE_BYTE;
}
if (type.equals("short")) {
return TYPE_SHORT;
}
if (type.equals("float")) {
return TYPE_FLOAT;
}
if (type.equals("double")) {
return TYPE_DOUBLE;
}
if (type.equals("char")) {
return TYPE_CHAR;
}
if (type.equals("java.lang.String")) {
return TYPE_STRING;
}
if (type.equals("boolean")) {
return TYPE_BOOLEAN;
}
if (type.equals("void")) {
return TYPE_VOID; //TODO: check if this is correct
}
return TYPE_OBJECT;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* Add a {@link VariableNode} as child.
*
* @param c the {@link VariableNode} to add.
*/
public void addChild(VariableNode c) {
children.add(c);
c.setParent(this);
}
/**
* Add multiple {@link VariableNode}s as children.
*
* @param children the list of {@link VariableNode}s to add.
*/
public void addChildren(List<VariableNode> children) {
for (VariableNode child : children) {
addChild(child);
}
}
@Override
public TreeNode getChildAt(int i) {
return children.get(i);
}
@Override
public int getChildCount() {
return children.size();
}
@Override
public TreeNode getParent() {
return parent;
}
@Override
public int getIndex(TreeNode tn) {
return children.indexOf(tn);
}
@Override
public boolean getAllowsChildren() {
if (value == null) {
return false;
}
// handle strings
if (getType() == TYPE_STRING) {
return false;
}
// handle arrays
if (getType() == TYPE_ARRAY) {
ArrayReference array = (ArrayReference) value;
return array.length() > 0;
}
// handle objects
if (getType() == TYPE_OBJECT) { // this also rules out null
// check if this object has any fields
ObjectReference obj = (ObjectReference) value;
return !obj.referenceType().visibleFields().isEmpty();
}
return false;
}
/**
* This controls the default icon and disclosure triangle.
*
* @return true, will show "folder" icon and disclosure triangle.
*/
@Override
public boolean isLeaf() {
//return children.size() == 0;
return !getAllowsChildren();
}
@Override
public Enumeration children() {
return Collections.enumeration(children);
}
/**
* Get a String representation of this {@link VariableNode}.
*
* @return the name of the variable (for sorting to work).
*/
@Override
public String toString() {
return getName(); // for sorting
}
/**
* Get a String description of this {@link VariableNode}. Contains the type,
* name and value.
*
* @return the description
*/
public String getDescription() {
String str = "";
if (type != null) {
str += type + " ";
}
str += name;
str += " = " + getStringValue();
return str;
}
@Override
public void insert(MutableTreeNode mtn, int i) {
children.add(i, this);
}
@Override
public void remove(int i) {
MutableTreeNode mtn = children.remove(i);
if (mtn != null) {
mtn.setParent(null);
}
}
@Override
public void remove(MutableTreeNode mtn) {
children.remove(mtn);
mtn.setParent(null);
}
/**
* Remove all children from this {@link VariableNode}.
*/
public void removeAllChildren() {
for (MutableTreeNode mtn : children) {
mtn.setParent(null);
}
children.clear();
}
@Override
public void setUserObject(Object o) {
if (o instanceof Value) {
value = (Value) o;
}
}
@Override
public void removeFromParent() {
parent.remove(this);
this.parent = null;
}
@Override
public void setParent(MutableTreeNode mtn) {
parent = mtn;
}
/**
* Test for equality. To be equal, two {@link VariableNode}s need to have
* equal type, name and value.
*
* @param obj the object to test for equality with this {@link VariableNode}
* @return true if the given object is equal to this {@link VariableNode}
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final VariableNode other = (VariableNode) obj;
if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) {
//System.out.println("type not equal");
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
//System.out.println("name not equal");
return false;
}
if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
//System.out.println("value not equal");
return false;
}
// if (this.parent != other.parent && (this.parent == null || !this.parent.equals(other.parent))) {
// System.out.println("parent not equal: " + this.parent + "/" + other.parent);
// return false;
// }
return true;
}
/**
* Returns a hash code based on type, name and value.
*/
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + (this.type != null ? this.type.hashCode() : 0);
hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 97 * hash + (this.value != null ? this.value.hashCode() : 0);
// hash = 97 * hash + (this.parent != null ? this.parent.hashCode() : 0);
return hash;
}
}

View File

@@ -0,0 +1,131 @@
package processing.mode.java2;
/*
Part of the XQMode project - https://github.com/Manindra29/XQMode
Under Google Summer of Code 2012 -
http://www.google-melange.com/gsoc/homepage/google/gsoc2012
Copyright (C) 2012 Manindra Moharana
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
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPanel;
/**
* Toggle Button displayed in the editor line status panel for toggling bewtween
* console and problems list. Glorified JPanel.
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class XQConsoleToggle extends JPanel implements MouseListener {
public static final String[] text = { "Console", "Errors" };
private boolean toggleText = true;
private boolean toggleBG = true;
/**
* Height of the component
*/
protected int height;
protected DebugEditor editor;
protected String buttonName;
public XQConsoleToggle(DebugEditor editor, String buttonName, int height) {
this.editor = editor;
this.height = height;
this.buttonName = buttonName;
}
public Dimension getPreferredSize() {
return new Dimension(70, height);
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public Dimension getMaximumSize() {
return getPreferredSize();
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// On mouse hover, text and background color are changed.
if (toggleBG) {
g.setColor(new Color(0xff9DA7B0));
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(new Color(0xff29333D));
g.fillRect(0, 0, 4, this.getHeight());
g.setColor(Color.BLACK);
} else {
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(new Color(0xff29333D));
g.fillRect(0, 0, 4, this.getHeight());
g.setColor(Color.WHITE);
}
g.drawString(buttonName, getWidth() / 2 + 2 // + 2 is a offset
- getFontMetrics(getFont()).stringWidth(buttonName) / 2,
this.getHeight() - 6);
}
@Override
public void mouseClicked(MouseEvent arg0) {
this.repaint();
try {
editor.toggleView(buttonName);
} catch (Exception e) {
System.out.println(e);
// e.printStackTrace();
}
toggleText = !toggleText;
}
@Override
public void mouseEntered(MouseEvent arg0) {
toggleBG = !toggleBG;
this.repaint();
}
@Override
public void mouseExited(MouseEvent arg0) {
toggleBG = !toggleBG;
this.repaint();
}
@Override
public void mousePressed(MouseEvent arg0) {
}
@Override
public void mouseReleased(MouseEvent arg0) {
}
}

View File

@@ -0,0 +1,161 @@
package processing.mode.java2;
/*
Part of the XQMode project - https://github.com/Manindra29/XQMode
Under Google Summer of Code 2012 -
http://www.google-melange.com/gsoc/homepage/google/gsoc2012
Copyright (C) 2012 Manindra Moharana
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
*/
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableModel;
/**
* Custom JTable implementation for XQMode. Minor tweaks and addtions.
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class XQErrorTable extends JTable {
/**
* Column Names of JTable
*/
public static final String[] columnNames = { "Problem", "Tab", "Line" };
/**
* Column Widths of JTable.
*/
public int[] columnWidths = { 600, 100, 50 }; // Default Values
/**
* Is the column being resized?
*/
private boolean columnResizing = false;
protected ErrorCheckerService errorCheckerService;
@Override
public boolean isCellEditable(int rowIndex, int colIndex) {
return false; // Disallow the editing of any cell
}
public XQErrorTable(final ErrorCheckerService errorCheckerService) {
this.errorCheckerService = errorCheckerService;
for (int i = 0; i < this.getColumnModel().getColumnCount(); i++) {
this.getColumnModel().getColumn(i)
.setPreferredWidth(columnWidths[i]);
}
this.getTableHeader().setReorderingAllowed(false);
this.addMouseListener(new MouseAdapter() {
@Override
synchronized public void mouseReleased(MouseEvent e) {
try {
errorCheckerService.scrollToErrorLine(((XQErrorTable) e
.getSource()).getSelectedRow());
// System.out.print("Row clicked: "
// + ((XQErrorTable) e.getSource()).getSelectedRow());
} catch (Exception e1) {
System.out.println("Exception XQErrorTable mouseReleased "
+ e);
}
}
});
// Handles the resizing of columns. When mouse press is detected on
// table header, Stop updating the table, store new values of column
// widths,and resume updating. Updating is disabled as long as
// columnResizing is true
this.getTableHeader().addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
columnResizing = true;
}
@Override
public void mouseReleased(MouseEvent e) {
columnResizing = false;
for (int i = 0; i < ((JTableHeader) e.getSource())
.getColumnModel().getColumnCount(); i++) {
columnWidths[i] = ((JTableHeader) e.getSource())
.getColumnModel().getColumn(i).getWidth();
// System.out.println("nw " + columnWidths[i]);
}
}
});
}
/**
* Updates table contents with new data
* @param tableModel - TableModel
* @return boolean - If table data was updated
*/
@SuppressWarnings("rawtypes")
synchronized public boolean updateTable(final TableModel tableModel) {
// If problems list is not visible, no need to update
if (!this.isVisible())
return false;
SwingWorker worker = new SwingWorker() {
protected Object doInBackground() throws Exception {
return null;
}
protected void done() {
try {
setModel(tableModel);
// Set column widths to user defined widths
for (int i = 0; i < getColumnModel().getColumnCount(); i++) {
getColumnModel().getColumn(i).setPreferredWidth(
columnWidths[i]);
}
getTableHeader().setReorderingAllowed(false);
validate();
repaint();
} catch (Exception e) {
System.out.println("Exception at XQErrorTable.updateTable " + e);
// e.printStackTrace();
}
}
};
try {
if (!columnResizing)
worker.execute();
} catch (Exception e) {
System.out.println("ErrorTable updateTable Worker's slacking."
+ e.getMessage());
// e.printStackTrace();
}
return true;
}
}

View File

@@ -0,0 +1,245 @@
/*
Part of the XQMode project - https://github.com/Manindra29/XQMode
Under Google Summer of Code 2012 -
http://www.google-melange.com/gsoc/homepage/google/gsoc2012
Copyright (C) 2012 Manindra Moharana
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.java2;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import processing.app.Preferences;
import processing.core.PApplet;
/**
* My implementation of P5 preprocessor. Uses Eclipse JDT features instead of
* ANTLR. Performance gains mostly and full control over debug output. But needs
* lots and lots of testing. There will always an option to switch back to PDE
* preproc.
*
* @author Manindra Moharana &lt;me@mkmoharana.com&gt;
*
*/
public class XQPreprocessor {
private ASTRewrite rewrite = null;
public int mainClassOffset = 0;
ArrayList<String> imports;
ArrayList<ImportStatement> extraImports;
/**
* The main method that performs preprocessing. Converts code into compilable java.
* @param source - String
* @param programImports - List of import statements
* @return String - Compile ready java code
*/
public String doYourThing(String source,
ArrayList<ImportStatement> programImports) {
this.extraImports = programImports;
source = prepareImports() + source;
Document doc = new Document(source);
ASTParser parser = ASTParser.newParser(AST.JLS4);
parser.setSource(doc.get().toCharArray());
parser.setKind(ASTParser.K_COMPILATION_UNIT);
@SuppressWarnings("unchecked")
Map<String, String> options = JavaCore.getOptions();
// Ben has decided to move on to 1.6. Yay!
JavaCore.setComplianceOptions(JavaCore.VERSION_1_6, options);
options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6);
parser.setCompilerOptions(options);
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
cu.recordModifications();
rewrite = ASTRewrite.create(cu.getAST());
cu.accept(new XQASTVisitor());
TextEdit edits = cu.rewrite(doc, null);
try {
edits.apply(doc);
} catch (MalformedTreeException e) {
e.printStackTrace();
} catch (BadLocationException e) {
e.printStackTrace();
}
// System.out.println("------------XQPreProc-----------------");
// System.out.println(doc.get());
// System.out.println("------------XQPreProc End-----------------");
// Calculate main class offset
int position = doc.get().indexOf("{") + 1;
int lines = 0;
for (int i = 0; i < position; i++) {
if (doc.get().charAt(i) == '\n')
lines++;
}
lines += 2;
// System.out.println("Lines: " + lines);
mainClassOffset = lines;
return doc.get();
}
/**
* Returns all import statements as lines of code
*
* @return String - All import statements combined
*/
public String prepareImports() {
imports = new ArrayList<String>();
for (int i = 0; i < extraImports.size(); i++) {
imports.add(new String(extraImports.get(i).importName));
}
imports.add(new String("// Default Imports"));
for (int i = 0; i < getCoreImports().length; i++) {
imports.add(new String("import " + getCoreImports()[i] + ";"));
}
for (int i = 0; i < getDefaultImports().length; i++) {
imports.add(new String("import " + getDefaultImports()[i] + ";"));
}
String totalImports = "";
for (int i = 0; i < imports.size(); i++) {
totalImports += imports.get(i) + "\n";
}
totalImports += "\n";
return totalImports;
}
public String[] getCoreImports() {
return new String[] { "processing.core.*", "processing.data.*",
"processing.opengl.*" };
}
public String[] getDefaultImports() {
// These may change in-between (if the prefs panel adds this option)
String prefsLine = Preferences.get("preproc.imports.list");
return PApplet.splitTokens(prefsLine, ", ");
}
/**
* Visitor implementation that does all the substitution dirty work. <br>
* <LI>Any function not specified as being protected or private will be made
* 'public'. This means that <TT>void setup()</TT> becomes
* <TT>public void setup()</TT>.
*
* <LI>Converts doubles into floats, i.e. 12.3 becomes 12.3f so that people
* don't have to add f after their numbers all the time since it's confusing
* for beginners. Also, most functions of p5 core deal with floats only.
*
* @author Manindra Moharana
*
*/
private class XQASTVisitor extends ASTVisitor {
@SuppressWarnings({ "unchecked", "rawtypes" })
public boolean visit(MethodDeclaration node) {
if (node.getReturnType2() != null) {
// if return type is color, make it int
// if (node.getReturnType2().toString().equals("color")) {
// System.err.println("color type detected!");
// node.setReturnType2(rewrite.getAST().newPrimitiveType(
// PrimitiveType.INT));
// }
// The return type is not void, no need to make it public
// if (!node.getReturnType2().toString().equals("void"))
// return true;
}
// Simple method, make it public
if (node.modifiers().size() == 0 && !node.isConstructor()) {
// rewrite.set(node, node.getModifiersProperty(),
// Modifier.PUBLIC,
// null);
// rewrite.getListRewrite(node,
// node.getModifiersProperty()).insertLast(Modifier., null)
List newMod = rewrite.getAST().newModifiers(Modifier.PUBLIC);
node.modifiers().add(newMod.get(0));
}
return true;
}
public boolean visit(NumberLiteral node) {
if (!node.getToken().endsWith("f")
&& !node.getToken().endsWith("d")) {
for (int i = 0; i < node.getToken().length(); i++) {
if (node.getToken().charAt(i) == '.') {
String s = node.getToken() + "f";
node.setToken(s);
break;
}
}
}
return true;
}
// public boolean visit(FieldDeclaration node) {
// if (node.getType().toString().equals("color")){
// System.err.println("color type detected!");
// node.setType(rewrite.getAST().newPrimitiveType(
// PrimitiveType.INT));
// }
// return true;
// }
//
// public boolean visit(VariableDeclarationStatement node) {
// if (node.getType().toString().equals("color")){
// System.err.println("color type detected!");
// node.setType(rewrite.getAST().newPrimitiveType(
// PrimitiveType.INT));
// }
// return true;
// }
/**
* This is added just for debugging purposes - to make sure that all
* instances of color type have been substituded as in by the regex
* search in ErrorCheckerService.preprocessCode().
*/
public boolean visit(SimpleType node) {
if (node.toString().equals("color")) {
System.err
.println("color type detected! \nThis shouldn't be happening! Please report this as an issue on www.github.com/Manindra29/XQMode");
}
return true;
}
}
}