moving things back off the branch, and into full disaster mode

This commit is contained in:
benfry
2011-01-20 19:29:39 +00:00
parent 4922d95e59
commit 5ecf3fdf36
67 changed files with 6032 additions and 3645 deletions

View File

@@ -0,0 +1,678 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Original Copyright (c) 1997, 1998 Van Di-Han HO. All Rights Reserved.
Updates Copyright (c) 2001 Jason Pell.
Further updates Copyright (c) 2003 Martin Gomez, Ateneo de Manila University
Additional updates Copyright (c) 2005-10 Ben Fry and Casey Reas
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, version 2.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java;
import java.util.Stack;
import java.util.regex.Pattern;
import processing.app.Formatter;
import processing.app.Preferences;
import processing.core.PApplet;
/**
* Handler for dealing with auto format.
* Contributed by Martin Gomez, additional bug fixes by Ben Fry.
* Additional fixes by Jonathan Feinberg in March 2010.
* <p/>
* After some further digging, this code in fact appears to be a modified
* version of Jason Pell's GPLed "Java Beautifier" class found
* <a href="http://www.geocities.com/jasonpell/programs.html">here</a>.
* Which is itself based on code from Van Di-Han Ho from
* <a href="http://www.geocities.com/~starkville/vancbj_idx.html">here</a>.
* [Ben Fry, August 2009]
*/
public class AutoFormat implements Formatter {
private char[] chars;
private final StringBuilder buf = new StringBuilder();
private final StringBuilder result = new StringBuilder();
private int indentValue;
private boolean EOF;
private boolean a_flg, e_flg, if_flg, s_flag, q_flg;
private boolean s_if_flg[];
private int pos, lineNumber;
private int s_level[];
private int c_level;
private int sp_flg[][];
private int s_ind[][];
private int s_if_lev[];
private int if_lev, level;
private int ind[];
private int paren;
private int p_flg[];
private char l_char;
private int ct;
private int s_tabs[][];
private boolean jdoc_flag;
private char cc;
private int tabs;
private char c;
private final Stack<Boolean> castFlags = new Stack<Boolean>();
private void comment() {
final boolean save_s_flg = s_flag;
buf.append(c = next()); // extra char
while (true) {
buf.append(c = next());
while ((c != '/')) {
if (c == '\n') {
lineNumber++;
writeIndentedComment();
s_flag = true;
}
buf.append(c = next());
}
if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '*') {
jdoc_flag = false;
break;
}
}
writeIndentedComment();
s_flag = save_s_flg;
jdoc_flag = false;
return;
}
private char get_string() {
char ch;
while (true) {
buf.append(ch = next());
if (ch == '\\') {
buf.append(next());
continue;
}
if (ch == '\'' || ch == '"') {
buf.append(cc = next());
while (!EOF && cc != ch) {
if (cc == '\\') {
buf.append(next());
}
buf.append(cc = next());
}
continue;
}
if (ch == '\n') {
writeIndentedLine();
a_flg = true;
continue;
}
return ch;
}
}
private void writeIndentedLine() {
if (buf.length() == 0) {
if (s_flag) {
s_flag = a_flg = false;
}
return;
}
if (s_flag) {
final boolean shouldIndent = (tabs > 0) && (buf.charAt(0) != '{')
&& a_flg;
if (shouldIndent) {
tabs++;
}
printIndentation();
s_flag = false;
if (shouldIndent) {
tabs--;
}
a_flg = false;
}
result.append(buf);
buf.setLength(0);
}
private void writeIndentedComment() {
final boolean saved_s_flag = s_flag;
if (buf.length() > 0) {
if (s_flag) {
printIndentation();
s_flag = false;
}
int i = 0;
while (buf.charAt(i) == ' ') {
i++;
}
if (lookup_com("/**")) {
jdoc_flag = true;
}
if (buf.charAt(i) == '/' && buf.charAt(i + 1) == '*') {
if (saved_s_flag && prev() != ';') {
result.append(buf.substring(i));
} else {
result.append(buf);
}
} else {
if (buf.charAt(i) == '*' || !jdoc_flag) {
result.append((" " + buf.substring(i)));
} else {
result.append((" * " + buf.substring(i)));
}
}
buf.setLength(0);
}
}
private void handleSingleLineComment() {
c = next();
while (c != '\n') {
buf.append(c);
c = next();
}
lineNumber++;
writeIndentedLine();
s_flag = true;
}
private void printIndentation() {
if (tabs < 0) {
tabs = 0;
}
if (tabs == 0) {
return;
}
final int spaces = tabs * indentValue;
for (int k = 0; k < spaces; k++) {
result.append(" ");
}
}
private char peek() {
if (pos + 1 >= chars.length) {
return 0;
}
return chars[pos + 1];
}
private char lastNonWhitespace = 0;
private int prev() {
return lastNonWhitespace;
}
private void advanceToNonSpace() {
if (EOF) {
return;
}
do {
pos++;
} while (pos < chars.length && chars[pos] == ' ');
if (pos == chars.length - 1) {
EOF = true;
} else {
pos--; // reset for next()
}
}
private char next() {
if (EOF) {
return 0;
}
pos++;
final char c;
if (pos < chars.length) {
c = chars[pos];
if (!Character.isWhitespace(c))
lastNonWhitespace = c;
} else {
c = 0;
}
if (pos == chars.length - 1) {
EOF = true;
}
return c;
}
/* else processing */
private void gotelse() {
tabs = s_tabs[c_level][if_lev];
p_flg[level] = sp_flg[c_level][if_lev];
ind[level] = s_ind[c_level][if_lev];
if_flg = true;
}
/* read to new_line */
private boolean getnl() {
final int savedTabs = tabs;
char c = peek();
while (!EOF && (c == '\t' || c == ' ')) {
buf.append(next());
c = peek();
}
if (c == '/') {
buf.append(next());
c = peek();
if (c == '*') {
buf.append(next());
comment();
} else if (c == '/') {
buf.append(next());
handleSingleLineComment();
return true;
}
}
c = peek();
if (c == '\n') {
// eat it
next();
lineNumber++;
tabs = savedTabs;
return true;
}
return false;
}
private boolean lookup(final String keyword) {
return Pattern.matches("^\\s*" + keyword + "(?![a-zA-Z0-9_&]).*$", buf);
}
private boolean lookup_com(final String keyword) {
final String regex = "^\\s*" + keyword.replaceAll("\\*", "\\\\*") + ".*$";
return Pattern.matches(regex, buf);
}
private void trimRight(final StringBuilder sb) {
while (sb.length() >= 1
&& Character.isWhitespace(sb.charAt(sb.length() - 1)))
sb.setLength(sb.length() - 1);
}
public String format(final String source) {
final String normalizedText = source.replaceAll("\r", "");
final String cleanText = normalizedText
+ (normalizedText.endsWith("\n") ? "" : "\n");
result.setLength(0);
indentValue = Preferences.getInteger("editor.tabs.size");
lineNumber = 0;
q_flg = e_flg = a_flg = if_flg = false;
s_flag = true;
c_level = if_lev = level = paren = 0;
tabs = 0;
jdoc_flag = false;
s_level = new int[10];
sp_flg = new int[20][10];
s_ind = new int[20][10];
s_if_lev = new int[10];
s_if_flg = new boolean[10];
ind = new int[10];
p_flg = new int[10];
s_tabs = new int[20][10];
pos = -1;
chars = cleanText.toCharArray();
lineNumber = 1;
EOF = false; // set in next() when EOF
while (!EOF) {
c = next();
switch (c) {
default:
buf.append(c);
l_char = c;
break;
case ',':
trimRight(buf);
buf.append(c);
buf.append(' ');
advanceToNonSpace();
break;
case ' ':
case '\t':
if (lookup("else")) {
gotelse();
if ((!s_flag) || buf.length() > 0) {
buf.append(c);
}
writeIndentedLine();
s_flag = false;
break;
}
if ((!s_flag) || buf.length() > 0) {
buf.append(c);
}
break;
case '\n':
lineNumber++;
if (EOF) {
break;
}
e_flg = lookup("else");
if (e_flg) {
gotelse();
}
if (lookup_com("//")) {
final char lastChar = buf.charAt(buf.length() - 1);
if (lastChar == '\n') {
buf.setLength(buf.length() - 1);
}
}
writeIndentedLine();
result.append("\n");
s_flag = true;
if (e_flg) {
p_flg[level]++;
tabs++;
} else if (prev() == l_char) {
a_flg = true;
}
break;
case '{':
if (lookup("else")) {
gotelse();
}
if (s_if_lev.length == c_level) {
s_if_lev = PApplet.expand(s_if_lev);
s_if_flg = PApplet.expand(s_if_flg);
}
s_if_lev[c_level] = if_lev;
s_if_flg[c_level] = if_flg;
if_lev = 0;
if_flg = false;
c_level++;
if (s_flag && p_flg[level] != 0) {
p_flg[level]--;
tabs--;
}
trimRight(buf);
if (buf.length() > 0
|| (result.length() > 0 && !Character.isWhitespace(result
.charAt(result.length() - 1))))
buf.append(" ");
buf.append(c);
writeIndentedLine();
getnl();
writeIndentedLine();
//fprintf(outfil,"\n");
result.append("\n");
tabs++;
s_flag = true;
if (p_flg[level] > 0) {
ind[level] = 1;
level++;
s_level[level] = c_level;
}
break;
case '}':
c_level--;
if (c_level < 0) {
c_level = 0;
buf.append(c);
writeIndentedLine();
} else {
if_lev = s_if_lev[c_level] - 1;
if (if_lev < 0) {
if_lev = 0;
}
if_flg = s_if_flg[c_level];
trimRight(buf);
writeIndentedLine();
tabs--;
trimRight(result);
result.append("\n");
printIndentation();
result.append(c);
if (peek() == ';') {
result.append(next());
}
getnl();
writeIndentedLine();
result.append("\n");
s_flag = true;
if (c_level < s_level[level]) {
if (level > 0) {
level--;
}
}
if (ind[level] != 0) {
tabs -= p_flg[level];
p_flg[level] = 0;
ind[level] = 0;
}
}
break;
case '"':
case '\'':
buf.append(c);
cc = next();
while (!EOF && cc != c) {
buf.append(cc);
if (cc == '\\') {
buf.append(cc = next());
}
if (cc == '\n') {
lineNumber++;
writeIndentedLine();
s_flag = true;
}
cc = next();
}
buf.append(cc);
if (getnl()) {
l_char = cc;
// push a newline into the stream
chars[pos--] = '\n';
}
break;
case ';':
buf.append(c);
writeIndentedLine();
if (p_flg[level] > 0 && ind[level] == 0) {
tabs -= p_flg[level];
p_flg[level] = 0;
}
getnl();
writeIndentedLine();
result.append("\n");
s_flag = true;
if (if_lev > 0) {
if (if_flg) {
if_lev--;
if_flg = false;
} else {
if_lev = 0;
}
}
break;
case '\\':
buf.append(c);
buf.append(next());
break;
case '?':
q_flg = true;
buf.append(c);
break;
case ':':
buf.append(c);
if (peek() == ':') {
writeIndentedLine();
result.append(next());
break;
}
if (q_flg) {
q_flg = false;
break;
}
if (!lookup("default") && !lookup("case")) {
s_flag = false;
writeIndentedLine();
} else {
tabs--;
writeIndentedLine();
tabs++;
}
if (peek() == ';') {
result.append(next());
}
getnl();
writeIndentedLine();
result.append("\n");
s_flag = true;
break;
case '/':
final char la = peek();
if (la == '/') {
buf.append(c).append(next());
handleSingleLineComment();
result.append("\n");
} else if (la == '*') {
if (buf.length() > 0) {
writeIndentedLine();
}
buf.append(c).append(next());
comment();
} else {
buf.append(c);
}
break;
case ')':
final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop();
paren--;
if (paren < 0) {
paren = 0;
}
buf.append(c);
writeIndentedLine();
if (getnl()) {
chars[pos--] = '\n';
if (paren != 0) {
a_flg = true;
} else if (tabs > 0 && !isCast) {
p_flg[level]++;
tabs++;
ind[level] = 0;
}
}
break;
case '(':
castFlags.push(Pattern.matches("^.*?(?:int|color|float)\\s*$", buf));
final boolean isFor = lookup("for");
final boolean isIf = lookup("if");
if (isFor || isIf || lookup("while")) {
if (!Character.isWhitespace(buf.charAt(buf.length() - 1))) {
buf.append(' ');
}
}
buf.append(c);
paren++;
if (isFor) {
// TODO(feinberg): handle new-style for loops
c = get_string();
while (c != ';' && c != ':') {
c = get_string();
}
ct = 0;
int for_done = 0;
while (for_done == 0) {
c = get_string();
while (c != ')') {
if (c == '(') {
ct++;
}
c = get_string();
}
if (ct != 0) {
ct--;
} else {
for_done = 1;
}
} // endwhile for_done
paren--;
if (paren < 0) {
paren = 0;
}
writeIndentedLine();
if (getnl()) {
chars[pos--] = '\n';
p_flg[level]++;
tabs++;
ind[level] = 0;
}
break;
} else if (isIf) {
writeIndentedLine();
s_tabs[c_level][if_lev] = tabs;
sp_flg[c_level][if_lev] = p_flg[level];
s_ind[c_level][if_lev] = ind[level];
if_lev++;
if_flg = true;
}
} // end switch
} // end while not EOF
final String formatted = result.toString();
return formatted.equals(cleanText) ? source : formatted;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2008-10 Ben Fry and Casey Reas
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.java;
/*
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import processing.app.Base;
import processing.app.Preferences;
import processing.app.SketchException;
import processing.app.RunnerListener;
import processing.app.Sketch;
import processing.core.PApplet;
import processing.mode.java.runner.*;
*/
/**
* Class to handle running Processing from the command line.
* <PRE>
* --help Show the help text.
*
* --sketch=&lt;name&gt; Specify the sketch folder (required)
* --output=&lt;name&gt; Specify the output folder (required and
* cannot be the same as the sketch folder.)
*
* --preprocess Preprocess a sketch into .java files.
* --build Preprocess and compile a sketch into .class files.
* --run Preprocess, compile, and run a sketch.
* --present Preprocess, compile, and run a sketch full screen.
*
* --export-applet Export an applet.
* --export-application Export an application.
* --platform Specify the platform (export to application only).
* Should be one of 'windows', 'macosx', or 'linux'.
* --bits Must be specified if libraries are used that are
* 32- or 64-bit specific such as the OpenGL library.
* Otherwise specify 0 or leave it out.
*
* --preferences=&lt;file&gt; Specify a preferences file to use. Required if the
* sketch uses libraries found in your sketchbook folder.
* </PRE>
*
* To build the command line version, first build for your platform,
* then cd to processing/build/cmd and type 'dist.sh'. This will create a
* usable installation plus a zip file of the same.
*
* @author fry
*/
/*
public class Commander implements RunnerListener {
static final String helpArg = "--help";
static final String preprocArg = "--preprocess";
static final String buildArg = "--build";
static final String runArg = "--run";
static final String presentArg = "--present";
static final String sketchArg = "--sketch=";
static final String outputArg = "--output=";
static final String exportAppletArg = "--export-applet";
static final String exportApplicationArg = "--export-application";
static final String platformArg = "--platform=";
static final String bitsArg = "--bits=";
static final String preferencesArg = "--preferences=";
static final int HELP = -1;
static final int PREPROCESS = 0;
static final int BUILD = 1;
static final int RUN = 2;
static final int PRESENT = 3;
static final int EXPORT_APPLET = 4;
static final int EXPORT_APPLICATION = 5;
Sketch sketch;
static public void main(String[] args) {
// Do this early so that error messages go to the console
Base.setCommandLine();
// init the platform so that prefs and other native code is ready to go
Base.initPlatform();
// make sure a full JDK is installed
Base.initRequirements();
// run static initialization that grabs all the prefs
//Preferences.init(null);
// launch command line handler
new Commander(args);
}
public Commander(String[] args) {
String sketchFolder = null;
String pdePath = null; // path to the .pde file
String outputPath = null;
String preferencesPath = null;
int platformIndex = PApplet.platform; // default to this platform
int platformBits = 0;
int mode = HELP;
for (String arg : args) {
if (arg.length() == 0) {
// ignore it, just the crappy shell script
} else if (arg.equals(helpArg)) {
// mode already set to HELP
} else if (arg.equals(buildArg)) {
mode = BUILD;
} else if (arg.equals(runArg)) {
mode = RUN;
} else if (arg.equals(presentArg)) {
mode = PRESENT;
} else if (arg.equals(preprocArg)) {
mode = PREPROCESS;
} else if (arg.equals(exportAppletArg)) {
mode = EXPORT_APPLET;
} else if (arg.equals(exportApplicationArg)) {
mode = EXPORT_APPLICATION;
} else if (arg.startsWith(platformArg)) {
String platformStr = arg.substring(platformArg.length());
platformIndex = Base.getPlatformIndex(platformStr);
if (platformIndex == -1) {
complainAndQuit(platformStr + " should instead be " +
"'windows', 'macosx', or 'linux'.");
}
} else if (arg.startsWith(sketchArg)) {
sketchFolder = arg.substring(sketchArg.length());
File sketchy = new File(sketchFolder);
File pdeFile = new File(sketchy, sketchy.getName() + ".pde");
pdePath = pdeFile.getAbsolutePath();
} else if (arg.startsWith(preferencesArg)) {
preferencesPath = arg.substring(preferencesArg.length());
} else if (arg.startsWith(outputArg)) {
outputPath = arg.substring(outputArg.length());
} else {
complainAndQuit("I don't know anything about " + arg + ".");
}
}
if ((outputPath == null) &&
(mode == PREPROCESS || mode == BUILD ||
mode == RUN || mode == PRESENT)) {
complainAndQuit("An output path must be specified when using " +
preprocArg + ", " + buildArg + ", " +
runArg + ", or " + presentArg + ".");
}
if (mode == HELP) {
printCommandLine(System.out);
System.exit(0);
}
// --present --platform=windows "--sketch=/Applications/Processing 0148/examples/Basics/Arrays/Array" --output=test-build
File outputFolder = new File(outputPath);
if (!outputFolder.exists()) {
if (!outputFolder.mkdirs()) {
complainAndQuit("Could not create the output folder.");
}
}
// run static initialization that grabs all the prefs
// (also pass in a prefs path if that was specified)
Preferences.init(preferencesPath);
if (sketchFolder == null) {
complainAndQuit("No sketch path specified.");
} else if (outputPath.equals(pdePath)) {
complainAndQuit("The sketch path and output path cannot be identical.");
} else if (!pdePath.toLowerCase().endsWith(".pde")) {
complainAndQuit("Sketch path must point to the main .pde file.");
} else {
//Sketch sketch = null;
boolean success = false;
try {
sketch = new Sketch(null, pdePath);
if (mode == PREPROCESS) {
success = sketch.preprocess(new File(outputPath)) != null;
} else if (mode == BUILD) {
success = sketch.build(outputPath) != null;
} else if (mode == RUN || mode == PRESENT) {
String className = sketch.build(outputPath);
if (className != null) {
success = true;
Runner runner = new Runner(this, sketch);
runner.launch(className, mode == PRESENT);
} else {
success = false;
}
} else if (mode == EXPORT_APPLET) {
if (outputPath != null) {
success = sketch.exportApplet(outputPath);
} else {
String target = sketchFolder + File.separatorChar + "applet";
success = sketch.exportApplet(target);
}
} else if (mode == EXPORT_APPLICATION) {
if (outputPath != null) {
success = sketch.exportApplication(outputPath, platformIndex, platformBits);
} else {
//String sketchFolder =
// pdePath.substring(0, pdePath.lastIndexOf(File.separatorChar));
outputPath =
sketchFolder + File.separatorChar +
"application." + Base.getPlatformName(platformIndex);
success = sketch.exportApplication(outputPath, platformIndex, platformBits);
}
}
System.exit(success ? 0 : 1);
} catch (SketchException re) {
statusError(re);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
public void statusNotice(String message) {
System.err.println(message);
}
public void statusError(String message) {
System.err.println(message);
}
public void statusError(Exception exception) {
if (exception instanceof SketchException) {
SketchException re = (SketchException) exception;
// format the runner exception like emacs
//blah.java:2:10:2:13: Syntax Error: This is a big error message
String filename = sketch.getCode(re.getCodeIndex()).getFileName();
int line = re.getCodeLine();
int column = re.getCodeColumn();
if (column == -1) column = 0;
// TODO if column not specified, should just select the whole line.
System.err.println(filename + ":" +
line + ":" + column + ":" +
line + ":" + column + ":" + " " + re.getMessage());
} else {
exception.printStackTrace();
}
}
static void complainAndQuit(String lastWords) {
printCommandLine(System.err);
System.err.println(lastWords);
System.exit(1);
}
static void printCommandLine(PrintStream out) {
out.println("Processing " + Base.VERSION_NAME + " rocks the console.");
out.println();
out.println("--help Show this help text.");
out.println();
out.println("--sketch=<name> Specify the sketch folder (required)");
out.println("--output=<name> Specify the output folder (required and");
out.println(" cannot be the same as the sketch folder.)");
out.println();
out.println("--preprocess Preprocess a sketch into .java files.");
out.println("--build Preprocess and compile a sketch into .class files.");
out.println("--run Preprocess, compile, and run a sketch.");
out.println("--present Preprocess, compile, and run a sketch full screen.");
out.println();
out.println("--export-applet Export an applet.");
out.println("--export-application Export an application.");
out.println("--platform Specify the platform (export to application only).");
out.println(" Should be one of 'windows', 'macosx', or 'linux'.");
out.println("--bits Must be specified if libraries are used that are");
out.println(" 32- or 64-bit specific such as the OpenGL library.");
out.println(" Otherwise specify 0 or leave it out.");
out.println();
out.println("--preferences=<file> Specify a preferences file to use. Required if the");
out.println(" sketch uses libraries found in your sketchbook folder.");
}
}
*/

View File

@@ -0,0 +1,669 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-10 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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.java;
import processing.app.*;
import processing.core.*;
import java.io.*;
import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
import org.eclipse.jdt.core.compiler.CompilationProgress;
public class Compiler {
// public Compiler() { }
/**
* Compile with ECJ. See http://j.mp/8paifz for documentation.
*
* @param sketch Sketch object to be compiled, used for placing exceptions
* @param buildPath Where the temporary files live and will be built from.
* @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(Build build) throws SketchException {
// This will be filled in if anyone gets angry
SketchException exception = null;
boolean success = false;
String baseCommand[] = new String[] {
"-Xemacs",
//"-noExit", // not necessary for ecj
"-source", "1.5",
"-target", "1.5",
"-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);
CompilationProgress progress = null;
PrintWriter outWriter = new PrintWriter(System.out);
success = BatchCompiler.compile(command, outWriter, writer, progress);
// 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());
what = what.substring(0, what.indexOf(' '));
System.err.println("Note that release 1.0, libraries must be " +
"installed in a folder named 'libraries' " +
"inside the 'sketchbook' folder.");
exception.setMessage("The package " +
"\u201C" + what + "\u201D" +
" does not exist. " +
"You might be missing a library.");
// // 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")) {
handleCrustyCode(exception);
} 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") ||
what.equals("framerate")) {
handleCrustyCode(exception);
} 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)") ||
parts[1].equals("push()")) {
handleCrustyCode(exception);
} 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;
}
/**
* Fire up 'ole javac based on <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/javac.html#proginterface">this interface</a>.
*
* @param sketch Sketch object to be compiled.
* @param buildPath Where the temporary files live and will be built from.
* @return
* @throws RunnerException Only if there's a problem. Only then.
*/
// public boolean compileJavac(Sketch sketch,
// String buildPath) throws RunnerException {
// // This will be filled in if anyone gets angry
// RunnerException exception = null;
//
// String baseCommand[] = new String[] {
// "-source", "1.5",
// "-target", "1.5",
// "-classpath", sketch.getClassPath(),
// "-nowarn", // we're not currently interested in warnings (ignored?)
// "-d", buildPath // output the classes in the buildPath
// };
// //PApplet.println(baseCommand);
//
// // make list of code files that need to be compiled
// // (some files are skipped if they contain no class)
// String[] preprocNames = new String[sketch.getCodeCount()];
// int preprocCount = 0;
// for (int i = 0; i < sketch.getCodeCount(); i++) {
// if (sketch.getCode(i).preprocName != null) {
// preprocNames[preprocCount++] = sketch.getCode(i).preprocName;
// }
// }
// String[] command = new String[baseCommand.length + preprocCount];
// System.arraycopy(baseCommand, 0, command, 0, baseCommand.length);
// // append each of the files to the command string
// for (int i = 0; i < preprocCount; i++) {
// command[baseCommand.length + i] =
// buildPath + File.separator + preprocNames[i];
// }
// //PApplet.println(command);
//
// int result = -1; // needs to be set bad by default, in case hits IOE below
//
// try {
// // Load errors into a local StringBuffer
// final StringBuffer errorBuffer = new StringBuffer();
//
// // Create single method dummy writer class to slurp errors from javac
// 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);
//
// // 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 m = errorBuffer.toString();
// //ParsePosition mp = new ParsePosition(0);
//
//// while (mp.getIndex() < m.length()) { // reading messages
// String line = null;
// int lineIndex = 0;
// String[] lines = PApplet.split(errorBuffer.toString(), '\n');
// int lineCount = lines.length;
// while (lineIndex < lineCount) {
// //while ((line = reader.readLine()) != null) {
// //System.out.println("got line " + line); // debug
//
// /*
//compiler.misc.count.error=\
// {0} error
//compiler.misc.count.error.plural=\
// {0} errors
//compiler.misc.count.warn=\
// {0} warning
//compiler.misc.count.warn.plural=\
// {0} warnings
// */
// // Check to see if this is the last line.
//// if ((PApplet.match(line, "\\d+ error[s]?") != null) ||
//// (PApplet.match(line, "\\d+ warning[s]?") != null)) {
//// break;
//// }
// if (isCompilerMatch(line, "compiler.misc.count.error") ||
// isCompilerMatch(line, "compiler.misc.count.error.plural") ||
// isCompilerMatch(line, "compiler.misc.count.warn") ||
// isCompilerMatch(line, "compiler.misc.count.warn.plural")) {
// break;
// }
//
// // Hide these because people are getting confused
// // http://dev.processing.org/bugs/show_bug.cgi?id=817
// // com/sun/tools/javac/resources/compiler.properties
// //if (line.startsWith("Note: ")) {
// String compilerNote = compilerResources.getString("compiler.note.note");
// MessageFormat noteFormat = new MessageFormat(compilerNote + " {0}");
// Object[] noteFound;
// try {
// noteFound = noteFormat.parse(line);
// if (noteFound != null) {
// System.out.println("gefunden " + noteFound[0]);
//
// /*
// // if you mention serialVersionUID one more time, i'm kickin' you out
// if (line.indexOf("serialVersionUID") != -1) continue;
// // {0} uses unchecked or unsafe operations.
// // Some input files use unchecked or unsafe operations.
// if (line.indexOf("or unsafe operations") != -1) continue;
// // {0} uses or overrides a deprecated API.
// // Some input files use or override a deprecated API.
// if (line.indexOf("or override") != -1) continue;
// // Recompile with -Xlint:deprecation for details.
// // Recompile with -Xlint:unchecked for details.
// if (line.indexOf("Recompile with -Xlint:") != -1) continue;
// System.err.println(line);
// */
// continue;
// }
// } catch (ParseException e) {
// e.printStackTrace();
// }
//
// // 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*";
// String[] pieces = PApplet.match(line, errorFormat);
//
// // if it's something unexpected, die and print the mess to the console
// if (pieces == null) {
// exception = new RunnerException("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) {
// for (int i = lineIndex; i < lineCount; i++) {
// System.err.println(lines[i]);
// }
// 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[0];
// // Line numbers are 1-indexed from javac
// int dotJavaLineIndex = PApplet.parseInt(pieces[1]) - 1;
// String errorMessage = pieces[2];
//
// int codeIndex = -1;
// int codeLine = -1;
// for (int i = 0; i < sketch.getCodeCount(); i++) {
// String name = sketch.getCode(i).preprocName;
// if ((name != null) && dotJavaFilename.equals(name)) {
// codeIndex = i;
// }
// }
// //System.out.println("code index/line are " + codeIndex + " " + codeLine);
// //System.out.println("java line number " + dotJavaLineIndex + " from " + dotJavaFilename);
//
// if (codeIndex == 0) { // main class, figure out which tab
// for (int i = 1; i < sketch.getCodeCount(); i++) {
// SketchCode code = sketch.getCode(i);
//
// if (code.flavor == Sketch.PDE) {
// if (code.preprocOffset <= dotJavaLineIndex) {
// codeIndex = i;
// //System.out.println("i'm thinkin file " + i);
// }
// }
// }
// }
// //System.out.println("preproc offset is " + sketch.getCode(codeIndex).preprocOffset);
// codeLine = dotJavaLineIndex - sketch.getCode(codeIndex).preprocOffset;
// //System.out.println("code line now " + codeLine);
// exception = new RunnerException(errorMessage, codeIndex, codeLine, -1, false);
//
// // 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 (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 RunnerException(bigSigh);
// e.printStackTrace();
// result = 1;
// }
// // In case there was something else.
// if (exception != null) throw exception;
//
// // Success means that 'result' is set to zero
// return (result == 0);
// }
//
//
// boolean isCompilerMatch(String line, String format) {
// return compilerMatch(line, format) != null;
// }
//
//
// Object[] compilerMatch(String line, String name) {
// String format = compilerResources.getString(name);
// MessageFormat mf = new MessageFormat(format);
// Object[] matches = null;
// try {
// matches = mf.parse(line);
// } catch (ParseException e) {
// e.printStackTrace();
// }
// return matches;
// }
// boolean isCompilerMatch(String line, ParsePosition pos, String format) {
// return compilerMatch(line, pos, format) != null;
// }
//
//
// Object[] compilerMatch(String line, ParsePosition pos, String name) {
// String format = compilerResources.getString(name);
// MessageFormat mf = new MessageFormat(format);
// Object[] matches = mf.parse(line, pos);
// return matches;
// }
// Tell-tale signs of old code copied and pasted from the web.
// Detect classes BFont, BGraphics, BImage; methods framerate, push;
// and variables LINE_LOOP and LINE_STRIP.
// static HashMap crusties = new HashMap();
// static {
// crusties.put("BFont", new Object());
// crusties.put("BGraphics", new Object());
// crusties.put("BImage", new Object());
// crusties.put("framerate", new Object());
// crusties.put("push", new Object());
// crusties.put("LINE_LOOP", new Object());
// crusties.put("LINE_STRIP", new Object());
// }
// void handleCannotFindSymbol(BufferedReader reader,
// RunnerException rex) throws IOException {
// String symbolLine = reader.readLine();
// String locationLine = reader.readLine();
// /*String codeLine =*/ reader.readLine();
// String caretLine = reader.readLine();
// rex.setCodeColumn(caretColumn(caretLine));
//
// String[] pieces =
// PApplet.match(symbolLine, "symbol\\s*:\\s*(\\w+)\\s+(.*)");
// if (pieces != null) {
// if (pieces[0].equals("class") ||
// pieces[0].equals("variable")) {
// rex.setMessage("Cannot find a " + pieces[0] + " " +
// "named \u201C" + pieces[1] + "\u201D");
// if (crusties.get(pieces[1]) != null) {
// handleCrustyCode(rex);
// }
//
// } else if (pieces[0].equals("method")) {
// int leftParen = pieces[1].indexOf("(");
// int rightParen = pieces[1].indexOf(")");
//
// String methodName = pieces[1].substring(0, leftParen);
// String methodParams = pieces[1].substring(leftParen + 1, rightParen);
//
// String message =
// "Cannot find a function named \u201C" + methodName + "\u201D";
// if (methodParams.length() > 0) {
// if (methodParams.indexOf(',') != -1) {
// message += " with parameters ";
// } else {
// message += " with parameter ";
// }
// message += methodParams;
// }
//
// String locationClass = "location: class ";
// if (locationLine.startsWith(locationClass) &&
// // don't include the class name when it's a temp class
// locationLine.indexOf("Temporary_") == -1) {
// String className = locationLine.substring(locationClass.length());
// // If no dot exists, -1 + 1 is 0, so this will have no effect.
// className = className.substring(className.lastIndexOf('.') + 1);
// int bracket = className.indexOf('[');
// if (bracket == -1) {
// message += " in class " + className;
// } else {
// className = className.substring(0, bracket);
// message += " for an array of " + className + " objects";
// }
// }
// message += ".";
// rex.setMessage(message);
//
// // On second thought, make sure this isn't just some alpha/beta code
// if (crusties.get(methodName) != null) {
// handleCrustyCode(rex);
// }
//
// } else {
// System.out.println(symbolLine);
// }
// }
// }
static protected void handleCrustyCode(SketchException rex) {
rex.setMessage("This code needs to be updated, " +
"please read the Changes page on the Wiki.");
Base.showChanges();
}
protected int caretColumn(String caretLine) {
return caretLine.indexOf("^");
}
}

View File

@@ -0,0 +1,518 @@
package processing.mode.java;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import processing.app.Base;
import processing.app.Editor;
import processing.app.EditorToolbar;
import processing.app.Formatter;
import processing.app.Mode;
import processing.app.Preferences;
import processing.app.SketchException;
public class JavaEditor extends Editor {
JavaMode jmode;
protected JavaEditor(Base base, String path, int[] location, Mode mode) {
super(base, path, location, mode);
jmode = (JavaMode) mode;
}
public EditorToolbar createToolbar() {
return new Toolbar(this, base);
}
public Formatter createFormatter() {
return new AutoFormat();
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public JMenu buildFileMenu() {
JMenuItem exportApplet = Base.newJMenuItem("Export Applet", 'E');
exportApplet.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleExportApplet();
}
});
JMenuItem exportApplication = Base.newJMenuItemShift("Export Application", 'E');
exportApplication.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleExportApplication();
}
});
return buildFileMenu(new JMenuItem[] { exportApplet, exportApplication });
}
public JMenu buildSketchMenu() {
JMenuItem runItem = Base.newJMenuItem("Run", 'R');
runItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleRun();
}
});
JMenuItem presentItem = Base.newJMenuItemShift("Present", 'R');
presentItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handlePresent();
}
});
JMenuItem stopItem = new JMenuItem("Stop");
stopItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleStop();
}
});
return buildSketchMenu(new JMenuItem[] { runItem, presentItem, stopItem });
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public String getCommentPrefix() {
return "//";
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* Called by Sketch &rarr; Export.
* Handles calling the export() function on sketch, and
* queues all the gui status stuff that comes along with it.
* <p/>
* Made synchronized to (hopefully) avoid problems of people
* hitting export twice, quickly, and horking things up.
*/
public void handleExportApplet() {
if (handleExportCheckModified()) {
toolbar.activate(Toolbar.EXPORT);
try {
boolean success = jmode.handleExportApplet(sketch);
if (success) {
File appletFolder = new File(sketch.getFolder(), "applet");
Base.openFolder(appletFolder);
statusNotice("Done exporting.");
} else {
// error message will already be visible
}
} catch (Exception e) {
statusError(e);
}
toolbar.deactivate(Toolbar.EXPORT);
}
}
// public void handleExportApplication() {
// toolbar.activate(Toolbar.EXPORT);
// try {
// jmode.handleExportApplication();
// } catch (Exception e) {
// statusError(e);
// }
// toolbar.deactivate(Toolbar.EXPORT);
// }
/**
* Handler for Sketch &rarr; Export Application
*/
public void handleExportApplication() {
toolbar.activate(Toolbar.EXPORT);
if (handleExportCheckModified()) {
statusNotice("Exporting application...");
try {
if (exportApplicationPrompt()) {
Base.openFolder(sketch.getFolder());
statusNotice("Done exporting.");
} else {
// error message will already be visible
// or there was no error, in which case it was canceled.
}
} catch (Exception e) {
statusNotice("Error during export.");
e.printStackTrace();
}
}
toolbar.deactivate(Toolbar.EXPORT);
}
protected boolean exportApplicationPrompt() throws IOException, SketchException {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(Box.createVerticalStrut(6));
//Box panel = Box.createVerticalBox();
//Box labelBox = Box.createHorizontalBox();
// String msg = "<html>Click Export to Application to create a standalone, " +
// "double-clickable application for the selected plaforms.";
// String msg = "Export to Application creates a standalone, \n" +
// "double-clickable application for the selected plaforms.";
String line1 = "Export to Application creates double-clickable,";
String line2 = "standalone applications for the selected plaforms.";
JLabel label1 = new JLabel(line1, SwingConstants.CENTER);
JLabel label2 = new JLabel(line2, SwingConstants.CENTER);
label1.setAlignmentX(Component.LEFT_ALIGNMENT);
label2.setAlignmentX(Component.LEFT_ALIGNMENT);
// label1.setAlignmentX();
// label2.setAlignmentX(0);
panel.add(label1);
panel.add(label2);
int wide = label2.getPreferredSize().width;
panel.add(Box.createVerticalStrut(12));
final JCheckBox windowsButton = new JCheckBox("Windows");
//windowsButton.setMnemonic(KeyEvent.VK_W);
windowsButton.setSelected(Preferences.getBoolean("export.application.platform.windows"));
windowsButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
Preferences.setBoolean("export.application.platform.windows", windowsButton.isSelected());
}
});
final JCheckBox macosxButton = new JCheckBox("Mac OS X");
//macosxButton.setMnemonic(KeyEvent.VK_M);
macosxButton.setSelected(Preferences.getBoolean("export.application.platform.macosx"));
macosxButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
Preferences.setBoolean("export.application.platform.macosx", macosxButton.isSelected());
}
});
final JCheckBox linuxButton = new JCheckBox("Linux");
//linuxButton.setMnemonic(KeyEvent.VK_L);
linuxButton.setSelected(Preferences.getBoolean("export.application.platform.linux"));
linuxButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
Preferences.setBoolean("export.application.platform.linux", linuxButton.isSelected());
}
});
JPanel platformPanel = new JPanel();
//platformPanel.setLayout(new BoxLayout(platformPanel, BoxLayout.X_AXIS));
platformPanel.add(windowsButton);
platformPanel.add(Box.createHorizontalStrut(6));
platformPanel.add(macosxButton);
platformPanel.add(Box.createHorizontalStrut(6));
platformPanel.add(linuxButton);
platformPanel.setBorder(new TitledBorder("Platforms"));
//Dimension goodIdea = new Dimension(wide, platformPanel.getPreferredSize().height);
//platformPanel.setMaximumSize(goodIdea);
wide = Math.max(wide, platformPanel.getPreferredSize().width);
platformPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(platformPanel);
// Box indentPanel = Box.createHorizontalBox();
// indentPanel.add(Box.createHorizontalStrut(new JCheckBox().getPreferredSize().width));
final JCheckBox showStopButton = new JCheckBox("Show a Stop button");
//showStopButton.setMnemonic(KeyEvent.VK_S);
showStopButton.setSelected(Preferences.getBoolean("export.application.stop"));
showStopButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
Preferences.setBoolean("export.application.stop", showStopButton.isSelected());
}
});
showStopButton.setEnabled(Preferences.getBoolean("export.application.fullscreen"));
showStopButton.setBorder(new EmptyBorder(3, 13, 6, 13));
// indentPanel.add(showStopButton);
// indentPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
final JCheckBox fullScreenButton = new JCheckBox("Full Screen (Present mode)");
//fullscreenButton.setMnemonic(KeyEvent.VK_F);
fullScreenButton.setSelected(Preferences.getBoolean("export.application.fullscreen"));
fullScreenButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
boolean sal = fullScreenButton.isSelected();
Preferences.setBoolean("export.application.fullscreen", sal);
showStopButton.setEnabled(sal);
}
});
fullScreenButton.setBorder(new EmptyBorder(3, 13, 3, 13));
JPanel optionPanel = new JPanel();
optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS));
optionPanel.add(fullScreenButton);
optionPanel.add(showStopButton);
// optionPanel.add(indentPanel);
optionPanel.setBorder(new TitledBorder("Options"));
wide = Math.max(wide, platformPanel.getPreferredSize().width);
//goodIdea = new Dimension(wide, optionPanel.getPreferredSize().height);
optionPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
//optionPanel.setMaximumSize(goodIdea);
panel.add(optionPanel);
Dimension good;
//label1, label2, platformPanel, optionPanel
good = new Dimension(wide, label1.getPreferredSize().height);
label1.setMaximumSize(good);
good = new Dimension(wide, label2.getPreferredSize().height);
label2.setMaximumSize(good);
good = new Dimension(wide, platformPanel.getPreferredSize().height);
platformPanel.setMaximumSize(good);
good = new Dimension(wide, optionPanel.getPreferredSize().height);
optionPanel.setMaximumSize(good);
// JPanel actionPanel = new JPanel();
// optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.X_AXIS));
// optionPanel.add(Box.createHorizontalGlue());
// final JDialog frame = new JDialog(editor, "Export to Application");
// JButton cancelButton = new JButton("Cancel");
// cancelButton.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// frame.dispose();
// return false;
// }
// });
// Add the buttons in platform-specific order
// if (PApplet.platform == PConstants.MACOSX) {
// optionPanel.add(cancelButton);
// optionPanel.add(exportButton);
// } else {
// optionPanel.add(exportButton);
// optionPanel.add(cancelButton);
// }
String[] options = { "Export", "Cancel" };
final JOptionPane optionPane = new JOptionPane(panel,
JOptionPane.PLAIN_MESSAGE,
//JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION,
null,
options,
options[0]);
final JDialog dialog = new JDialog(this, "Export Options", true);
dialog.setContentPane(optionPane);
optionPane.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (dialog.isVisible() &&
(e.getSource() == optionPane) &&
(prop.equals(JOptionPane.VALUE_PROPERTY))) {
//If you were going to check something
//before closing the window, you'd do
//it here.
dialog.setVisible(false);
}
}
});
dialog.pack();
dialog.setResizable(false);
Rectangle bounds = getBounds();
dialog.setLocation(bounds.x + (bounds.width - dialog.getSize().width) / 2,
bounds.y + (bounds.height - dialog.getSize().height) / 2);
dialog.setVisible(true);
Object value = optionPane.getValue();
if (value.equals(options[0])) {
return jmode.handleExportApplication(sketch);
} else if (value.equals(options[1]) || value.equals(new Integer(-1))) {
// closed window by hitting Cancel or ESC
statusNotice("Export to Application canceled.");
}
return false;
}
/**
* Checks to see if the sketch has been modified, and if so,
* asks the user to save the sketch or cancel the export.
* This prevents issues where an incomplete version of the sketch
* would be exported, and is a fix for
* <A HREF="http://dev.processing.org/bugs/show_bug.cgi?id=157">Bug 157</A>
*/
protected boolean handleExportCheckModified() {
if (sketch.isModified()) {
Object[] options = { "OK", "Cancel" };
int result = JOptionPane.showOptionDialog(this,
"Save changes before export?",
"Save",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (result == JOptionPane.OK_OPTION) {
handleSaveRequest(true);
} else {
// why it's not CANCEL_OPTION is beyond me (at least on the mac)
// but f-- it.. let's get this shite done..
//} else if (result == JOptionPane.CANCEL_OPTION) {
statusNotice("Export canceled, changes must first be saved.");
//toolbar.clear();
return false;
}
}
return true;
}
protected void prepareRun() {
internalCloseRunner();
toolbar.activate(Toolbar.RUN);
statusEmpty();
// do this to advance/clear the terminal window / dos prompt / etc
for (int i = 0; i < 10; i++) System.out.println();
// clear the console on each run, unless the user doesn't want to
if (Preferences.getBoolean("console.auto_clear")) {
console.clear();
}
}
public void handleRun() {
prepareRun();
try {
jmode.handleRun(sketch, this);
} catch (Exception e) {
statusError(e);
}
}
public void handlePresent() {
prepareRun();
try {
jmode.handlePresent(sketch, this);
} catch (Exception e) {
statusError(e);
}
}
public void handleStop() {
toolbar.activate(Toolbar.STOP);
try {
jmode.handleStop();
} catch (Exception e) {
statusError(e);
}
toolbar.deactivate(Toolbar.RUN);
toolbar.deactivate(Toolbar.STOP);
// focus the PDE again after quitting presentation mode [toxi 030903]
toFront();
}
public void handleSave() {
toolbar.activate(Toolbar.SAVE);
handleStop();
super.handleSave();
toolbar.deactivate(Toolbar.SAVE);
}
public boolean handleSaveAs() {
toolbar.activate(Toolbar.SAVE);
handleStop();
boolean result = super.handleSaveAs();
toolbar.deactivate(Toolbar.SAVE);
return result;
}
/**
* Add import statements to the current tab for all of packages inside
* the specified jar file.
*/
public void handleImportLibrary(String jarPath) {
// make sure the user didn't hide the sketch folder
sketch.ensureExistence();
// import statements into the main sketch file (code[0])
// if the current code is a .java file, insert into current
//if (current.flavor == PDE) {
if (mode.isDefaultExtension(sketch.getCurrentCode())) {
sketch.setCurrentCode(0);
}
// could also scan the text in the file to see if each import
// statement is already in there, but if the user has the import
// commented out, then this will be a problem.
String[] list = Base.packageListFromClassPath(jarPath);
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < list.length; i++) {
buffer.append("import ");
buffer.append(list[i]);
buffer.append(".*;\n");
}
buffer.append('\n');
buffer.append(getText());
setText(buffer.toString());
setSelection(0, 0); // scroll to start
sketch.setModified(true);
}
/**
* Deactivate the Run button. This is called by Runner to notify that the
* sketch has stopped running, usually in response to an error (or maybe
* the sketch completing and exiting?) Tools should not call this function.
* To initiate a "stop" action, call handleStop() instead.
*/
public void deactivateRun() {
toolbar.deactivate(Toolbar.RUN);
}
public void deactivateExport() {
toolbar.deactivate(Toolbar.EXPORT);
}
public void internalCloseRunner() {
jmode.handleStop();
}
}

