mirror of
https://github.com/processing/processing4.git
synced 2026-02-13 10:30:44 +01:00
moving things back off the branch, and into full disaster mode
This commit is contained in:
678
app/src/processing/mode/java/AutoFormat.java
Normal file
678
app/src/processing/mode/java/AutoFormat.java
Normal 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;
|
||||
}
|
||||
}
|
||||
1594
app/src/processing/mode/java/Build.java
Normal file
1594
app/src/processing/mode/java/Build.java
Normal file
File diff suppressed because it is too large
Load Diff
322
app/src/processing/mode/java/Commander.java
Normal file
322
app/src/processing/mode/java/Commander.java
Normal 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=<name> Specify the sketch folder (required)
|
||||
* --output=<name> 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=<file> 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.");
|
||||
}
|
||||
}
|
||||
*/
|
||||
669
app/src/processing/mode/java/Compiler.java
Normal file
669
app/src/processing/mode/java/Compiler.java
Normal 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("^");
|
||||
}
|
||||
}
|
||||
518
app/src/processing/mode/java/JavaEditor.java
Normal file
518
app/src/processing/mode/java/JavaEditor.java
Normal 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 → 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 → 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();
|
||||
}
|
||||
}
|
||||
216
app/src/processing/mode/java/JavaMode.java
Normal file
216
app/src/processing/mode/java/JavaMode.java
Normal 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 → 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();
|
||||
}
|
||||
}
|
||||
601
app/src/processing/mode/java/PdeKeyListener.java
Normal file
601
app/src/processing/mode/java/PdeKeyListener.java
Normal 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);
|
||||
}
|
||||
}
|
||||
130
app/src/processing/mode/java/PresentMode.java
Normal file
130
app/src/processing/mode/java/PresentMode.java
Normal 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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
110
app/src/processing/mode/java/Toolbar.java
Normal file
110
app/src/processing/mode/java/Toolbar.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
app/src/processing/mode/java/preproc/.cvsignore
Normal file
9
app/src/processing/mode/java/preproc/.cvsignore
Normal file
@@ -0,0 +1,9 @@
|
||||
*Lexer.java
|
||||
*Recognizer.java
|
||||
*TokenTypes.java
|
||||
*TokenTypes.txt
|
||||
*TreeParser.java
|
||||
*TreeParserTokenTypes.java
|
||||
*TreeParserTokenTypes.txt
|
||||
expanded*.g
|
||||
|
||||
766
app/src/processing/mode/java/preproc/PdeEmitter.java
Normal file
766
app/src/processing/mode/java/preproc/PdeEmitter.java
Normal 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 <jdf@pobox.com>
|
||||
*/
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
755
app/src/processing/mode/java/preproc/PdePreprocessor.java
Normal file
755
app/src/processing/mode/java/preproc/PdePreprocessor.java
Normal 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 <jdf@pobox.com>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
32
app/src/processing/mode/java/preproc/PreprocessorResult.java
Normal file
32
app/src/processing/mode/java/preproc/PreprocessorResult.java
Normal 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 <jdf@pobox.com>
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
30
app/src/processing/mode/java/preproc/TokenUtil.java
Normal file
30
app/src/processing/mode/java/preproc/TokenUtil.java
Normal 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 <jdf@pobox.com>
|
||||
*
|
||||
*/
|
||||
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()];
|
||||
}
|
||||
}
|
||||
1832
app/src/processing/mode/java/preproc/java15.g
Normal file
1832
app/src/processing/mode/java/preproc/java15.g
Normal file
File diff suppressed because it is too large
Load Diff
393
app/src/processing/mode/java/preproc/pde.g
Normal file
393
app/src/processing/mode/java/preproc/pde.g
Normal 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)+
|
||||
;
|
||||
|
||||
404
app/src/processing/mode/java/runner/EventThread.java
Normal file
404
app/src/processing/mode/java/runner/EventThread.java
Normal 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 --");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
app/src/processing/mode/java/runner/MessageConsumer.java
Normal file
42
app/src/processing/mode/java/runner/MessageConsumer.java
Normal 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);
|
||||
|
||||
}
|
||||
92
app/src/processing/mode/java/runner/MessageSiphon.java
Normal file
92
app/src/processing/mode/java/runner/MessageSiphon.java
Normal 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;
|
||||
}
|
||||
}
|
||||
62
app/src/processing/mode/java/runner/MessageStream.java
Normal file
62
app/src/processing/mode/java/runner/MessageStream.java
Normal 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) + "'");
|
||||
}
|
||||
}
|
||||
678
app/src/processing/mode/java/runner/Runner.java
Normal file
678
app/src/processing/mode/java/runner/Runner.java
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user