mirror of
https://github.com/processing/processing4.git
synced 2026-02-03 21:59:20 +01:00
moving to new location
This commit is contained in:
65
java2/src/processing/mode/java2/ArrayFieldNode.java
Executable file
65
java2/src/processing/mode/java2/ArrayFieldNode.java
Executable 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;
|
||||
}
|
||||
}
|
||||
37
java2/src/processing/mode/java2/ClassLoadListener.java
Executable file
37
java2/src/processing/mode/java2/ClassLoadListener.java
Executable 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);
|
||||
}
|
||||
367
java2/src/processing/mode/java2/Compiler.java
Executable file
367
java2/src/processing/mode/java2/Compiler.java
Executable 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;
|
||||
}
|
||||
}
|
||||
73
java2/src/processing/mode/java2/DebugBuild.java
Executable file
73
java2/src/processing/mode/java2/DebugBuild.java
Executable 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;
|
||||
}
|
||||
}
|
||||
1216
java2/src/processing/mode/java2/DebugEditor.java
Executable file
1216
java2/src/processing/mode/java2/DebugEditor.java
Executable file
File diff suppressed because it is too large
Load Diff
158
java2/src/processing/mode/java2/DebugMode.java
Executable file
158
java2/src/processing/mode/java2/DebugMode.java
Executable 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;
|
||||
}
|
||||
}
|
||||
85
java2/src/processing/mode/java2/DebugRunner.java
Executable file
85
java2/src/processing/mode/java2/DebugRunner.java
Executable 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;
|
||||
}
|
||||
}
|
||||
254
java2/src/processing/mode/java2/DebugToolbar.java
Executable file
254
java2/src/processing/mode/java2/DebugToolbar.java
Executable 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];
|
||||
}
|
||||
}
|
||||
1364
java2/src/processing/mode/java2/Debugger.java
Executable file
1364
java2/src/processing/mode/java2/Debugger.java
Executable file
File diff suppressed because it is too large
Load Diff
373
java2/src/processing/mode/java2/ErrorBar.java
Executable file
373
java2/src/processing/mode/java2/ErrorBar.java
Executable 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 <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
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) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
1097
java2/src/processing/mode/java2/ErrorCheckerService.java
Executable file
1097
java2/src/processing/mode/java2/ErrorCheckerService.java
Executable file
File diff suppressed because it is too large
Load Diff
36
java2/src/processing/mode/java2/ErrorMarker.java
Executable file
36
java2/src/processing/mode/java2/ErrorMarker.java
Executable file
@@ -0,0 +1,36 @@
|
||||
package processing.mode.java2;
|
||||
/**
|
||||
* Error markers displayed on the Error Bar.
|
||||
*
|
||||
* @author Manindra Moharana <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
374
java2/src/processing/mode/java2/ErrorWindow.java
Executable file
374
java2/src/processing/mode/java2/ErrorWindow.java
Executable 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 <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
65
java2/src/processing/mode/java2/FieldNode.java
Executable file
65
java2/src/processing/mode/java2/FieldNode.java
Executable 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;
|
||||
}
|
||||
}
|
||||
51
java2/src/processing/mode/java2/ImportStatement.java
Executable file
51
java2/src/processing/mode/java2/ImportStatement.java
Executable 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 <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
218
java2/src/processing/mode/java2/LineBreakpoint.java
Executable file
218
java2/src/processing/mode/java2/LineBreakpoint.java
Executable 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
196
java2/src/processing/mode/java2/LineHighlight.java
Executable file
196
java2/src/processing/mode/java2/LineHighlight.java
Executable 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
269
java2/src/processing/mode/java2/LineID.java
Executable file
269
java2/src/processing/mode/java2/LineID.java
Executable 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.
|
||||
}
|
||||
}
|
||||
35
java2/src/processing/mode/java2/LineListener.java
Executable file
35
java2/src/processing/mode/java2/LineListener.java
Executable 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);
|
||||
}
|
||||
66
java2/src/processing/mode/java2/LocalVariableNode.java
Executable file
66
java2/src/processing/mode/java2/LocalVariableNode.java
Executable 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;
|
||||
}
|
||||
}
|
||||
160
java2/src/processing/mode/java2/Problem.java
Executable file
160
java2/src/processing/mode/java2/Problem.java
Executable 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 <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
335
java2/src/processing/mode/java2/TextArea.java
Executable file
335
java2/src/processing/mode/java2/TextArea.java
Executable 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
313
java2/src/processing/mode/java2/TextAreaPainter.java
Executable file
313
java2/src/processing/mode/java2/TextAreaPainter.java
Executable 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);
|
||||
}
|
||||
}
|
||||
36
java2/src/processing/mode/java2/VMEventListener.java
Executable file
36
java2/src/processing/mode/java2/VMEventListener.java
Executable 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);
|
||||
}
|
||||
68
java2/src/processing/mode/java2/VMEventReader.java
Executable file
68
java2/src/processing/mode/java2/VMEventReader.java
Executable 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
java2/src/processing/mode/java2/VariableInspector.form
Executable file
53
java2/src/processing/mode/java2/VariableInspector.form
Executable 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>
|
||||
930
java2/src/processing/mode/java2/VariableInspector.java
Executable file
930
java2/src/processing/mode/java2/VariableInspector.java
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
358
java2/src/processing/mode/java2/VariableNode.java
Executable file
358
java2/src/processing/mode/java2/VariableNode.java
Executable 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;
|
||||
}
|
||||
}
|
||||
131
java2/src/processing/mode/java2/XQConsoleToggle.java
Executable file
131
java2/src/processing/mode/java2/XQConsoleToggle.java
Executable 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 <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
|
||||
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) {
|
||||
}
|
||||
}
|
||||
161
java2/src/processing/mode/java2/XQErrorTable.java
Executable file
161
java2/src/processing/mode/java2/XQErrorTable.java
Executable 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 <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
245
java2/src/processing/mode/java2/XQPreprocessor.java
Executable file
245
java2/src/processing/mode/java2/XQPreprocessor.java
Executable 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 <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user