View File

@@ -0,0 +1,216 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2010 Ben Fry and Casey Reas
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import processing.app.Base;
import processing.app.Editor;
import processing.app.Mode;
import processing.app.RunnerListener;
import processing.app.Settings;
import processing.app.Sketch;
import processing.app.SketchException;
import processing.app.syntax.PdeKeywords;
import processing.core.PApplet;
import processing.mode.java.runner.Runner;
public class JavaMode extends Mode {
private Runner runtime;
// classpath for all known libraries for p5
// (both those in the p5/libs folder and those with lib subfolders
// found in the sketchbook)
// static public String librariesClassPath;
public Editor createEditor(Base base, String path, int[] location) {
return new JavaEditor(base, path, location, this);
}
public JavaMode(Base base, File folder) {
super(base, folder);
try {
loadKeywords();
} catch (IOException e) {
Base.showError("Problem loading keywords",
"Could not load keywords.txt, please re-install Processing.", e);
}
try {
theme = new Settings(new File(folder, "theme.txt"));
} catch (IOException e) {
Base.showError("Problem loading theme.txt",
"Could not load theme.txt, please re-install Processing", e);
}
/*
item = newJMenuItem("Export", 'E');
if (editor != null) {
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.handleExport();
}
});
} else {
item.setEnabled(false);
}
fileMenu.add(item);
item = newJMenuItemShift("Export Application", 'E');
if (editor != null) {
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.handleExportApplication();
}
});
} else {
item.setEnabled(false);
}
fileMenu.add(item);
*/
}
protected void loadKeywords() throws IOException {
File file = new File(folder, "keywords.txt");
BufferedReader reader = PApplet.createReader(file);
tokenMarker = new PdeKeywords();
keywordToReference = new HashMap<String, String>();
String line = null;
while ((line = reader.readLine()) != null) {
String[] pieces = PApplet.trim(PApplet.split(line, '\t'));
if (pieces.length >= 2) {
String keyword = pieces[0];
String coloring = pieces[1];
if (coloring.length() > 0) {
tokenMarker.addColoring(keyword, coloring);
}
if (pieces.length == 3) {
String htmlFilename = pieces[2];
if (htmlFilename.length() > 0) {
keywordToReference.put(keyword, htmlFilename);
}
}
}
}
}
public String getTitle() {
return "Standard";
}
// public EditorToolbar createToolbar(Editor editor) {
// return new Toolbar(editor);
// }
// public Formatter createFormatter() {
// return new AutoFormat();
// }
// public Editor createEditor(Base ibase, String path, int[] location) {
// }
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public String getDefaultExtension() {
return "pde";
}
public String[] getExtensions() {
return new String[] { "pde", "java" };
}
public String[] getIgnorable() {
return new String[] {
"applet",
"application.macosx",
"application.windows",
"application.linux"
};
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* Implements Sketch &rarr; Run.
* @param present Set true to run in full screen (present mode).
* @throws SketchException
*/
public void handleRun(Sketch sketch, RunnerListener listener) throws SketchException {
Build build = new Build(sketch);
String appletClassName = build.build();
if (appletClassName != null) {
runtime = new Runner(build, listener);
runtime.launch(false);
}
}
public void handlePresent(Sketch sketch, RunnerListener listener) throws SketchException {
Build build = new Build(sketch);
String appletClassName = build.build();
if (appletClassName != null) {
runtime = new Runner(build, listener);
runtime.launch(true);
}
}
public void handleStop() {
if (runtime != null) {
runtime.close(); // kills the window
runtime = null; // will this help?
}
}
public boolean handleExportApplet(Sketch sketch) throws SketchException, IOException {
Build build = new Build(sketch);
return build.exportApplet();
}
public boolean handleExportApplication(Sketch sketch) throws SketchException, IOException {
Build build = new Build(sketch);
return build.exportApplication();
}
}

View File

@@ -0,0 +1,601 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-10 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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.java;
import processing.app.Editor;
import processing.app.Preferences;
import processing.app.Sketch;
import processing.app.syntax.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
/**
* Filters key events for tab expansion/indent/etc.
* <p/>
* For version 0099, some changes have been made to make the indents
* smarter. There are still issues though:
* <UL>
* <LI> indent happens when it picks up a curly brace on the previous line,
* but not if there's a blank line between them.
* <LI> It also doesn't handle single indent situations where a brace
* isn't used (i.e. an if statement or for loop that's a single line).
* It shouldn't actually be using braces.
* </UL>
* Solving these issues, however, would probably best be done by a
* smarter parser/formatter, rather than continuing to hack this class.
*/
public class PdeKeyListener {
private Editor editor;
private JEditTextArea textarea;
// private boolean externalEditor;
// private boolean tabsExpand;
//// private boolean tabsIndent;
// private int tabSize;
// private String tabString;
// private boolean autoIndent;
// private int selectionStart, selectionEnd;
// private int position;
/** ctrl-alt on windows and linux, cmd-alt on mac os x */
static final int CTRL_ALT = ActionEvent.ALT_MASK |
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
public PdeKeyListener(Editor editor, JEditTextArea textarea) {
this.editor = editor;
this.textarea = textarea;
// let him know that i'm leechin'
textarea.editorListener = this;
// applyPreferences();
}
// public void applyPreferences() {
// tabsExpand = Preferences.getBoolean("editor.tabs.expand");
// tabSize = Preferences.getInteger("editor.tabs.size");
// tabString = spaces(tabSize);
// autoIndent = Preferences.getBoolean("editor.indent");
// externalEditor = Preferences.getBoolean("editor.external");
// }
/**
* Intercepts key pressed events for JEditTextArea.
* <p/>
* Called by JEditTextArea inside processKeyEvent(). Note that this
* won't intercept actual characters, because those are fired on
* keyTyped().
* @return true if the event has been handled (to remove it from the queue)
*/
public boolean keyPressed(KeyEvent event) {
// don't do things if the textarea isn't editable
if (Preferences.getBoolean("editor.external")) return false;
//deselect(); // this is for paren balancing
char c = event.getKeyChar();
int code = event.getKeyCode();
// if (code == KeyEvent.VK_SHIFT) {
// editor.toolbar.setShiftPressed(true);
// }
//System.out.println((int)c + " " + code + " " + event);
//System.out.println();
Sketch sketch = editor.getSketch();
if ((event.getModifiers() & CTRL_ALT) == CTRL_ALT) {
if (code == KeyEvent.VK_LEFT) {
sketch.handlePrevCode();
return true;
} else if (code == KeyEvent.VK_RIGHT) {
sketch.handleNextCode();
return true;
}
}
if ((event.getModifiers() & KeyEvent.META_MASK) != 0) {
//event.consume(); // does nothing
return false;
}
// TODO i don't like these accessors. clean em up later.
if (!editor.getSketch().isModified()) {
if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) ||
(code == KeyEvent.VK_ENTER) || ((c >= 32) && (c < 128))) {
sketch.setModified(true);
}
}
if ((code == KeyEvent.VK_UP) &&
((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
// back up to the last empty line
char contents[] = textarea.getText().toCharArray();
//int origIndex = textarea.getCaretPosition() - 1;
int caretIndex = textarea.getCaretPosition();
int index = calcLineStart(caretIndex - 1, contents);
//System.out.println("line start " + (int) contents[index]);
index -= 2; // step over the newline
//System.out.println((int) contents[index]);
boolean onlySpaces = true;
while (index > 0) {
if (contents[index] == 10) {
if (onlySpaces) {
index++;
break;
} else {
onlySpaces = true; // reset
}
} else if (contents[index] != ' ') {
onlySpaces = false;
}
index--;
}
// if the first char, index will be -2
if (index < 0) index = 0;
if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
textarea.setSelectionStart(caretIndex);
textarea.setSelectionEnd(index);
} else {
textarea.setCaretPosition(index);
}
event.consume();
return true;
} else if ((code == KeyEvent.VK_DOWN) &&
((event.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
char contents[] = textarea.getText().toCharArray();
int caretIndex = textarea.getCaretPosition();
int index = caretIndex;
int lineStart = 0;
boolean onlySpaces = false; // don't count this line
while (index < contents.length) {
if (contents[index] == 10) {
if (onlySpaces) {
index = lineStart; // this is it
break;
} else {
lineStart = index + 1;
onlySpaces = true; // reset
}
} else if (contents[index] != ' ') {
onlySpaces = false;
}
index++;
}
// if the first char, index will be -2
//if (index < 0) index = 0;
//textarea.setSelectionStart(index);
//textarea.setSelectionEnd(index);
if ((event.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
textarea.setSelectionStart(caretIndex);
textarea.setSelectionEnd(index);
} else {
textarea.setCaretPosition(index);
}
event.consume();
return true;
}
switch ((int) c) {
case 9: // TAB
if (textarea.isSelectionActive()) {
if ((event.getModifiers() & KeyEvent.SHIFT_MASK) == 0) {
editor.handleIndent();
} else {
editor.handleOutdent();
}
} else if (Preferences.getBoolean("editor.tabs.expand")) {
int tabSize = Preferences.getInteger("editor.tabs.size");
textarea.setSelectedText(spaces(tabSize));
event.consume();
return true;
// } else if (tabsIndent) {
// // this code is incomplete
//
// // if this brace is the only thing on the line, outdent
// //char contents[] = getCleanedContents();
// char contents[] = textarea.getText().toCharArray();
// // index to the character to the left of the caret
// int prevCharIndex = textarea.getCaretPosition() - 1;
//
// // now find the start of this line
// int lineStart = calcLineStart(prevCharIndex, contents);
//
// int lineEnd = lineStart;
// while ((lineEnd < contents.length - 1) &&
// (contents[lineEnd] != 10)) {
// lineEnd++;
// }
//
// // get the number of braces, to determine whether this is an indent
// int braceBalance = 0;
// int index = lineStart;
// while ((index < contents.length) &&
// (contents[index] != 10)) {
// if (contents[index] == '{') {
// braceBalance++;
// } else if (contents[index] == '}') {
// braceBalance--;
// }
// index++;
// }
//
// // if it's a starting indent, need to ignore it, so lineStart
// // will be the counting point. but if there's a closing indent,
// // then the lineEnd should be used.
// int where = (braceBalance > 0) ? lineStart : lineEnd;
// int indent = calcBraceIndent(where, contents);
// if (indent == -1) {
// // no braces to speak of, do nothing
// indent = 0;
// } else {
// indent += tabSize;
// }
//
// // and the number of spaces it has
// int spaceCount = calcSpaceCount(prevCharIndex, contents);
//
// textarea.setSelectionStart(lineStart);
// textarea.setSelectionEnd(lineStart + spaceCount);
// textarea.setSelectedText(Editor.EMPTY.substring(0, indent));
//
// event.consume();
// return true;
}
break;
case 10: // auto-indent
case 13:
if (Preferences.getBoolean("editor.indent")) {
char contents[] = textarea.getText().toCharArray();
int tabSize = Preferences.getInteger("editor.tabs.size");
// this is the previous character
// (i.e. when you hit return, it'll be the last character
// just before where the newline will be inserted)
int origIndex = textarea.getCaretPosition() - 1;
// NOTE all this cursing about CRLF stuff is probably moot
// NOTE since the switch to JEditTextArea, which seems to use
// NOTE only LFs internally (thank god). disabling for 0099.
// walk through the array to the current caret position,
// and count how many weirdo windows line endings there are,
// which would be throwing off the caret position number
/*
int offset = 0;
int realIndex = origIndex;
for (int i = 0; i < realIndex-1; i++) {
if ((contents[i] == 13) && (contents[i+1] == 10)) {
offset++;
realIndex++;
}
}
// back up until \r \r\n or \n.. @#($* cross platform
//System.out.println(origIndex + " offset = " + offset);
origIndex += offset; // ARGH!#(* WINDOWS#@($*
*/
// if the previous thing is a brace (whether prev line or
// up farther) then the correct indent is the number of spaces
// on that line + 'indent'.
// if the previous line is not a brace, then just use the
// identical indentation to the previous line
// calculate the amount of indent on the previous line
// this will be used *only if the prev line is not an indent*
int spaceCount = calcSpaceCount(origIndex, contents);
// If the last character was a left curly brace, then indent.
// For 0122, walk backwards a bit to make sure that the there
// isn't a curly brace several spaces (or lines) back. Also
// moved this before calculating extraCount, since it'll affect
// that as well.
int index2 = origIndex;
while ((index2 >= 0) &&
Character.isWhitespace(contents[index2])) {
index2--;
}
if (index2 != -1) {
// still won't catch a case where prev stuff is a comment
if (contents[index2] == '{') {
// intermediate lines be damned,
// use the indent for this line instead
spaceCount = calcSpaceCount(index2, contents);
spaceCount += tabSize;
}
}
//System.out.println("spaceCount should be " + spaceCount);
// now before inserting this many spaces, walk forward from
// the caret position and count the number of spaces,
// so that the number of spaces aren't duplicated again
int index = origIndex + 1;
int extraCount = 0;
while ((index < contents.length) &&
(contents[index] == ' ')) {
//spaceCount--;
extraCount++;
index++;
}
int braceCount = 0;
while ((index < contents.length) && (contents[index] != '\n')) {
if (contents[index] == '}') {
braceCount++;
}
index++;
}
// hitting return on a line with spaces *after* the caret
// can cause trouble. for 0099, was ignoring the case, but this is
// annoying, so in 0122 we're trying to fix that.
/*
if (spaceCount - extraCount > 0) {
spaceCount -= extraCount;
}
*/
spaceCount -= extraCount;
//if (spaceCount < 0) spaceCount = 0;
//System.out.println("extraCount is " + extraCount);
// now, check to see if the current line contains a } and if so,
// outdent again by indent
//if (braceCount > 0) {
//spaceCount -= 2;
//}
if (spaceCount < 0) {
// for rev 0122, actually delete extra space
//textarea.setSelectionStart(origIndex + 1);
textarea.setSelectionEnd(textarea.getSelectionStop() - spaceCount);
textarea.setSelectedText("\n");
} else {
String insertion = "\n" + spaces(spaceCount);
textarea.setSelectedText(insertion);
}
// not gonna bother handling more than one brace
if (braceCount > 0) {
int sel = textarea.getSelectionStart();
// sel - tabSize will be -1 if start/end parens on the same line
// http://dev.processing.org/bugs/show_bug.cgi?id=484
if (sel - tabSize >= 0) {
textarea.select(sel - tabSize, sel);
String s = spaces(tabSize);
// if these are spaces that we can delete
if (textarea.getSelectedText().equals(s)) {
textarea.setSelectedText("");
} else {
textarea.select(sel, sel);
}
}
}
} else {
// Enter/Return was being consumed by somehow even if false
// was returned, so this is a band-aid to simply fire the event again.
// http://dev.processing.org/bugs/show_bug.cgi?id=1073
textarea.setSelectedText(String.valueOf(c));
}
// mark this event as already handled (all but ignored)
event.consume();
return true;
case '}':
if (Preferences.getBoolean("editor.indent")) {
// first remove anything that was there (in case this multiple
// characters are selected, so that it's not in the way of the
// spaces for the auto-indent
if (textarea.getSelectionStart() != textarea.getSelectionStop()) {
textarea.setSelectedText("");
}
// if this brace is the only thing on the line, outdent
char contents[] = textarea.getText().toCharArray();
// index to the character to the left of the caret
int prevCharIndex = textarea.getCaretPosition() - 1;
// backup from the current caret position to the last newline,
// checking for anything besides whitespace along the way.
// if there's something besides whitespace, exit without
// messing any sort of indenting.
int index = prevCharIndex;
boolean finished = false;
while ((index != -1) && (!finished)) {
if (contents[index] == 10) {
finished = true;
index++;
} else if (contents[index] != ' ') {
// don't do anything, this line has other stuff on it
return false;
} else {
index--;
}
}
if (!finished) return false; // brace with no start
int lineStartIndex = index;
int pairedSpaceCount = calcBraceIndent(prevCharIndex, contents); //, 1);
if (pairedSpaceCount == -1) return false;
textarea.setSelectionStart(lineStartIndex);
textarea.setSelectedText(spaces(pairedSpaceCount));
// mark this event as already handled
event.consume();
return true;
}
break;
}
return false;
}
public boolean keyTyped(KeyEvent event) {
char c = event.getKeyChar();
if ((event.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
// on linux, ctrl-comma (prefs) being passed through to the editor
if (c == KeyEvent.VK_COMMA) {
event.consume();
return true;
}
}
return false;
}
/**
* Return the index for the first character on this line.
*/
protected int calcLineStart(int index, char contents[]) {
// backup from the current caret position to the last newline,
// so that we can figure out how far this line was indented
/*int spaceCount = 0;*/
boolean finished = false;
while ((index != -1) && (!finished)) {
if ((contents[index] == 10) ||
(contents[index] == 13)) {
finished = true;
//index++; // maybe ?
} else {
index--; // new
}
}
// add one because index is either -1 (the start of the document)
// or it's the newline character for the previous line
return index + 1;
}
/**
* Calculate the number of spaces on this line.
*/
protected int calcSpaceCount(int index, char contents[]) {
index = calcLineStart(index, contents);
int spaceCount = 0;
// now walk forward and figure out how many spaces there are
while ((index < contents.length) && (index >= 0) &&
(contents[index++] == ' ')) {
spaceCount++;
}
return spaceCount;
}
/**
* Walk back from 'index' until the brace that seems to be
* the beginning of the current block, and return the number of
* spaces found on that line.
*/
protected int calcBraceIndent(int index, char contents[]) {
// now that we know things are ok to be indented, walk
// backwards to the last { to see how far its line is indented.
// this isn't perfect cuz it'll pick up commented areas,
// but that's not really a big deal and can be fixed when
// this is all given a more complete (proper) solution.
int braceDepth = 1;
boolean finished = false;
while ((index != -1) && (!finished)) {
if (contents[index] == '}') {
// aww crap, this means we're one deeper
// and will have to find one more extra {
braceDepth++;
//if (braceDepth == 0) {
//finished = true;
//}
index--;
} else if (contents[index] == '{') {
braceDepth--;
if (braceDepth == 0) {
finished = true;
}
index--;
} else {
index--;
}
}
// never found a proper brace, be safe and don't do anything
if (!finished) return -1;
// check how many spaces on the line with the matching open brace
//int pairedSpaceCount = calcSpaceCount(index, contents);
//System.out.println(pairedSpaceCount);
return calcSpaceCount(index, contents);
}
// /**
// * Get the character array and blank out the commented areas.
// * This hasn't yet been tested, the plan was to make auto-indent
// * less gullible (it gets fooled by braces that are commented out).
// */
// protected char[] getCleanedContents() {
// char c[] = textarea.getText().toCharArray();
//
// int index = 0;
// while (index < c.length - 1) {
// if ((c[index] == '/') && (c[index+1] == '*')) {
// c[index++] = 0;
// c[index++] = 0;
// while ((index < c.length - 1) &&
// !((c[index] == '*') && (c[index+1] == '/'))) {
// c[index++] = 0;
// }
//
// } else if ((c[index] == '/') && (c[index+1] == '/')) {
// // clear out until the end of the line
// while ((index < c.length) && (c[index] != 10)) {
// c[index++] = 0;
// }
// if (index != c.length) {
// index++; // skip over the newline
// }
// }
// }
// return c;
// }
static String spaces(int count) {
char[] c = new char[count];
Arrays.fill(c, ' ');
return new String(c);
}
}

View File

@@ -0,0 +1,130 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005-09 Ben Fry and Casey Reas
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.java;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import javax.swing.*;
import processing.app.Preferences;
/**
* Helper class for full-screen presentation mode.
*/
public class PresentMode {
static GraphicsDevice devices[];
/**
* Index of the default display device, probably the one that p5 was
* started from.
*/
static int defaultIndex;
/**
* Menu object for preferences window
*/
//JMenu preferencesMenu;
static JComboBox selector;
/**
* Index of the currently selected display to be used for present mode.
*/
static GraphicsDevice device;
static {
GraphicsEnvironment environment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
devices = environment.getScreenDevices();
GraphicsDevice defaultDevice = environment.getDefaultScreenDevice();
Vector<String> names = new Vector<String>();
for (int i = 0; i < devices.length; i++) {
String name = String.valueOf(i + 1);
if (devices[i] == defaultDevice) {
defaultIndex = i;
name += " (default)";
}
names.add(name);
}
selector = new JComboBox(names);
selector.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int index = selector.getSelectedIndex();
//device = devices[index];
Preferences.setInteger("run.present.display", index + 1);
}
});
}
static public JComboBox getSelector() {
int deviceIndex = Preferences.getInteger("run.present.display") - 1;
selector.setSelectedIndex(deviceIndex);
return selector;
}
/*
static public JFrame create() {
int deviceIndex = PrePreferences.getInteger("run.present.display") - 1;
if ((deviceIndex < 0) || (deviceIndex >= devices.length)) {
Base.showWarning("Display doesn't exist",
"Present Mode is set to use display " +
(deviceIndex+1) +
" but that doesn't seem to exist. \n" +
"This preference will be reset to " +
"use the default display.", null);
deviceIndex = defaultIndex;
}
//GraphicsDevice device = devices[
//JFrame frame = new JFrame(devices[deviceIndex]);
PresentFrame frame = new PresentFrame(devices[deviceIndex]);
}
public void show() {
setUndecorated(true);
setResizable(false);
device.setFullScreenWindow(this);
}
public Window getWindow() {
return device.getFullScreenWindow(); // isn't this just me?
}
public void dispose() {
Window window = device.getFullScreenWindow();
if (window != null) {
window.dispose();
}
device.setFullScreenWindow(null);
}
*/
}

View File

@@ -0,0 +1,110 @@
/*
Part of the Processing project - http://processing.org
Copyright (c) 2010 Ben Fry and Casey Reas
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java;
import java.awt.Image;
import java.awt.event.MouseEvent;
import javax.swing.JPopupMenu;
import processing.app.Base;
import processing.app.Editor;
import processing.app.EditorToolbar;
public class Toolbar extends EditorToolbar {
/** Rollover titles for each button. */
static final String title[] = {
"Run", "Stop", "New", "Open", "Save", "Export"
};
/** Titles for each button when the shift key is pressed. */
static final String titleShift[] = {
"Present", "Stop", "New Editor Window", "Open in Another Window", "Save", "Export to Application"
};
static final int RUN = 0;
static final int STOP = 1;
static final int NEW = 2;
static final int OPEN = 3;
static final int SAVE = 4;
static final int EXPORT = 5;
// JPopupMenu popup;
// JMenu menu;
public Toolbar(Editor editor, Base base) {
super(editor, base);
Image[][] images = loadImages();
for (int i = 0; i < 6; i++) {
addButton(title[i], titleShift[i], images[i], i == NEW);
}
}
public void handlePressed(MouseEvent e, int sel) {
boolean shift = e.isShiftDown();
JavaEditor jeditor = (JavaEditor) editor;
switch (sel) {
case RUN:
if (shift) {
jeditor.handlePresent();
} else {
jeditor.handleRun();
}
break;
case STOP:
jeditor.handleStop();
break;
case OPEN:
// popup = menu.getPopupMenu();
// TODO I think we need a longer chain of accessors here.
JPopupMenu popup = editor.getMode().getToolbarMenu().getPopupMenu();
popup.show(this, e.getX(), e.getY());
break;
case NEW:
if (shift) {
base.handleNew();
} else {
base.handleNewReplace();
}
break;
case SAVE:
jeditor.handleSaveRequest(false);
break;
case EXPORT:
if (shift) {
jeditor.handleExportApplication();
} else {
jeditor.handleExportApplet();
}
break;
}
}
}

View File

@@ -0,0 +1,9 @@
*Lexer.java
*Recognizer.java
*TokenTypes.java
*TokenTypes.txt
*TreeParser.java
*TreeParserTokenTypes.java
*TreeParserTokenTypes.txt
expanded*.g

View File

@@ -0,0 +1,766 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
package processing.mode.java.preproc;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.BitSet;
import java.util.Stack;
import processing.app.Preferences;
import processing.app.SketchException;
import processing.app.antlr.PdeTokenTypes;
import antlr.CommonASTWithHiddenTokens;
import antlr.CommonHiddenStreamToken;
import antlr.collections.AST;
/* Based on original code copyright (c) 2003 Andy Tripp <atripp@comcast.net>.
* shipped under GPL with permission.
*/
/**
* PDEEmitter: A class that can take an ANTLR Java AST and produce
* reasonably formatted Java code from it. To use it, create a
* PDEEmitter object, call setOut() if you want to print to something
* other than System.out, and then call print(), passing the
* AST. Typically, the AST node that you pass would be the root of a
* tree - the ROOT_ID node that represents a Java file.
*
* Modified March 2010 to support Java 5 type arguments and for loops by
* @author Jonathan Feinberg &lt;jdf@pobox.com&gt;
*/
@SuppressWarnings("serial")
public class PdeEmitter implements PdeTokenTypes {
private final PdePreprocessor pdePreprocessor;
private final PrintWriter out;
private final PrintStream debug = System.err;
private final Stack<AST> stack = new Stack<AST>();
private final static int ROOT_ID = 0;
public PdeEmitter(final PdePreprocessor pdePreprocessor, final PrintWriter out) {
this.pdePreprocessor = pdePreprocessor;
this.out = out;
}
/**
* Find a child of the given AST that has the given type
* @returns a child AST of the given type. If it can't find a child of the
* given type, return null.
*/
private AST getChild(final AST ast, final int childType) {
AST child = ast.getFirstChild();
while (child != null) {
if (child.getType() == childType) {
// debug.println("getChild: found:" + name(ast));
return child;
}
child = child.getNextSibling();
}
return null;
}
/**
* Dump the list of hidden tokens linked to after the AST node passed in.
* Most hidden tokens are dumped from this function.
*/
private void dumpHiddenAfter(final AST ast) {
dumpHiddenTokens(((CommonASTWithHiddenTokens) ast).getHiddenAfter());
}
/**
* Dump the list of hidden tokens linked to before the AST node passed in.
* The only time hidden tokens need to be dumped with this function is when
* dealing parts of the tree where automatic tree construction was
* turned off with the ! operator in the grammar file and the nodes were
* manually constructed in such a way that the usual tokens don't have the
* necessary hiddenAfter links.
*/
private void dumpHiddenBefore(final AST ast) {
antlr.CommonHiddenStreamToken child = null, parent = ((CommonASTWithHiddenTokens) ast)
.getHiddenBefore();
// if there aren't any hidden tokens here, quietly return
//
if (parent == null) {
return;
}
// traverse back to the head of the list of tokens before this node
do {
child = parent;
parent = child.getHiddenBefore();
} while (parent != null);
// dump that list
dumpHiddenTokens(child);
}
/**
* Dump the list of hidden tokens linked to from the token passed in.
*/
private void dumpHiddenTokens(CommonHiddenStreamToken t) {
for (; t != null; t = pdePreprocessor.getHiddenAfter(t)) {
out.print(t.getText());
}
}
/**
* Print the children of the given AST
* @param ast The AST to print
* @returns true iff anything was printed
*/
private boolean printChildren(final AST ast) throws SketchException {
boolean ret = false;
AST child = ast.getFirstChild();
while (child != null) {
ret = true;
print(child);
child = child.getNextSibling();
}
return ret;
}
/**
* Tells whether an AST has any children or not.
* @return true iff the AST has at least one child
*/
private boolean hasChildren(final AST ast) {
return (ast.getFirstChild() != null);
}
/**
* Gets the best node in the subtree for printing. This really means
* the next node which could potentially have hiddenBefore data. It's
* usually the first printable leaf, but not always.
*
* @param includeThisNode Should this node be included in the search?
* If false, only descendants are searched.
*
* @return the first printable leaf node in an AST
*/
private AST getBestPrintableNode(final AST ast, final boolean includeThisNode) {
AST child;
if (includeThisNode) {
child = ast;
} else {
child = ast.getFirstChild();
}
if (child != null) {
switch (child.getType()) {
// the following node types are printing nodes that print before
// any children, but then also recurse over children. So they
// may have hiddenBefore chains that need to be printed first. Many
// statements and all unary expression types qualify. Return these
// nodes directly
case CLASS_DEF:
case LITERAL_if:
case LITERAL_new:
case LITERAL_for:
case LITERAL_while:
case LITERAL_do:
case LITERAL_break:
case LITERAL_continue:
case LITERAL_return:
case LITERAL_switch:
case LITERAL_try:
case LITERAL_throw:
case LITERAL_synchronized:
case LITERAL_assert:
case BNOT:
case LNOT:
case INC:
case DEC:
case UNARY_MINUS:
case UNARY_PLUS:
return child;
// Some non-terminal node types (at the moment, I only know of
// MODIFIERS, but there may be other such types), can be
// leaves in the tree but not have any children. If this is
// such a node, move on to the next sibling.
case MODIFIERS:
if (child.getFirstChild() == null) {
return getBestPrintableNode(child.getNextSibling(), false);
}
// new jikes doesn't like fallthrough, so just duplicated here:
return getBestPrintableNode(child, false);
default:
return getBestPrintableNode(child, false);
}
}
return ast;
}
// Because the meanings of <, >, >>, and >>> are overloaded to support
// type arguments and type parameters, we have to treat them
// as copyable to hidden text (or else the following syntax,
// such as (); and what not gets lost under certain circumstances
//
// Since they are copied to the hidden stream, you don't want
// to print them explicitly; they come out in the dumpHiddenXXX methods.
// -- jdf
private static final BitSet OTHER_COPIED_TOKENS = new BitSet() {
{
set(LT);
set(GT);
set(SR);
set(BSR);
}
};
/**
* Prints a binary operator
*/
private void printBinaryOperator(final AST ast) throws SketchException {
print(ast.getFirstChild());
if (!OTHER_COPIED_TOKENS.get(ast.getType())) {
out.print(ast.getText());
dumpHiddenAfter(ast);
}
print(ast.getFirstChild().getNextSibling());
}
private void printMethodDef(final AST ast) throws SketchException {
final AST modifiers = ast.getFirstChild();
final AST typeParameters, type;
if (modifiers.getNextSibling().getType() == TYPE_PARAMETERS) {
typeParameters = modifiers.getNextSibling();
type = typeParameters.getNextSibling();
} else {
typeParameters = null;
type = modifiers.getNextSibling();
}
final AST methodName = type.getNextSibling();
if (methodName.getText().equals("main")) {
pdePreprocessor.setFoundMain(true);
}
printChildren(ast);
}
private void printIfThenElse(final AST literalIf) throws SketchException {
out.print(literalIf.getText());
dumpHiddenAfter(literalIf);
final AST condition = literalIf.getFirstChild();
print(condition); // the "if" condition: an EXPR
// the "then" clause is either an SLIST or an EXPR
final AST thenPath = condition.getNextSibling();
print(thenPath);
// optional "else" clause: an SLIST or an EXPR
// what could be simpler?
final AST elsePath = thenPath.getNextSibling();
if (elsePath != null) {
out.print("else");
final AST bestPrintableNode = getBestPrintableNode(elsePath, true);
dumpHiddenBefore(bestPrintableNode);
final CommonHiddenStreamToken hiddenBefore =
((CommonASTWithHiddenTokens) elsePath).getHiddenBefore();
if (elsePath.getType() == PdeTokenTypes.SLIST && elsePath.getNumberOfChildren() == 0 &&
hiddenBefore == null) {
out.print("{");
final CommonHiddenStreamToken hiddenAfter =
((CommonASTWithHiddenTokens) elsePath).getHiddenAfter();
if (hiddenAfter == null) {
out.print("}");
} else {
dumpHiddenTokens(hiddenAfter);
}
} else {
print(elsePath);
}
}
}
/**
* Print the given AST. Call this function to print your PDE code.
*
* It works by making recursive calls to print children.
* So the code below is one big "switch" statement on the passed AST type.
*/
public void print(final AST ast) throws SketchException {
if (ast == null) {
return;
}
stack.push(ast);
final AST child1 = ast.getFirstChild();
AST child2 = null;
AST child3 = null;
if (child1 != null) {
child2 = child1.getNextSibling();
if (child2 != null) {
child3 = child2.getNextSibling();
}
}
switch (ast.getType()) {
// The top of the tree looks like this:
// ROOT_ID "Whatever.java"
// package
// imports
// class definition
case ROOT_ID:
dumpHiddenTokens(pdePreprocessor.getInitialHiddenToken());
printChildren(ast);
break;
// supporting a "package" statement in a PDE program has
// a bunch of issues with it that need to dealt in the compilation
// code too, so this isn't actually tested.
case PACKAGE_DEF:
out.print("package");
dumpHiddenAfter(ast);
print(ast.getFirstChild());
break;
// IMPORT has exactly one child
case IMPORT:
out.print("import");
dumpHiddenAfter(ast);
print(ast.getFirstChild());
break;
case STATIC_IMPORT:
out.print("import static");
dumpHiddenAfter(ast);
print(ast.getFirstChild());
break;
case CLASS_DEF:
case INTERFACE_DEF:
print(getChild(ast, MODIFIERS));
if (ast.getType() == CLASS_DEF) {
out.print("class");
} else {
out.print("interface");
}
dumpHiddenBefore(getChild(ast, IDENT));
print(getChild(ast, IDENT));
print(getChild(ast, TYPE_PARAMETERS));
print(getChild(ast, EXTENDS_CLAUSE));
print(getChild(ast, IMPLEMENTS_CLAUSE));
print(getChild(ast, OBJBLOCK));
break;
case EXTENDS_CLAUSE:
if (hasChildren(ast)) {
out.print("extends");
dumpHiddenBefore(getBestPrintableNode(ast, false));
printChildren(ast);
}
break;
case IMPLEMENTS_CLAUSE:
if (hasChildren(ast)) {
out.print("implements");
dumpHiddenBefore(getBestPrintableNode(ast, false));
printChildren(ast);
}
break;
// DOT always has exactly two children.
case DOT:
print(child1);
out.print(".");
dumpHiddenAfter(ast);
print(child2);
break;
case MODIFIERS:
case OBJBLOCK:
case CTOR_DEF:
//case METHOD_DEF:
case PARAMETERS:
case PARAMETER_DEF:
case VARIABLE_PARAMETER_DEF:
case VARIABLE_DEF:
case TYPE:
case SLIST:
case ELIST:
case ARRAY_DECLARATOR:
case TYPECAST:
case EXPR:
case ARRAY_INIT:
case FOR_INIT:
case FOR_CONDITION:
case FOR_ITERATOR:
case METHOD_CALL:
case INSTANCE_INIT:
case INDEX_OP:
case SUPER_CTOR_CALL:
case CTOR_CALL:
printChildren(ast);
break;
case METHOD_DEF:
printMethodDef(ast);
break;
// if we have two children, it's of the form "a=0"
// if just one child, it's of the form "=0" (where the
// lhs is above this AST).
case ASSIGN:
if (child2 != null) {
print(child1);
out.print("=");
dumpHiddenAfter(ast);
print(child2);
} else {
out.print("=");
dumpHiddenAfter(ast);
print(child1);
}
break;
// binary operators:
case PLUS:
case MINUS:
case DIV:
case MOD:
case NOT_EQUAL:
case EQUAL:
case LE:
case GE:
case LOR:
case LAND:
case BOR:
case BXOR:
case BAND:
case SL:
case SR:
case BSR:
case LITERAL_instanceof:
case PLUS_ASSIGN:
case MINUS_ASSIGN:
case STAR_ASSIGN:
case DIV_ASSIGN:
case MOD_ASSIGN:
case SR_ASSIGN:
case BSR_ASSIGN:
case SL_ASSIGN:
case BAND_ASSIGN:
case BXOR_ASSIGN:
case BOR_ASSIGN:
case LT:
case GT:
printBinaryOperator(ast);
break;
case LITERAL_for:
out.print(ast.getText());
dumpHiddenAfter(ast);
if (child1.getType() == FOR_EACH_CLAUSE) {
printChildren(child1);
print(child2);
} else {
printChildren(ast);
}
break;
case POST_INC:
case POST_DEC:
print(child1);
out.print(ast.getText());
dumpHiddenAfter(ast);
break;
// unary operators:
case BNOT:
case LNOT:
case INC:
case DEC:
case UNARY_MINUS:
case UNARY_PLUS:
out.print(ast.getText());
dumpHiddenAfter(ast);
print(child1);
break;
case LITERAL_new:
out.print("new");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_return:
out.print("return");
dumpHiddenAfter(ast);
print(child1);
break;
case STATIC_INIT:
out.print("static");
dumpHiddenBefore(getBestPrintableNode(ast, false));
print(child1);
break;
case LITERAL_switch:
out.print("switch");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LABELED_STAT:
case CASE_GROUP:
printChildren(ast);
break;
case LITERAL_case:
out.print("case");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_default:
out.print("default");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case NUM_INT:
case CHAR_LITERAL:
case STRING_LITERAL:
case NUM_FLOAT:
case NUM_LONG:
out.print(ast.getText());
dumpHiddenAfter(ast);
break;
case LITERAL_synchronized: // 0137 to fix bug #136
case LITERAL_assert:
out.print(ast.getText());
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_private:
case LITERAL_public:
case LITERAL_protected:
case LITERAL_static:
case LITERAL_transient:
case LITERAL_native:
case LITERAL_threadsafe:
//case LITERAL_synchronized: // 0137 to fix bug #136
case LITERAL_volatile:
case LITERAL_class: // 0176 to fix bug #1466
case FINAL:
case ABSTRACT:
case LITERAL_package:
case LITERAL_void:
case LITERAL_boolean:
case LITERAL_byte:
case LITERAL_char:
case LITERAL_short:
case LITERAL_int:
case LITERAL_float:
case LITERAL_long:
case LITERAL_double:
case LITERAL_true:
case LITERAL_false:
case LITERAL_null:
case SEMI:
case LITERAL_this:
case LITERAL_super:
out.print(ast.getText());
dumpHiddenAfter(ast);
break;
case EMPTY_STAT:
case EMPTY_FIELD:
break;
case LITERAL_continue:
case LITERAL_break:
out.print(ast.getText());
dumpHiddenAfter(ast);
if (child1 != null) {// maybe label
print(child1);
}
break;
// yuck: Distinguish between "import x.y.*" and "x = 1 * 3"
case STAR:
if (hasChildren(ast)) { // the binary mult. operator
printBinaryOperator(ast);
} else { // the special "*" in import:
out.print("*");
dumpHiddenAfter(ast);
}
break;
case LITERAL_throws:
out.print("throws");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_if:
printIfThenElse(ast);
break;
case LITERAL_while:
out.print("while");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_do:
out.print("do");
dumpHiddenAfter(ast);
print(child1); // an SLIST
out.print("while");
dumpHiddenBefore(getBestPrintableNode(child2, false));
print(child2); // an EXPR
break;
case LITERAL_try:
out.print("try");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_catch:
out.print("catch");
dumpHiddenAfter(ast);
printChildren(ast);
break;
// the first child is the "try" and the second is the SLIST
case LITERAL_finally:
out.print("finally");
dumpHiddenAfter(ast);
printChildren(ast);
break;
case LITERAL_throw:
out.print("throw");
dumpHiddenAfter(ast);
print(child1);
break;
// the dreaded trinary operator
case QUESTION:
print(child1);
out.print("?");
dumpHiddenAfter(ast);
print(child2);
print(child3);
break;
// pde specific or modified tokens start here
// Image -> BImage, Font -> BFont as appropriate
case IDENT:
/*
if (ast.getText().equals("Image") &&
Preferences.getBoolean("preproc.substitute_image")) { //, true)) {
out.print("BImage");
} else if (ast.getText().equals("Font") &&
Preferences.getBoolean("preproc.substitute_font")) { //, true)) {
out.print("BFont");
} else {
*/
out.print(ast.getText());
//}
dumpHiddenAfter(ast);
break;
// the color datatype is just an alias for int
case LITERAL_color:
out.print("int");
dumpHiddenAfter(ast);
break;
case WEBCOLOR_LITERAL:
if (ast.getText().length() != 6) {
System.err.println("Internal error: incorrect length of webcolor "
+ "literal should have been detected sooner.");
break;
}
out.print("0xff" + ast.getText());
dumpHiddenAfter(ast);
break;
// allow for stuff like int(43.2).
case CONSTRUCTOR_CAST:
final AST terminalTypeNode = child1.getFirstChild();
final AST exprToCast = child2;
final String pooType = terminalTypeNode.getText();
out.print("PApplet.parse" + Character.toUpperCase(pooType.charAt(0))
+ pooType.substring(1));
dumpHiddenAfter(terminalTypeNode); // the left paren
print(exprToCast);
break;
// making floating point literals default to floats, not doubles
case NUM_DOUBLE:
final String literalDouble = ast.getText().toLowerCase();
out.print(literalDouble);
if (Preferences.getBoolean("preproc.substitute_floats")
&& literalDouble.indexOf('d') == -1) { // permit literal doubles
out.print("f");
}
dumpHiddenAfter(ast);
break;
case TYPE_ARGUMENTS:
case TYPE_PARAMETERS:
printChildren(ast);
break;
case TYPE_ARGUMENT:
case TYPE_PARAMETER:
printChildren(ast);
break;
case WILDCARD_TYPE:
out.print(ast.getText());
dumpHiddenAfter(ast);
print(ast.getFirstChild());
break;
case TYPE_LOWER_BOUNDS:
case TYPE_UPPER_BOUNDS:
out.print(ast.getType() == TYPE_LOWER_BOUNDS ? "super" : "extends");
dumpHiddenBefore(getBestPrintableNode(ast, false));
printChildren(ast);
break;
case ANNOTATION:
out.print("@");
printChildren(ast);
break;
case ANNOTATION_ARRAY_INIT:
printChildren(ast);
break;
case ANNOTATION_MEMBER_VALUE_PAIR:
print(ast.getFirstChild());
out.print("=");
dumpHiddenBefore(getBestPrintableNode(ast.getFirstChild().getNextSibling(), false));
print(ast.getFirstChild().getNextSibling());
break;
default:
debug.println("Unrecognized type:" + ast.getType() + " ("
+ TokenUtil.nameOf(ast) + ")");
break;
}
stack.pop();
}
}

View File

@@ -0,0 +1,755 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdePreprocessor - wrapper for default ANTLR-generated parser
Part of the Processing project - http://processing.org
Copyright (c) 2004-10 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
ANTLR-generated parser and several supporting classes written
by Dan Mosedale via funding from the Interaction Institute IVREA.
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.java.preproc;
import java.io.*;
import java.util.*;
import java.util.regex.Pattern;
import processing.app.Preferences;
import processing.app.SketchException;
import processing.app.antlr.PdeLexer;
import processing.app.antlr.PdeRecognizer;
import processing.app.antlr.PdeTokenTypes;
import processing.core.PApplet;
import antlr.*;
import antlr.collections.AST;
/**
* Class that orchestrates preprocessing p5 syntax into straight Java.
* <P/>
* <B>Current Preprocessor Subsitutions:</B>
* <UL>
* <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>. This is important to note when
* coding with core.jar outside of the PDE.
* <LI><TT>compiler.substitute_floats</TT> (currently "substitute_f")
* treat doubles as 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.
* <LI><TT>compiler.enhanced_casting</TT> byte(), char(), int(), float()
* works for casting. this is basic in the current implementation, but
* should be expanded as described above. color() works similarly to int(),
* however there is also a *function* called color(r, g, b) in p5.
* <LI><TT>compiler.color_datatype</TT> 'color' is aliased to 'int'
* as a datatype to represent ARGB packed into a single int, commonly
* used in p5 for pixels[] and other color operations. this is just a
* search/replace type thing, and it can be used interchangeably with int.
* <LI><TT>compiler.web_colors</TT> (currently "inline_web_colors")
* color c = #cc0080; should unpack to 0xffcc0080 (the ff at the top is
* so that the color is opaque), which is just an int.
* </UL>
* <B>Other preprocessor functionality</B>
* <UL>
* <LI>detects what 'mode' the program is in: static (no function
* brackets at all, just assumes everything is in draw), active
* (setup plus draw or loop), and java mode (full java support).
* http://processing.org/reference/environment/
* </UL>
* <P/>
* The PDE Preprocessor is based on the Java Grammar that comes with
* ANTLR 2.7.2. Moving it forward to a new version of the grammar
* shouldn't be too difficult.
* <P/>
* Here's some info about the various files in this directory:
* <P/>
* <TT>java.g:</TT> this is the ANTLR grammar for Java 1.3/1.4 from the
* ANTLR distribution. It is in the public domain. The only change to
* this file from the original this file is the uncommenting of the
* clauses required to support assert().
* <P/>
* <TT>java.tree.g:</TT> this describes the Abstract Syntax Tree (AST)
* generated by java.g. It is only here as a reference for coders hacking
* on the preprocessor, it is not built or used at all. Note that pde.g
* overrides some of the java.g rules so that in PDE ASTs, there are a
* few minor differences. Also in the public domain.
* <P/>
* <TT>pde.g:</TT> this is the grammar and lexer for the PDE language
* itself. It subclasses the java.g grammar and lexer. There are a couple
* of overrides to java.g that I hope to convince the ANTLR folks to fold
* back into their grammar, but most of this file is highly specific to
* PDE itself.
* <TT>PdeEmitter.java:</TT> this class traverses the AST generated by
* the PDE Recognizer, and emits it as Java code, doing any necessary
* transformations along the way. It is based on JavaEmitter.java,
* available from antlr.org, written by Andy Tripp <atripp@comcast.net>,
* who has given permission for it to be distributed under the GPL.
* <P/>
* <TT>ExtendedCommonASTWithHiddenTokens.java:</TT> this adds a necessary
* initialize() method, as well as a number of methods to allow for XML
* serialization of the parse tree in a such a way that the hidden tokens
* are visible. Much of the code is taken from the original
* CommonASTWithHiddenTokens class. I hope to convince the ANTLR folks
* to fold these changes back into that class so that this file will be
* unnecessary.
* <P/>
* <TT>TokenStreamCopyingHiddenTokenFilter.java:</TT> this class provides
* TokenStreamHiddenTokenFilters with the concept of tokens which can be
* copied so that they are seen by both the hidden token stream as well
* as the parser itself. This is useful when one wants to use an
* existing parser (like the Java parser included with ANTLR) that throws
* away some tokens to create a parse tree which can be used to spit out
* a copy of the code with only minor modifications. Partially derived
* from ANTLR code. I hope to convince the ANTLR folks to fold this
* functionality back into ANTLR proper as well.
* <P/>
* <TT>whitespace_test.pde:</TT> a torture test to ensure that the
* preprocessor is correctly preserving whitespace, comments, and other
* hidden tokens correctly. See the comments in the code for details about
* how to run the test.
* <P/>
* All other files in this directory are generated at build time by ANTLR
* itself. The ANTLR manual goes into a fair amount of detail about the
* what each type of file is for.
* <P/>
*
* Hacked to death in 2010 by
* @author Jonathan Feinberg &lt;jdf@pobox.com&gt;
*/
public class PdePreprocessor {
// used for calling the ASTFactory to get the root node
private static final int ROOT_ID = 0;
protected final String indent;
private final String name;
public static enum Mode {
STATIC, ACTIVE, JAVA
}
private TokenStreamCopyingHiddenTokenFilter filter;
private boolean foundMain;
public void setFoundMain(boolean foundMain) {
this.foundMain = foundMain;
}
public boolean getFoundMain() {
return foundMain;
}
private String advClassName = "";
public void setAdvClassName(final String advClassName) {
this.advClassName = advClassName;
}
protected Mode mode;
public void setMode(final Mode mode) {
// System.err.println("Setting mode to " + mode);
this.mode = mode;
}
public PdePreprocessor(final String sketchName) {
this(sketchName, Preferences.getInteger("editor.tabs.size"));
}
public PdePreprocessor(final String sketchName, final int tabSize) {
this.name = sketchName;
final char[] indentChars = new char[tabSize];
Arrays.fill(indentChars, ' ');
indent = new String(indentChars);
}
CommonHiddenStreamToken getHiddenAfter(final CommonHiddenStreamToken t) {
return filter.getHiddenAfter(t);
}
CommonHiddenStreamToken getInitialHiddenToken() {
return filter.getInitialHiddenToken();
}
private static int countNewlines(final String s) {
int count = 0;
for (int pos = s.indexOf('\n', 0); pos >= 0; pos = s.indexOf('\n', pos + 1))
count++;
return count;
}
private static void checkForUnterminatedMultilineComment(final String program)
throws SketchException {
final int length = program.length();
for (int i = 0; i < length; i++) {
// for any double slash comments, ignore until the end of the line
if ((program.charAt(i) == '/') && (i < length - 1)
&& (program.charAt(i + 1) == '/')) {
i += 2;
while ((i < length) && (program.charAt(i) != '\n')) {
i++;
}
// check to see if this is the start of a new multiline comment.
// if it is, then make sure it's actually terminated somewhere.
} else if ((program.charAt(i) == '/') && (i < length - 1)
&& (program.charAt(i + 1) == '*')) {
final int startOfComment = i;
i += 2;
boolean terminated = false;
while (i < length - 1) {
if ((program.charAt(i) == '*') && (program.charAt(i + 1) == '/')) {
i += 2;
terminated = true;
break;
} else {
i++;
}
}
if (!terminated) {
throw new SketchException("Unclosed /* comment */", 0,
countNewlines(program.substring(0,
startOfComment)));
}
} else if (program.charAt(i) == '"') {
final int stringStart = i;
boolean terminated = false;
for (i++; i < length; i++) {
final char c = program.charAt(i);
if (c == '"') {
terminated = true;
break;
} else if (c == '\\') {
if (i == length - 1) {
break;
}
i++;
} else if (c == '\n') {
break;
}
}
if (!terminated) {
throw new SketchException("Unterminated string constant", 0,
countNewlines(program.substring(0,
stringStart)));
}
} else if (program.charAt(i) == '\'') {
i++;
if (i >= length) {
throw new SketchException("Unterminated character constant", 0,
countNewlines(program.substring(0, i)));
}
if (program.charAt(i) == '\\') {
i++;
}
i++;
if (i >= length) {
throw new SketchException("Unterminated character constant", 0,
countNewlines(program.substring(0, i)));
}
if (program.charAt(i) != '\'') {
throw new SketchException("Badly formed character constant", 0,
countNewlines(program.substring(0, i)));
}
}
}
}
public PreprocessorResult write(final Writer out, String program)
throws SketchException, RecognitionException, TokenStreamException {
return write(out, program, null);
}
public PreprocessorResult write(Writer out, String program,
String codeFolderPackages[])
throws SketchException, RecognitionException, TokenStreamException {
// these ones have the .* at the end, since a class name might be at the end
// instead of .* which would make trouble other classes using this can lop
// off the . and anything after it to produce a package name consistently.
final ArrayList<String> programImports = new ArrayList<String>();
// imports just from the code folder, treated differently
// than the others, since the imports are auto-generated.
final ArrayList<String> codeFolderImports = new ArrayList<String>();
// need to reset whether or not this has a main()
foundMain = false;
// bug #5
if (!program.endsWith("\n"))
program += "\n";
checkForUnterminatedMultilineComment(program);
if (Preferences.getBoolean("preproc.substitute_unicode")) {
program = substituteUnicode(program);
}
final String importRegexp = "(?:^|;)\\s*(import\\s+)((?:static\\s+)?\\S+)(\\s*;)";
do {
String[] pieces = PApplet.match(program, importRegexp);
// Stop the loop if we've removed all the importy lines
if (pieces == null)
break;
String piece = pieces[1] + pieces[2] + pieces[3];
int len = piece.length(); // how much to trim out
programImports.add(pieces[2]); // the package name
// find index of this import in the program
int idx = program.indexOf(piece);
// Remove the import from the main program
program = program.substring(0, idx) + program.substring(idx + len);
} while (true);
if (codeFolderPackages != null) {
for (String item : codeFolderPackages) {
codeFolderImports.add(item + ".*");
}
}
final PrintWriter stream = new PrintWriter(out);
final int headerOffset = writeImports(stream, programImports,
codeFolderImports);
return new PreprocessorResult(mode, headerOffset + 2, write(program, stream),
programImports);
}
static String substituteUnicode(String program) {
// check for non-ascii chars (these will be/must be in unicode format)
char p[] = program.toCharArray();
int unicodeCount = 0;
for (int i = 0; i < p.length; i++) {
if (p[i] > 127)
unicodeCount++;
}
if (unicodeCount == 0)
return program;
// if non-ascii chars are in there, convert to unicode escapes
// add unicodeCount * 5.. replacing each unicode char
// with six digit uXXXX sequence (xxxx is in hex)
// (except for nbsp chars which will be a replaced with a space)
int index = 0;
char p2[] = new char[p.length + unicodeCount * 5];
for (int i = 0; i < p.length; i++) {
if (p[i] < 128) {
p2[index++] = p[i];
} else if (p[i] == 160) { // unicode for non-breaking space
p2[index++] = ' ';
} else {
int c = p[i];
p2[index++] = '\\';
p2[index++] = 'u';
char str[] = Integer.toHexString(c).toCharArray();
// add leading zeros, so that the length is 4
//for (int i = 0; i < 4 - str.length; i++) p2[index++] = '0';
for (int m = 0; m < 4 - str.length; m++)
p2[index++] = '0';
System.arraycopy(str, 0, p2, index, str.length);
index += str.length;
}
}
return new String(p2, 0, index);
}
private static final Pattern PUBLIC_CLASS =
Pattern.compile("(^|;)\\s*public\\s+class", Pattern.MULTILINE);
private static final Pattern FUNCTION_DECL =
Pattern.compile("(^|;)\\s*((public|private|protected|final|static)\\s+)*" +
"(void|int|float|double|String|char|byte)" +
"(\\s*\\[\\s*\\])?\\s+[a-zA-Z0-9]+\\s*\\(",
Pattern.MULTILINE);
/**
* preprocesses a pde file and writes out a java file
* @return the class name of the exported Java
*/
private String write(final String program, final PrintWriter stream)
throws SketchException, RecognitionException, TokenStreamException {
PdeRecognizer parser = createParser(program);
if (PUBLIC_CLASS.matcher(program).find()) {
try {
final PrintStream saved = System.err;
try {
// throw away stderr for this tentative parse
System.setErr(new PrintStream(new ByteArrayOutputStream()));
parser.javaProgram();
} finally {
System.setErr(saved);
}
setMode(Mode.JAVA);
} catch (Exception e) {
// I can't figure out any other way of resetting the parser.
parser = createParser(program);
parser.pdeProgram();
}
} else if (FUNCTION_DECL.matcher(program).find()) {
setMode(Mode.ACTIVE);
parser.activeProgram();
} else {
parser.pdeProgram();
}
// set up the AST for traversal by PdeEmitter
//
ASTFactory factory = new ASTFactory();
AST parserAST = parser.getAST();
AST rootNode = factory.create(ROOT_ID, "AST ROOT");
rootNode.setFirstChild(parserAST);
makeSimpleMethodsPublic(rootNode);
// unclear if this actually works, but it's worth a shot
//
//((CommonAST)parserAST).setVerboseStringConversion(
// true, parser.getTokenNames());
// (made to use the static version because of jikes 1.22 warning)
CommonAST.setVerboseStringConversion(true, parser.getTokenNames());
final String className;
if (mode == Mode.JAVA) {
// if this is an advanced program, the classname is already defined.
className = getFirstClassName(parserAST);
} else {
className = this.name;
}
// if 'null' was passed in for the name, but this isn't
// a 'java' mode class, then there's a problem, so punt.
//
if (className == null)
return null;
// debug
if (false) {
final StringWriter buf = new StringWriter();
final PrintWriter bufout = new PrintWriter(buf);
writeDeclaration(bufout, className);
new PdeEmitter(this, bufout).print(rootNode);
writeFooter(bufout, className);
debugAST(rootNode, true);
System.err.println(buf.toString());
}
writeDeclaration(stream, className);
new PdeEmitter(this, stream).print(rootNode);
writeFooter(stream, className);
// if desired, serialize the parse tree to an XML file. can
// be viewed usefully with Mozilla or IE
if (Preferences.getBoolean("preproc.output_parse_tree")) {
writeParseTree("parseTree.xml", parserAST);
}
return className;
}
private PdeRecognizer createParser(final String program) {
// create a lexer with the stream reader, and tell it to handle
// hidden tokens (eg whitespace, comments) since we want to pass these
// through so that the line numbers when the compiler reports errors
// match those that will be highlighted in the PDE IDE
//
PdeLexer lexer = new PdeLexer(new StringReader(program));
lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken");
// create the filter for hidden tokens and specify which tokens to
// hide and which to copy to the hidden text
//
filter = new TokenStreamCopyingHiddenTokenFilter(lexer);
filter.hide(PdeRecognizer.SL_COMMENT);
filter.hide(PdeRecognizer.ML_COMMENT);
filter.hide(PdeRecognizer.WS);
filter.copy(PdeRecognizer.SEMI);
filter.copy(PdeRecognizer.LPAREN);
filter.copy(PdeRecognizer.RPAREN);
filter.copy(PdeRecognizer.LCURLY);
filter.copy(PdeRecognizer.RCURLY);
filter.copy(PdeRecognizer.COMMA);
filter.copy(PdeRecognizer.RBRACK);
filter.copy(PdeRecognizer.LBRACK);
filter.copy(PdeRecognizer.COLON);
filter.copy(PdeRecognizer.TRIPLE_DOT);
// Because the meanings of < and > are overloaded to support
// type arguments and type parameters, we have to treat them
// as copyable to hidden text (or else the following syntax,
// such as (); and what not gets lost under certain circumstances)
// -- jdf
filter.copy(PdeRecognizer.LT);
filter.copy(PdeRecognizer.GT);
filter.copy(PdeRecognizer.SR);
filter.copy(PdeRecognizer.BSR);
// create a parser and set what sort of AST should be generated
//
final PdeRecognizer parser = new PdeRecognizer(this, filter);
// use our extended AST class
//
parser.setASTNodeClass("antlr.ExtendedCommonASTWithHiddenTokens");
return parser;
}
/**
* Walk the tree looking for METHOD_DEFs. Any simple METHOD_DEF (one
* without TYPE_PARAMETERS) lacking an
* access specifier is given public access.
* @param node
*/
private void makeSimpleMethodsPublic(final AST node) {
if (node.getType() == PdeTokenTypes.METHOD_DEF) {
final AST mods = node.getFirstChild();
final AST oldFirstMod = mods.getFirstChild();
for (AST mod = oldFirstMod; mod != null; mod = mod.getNextSibling()) {
final int t = mod.getType();
if (t == PdeTokenTypes.LITERAL_private ||
t == PdeTokenTypes.LITERAL_protected ||
t == PdeTokenTypes.LITERAL_public) {
return;
}
}
if (mods.getNextSibling().getType() == PdeTokenTypes.TYPE_PARAMETERS) {
return;
}
final CommonHiddenStreamToken publicToken =
new CommonHiddenStreamToken(PdeTokenTypes.LITERAL_public, "public") {
{
setHiddenAfter(new CommonHiddenStreamToken(PdeTokenTypes.WS, " "));
}
};
final AST publicNode = new CommonASTWithHiddenTokens(publicToken);
publicNode.setNextSibling(oldFirstMod);
mods.setFirstChild(publicNode);
} else {
for (AST kid = node.getFirstChild(); kid != null; kid = kid
.getNextSibling())
makeSimpleMethodsPublic(kid);
}
}
protected void writeParseTree(String filename, AST ast) {
try {
PrintStream stream = new PrintStream(new FileOutputStream(filename));
stream.println("<?xml version=\"1.0\"?>");
stream.println("<document>");
OutputStreamWriter writer = new OutputStreamWriter(stream);
if (ast != null) {
((CommonAST) ast).xmlSerialize(writer);
}
writer.flush();
stream.println("</document>");
writer.close();
} catch (IOException e) {
}
}
/**
*
* @param out
* @param programImports
* @param codeFolderImports
* @return the header offset
*/
protected int writeImports(final PrintWriter out,
final List<String> programImports,
final List<String> codeFolderImports) {
int count = writeImportList(out, getCoreImports());
count += writeImportList(out, programImports);
count += writeImportList(out, codeFolderImports);
count += writeImportList(out, getDefaultImports());
return count;
}
protected int writeImportList(PrintWriter out, List<String> imports) {
return writeImportList(out, (String[]) imports.toArray(new String[0]));
}
protected int writeImportList(PrintWriter out, String[] imports) {
int count = 0;
if (imports != null && imports.length != 0) {
for (String item : imports) {
out.println("import " + item + "; ");
count++;
}
out.println();
count++;
}
return count;
}
/**
* Write any required header material (eg imports, class decl stuff)
*
* @param out PrintStream to write it to.
* @param exporting Is this being exported from PDE?
* @param className Name of the class being created.
*/
protected void writeDeclaration(PrintWriter out, String className) {
if (mode == Mode.JAVA) {
// Print two blank lines so that the offset doesn't change
out.println();
out.println();
} else if (mode == Mode.ACTIVE) {
// Print an extra blank line so the offset is identical to the others
out.println("public class " + className + " extends PApplet {");
out.println();
} else if (mode == Mode.STATIC) {
out.println("public class " + className + " extends PApplet {");
out.println(indent + "public void setup() {");
}
}
/**
* Write any necessary closing text.
*
* @param out PrintStream to write it to.
*/
protected void writeFooter(PrintWriter out, String className) {
if (mode == Mode.STATIC) {
// close off draw() definition
out.println(indent + "noLoop();");
out.println("} ");
}
if ((mode == Mode.STATIC) || (mode == Mode.ACTIVE)) {
if (!foundMain) {
out.println(indent + "static public void main(String args[]) {");
out.print(indent + indent + "PApplet.main(new String[] { ");
if (Preferences.getBoolean("export.application.fullscreen")) {
out.print("\"" + PApplet.ARGS_PRESENT + "\", ");
String farbe = Preferences.get("run.present.bgcolor");
out.print("\"" + PApplet.ARGS_BGCOLOR + "=" + farbe + "\", ");
if (Preferences.getBoolean("export.application.stop")) {
farbe = Preferences.get("run.present.stop.color");
out.print("\"" + PApplet.ARGS_STOP_COLOR + "=" + farbe + "\", ");
} else {
out.print("\"" + PApplet.ARGS_HIDE_STOP + "\", ");
}
} else {
String farbe = Preferences.get("run.window.bgcolor");
out.print("\"" + PApplet.ARGS_BGCOLOR + "=" + farbe + "\", ");
}
out.println("\"" + className + "\" });");
out.println(indent + "}");
}
// close off the class definition
out.println("}");
}
}
public String[] getCoreImports() {
return new String[] { "processing.core.*", "processing.xml.*" };
}
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, ", ");
}
/**
* Find the first CLASS_DEF node in the tree, and return the name of the
* class in question.
*
* TODO [dmose] right now, we're using a little hack to the grammar to get
* this info. In fact, we should be descending the AST passed in.
*/
String getFirstClassName(AST ast) {
String t = advClassName;
advClassName = "";
return t;
}
public void debugAST(final AST ast, final boolean includeHidden) {
System.err.println("------------------");
debugAST(ast, includeHidden, 0);
}
private void debugAST(final AST ast, final boolean includeHidden,
final int indent) {
for (int i = 0; i < indent; i++)
System.err.print(" ");
if (includeHidden) {
System.err.print(debugHiddenBefore(ast));
}
if (ast.getType() > 0 && !ast.getText().equals(TokenUtil.nameOf(ast))) {
System.err.print(TokenUtil.nameOf(ast) + "/");
}
System.err.print(ast.getText().replace("\n", "\\n"));
if (includeHidden) {
System.err.print(debugHiddenAfter(ast));
}
System.err.println();
for (AST kid = ast.getFirstChild(); kid != null; kid = kid.getNextSibling())
debugAST(kid, includeHidden, indent + 1);
}
private String debugHiddenAfter(AST ast) {
if (!(ast instanceof antlr.CommonASTWithHiddenTokens))
return "";
return debugHiddenTokens(((antlr.CommonASTWithHiddenTokens) ast)
.getHiddenAfter());
}
private String debugHiddenBefore(AST ast) {
if (!(ast instanceof antlr.CommonASTWithHiddenTokens))
return "";
antlr.CommonHiddenStreamToken child = null, parent = ((antlr.CommonASTWithHiddenTokens) ast)
.getHiddenBefore();
if (parent == null) {
return "";
}
do {
child = parent;
parent = child.getHiddenBefore();
} while (parent != null);
return debugHiddenTokens(child);
}
private String debugHiddenTokens(antlr.CommonHiddenStreamToken t) {
final StringBuilder sb = new StringBuilder();
for (; t != null; t = filter.getHiddenAfter(t)) {
if (sb.length() == 0)
sb.append("[");
sb.append(t.getText().replace("\n", "\\n"));
}
if (sb.length() > 0)
sb.append("]");
return sb.toString();
}
}

View File

@@ -0,0 +1,32 @@
package processing.mode.java.preproc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import processing.app.SketchException;
/**
*
* @author Jonathan Feinberg &lt;jdf@pobox.com&gt;
*
*/
public class PreprocessorResult {
public final int headerOffset;
public final String className;
public final List<String> extraImports;
public final PdePreprocessor.Mode programType;
public PreprocessorResult(PdePreprocessor.Mode programType,
int headerOffset, String className,
final List<String> extraImports) throws SketchException {
if (className == null) {
throw new SketchException("Could not find main class");
}
this.headerOffset = headerOffset;
this.className = className;
this.extraImports = Collections.unmodifiableList(new ArrayList<String>(extraImports));
this.programType = programType;
}
}

View File

@@ -0,0 +1,30 @@
package processing.mode.java.preproc;
import java.lang.reflect.Field;
import antlr.collections.AST;
import processing.app.antlr.PdeTokenTypes;
/**
*
* @author Jonathan Feinberg &lt;jdf@pobox.com&gt;
*
*/
public class TokenUtil {
private static final String[] tokenNames= new String[200];
static {
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = "ERROR:" + i;
}
for (final Field f : PdeTokenTypes.class.getDeclaredFields()) {
try {
tokenNames[f.getInt(null)] = f.getName();
} catch (Exception unexpected) {
throw new RuntimeException(unexpected);
}
}
}
public static String nameOf(final AST node) {
return tokenNames[node.getType()];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,393 @@
/* -*- mode: antlr; c-basic-offset: 4; indent-tabs-mode: nil -*- */
header {
package processing.mode.java.preproc;
}
class PdeRecognizer extends JavaRecognizer;
options {
importVocab = Java;
exportVocab = PdePartial;
//codeGenMakeSwitchThreshold=10; // this is set high for debugging
//codeGenBitsetTestThreshold=10; // this is set high for debugging
// developers may to want to set this to true for better
// debugging messages, however, doing so disables highlighting errors
// in the editor.
defaultErrorHandler = false; //true;
}
tokens {
CONSTRUCTOR_CAST; EMPTY_FIELD;
}
{
// this clause copied from java15.g! ANTLR does not copy this
// section from the super grammar.
/**
* Counts the number of LT seen in the typeArguments production.
* It is used in semantic predicates to ensure we have seen
* enough closing '>' characters; which actually may have been
* either GT, SR or BSR tokens.
*/
private int ltCounter = 0;
private PdePreprocessor pp;
public PdeRecognizer(final PdePreprocessor pp, final TokenStream ts) {
this(ts);
this.pp = pp;
}
private void mixed() throws RecognitionException, TokenStreamException {
throw new RecognitionException("It looks like you're mixing \"active\" and \"static\" modes.",
getFilename(), LT(1).getLine(), LT(1).getColumn());
}
}
pdeProgram
:
// Some programs can be equally well interpreted as STATIC or ACTIVE;
// this forces the parser to prefer the STATIC interpretation.
(staticProgram) => staticProgram
{ pp.setMode(PdePreprocessor.Mode.STATIC); }
| (activeProgram) => activeProgram
{ pp.setMode(PdePreprocessor.Mode.ACTIVE); }
| staticProgram
{ pp.setMode(PdePreprocessor.Mode.STATIC); }
;
// advanced mode is really just a normal java file
javaProgram
: compilationUnit
;
activeProgram
: (
(IDENT LPAREN) => IDENT LPAREN { mixed(); }
| possiblyEmptyField
)+ EOF!
;
staticProgram
: (
statement
)* EOF!
;
// copy of the java.g rule with WEBCOLOR_LITERAL added
constant
: NUM_INT
| CHAR_LITERAL
| STRING_LITERAL
| NUM_FLOAT
| NUM_LONG
| NUM_DOUBLE
| webcolor_literal
;
// fix bug http://dev.processing.org/bugs/show_bug.cgi?id=1519
// by altering a syntactic predicate whose sole purpose is to
// emit a useless error with no line numbers.
// These are from Java15.g, with a few lines edited to make nice errors.
// Type arguments to a class or interface type
typeArguments
{int currentLtLevel = 0;}
:
{currentLtLevel = ltCounter;}
LT! {ltCounter++;}
typeArgument
(options{greedy=true;}: // match as many as possible
{if (! (inputState.guessing !=0 || ltCounter == currentLtLevel + 1)) {
throw new RecognitionException("Maybe too many > characters?",
getFilename(), LT(1).getLine(), LT(1).getColumn());
}}
COMMA! typeArgument
)*
( // turn warning off since Antlr generates the right code,
// plus we have our semantic predicate below
options{generateAmbigWarnings=false;}:
typeArgumentsOrParametersEnd
)?
// make sure we have gobbled up enough '>' characters
// if we are at the "top level" of nested typeArgument productions
{if (! ((currentLtLevel != 0) || ltCounter == currentLtLevel)) {
throw new RecognitionException("Maybe too many > characters?",
getFilename(), LT(1).getLine(), LT(1).getColumn());
}}
{#typeArguments = #(#[TYPE_ARGUMENTS, "TYPE_ARGUMENTS"], #typeArguments);}
;
typeParameters
{int currentLtLevel = 0;}
:
{currentLtLevel = ltCounter;}
LT! {ltCounter++;}
typeParameter (COMMA! typeParameter)*
(typeArgumentsOrParametersEnd)?
// make sure we have gobbled up enough '>' characters
// if we are at the "top level" of nested typeArgument productions
{if (! ((currentLtLevel != 0) || ltCounter == currentLtLevel)) {
throw new RecognitionException("Maybe too many > characters?",
getFilename(), LT(1).getLine(), LT(1).getColumn());
}}
{#typeParameters = #(#[TYPE_PARAMETERS, "TYPE_PARAMETERS"], #typeParameters);}
;
// this gobbles up *some* amount of '>' characters, and counts how many
// it gobbled.
protected typeArgumentsOrParametersEnd
: GT! {ltCounter-=1;}
| SR! {ltCounter-=2;}
| BSR! {ltCounter-=3;}
;
// of the form #cc008f in PDE
webcolor_literal
: w:WEBCOLOR_LITERAL
{ if (! (processing.app.Preferences.getBoolean("preproc.web_colors")
&&
w.getText().length() == 6)) {
throw new RecognitionException("Web colors must be exactly 6 hex digits. This looks like " + w.getText().length() + ".",
getFilename(), LT(1).getLine(), LT(1).getColumn());
}} // must be exactly 6 hex digits
;
// copy of the java.g builtInType rule
builtInConsCastType
: "void"
| "boolean"
| "byte"
| "char"
| "short"
| "int"
| "float"
| "long"
| "double"
;
// our types include the java types and "color". this is separated into two
// rules so that constructor casts can just use the original typelist, since
// we don't want to support the color type as a constructor cast.
//
builtInType
: builtInConsCastType
| "color" // aliased to an int in PDE
{ processing.app.Preferences.getBoolean("preproc.color_datatype") }?
;
// constructor style casts.
constructorCast!
: t:consCastTypeSpec[true]
LPAREN!
e:expression
RPAREN!
// if this is a string literal, make sure the type we're trying to cast
// to is one of the supported ones
//
{ #e.getType() != STRING_LITERAL ||
( #t.getType() == LITERAL_byte ||
#t.getType() == LITERAL_double ||
#t.getType() == LITERAL_float ||
#t.getType() == LITERAL_int ||
#t.getType() == LITERAL_long ||
#t.getType() == LITERAL_short ) }?
// create the node
//
{#constructorCast = #(#[CONSTRUCTOR_CAST,"CONSTRUCTOR_CAST"], t, e);}
;
// A list of types that be used as the destination type in a constructor-style
// cast. Ideally, this would include all class types, not just "String".
// Unfortunately, it's not possible to tell whether Foo(5) is supposed to be
// a method call or a constructor cast without have a table of all valid
// types or methods, which requires semantic analysis (eg processing of import
// statements). So we accept the set of built-in types plus "String".
//
consCastTypeSpec[boolean addImagNode]
// : stringTypeSpec[addImagNode]
// | builtInConsCastTypeSpec[addImagNode]
: builtInConsCastTypeSpec[addImagNode]
// trying to remove String() cast [fry]
;
//stringTypeSpec[boolean addImagNode]
// : id:IDENT { #id.getText().equals("String") }?
// {
// if ( addImagNode ) {
// #stringTypeSpec = #(#[TYPE,"TYPE"],
// #stringTypeSpec);
// }
// }
// ;
builtInConsCastTypeSpec[boolean addImagNode]
: builtInConsCastType
{
if ( addImagNode ) {
#builtInConsCastTypeSpec = #(#[TYPE,"TYPE"],
#builtInConsCastTypeSpec);
}
}
;
// Since "color" tokens are lexed as LITERAL_color now, we need to have a rule
// that can generate a method call from an expression that starts with this
// token
//
colorMethodCall
: c:"color" {#c.setType(IDENT);} // this would default to LITERAL_color
lp:LPAREN^ {#lp.setType(METHOD_CALL);}
argList
RPAREN!
;
// copy of the java.g rule with added constructorCast and colorMethodCall
// alternatives
primaryExpression
: (consCastTypeSpec[false] LPAREN) => constructorCast
{ processing.app.Preferences.getBoolean("preproc.enhanced_casting") }?
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
| constant
| "true"
| "false"
| "null"
| newExpression
| "this"
| "super"
| LPAREN! assignmentExpression RPAREN!
| colorMethodCall
// look for int.class and int[].class
| builtInType
( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
DOT^ "class"
;
// the below variable rule hacks are needed so that it's possible for the
// emitter to correctly output variable declarations of the form "float a, b"
// from the AST. This means that our AST has a somewhat different form in
// these rules than the java one does, and this new form may have its own
// semantic issues. But it seems to fix the comma declaration issues.
//
variableDefinitions![AST mods, AST t]
: vd:variableDeclarator[getASTFactory().dupTree(mods),
getASTFactory().dupTree(t)]
{#variableDefinitions = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods,
t, vd);}
;
variableDeclarator[AST mods, AST t]
: ( id:IDENT (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
v:varInitializer (COMMA!)? )+
;
// java.g builds syntax trees with an inconsistent structure. override one of
// the rules there to fix this.
//
explicitConstructorInvocation!
: (typeArguments)?
t:"this" LPAREN a1:argList RPAREN SEMI
{#explicitConstructorInvocation = #(#[CTOR_CALL, "CTOR_CALL"],
#t, #a1);}
| s:"super" LPAREN a2:argList RPAREN SEMI
{#explicitConstructorInvocation = #(#[SUPER_CTOR_CALL,
"SUPER_CTOR_CALL"],
#s, #a2);}
;
// quick-n-dirty hack to the get the advanced class name. we should
// really be getting it from the AST and not forking this rule from
// the java.g copy at all. Since this is a recursive descent parser, we get
// the last class name in the file so that we don't end up with the classname
// of an inner class. If there is more than one "outer" class in a file,
// this heuristic will fail.
//
classDefinition![AST modifiers]
: "class" i:IDENT
// it _might_ have type paramaters
(tp:typeParameters)?
// it _might_ have a superclass...
sc:superClassClause
// it might implement some interfaces...
ic:implementsClause
// now parse the body of the class
cb:classBlock
{#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"],
modifiers,i,tp,sc,ic,cb);
pp.setAdvClassName(i.getText());}
;
possiblyEmptyField
: classField
| s:SEMI {#s.setType(EMPTY_FIELD);}
;
class PdeLexer extends JavaLexer;
options {
importVocab=PdePartial;
exportVocab=Pde;
}
// We need to preserve whitespace and commentary instead of ignoring
// like the supergrammar does. Otherwise Jikes won't be able to give
// us error messages that point to the equivalent PDE code.
// WS, SL_COMMENT, ML_COMMENT are copies of the original productions,
// but with the SKIP assigment removed.
WS : ( ' '
| '\t'
| '\f'
// handle newlines
| ( options {generateAmbigWarnings=false;}
: "\r\n" // Evil DOS
| '\r' // Macintosh
| '\n' // Unix (the right way)
)
{ newline(); }
)+
;
// Single-line comments
SL_COMMENT
: "//"
(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
{newline();}
;
// multiple-line comments
ML_COMMENT
: "/*"
( /* '\r' '\n' can be matched in one alternative or by matching
'\r' in one iteration and '\n' in another. I am trying to
handle any flavor of newline that comes in, but the language
that allows both "\r\n" and "\r" and "\n" to all be valid
newline is ambiguous. Consequently, the resulting grammar
must be ambiguous. I'm shutting this warning off.
*/
options {
generateAmbigWarnings=false;
}
:
{ LA(2)!='/' }? '*'
| '\r' '\n' {newline();}
| '\r' {newline();}
| '\n' {newline();}
| ~('*'|'\n'|'\r')
)*
"*/"
;
WEBCOLOR_LITERAL
: '#'! (HEX_DIGIT)+
;

View File

@@ -0,0 +1,404 @@
/*
* @(#)EventThread.java 1.4 03/01/23
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
* Copyright (c) 1997-2001 by Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
package processing.mode.java.runner;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.sun.jdi.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
/**
* This class processes incoming JDI events and displays them
*
* @version @(#) EventThread.java 1.4 03/01/23 23:33:38
* @author Robert Field
*/
public class EventThread extends Thread {
private final Runner parent;
private final VirtualMachine vm; // Running VM
private final String[] excludes; // Packages to exclude
private final PrintWriter writer; // Where output goes
static String nextBaseIndent = ""; // Starting indent for next thread
private boolean connected = true; // Connected to VM
private boolean vmDied = true; // VMDeath occurred
// Maps ThreadReference to ThreadTrace instances
private Map traceMap = new HashMap();
public EventThread(Runner parent, VirtualMachine vm, String[] excludes, PrintWriter writer) {
super("event-handler");
this.parent = parent;
this.vm = vm;
this.excludes = excludes;
this.writer = writer;
}
/**
* Run the event handling thread.
* As long as we are connected, get event sets off
* the queue and dispatch the events within them.
*/
public void run() {
EventQueue queue = vm.eventQueue();
while (connected) {
try {
EventSet eventSet = queue.remove();
EventIterator it = eventSet.eventIterator();
while (it.hasNext()) {
handleEvent(it.nextEvent());
}
eventSet.resume();
} catch (InterruptedException exc) {
// Ignore
} catch (VMDisconnectedException discExc) {
handleDisconnectedException();
break;
}
}
}
/**
* Create the desired event requests, and enable
* them so that we will get events.
* @param excludes Class patterns for which we don't want events
* @param watchFields Do we want to watch assignments to fields
*/
public void setEventRequests(boolean watchFields) {
EventRequestManager mgr = vm.eventRequestManager();
// VMDeathRequest deathReq = mgr.createVMDeathRequest();
// deathReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
// deathReq.enable();
// get only the uncaught exceptions
ExceptionRequest excReq = mgr.createExceptionRequest(null, false, true);
// this version reports all exceptions, caught or uncaught
//ExceptionRequest excReq = mgr.createExceptionRequest(null, true, true);
// suspend so we can step
excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
excReq.enable();
/*
MethodEntryRequest menr = mgr.createMethodEntryRequest();
for (int i=0; i<excludes.length; ++i) {
menr.addClassExclusionFilter(excludes[i]);
}
menr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
menr.enable();
MethodExitRequest mexr = mgr.createMethodExitRequest();
for (int i=0; i<excludes.length; ++i) {
mexr.addClassExclusionFilter(excludes[i]);
}
mexr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
mexr.enable();
ThreadDeathRequest tdr = mgr.createThreadDeathRequest();
// Make sure we sync on thread death
tdr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
tdr.enable();
*/
// turn on field watching (waaay slow)
/*
// if (watchFields) {
ClassPrepareRequest cpr = mgr.createClassPrepareRequest();
for (int i=0; i<excludes.length; ++i) {
cpr.addClassExclusionFilter(excludes[i]);
}
cpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
cpr.enable();
// }
*/
}
/**
* This class keeps context on events in one thread.
* In this implementation, context is the indentation prefix.
*/
class ThreadTrace {
final ThreadReference thread;
final String baseIndent;
static final String threadDelta = " ";
StringBuffer indent;
ThreadTrace(ThreadReference thread) {
this.thread = thread;
this.baseIndent = nextBaseIndent;
indent = new StringBuffer(baseIndent);
nextBaseIndent += threadDelta;
println("====== " + thread.name() + " ======");
}
private void println(String str) {
if (writer != null) {
writer.print(indent);
writer.println(str);
writer.flush();
}
}
void methodEntryEvent(MethodEntryEvent event) {
println(event.method().name() + " -- "
+ event.method().declaringType().name());
indent.append("| ");
}
void methodExitEvent(MethodExitEvent event) {
indent.setLength(indent.length()-2);
}
void fieldWatchEvent(ModificationWatchpointEvent event) {
Field field = event.field();
Value value = event.valueToBe();
println(" " + field.name() + " = " + value);
}
void exceptionEvent(ExceptionEvent event) {
println("Exception: " + event.exception() +
" catch: " + event.catchLocation());
// System.out.println("Exception: " + event.exception() +
// " catch: " + event.catchLocation());
// Step to the catch
EventRequestManager mgr = vm.eventRequestManager();
StepRequest req = mgr.createStepRequest(thread,
StepRequest.STEP_MIN,
StepRequest.STEP_INTO);
req.addCountFilter(1); // next step only
req.setSuspendPolicy(EventRequest.SUSPEND_ALL);
req.enable();
}
// Step to exception catch
void stepEvent(StepEvent event) {
// Adjust call depth
int cnt = 0;
indent = new StringBuffer(baseIndent);
try {
cnt = thread.frameCount();
} catch (IncompatibleThreadStateException exc) {
}
while (cnt-- > 0) {
indent.append("| ");
}
EventRequestManager mgr = vm.eventRequestManager();
mgr.deleteEventRequest(event.request());
}
void threadDeathEvent(ThreadDeathEvent event) {
indent = new StringBuffer(baseIndent);
println("====== " + thread.name() + " end ======");
}
}
/**
* Returns the ThreadTrace instance for the specified thread,
* creating one if needed.
*/
ThreadTrace threadTrace(ThreadReference thread) {
ThreadTrace trace = (ThreadTrace)traceMap.get(thread);
if (trace == null) {
trace = new ThreadTrace(thread);
traceMap.put(thread, trace);
}
return trace;
}
/**
* Dispatch incoming events
*/
private void handleEvent(Event event) {
if (event instanceof ExceptionEvent) {
exceptionEvent((ExceptionEvent)event);
} else if (event instanceof ModificationWatchpointEvent) {
fieldWatchEvent((ModificationWatchpointEvent)event);
} else if (event instanceof MethodEntryEvent) {
methodEntryEvent((MethodEntryEvent)event);
} else if (event instanceof MethodExitEvent) {
methodExitEvent((MethodExitEvent)event);
} else if (event instanceof StepEvent) {
stepEvent((StepEvent)event);
} else if (event instanceof ThreadDeathEvent) {
threadDeathEvent((ThreadDeathEvent)event);
} else if (event instanceof ClassPrepareEvent) {
classPrepareEvent((ClassPrepareEvent)event);
} else if (event instanceof VMStartEvent) {
vmStartEvent((VMStartEvent)event);
} else if (event instanceof VMDeathEvent) {
vmDeathEvent((VMDeathEvent)event);
} else if (event instanceof VMDisconnectEvent) {
vmDisconnectEvent((VMDisconnectEvent)event);
} else {
throw new Error("Unexpected event type");
}
}
/***
* A VMDisconnectedException has happened while dealing with
* another event. We need to flush the event queue, dealing only
* with exit events (VMDeath, VMDisconnect) so that we terminate
* correctly.
*/
synchronized void handleDisconnectedException() {
EventQueue queue = vm.eventQueue();
while (connected) {
try {
EventSet eventSet = queue.remove();
EventIterator iter = eventSet.eventIterator();
while (iter.hasNext()) {
Event event = iter.nextEvent();
if (event instanceof VMDeathEvent) {
vmDeathEvent((VMDeathEvent)event);
} else if (event instanceof VMDisconnectEvent) {
vmDisconnectEvent((VMDisconnectEvent)event);
}
}
eventSet.resume(); // Resume the VM
} catch (InterruptedException exc) {
// ignore
}
}
}
private void vmStartEvent(VMStartEvent event) {
if (writer != null) writer.println("-- VM Started --");
}
// Forward event for thread specific processing
private void methodEntryEvent(MethodEntryEvent event) {
threadTrace(event.thread()).methodEntryEvent(event);
}
// Forward event for thread specific processing
private void methodExitEvent(MethodExitEvent event) {
threadTrace(event.thread()).methodExitEvent(event);
}
// Forward event for thread specific processing
private void stepEvent(StepEvent event) {
threadTrace(event.thread()).stepEvent(event);
}
// Forward event for thread specific processing
private void fieldWatchEvent(ModificationWatchpointEvent event) {
threadTrace(event.thread()).fieldWatchEvent(event);
}
void threadDeathEvent(ThreadDeathEvent event) {
ThreadTrace trace = (ThreadTrace)traceMap.get(event.thread());
if (trace != null) { // only want threads we care about
trace.threadDeathEvent(event); // Forward event
}
}
/**
* A new class has been loaded.
* Set watchpoints on each of its fields
*/
private void classPrepareEvent(ClassPrepareEvent event) {
// System.out.println(event);
// List list = event.referenceType().methodsByName("stop");
// Object o = list.get(0);
// System.out.println("stop methods = " + list);
// System.out.println(o.getClass());
EventRequestManager mgr = vm.eventRequestManager();
List fields = event.referenceType().visibleFields();
for (Iterator it = fields.iterator(); it.hasNext(); ) {
Field field = (Field)it.next();
ModificationWatchpointRequest req =
mgr.createModificationWatchpointRequest(field);
for (int i=0; i<excludes.length; ++i) {
req.addClassExclusionFilter(excludes[i]);
}
req.setSuspendPolicy(EventRequest.SUSPEND_NONE);
req.enable();
}
}
private void exceptionEvent(ExceptionEvent event) {
// com.sun.jdi.ObjectReference or = event.exception();
// System.out.println("exceptionEvent() fired " + or);
// System.out.println("catch location " + event.catchLocation());
parent.exception(event);
/*
ObjectReference or = event.exception();
ThreadReference thread = event.thread();
ThreadTrace trace = (ThreadTrace)traceMap.get(thread);
if (trace != null) { // only want threads we care about
trace.exceptionEvent(event); // Forward event
}
try {
List frames = thread.frames();
for (Object item : frames) {
System.out.println("got " + item);
}
//System.out.println(frames);
} catch (IncompatibleThreadStateException e) {
e.printStackTrace();
}
*/
}
public void vmDeathEvent(VMDeathEvent event) {
//System.err.println("vm is dead! dead!");
vmDied = true;
if (writer != null) {
writer.println("-- The application exited --");
}
}
public void vmDisconnectEvent(VMDisconnectEvent event) {
connected = false;
if (!vmDied) {
if (writer != null) {
writer.println("-- The application has been disconnected --");
}
}
}
}

View File

@@ -0,0 +1,42 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-06 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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.java.runner;
/**
* Interface for dealing with parser/compiler output.
* <P>
* Different instances of MessageStream need to do different things with
* messages. In particular, a stream instance used for parsing output from
* the compiler compiler has to interpret its messages differently than one
* parsing output from the runtime.
* <P>
* Classes which consume messages and do something with them
* should implement this interface.
*/
public interface MessageConsumer {
public void message(String s);
}

View File

@@ -0,0 +1,92 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-06 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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.java.runner;
import java.io.*;
/**
* Slurps up messages from compiler.
*/
public class MessageSiphon implements Runnable {
BufferedReader streamReader;
Thread thread;
MessageConsumer consumer;
public MessageSiphon(InputStream stream, MessageConsumer consumer) {
this.streamReader = new BufferedReader(new InputStreamReader(stream));
this.consumer = consumer;
thread = new Thread(this);
// don't set priority too low, otherwise exceptions won't
// bubble up in time (i.e. compile errors have a weird delay)
//thread.setPriority(Thread.MIN_PRIORITY);
thread.setPriority(Thread.MAX_PRIORITY-1);
//thread.start();
}
public void run() {
try {
// process data until we hit EOF; this will happily block
// (effectively sleeping the thread) until new data comes in.
// when the program is finally done, null will come through.
//
String currentLine;
while ((currentLine = streamReader.readLine()) != null) {
// \n is added again because readLine() strips it out
//EditorConsole.systemOut.println("messaging in");
consumer.message(currentLine + "\n");
//EditorConsole.systemOut.println("messaging out");
}
//EditorConsole.systemOut.println("messaging thread done");
thread = null;
} catch (NullPointerException npe) {
// Fairly common exception during shutdown
thread = null;
} catch (Exception e) {
// On Linux and sometimes on Mac OS X, a "bad file descriptor"
// message comes up when closing an applet that's run externally.
// That message just gets supressed here..
String mess = e.getMessage();
if ((mess != null) &&
(mess.indexOf("Bad file descriptor") != -1)) {
//if (e.getMessage().indexOf("Bad file descriptor") == -1) {
//System.err.println("MessageSiphon err " + e);
//e.printStackTrace();
} else {
e.printStackTrace();
}
thread = null;
}
}
public Thread getThread() {
return thread;
}
}

View File

@@ -0,0 +1,62 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-08 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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.java.runner;
import java.io.*;
/**
* OutputStream to handle stdout/stderr messages.
* <P>
* This is used by Editor, System.err is set to
* new PrintStream(new MessageStream()).
* It's also used by Compiler.
*/
class MessageStream extends OutputStream {
MessageConsumer messageConsumer;
public MessageStream(MessageConsumer messageConsumer) {
this.messageConsumer = messageConsumer;
}
public void close() { }
public void flush() { }
public void write(byte b[]) {
// this never seems to get called
System.out.println("leech1: " + new String(b));
}
public void write(byte b[], int offset, int length) {
//System.out.println("leech2: " + new String(b));
this.messageConsumer.message(new String(b, offset, length));
}
public void write(int b) {
// this never seems to get called
System.out.println("leech3: '" + ((char)b) + "'");
}
}

View File

@@ -0,0 +1,678 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-09 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
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.java.runner;
import processing.app.*;
import processing.app.exec.StreamRedirectThread;
import processing.core.*;
import processing.mode.java.Build;
import java.awt.Point;
import java.io.*;
import java.util.*;
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.ExceptionEvent;
/**
* Runs a compiled sketch. As of release 0136, all sketches are run externally
* to the environment so that a debugging interface can be used. This opens up
* future options for a decent debugger, but in the meantime fixes several
* problems with output and error streams, messages getting lost on Mac OS X,
* the run/stop buttons not working, libraries not shutting down, exceptions
* not coming through, exceptions being printed twice, having to force quit
* if you make a bad while() loop, and so on.
*/
public class Runner implements MessageConsumer {
private boolean presenting;
// Object that listens for error messages or exceptions.
protected RunnerListener listener;
// Running remote VM
protected VirtualMachine vm;
// Thread transferring remote error stream to our error stream
protected Thread errThread = null;
// Thread transferring remote output stream to our output stream
protected Thread outThread = null;
// Mode for tracing the Trace program (default= 0 off)
protected int debugTraceMode = 0;
// Do we want to watch assignments to fields
protected boolean watchFields = false;
// Class patterns for which we don't want events
protected String[] excludes = {
"java.*", "javax.*", "sun.*", "com.sun.*",
"apple.*",
"processing.*"
};
protected SketchException exception;
//private PrintStream leechErr;
protected Editor editor;
// protected Sketch sketch;
protected Build build;
// private String appletClassName;
public Runner(Build build, RunnerListener listener) {
this.listener = listener;
// this.sketch = sketch;
this.build = build;
if (listener instanceof Editor) {
this.editor = (Editor) listener;
// } else {
// System.out.println("actually it's a " + listener.getClass().getName());
}
}
// public void launch(String appletClassName, boolean presenting) {
// this.appletClassName = appletClassName;
public void launch(boolean presenting) {
this.presenting = presenting;
// all params have to be stored as separate items,
// so a growable array needs to be used. i.e. -Xms128m -Xmx1024m
// will throw an error if it's shoved into a single array element
//Vector params = new Vector();
// get around Apple's Java 1.5 bugs
//params.add("/System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Commands/java");
//params.add("java");
//System.out.println("0");
String[] machineParamList = getMachineParams();
String[] sketchParamList = getSketchParams();
vm = launchVirtualMachine(machineParamList, sketchParamList);
if (vm != null) {
generateTrace(null);
// try {
// generateTrace(new PrintWriter("/Users/fry/Desktop/output.txt"));
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
protected String[] getMachineParams() {
ArrayList<String> params = new ArrayList<String>();
//params.add("-Xint"); // interpreted mode
//params.add("-Xprof"); // profiler
//params.add("-Xaprof"); // allocation profiler
//params.add("-Xrunhprof:cpu=samples"); // old-style profiler
// TODO change this to use run.args = true, run.args.0, run.args.1, etc.
// so that spaces can be included in the arg names
String options = Preferences.get("run.options");
if (options.length() > 0) {
String pieces[] = PApplet.split(options, ' ');
for (int i = 0; i < pieces.length; i++) {
String p = pieces[i].trim();
if (p.length() > 0) {
params.add(p);
}
}
}
// params.add("-Djava.ext.dirs=nuffing");
if (Preferences.getBoolean("run.options.memory")) {
params.add("-Xms" + Preferences.get("run.options.memory.initial") + "m");
params.add("-Xmx" + Preferences.get("run.options.memory.maximum") + "m");
}
if (Base.isMacOS()) {
params.add("-Xdock:name=" + build.getSketchClassName());
// params.add("-Dcom.apple.mrj.application.apple.menu.about.name=" +
// sketch.getMainClassName());
}
// sketch.libraryPath might be ""
// librariesClassPath will always have sep char prepended
params.add("-Djava.library.path=" +
build.getJavaLibraryPath() +
File.pathSeparator +
System.getProperty("java.library.path"));
params.add("-cp");
params.add(build.getClassPath());
// params.add(sketch.getClassPath() +
// File.pathSeparator +
// Base.librariesClassPath);
// enable assertions - http://dev.processing.org/bugs/show_bug.cgi?id=1188
params.add("-ea");
//PApplet.println(PApplet.split(sketch.classPath, ':'));
String outgoing[] = new String[params.size()];
params.toArray(outgoing);
//PApplet.println(outgoing);
// PApplet.println(PApplet.split(outgoing[0], ":"));
// PApplet.println();
// PApplet.println("class path");
// PApplet.println(PApplet.split(outgoing[2], ":"));
return outgoing;
//return (String[]) params.toArray();
// System.out.println("sketch class path");
// PApplet.println(PApplet.split(sketch.classPath, ';'));
// System.out.println();
// System.out.println("libraries class path");
// PApplet.println(PApplet.split(Base.librariesClassPath, ';'));
// System.out.println();
}
protected String[] getSketchParams() {
ArrayList<String> params = new ArrayList<String>();
// It's dangerous to add your own main() to your code,
// but if you've done it, we'll respect your right to hang yourself.
// http://dev.processing.org/bugs/show_bug.cgi?id=1446
if (build.getFoundMain()) {
params.add(build.getSketchClassName());
} else {
params.add("processing.core.PApplet");
// If there was a saved location (this guy has been run more than once)
// then the location will be set to the last position of the sketch window.
// This will be passed to the PApplet runner using something like
// --location=30,20
// Otherwise, the editor location will be passed, and the applet will
// figure out where to place itself based on the editor location.
// --editor-location=150,20
if (editor != null) { // if running processing-cmd, don't do placement
Point windowLocation = editor.getSketchLocation();
if (windowLocation != null) {
params.add(PApplet.ARGS_LOCATION + "=" +
windowLocation.x + "," + windowLocation.y);
} else {
Point editorLocation = editor.getLocation();
params.add(PApplet.ARGS_EDITOR_LOCATION + "=" +
editorLocation.x + "," + editorLocation.y);
}
params.add(PApplet.ARGS_EXTERNAL);
}
params.add(PApplet.ARGS_DISPLAY + "=" +
Preferences.get("run.display"));
params.add(PApplet.ARGS_SKETCH_FOLDER + "=" +
build.getSketchPath());
if (presenting) {
params.add(PApplet.ARGS_PRESENT);
if (Preferences.getBoolean("run.present.exclusive")) {
params.add(PApplet.ARGS_EXCLUSIVE);
}
params.add(PApplet.ARGS_STOP_COLOR + "=" +
Preferences.get("run.present.stop.color"));
params.add(PApplet.ARGS_BGCOLOR + "=" +
Preferences.get("run.present.bgcolor"));
}
params.add(build.getSketchClassName());
}
// String outgoing[] = new String[params.size()];
// params.toArray(outgoing);
// return outgoing;
return (String[]) params.toArray(new String[0]);
}
protected VirtualMachine launchVirtualMachine(String[] vmParams,
String[] classParams) {
//vm = launchTarget(sb.toString());
LaunchingConnector connector = (LaunchingConnector)
findConnector("com.sun.jdi.RawCommandLineLaunch");
//PApplet.println(connector); // gets the defaults
//Map arguments = connectorArguments(connector, mainArgs);
Map arguments = connector.defaultArguments();
Connector.Argument commandArg =
(Connector.Argument)arguments.get("command");
// Using localhost instead of 127.0.0.1 sometimes causes a
// "Transport Error 202" error message when trying to run.
// http://dev.processing.org/bugs/show_bug.cgi?id=895
String addr = "127.0.0.1:" + (8000 + (int) (Math.random() * 1000));
//String addr = "localhost:" + (8000 + (int) (Math.random() * 1000));
//String addr = "" + (8000 + (int) (Math.random() * 1000));
String commandArgs =
"java -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
if (Base.isWindows()) {
commandArgs =
"java -Xrunjdwp:transport=dt_shmem,address=" + addr + ",suspend=y ";
} else if (Base.isMacOS()) {
if (System.getProperty("os.version").startsWith("10.4")) {
// -d32 not understood by 10.4 (and not needed)
commandArgs =
"java -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
} else {
commandArgs =
// "java -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
"java -d32 -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
}
}
for (int i = 0; i < vmParams.length; i++) {
commandArgs = addArgument(commandArgs, vmParams[i], ' ');
}
if (classParams != null) {
for (int i = 0; i < classParams.length; i++) {
commandArgs = addArgument(commandArgs, classParams[i], ' ');
}
}
commandArg.setValue(commandArgs);
Connector.Argument addressArg =
(Connector.Argument)arguments.get("address");
addressArg.setValue(addr);
//PApplet.println(connector); // prints the current
//com.sun.tools.jdi.AbstractLauncher al;
//com.sun.tools.jdi.RawCommandLineLauncher rcll;
//System.out.println(PApplet.javaVersion);
// http://java.sun.com/j2se/1.5.0/docs/guide/jpda/conninv.html#sunlaunch
try {
return connector.launch(arguments);
} catch (IOException exc) {
throw new Error("Unable to launch target VM: " + exc);
} catch (IllegalConnectorArgumentsException exc) {
throw new Error("Internal error: " + exc);
} catch (VMStartException exc) {
Process p = exc.process();
//System.out.println(p);
String[] errorStrings = PApplet.loadStrings(p.getErrorStream());
/*String[] inputStrings =*/ PApplet.loadStrings(p.getInputStream());
if (errorStrings != null && errorStrings.length > 1) {
if (errorStrings[0].indexOf("Invalid maximum heap size") != -1) {
Base.showWarning("Way Too High",
"Please lower the value for \u201Cmaximum available memory\u201D in the\n" +
"Preferences window. For more information, read Help \u2192 Troubleshooting.",
exc);
} else {
PApplet.println(errorStrings);
System.err.println("Using startup command:");
PApplet.println(arguments);
}
} else {
exc.printStackTrace();
System.err.println("Could not run the sketch (Target VM failed to initialize).");
if (Preferences.getBoolean("run.options.memory")) {
// Only mention this if they've even altered the memory setup
System.err.println("Make sure that you haven't set the maximum available memory too high.");
}
System.err.println("For more information, read revisions.txt and Help \u2192 Troubleshooting.");
}
// changing this to separate editor and listener [091124]
//if (editor != null) {
listener.statusError("Could not run the sketch.");
//}
return null;
}
}
private static boolean hasWhitespace(String string) {
int length = string.length();
for (int i = 0; i < length; i++) {
if (Character.isWhitespace(string.charAt(i))) {
return true;
}
}
return false;
}
private static String addArgument(String string, String argument, char sep) {
if (hasWhitespace(argument) || argument.indexOf(',') != -1) {
// Quotes were stripped out for this argument, add 'em back.
StringBuffer buffer = new StringBuffer(string);
buffer.append('"');
for (int i = 0; i < argument.length(); i++) {
char c = argument.charAt(i);
if (c == '"') {
buffer.append('\\');
}
buffer.append(c);
}
buffer.append('\"');
buffer.append(sep);
return buffer.toString();
} else {
return string + argument + String.valueOf(sep);
}
}
/**
* Generate the trace.
* Enable events, start thread to display events,
* start threads to forward remote error and output streams,
* resume the remote VM, wait for the final event, and shutdown.
*/
protected void generateTrace(PrintWriter writer) {
vm.setDebugTraceMode(debugTraceMode);
EventThread eventThread = null;
//if (writer != null) {
eventThread = new EventThread(this, vm, excludes, writer);
eventThread.setEventRequests(watchFields);
eventThread.start();
//}
//redirectOutput();
Process process = vm.process();
// processInput = new SystemOutSiphon(process.getInputStream());
// processError = new MessageSiphon(process.getErrorStream(), this);
// Copy target's output and error to our output and error.
// errThread = new StreamRedirectThread("error reader",
// process.getErrorStream(),
// System.err);
MessageSiphon ms = new MessageSiphon(process.getErrorStream(), this);
errThread = ms.getThread();
outThread = new StreamRedirectThread("output reader",
process.getInputStream(),
System.out);
errThread.start();
outThread.start();
vm.resume();
//System.out.println("done with resume");
// Shutdown begins when event thread terminates
try {
if (eventThread != null) eventThread.join();
// System.out.println("in here");
// Bug #852 tracked to this next line in the code.
// http://dev.processing.org/bugs/show_bug.cgi?id=852
errThread.join(); // Make sure output is forwarded
// System.out.println("and then");
outThread.join(); // before we exit
// System.out.println("out of it");
// At this point, disable the run button.
// This happens when the sketch is exited by hitting ESC,
// or the user manually closes the sketch window.
// TODO this should be handled better, should it not?
if (editor != null) {
editor.deactivateRun();
}
} catch (InterruptedException exc) {
// we don't interrupt
}
//System.out.println("and leaving");
if (writer != null) writer.close();
}
protected Connector findConnector(String connectorName) {
List connectors = Bootstrap.virtualMachineManager().allConnectors();
// debug: code to list available connectors
// Iterator iter2 = connectors.iterator();
// while (iter2.hasNext()) {
// Connector connector = (Connector)iter2.next();
// System.out.println("connector name is " + connector.name());
// }
Iterator iter = connectors.iterator();
while (iter.hasNext()) {
Connector connector = (Connector)iter.next();
if (connector.name().equals(connectorName)) {
return connector;
}
}
throw new Error("No connector");
}
public void exception(ExceptionEvent event) {
// System.out.println(event);
ObjectReference or = event.exception();
ReferenceType rt = or.referenceType();
String exceptionName = rt.name();
//Field messageField = Throwable.class.getField("detailMessage");
Field messageField = rt.fieldByName("detailMessage");
// System.out.println("field " + messageField);
Value messageValue = or.getValue(messageField);
// System.out.println("mess val " + messageValue);
int last = exceptionName.lastIndexOf('.');
String message = exceptionName.substring(last + 1);
if (messageValue != null) {
String messageStr = messageValue.toString();
if (messageStr.startsWith("\"")) {
messageStr = messageStr.substring(1, messageStr.length() - 1);
}
message += ": " + messageStr;
}
// System.out.println("mess type " + messageValue.type());
//StringReference messageReference = (StringReference) messageValue.type();
// System.out.println(or.referenceType().fields());
// if (name.startsWith("java.lang.")) {
// name = name.substring(10);
if (!handleCommonErrors(exceptionName, message, listener)) {
reportException(message, event.thread());
}
if (editor != null) {
editor.deactivateRun();
}
}
public static boolean handleCommonErrors(final String exceptionClass,
final String message,
final RunnerListener listener) {
if (exceptionClass.equals("java.lang.OutOfMemoryError")) {
if (message.contains("exceeds VM budget")) {
// TODO this is a kludge for Android, since there's no memory preference
listener.statusError("OutOfMemoryError: This code attempts to use more memory than available.");
System.err.println("An OutOfMemoryError means that your code is either using up too much memory");
System.err.println("because of a bug (e.g. creating an array that's too large, or unintentionally");
System.err.println("loading thousands of images), or simply that it's trying to use more memory");
System.err.println("than what is supported by the current device.");
} else {
listener.statusError("OutOfMemoryError: You may need to increase the memory setting in Preferences.");
System.err.println("An OutOfMemoryError means that your code is either using up too much memory");
System.err.println("because of a bug (e.g. creating an array that's too large, or unintentionally");
System.err.println("loading thousands of images), or that your sketch may need more memory to run.");
System.err.println("If your sketch uses a lot of memory (for instance if it loads a lot of data files)");
System.err.println("you can increase the memory available to your sketch using the Preferences window.");
}
} else if (exceptionClass.equals("java.lang.StackOverflowError")) {
listener.statusError("StackOverflowError: This sketch is attempting too much recursion.");
System.err.println("A StackOverflowError means that you have a bug that's causing a function");
System.err.println("to be called recursively (it's calling itself and going in circles),");
System.err.println("or you're intentionally calling a recursive function too much,");
System.err.println("and your code should be rewritten in a more efficient manner.");
} else if (exceptionClass.equals("java.lang.UnsupportedClassVersionError")) {
listener.statusError("UnsupportedClassVersionError: A library is using code compiled with an unsupported version of Java.");
System.err.println("This version of Processing only supports libraries and JAR files compiled for Java 1.5.");
System.err.println("A library used by this sketch was compiled for Java 1.6 or later, ");
System.err.println("and needs to be recompiled to be compatible with Java 1.5.");
} else if (exceptionClass.equals("java.lang.NoSuchMethodError") ||
exceptionClass.equals("java.lang.NoSuchFieldError")) {
listener.statusError(exceptionClass.substring(10) + ": " +
"You may be using a library that's incompatible " +
"with this version of Processing.");
} else if (message != null &&
message.equals("ClassNotFoundException: quicktime.std.StdQTException")) {
listener.statusError("Could not find QuickTime, please reinstall QuickTime 7 or later.");
} else {
return false;
}
return true;
}
// TODO: This may be called more than one time per error in the VM,
// presumably because exceptions might be wrapped inside others,
// and this will fire for both.
protected void reportException(String message, ThreadReference thread) {
listener.statusError(findException(message, thread));
}
/**
* Move through a list of stack frames, searching for references to code
* found in the current sketch. Return with a RunnerException that contains
* the location of the error, or if nothing is found, just return with a
* RunnerException that wraps the error message itself.
*/
SketchException findException(String message, ThreadReference thread) {
try {
// use to dump the stack for debugging
// for (StackFrame frame : thread.frames()) {
// System.out.println("frame: " + frame);
// }
List<StackFrame> frames = thread.frames();
for (StackFrame frame : frames) {
try {
Location location = frame.location();
String filename = null;
filename = location.sourceName();
int lineNumber = location.lineNumber() - 1;
SketchException rex =
build.placeException(message, filename, lineNumber);
if (rex != null) {
return rex;
}
} catch (AbsentInformationException e) {
// Any of the thread.blah() methods can throw an AbsentInformationEx
// if that bit of data is missing. If so, just write out the error
// message to the console.
//e.printStackTrace(); // not useful
exception = new SketchException(message);
exception.hideStackTrace();
listener.statusError(exception);
}
}
} catch (IncompatibleThreadStateException e) {
// This shouldn't happen, but if it does, print the exception in case
// it's something that needs to be debugged separately.
e.printStackTrace();
}
// Give up, nothing found inside the pile of stack frames
SketchException rex = new SketchException(message);
// exception is being created /here/, so stack trace is not useful
rex.hideStackTrace();
return rex;
}
public void close() {
// TODO make sure stop() has already been called to exit the sketch
// TODO actually kill off the vm here
if (vm != null) {
try {
vm.exit(0);
} catch (com.sun.jdi.VMDisconnectedException vmde) {
// if the vm has disconnected on its own, ignore message
//System.out.println("harmless disconnect " + vmde.getMessage());
// TODO shouldn't need to do this, need to do more cleanup
}
vm = null;
}
}
// made synchronized for rev 87
// attempted to remove synchronized for 0136 to fix bug #775 (no luck tho)
// http://dev.processing.org/bugs/show_bug.cgi?id=775
synchronized public void message(String s) {
//System.out.println("M" + s.length() + ":" + s.trim()); // + "MMM" + s.length());
// this eats the CRLFs on the lines.. oops.. do it later
//if (s.trim().length() == 0) return;
// this is PApplet sending a message (via System.out.println)
// that signals that the applet has been quit.
if (s.indexOf(PApplet.EXTERNAL_STOP) == 0) {
//System.out.println("external: quit");
if (editor != null) {
// editor.internalCloseRunner(); // [091124]
// editor.handleStop(); // prior to 0192
editor.internalCloseRunner(); // 0192
}
return;
}
// this is the PApplet sending us a message that the applet
// is being moved to a new window location
if (s.indexOf(PApplet.EXTERNAL_MOVE) == 0) {
String nums = s.substring(s.indexOf(' ') + 1).trim();
int space = nums.indexOf(' ');
int left = Integer.parseInt(nums.substring(0, space));
int top = Integer.parseInt(nums.substring(space + 1));
// this is only fired when connected to an editor
editor.setSketchLocation(new Point(left, top));
//System.out.println("external: move to " + left + " " + top);
return;
}
// these are used for debugging, in case there are concerns
// that some errors aren't coming through properly
// if (s.length() > 2) {
// System.err.println(newMessage);
// System.err.println("message " + s.length() + ":" + s);
// }
// always shove out the mesage, since it might not fall under
// the same setup as we're expecting
System.err.print(s);
//System.err.println("[" + s.length() + "] " + s);
System.err.flush();
}
}