moving the Java mode over here too

This commit is contained in:
Ben Fry
2015-01-20 14:57:32 -05:00
parent 260d131c00
commit d7da694d80
21 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,830 @@
/* -*- 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
Even more updates Copyright (c) 2014 George Bateman
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://web.archive.org/web/20091018220353/http://geocities.com/jasonpell/programs.html">here</a>,
* which is itself based on code from Van Di-Han Ho from
* <a href="http://web.archive.org/web/20091027043013/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();
/** The number of spaces in one indent. Constant. */
private int indentValue;
/** Set when the end of the chars array is reached. */
private boolean EOF;
private boolean inStatementFlag; // in a line of code
private boolean overflowFlag; // line overrunning?
private boolean startFlag; // No buf has yet been writen to this line.
private boolean if_flg;
private boolean elseFlag;
/** -1 if not in array or if just after it, otherwise increases from 0. */
private int arrayLevel;
private int arrayIndent; // Lowest value of the above for this line.
/** Number of ? entered without exiting at : of a?b:c structures. */
private int conditionalLevel;
private int[][] sp_flg;
private boolean[][] s_ind;
private int if_lev;
/** chars[pos] is where we're at. */
private int pos;
private int level;
/** Number of curly brackets entered and not exited,
excluding arrays. */
private int curlyLvl;
/** Number of parentheses entered and not exited. */
private int parenLevel;
private boolean[] ind;
private int[] p_flg;
private int[][] s_tabs;
/** At every {, this has a true pushed if it's a do-while,
and a false otherwise. It is then popped at }. */
private Stack<Boolean> doWhileFlags;
/** At every (, this has a true pushed for if, while, or for,
and a false otherwise. Popped at ). */
private Stack<Boolean> ifWhileForFlags;
private boolean jdoc_flag;
/** The number of times to indent at a given point */
private int tabs;
/** The last non-space seen by nextChar(). */
private char lastNonWhitespace = 0;
private void handleMultiLineComment() {
final boolean savedStartFlag = startFlag;
buf.append(nextChar()); // So /*/ isn't self-closing.
for (char ch = nextChar(); !EOF; ch = nextChar()) {
buf.append(ch);
while (ch != '/' && !EOF) {
if (ch == '\n') {
writeIndentedComment();
startFlag = true;
}
buf.append(ch = nextChar());
}
if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '*') {
jdoc_flag = false;
break;
}
}
writeIndentedComment();
startFlag = savedStartFlag;
jdoc_flag = false;
return;
}
/**
* Pumps nextChar into buf until \n or EOF, then calls
* writeIndentedLine() and sets startFlag to true.
*/
private void handleSingleLineComment() {
char ch = nextChar();
while (ch != '\n' && !EOF) {
buf.append(ch);
ch = nextChar();
}
writeIndentedLine();
startFlag = true;
}
private void writeIndentedLine() {
if (buf.length() == 0) {
if (startFlag) startFlag = elseFlag = false;
return;
}
if (startFlag) {
// Indent suppressed at eg. if<nl>{ and when
// buf is close-brackets only followed by ';'.
boolean indentMore = !buf.toString().matches("[\\s\\]\\}\\)]+;")
&& (buf.charAt(0) != '{' || arrayLevel >= 0)
&& overflowFlag;
if (indentMore) {
tabs++;
if (arrayIndent > 0) tabs += arrayIndent;
}
printIndentation();
startFlag = false;
if (indentMore) {
tabs--;
if (arrayIndent > 0) tabs -= arrayIndent;
}
}
if (lastNonSpaceChar() == '}' && bufStarts("else")) {
result.append(' ');
}
if (elseFlag) {
if (lastNonSpaceChar() == '}') {
trimRight(result);
result.append(' ');
}
elseFlag = false;
}
// If we're still in a statement at \n, that's overflow.
overflowFlag = inStatementFlag;
arrayIndent = arrayLevel;
result.append(buf);
buf.setLength(0);
}
/**
* @return the last character in <tt>result</tt> not ' ' or '\n'.
*/
private char lastNonSpaceChar() {
for (int i = result.length() - 1; i >= 0; i--) {
char chI = result.charAt(i);
if (chI != ' ' && chI != '\n') return chI;
}
return 0;
}
/**
* Called by handleMultilineComment.<br />
* Sets jdoc_flag if at the start of a doc comment.
* Sends buf to result with proper indents, then clears buf.<br />
* Does nothing if buf is empty.
*/
private void writeIndentedComment() {
if (buf.length() == 0) return;
int firstNonSpace = 0;
while (buf.charAt(firstNonSpace) == ' ') firstNonSpace++;
if (lookup_com("/**")) jdoc_flag = true;
if (startFlag) printIndentation();
if (buf.charAt(firstNonSpace) == '/' && buf.charAt(firstNonSpace+1) == '*') {
if (startFlag && lastNonWhitespace != ';') {
result.append(buf.substring(firstNonSpace));
} else {
result.append(buf);
}
} else {
if (buf.charAt(firstNonSpace) == '*' || !jdoc_flag) {
result.append(" " + buf.substring(firstNonSpace));
} else {
result.append(" * " + buf.substring(firstNonSpace));
}
}
buf.setLength(0);
}
/**
* Makes tabs &gt;= 0 and appends <tt>tabs*indentValue</tt>
* spaces to result.
*/
private void printIndentation() {
if (tabs <= 0) {
tabs = 0;
return;
}
final int spaces = tabs * indentValue;
for (int i = 0; i < spaces; i++) {
result.append(' ');
}
}
/**
* @return <tt>chars[pos+1]</tt> or '\0' if out-of-bounds.
*/
private char peek() {
return (pos + 1 >= chars.length) ? 0 : chars[pos + 1];
}
/**
* Sets pos to the position of the next character that is not ' '
* in chars. If chars[pos] != ' ' already, it will still move on.
* Then sets EOF if pos has reached the end, or reverses pos by 1 if it
* has not.
* <br/> Does nothing if EOF.
* @param allWsp (boolean) Eat newlines too (all of Character.isWhiteSpace()).
*/
private void advanceToNonSpace(boolean allWsp) {
if (EOF) return;
if (allWsp) {
do {
pos++;
} while (pos < chars.length && Character.isWhitespace(chars[pos]));
} else {
do {
pos++;
} while (pos < chars.length && chars[pos] == ' ');
}
if (pos == chars.length - 1) {
EOF = true;
} else {
pos--; // reset for nextChar()
}
}
/**
* Increments pos, sets EOF if needed, and returns the new
* chars[pos] or zero if out-of-bounds.
* Sets lastNonWhitespace if chars[pos] isn't whitespace.
* Does nothing and returns zero if already at EOF.
*/
private char nextChar() {
if (EOF) return 0;
pos++;
if (pos >= chars.length - 1) EOF = true;
if (pos >= chars.length) return 0;
char retVal = chars[pos];
if (!Character.isWhitespace(retVal)) lastNonWhitespace = retVal;
return retVal;
}
/**
* Called after else.
*/
private void gotElse() {
tabs = s_tabs[curlyLvl][if_lev];
p_flg[level] = sp_flg[curlyLvl][if_lev];
ind[level] = s_ind[curlyLvl][if_lev];
if_flg = true;
// We can't expect else to be followed by a semicolon, so must
// end the statemant manually.
inStatementFlag = false;
}
/**
* Pump any '\t' and ' ' to buf, handle any following comment,
* and if the next character is '\n', discard it.
* @return Whether a '\n' was found and discarded.
*/
private boolean readForNewLine() {
final int savedTabs = tabs;
char c = peek();
while (!EOF && (c == '\t' || c == ' ')) {
buf.append(nextChar());
c = peek();
}
if (c == '/') {
buf.append(nextChar());
c = peek();
if (c == '*') {
buf.append(nextChar());
handleMultiLineComment();
} else if (c == '/') {
buf.append(nextChar());
handleSingleLineComment();
return true;
}
}
c = peek();
if (c == '\n') {
// eat it
nextChar();
tabs = savedTabs;
return true;
}
return false;
}
/**
* @return last non-wsp in result+buf, or 0 on error.
*/
private char prevNonWhitespace() {
StringBuffer tot = new StringBuffer();
tot.append(result);
tot.append(buf);
for (int i = tot.length()-1; i >= 0; i--) {
if (!Character.isWhitespace(tot.charAt(i)))
return tot.charAt(i);
}
return 0;
}
/**
* Sees if buf is of the form [optional whitespace][keyword][optional anything].
* It won't allow keyword to be directly followed by an alphanumeric, _, or &amp;.
* Will be different if keyword contains regex codes.
*/
private boolean bufStarts(final String keyword) {
return Pattern.matches("^\\s*" + keyword + "(?![a-zA-Z0-9_&]).*$", buf);
}
/**
* Sees if buf is of the form [optional anything][keyword][optional whitespace].
* It won't allow keyword to be directly preceded by an alphanumeric, _, or &amp;.
* Will be different if keyword contains regex codes.
*/
private boolean bufEnds(final String keyword) {
return Pattern.matches("^.*(?<![a-zA-Z0-9_&])" + keyword + "\\s*$", buf);
}
/**
* Allows you to increase if_lev safely. Enlarges related arrays where needed;
* does not change if_lev itself.
*/
private void if_levSafe() {
if (s_tabs[0].length <= if_lev) {
for (int i = 0; i < s_tabs.length; i++) {
s_tabs[i] = PApplet.expand(s_tabs[i]);
}
}
if (sp_flg[0].length <= if_lev) {
for (int i = 0; i < sp_flg.length; i++) {
sp_flg[i] = PApplet.expand(sp_flg[i]);
}
}
if (s_ind[0].length <= if_lev) {
for (int i = 0; i < s_ind.length; i++) {
s_ind[i] = PApplet.expand(s_ind[i]);
}
}
}
/**
* Sees if buf is of the form [optional whitespace][keyword][optional anything].
* It *will* allow keyword to be directly followed by an alphanumeric, _, or &amp;.
* Will be different if keyword contains regex codes (except *, which is fine).
*/
private boolean lookup_com(final String keyword) {
final String regex = "^\\s*" + keyword.replace("*", "\\*") + ".*$";
return Pattern.matches(regex, buf);
}
/**
* Takes all whitespace off the end of its argument.
*/
private void trimRight(final StringBuilder sb) {
while (sb.length() >= 1 && Character.isWhitespace(sb.charAt(sb.length() - 1)))
sb.setLength(sb.length() - 1);
}
/**
* Entry point
*/
public String format(final String source) {
final String normalizedText = source.replaceAll("\r", "");
final String cleanText =
normalizedText + (normalizedText.endsWith("\n") ? "" : "\n");
// Globals' description at top of file.
result.setLength(0);
indentValue = Preferences.getInteger("editor.tabs.size");
boolean forFlag = if_flg = false;
startFlag = true;
int forParenthLevel = 0;
conditionalLevel = parenLevel = curlyLvl = if_lev = level = 0;
tabs = 0;
jdoc_flag = inStatementFlag = overflowFlag = false;
pos = arrayLevel = -1;
int[] s_level = new int[10];
sp_flg = new int[20][10];
s_ind = new boolean[20][10];
int[] s_if_lev = new int[10]; // Stack
boolean[] s_if_flg = new boolean[10]; // Stack
ind = new boolean[10];
p_flg = new int[10];
s_tabs = new int[20][10];
doWhileFlags = new Stack<Boolean>();
ifWhileForFlags = new Stack<Boolean>();
chars = cleanText.toCharArray();
EOF = false; // set in nextChar() when EOF
while (!EOF) {
char c = nextChar();
switch (c) {
default:
inStatementFlag = true;
buf.append(c);
break;
case ',':
inStatementFlag = true;
trimRight(buf);
buf.append(", ");
advanceToNonSpace(false);
break;
case ' ':
case '\t':
elseFlag = bufEnds("else");
if (elseFlag) {
gotElse();
if (!startFlag || buf.length() > 0) {
buf.append(c);
}
writeIndentedLine();
startFlag = false;
break;
}
// Only allow in the line, to nuke the old indent.
if (!startFlag || buf.length() > 0) buf.append(c);
break;
case '\n':
if (EOF) break;
elseFlag = bufEnds("else");
if (elseFlag) gotElse();
if (lookup_com("//")) {
if (buf.charAt(buf.length() - 1) == '\n') {
buf.setLength(buf.length() - 1);
}
}
if (elseFlag) {
writeIndentedLine();
result.append("\n");
p_flg[level]++;
tabs++;
} else {
writeIndentedLine();
result.append("\n");
}
startFlag = true;
break;
case '{':
elseFlag = bufEnds("else");
if (elseFlag) gotElse();
doWhileFlags.push(Boolean.valueOf(bufEnds("do")));
char prevChar = prevNonWhitespace();
if (arrayLevel >= 0 || prevChar == '=' || prevChar == ']') {
// If we're already in an array (lvl >= 0), increment level.
// Otherwise, the presence of a = or ] indicates an array is starting
// and we should start counting (set lvl=0).
arrayLevel++;
buf.append(c);
break; // Nothing fancy.
}
inStatementFlag = false; // eg. class declaration ends
if (s_if_lev.length == curlyLvl) {
s_if_lev = PApplet.expand(s_if_lev);
s_if_flg = PApplet.expand(s_if_flg);
}
s_if_lev[curlyLvl] = if_lev;
s_if_flg[curlyLvl] = if_flg;
if_lev = 0;
if_flg = false;
curlyLvl++;
if (startFlag && 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();
readForNewLine();
writeIndentedLine();
result.append('\n');
tabs++;
startFlag = true;
if (p_flg[level] > 0) {
ind[level] = true;
level++;
s_level[level] = curlyLvl;
}
break;
case '}':
if (arrayLevel >= 0) {
// Even less fancy. Note that }s cannot end array behaviour;
// a semicolon is needed.
if (arrayLevel > 0) arrayLevel--;
if (arrayIndent > arrayLevel) arrayIndent = arrayLevel;
buf.append(c);
break;
}
inStatementFlag = false;
curlyLvl--;
if (curlyLvl < 0) {
curlyLvl = 0;
buf.append(c);
writeIndentedLine();
} else {
if_lev = s_if_lev[curlyLvl] - 1;
if (if_lev < 0) if_lev = 0;
if_levSafe();
if_flg = s_if_flg[curlyLvl];
trimRight(buf);
writeIndentedLine();
tabs--;
trimRight(result);
result.append('\n');
printIndentation();
result.append(c);
if (peek() == ';') result.append(nextChar());
// doWhileFlags contains a TRUE if and only if the
// corresponding { was preceeded (bufEnds) by "do".
// Just checking for while would fail on if(){} while(){}.
if (doWhileFlags.empty() || !doWhileFlags.pop().booleanValue()
|| !new String(chars, pos+1, chars.length-pos-1).trim().startsWith("while")) {
readForNewLine();
writeIndentedLine();
result.append('\n');
startFlag = true;
} else {
// Correct spacing in "} while".
result.append(' ');
advanceToNonSpace(true);
startFlag = false;
}
if (curlyLvl < s_level[level] && level > 0) level--;
if (ind[level]) {
tabs -= p_flg[level];
p_flg[level] = 0;
ind[level] = false;
}
}
break;
case '"':
case '\'':
inStatementFlag = true;
buf.append(c);
char cc = nextChar();
while (!EOF && cc != c) {
buf.append(cc);
if (cc == '\\') {
buf.append(cc = nextChar());
}
if (cc == '\n') {
writeIndentedLine();
startFlag = true;
}
cc = nextChar();
}
buf.append(cc);
if (readForNewLine()) {
// push a newline into the stream
chars[pos--] = '\n';
}
break;
case ';':
if (forFlag) {
// This is like a comma.
trimRight(buf);
buf.append("; ");
// Not non-whitespace: allow \n.
advanceToNonSpace(false);
break;
}
buf.append(c);
inStatementFlag = false;
writeIndentedLine();
if (p_flg[level] > 0 && !ind[level]) {
tabs -= p_flg[level];
p_flg[level] = 0;
}
readForNewLine();
writeIndentedLine();
result.append("\n");
startFlag = true;
// Array behaviour ends at the end of a statement.
arrayLevel = -1;
if (if_lev > 0) {
if (if_flg) {
if_lev--;
if_flg = false;
} else {
if_lev = 0;
}
}
break;
case '\\':
buf.append(c);
buf.append(nextChar());
break;
case '?':
conditionalLevel++;
buf.append(c);
break;
case ':':
// Java 8 :: operator.
if (peek() == ':') {
result.append(c).append(nextChar());
break;
}
// End a?b:c structures.
else if (conditionalLevel > 0) {
conditionalLevel--;
buf.append(c);
break;
}
else if (forFlag) {
trimRight(buf);
buf.append(" : ");
// Not to non-whitespace: allow \n.
advanceToNonSpace(false);
break;
}
buf.append(c);
inStatementFlag = false;
arrayLevel = -1; // Unlikely to be needed; just in case.
//Same format for case, default, and other labels.
tabs--;
writeIndentedLine();
tabs++;
readForNewLine();
writeIndentedLine();
result.append('\n');
startFlag = true;
break;
case '/':
final char next = peek();
if (next == '/') {
// call nextChar to move on.
buf.append(c).append(nextChar());
handleSingleLineComment();
result.append("\n");
} else if (next == '*') {
if (buf.length() > 0) {
writeIndentedLine();
}
buf.append(c).append(nextChar());
handleMultiLineComment();
} else {
buf.append(c);
}
break;
case ')':
parenLevel--;
// If we're further back than the start of a for loop, we've
// left it.
if (forFlag && forParenthLevel > parenLevel) forFlag = false;
if (parenLevel < 0) parenLevel = 0;
buf.append(c);
boolean wasIfEtc = !ifWhileForFlags.empty() && ifWhileForFlags.pop().booleanValue();
if (wasIfEtc) {
inStatementFlag = false;
arrayLevel = -1; // This is important as it allows arrays in if statements.
}
writeIndentedLine();
// Short-circuiting means readForNewLine is only called for if/while/for;
// this is important.
if (wasIfEtc && readForNewLine()) {
chars[pos] = '\n';
pos--; // Make nextChar() return the new '\n'.
if (parenLevel == 0) {
p_flg[level]++;
tabs++;
ind[level] = false;
}
}
break;
case '(':
final boolean isFor = bufEnds("for");
final boolean isIf = bufEnds("if");
if (isFor || isIf || bufEnds("while")) {
if (!Character.isWhitespace(buf.charAt(buf.length() - 1))) {
buf.append(' ');
}
ifWhileForFlags.push(true);
} else {
ifWhileForFlags.push(false);
}
buf.append(c);
parenLevel++;
// isFor says "Is it the start of a for?". If it is, we set forFlag and
// forParenthLevel. If it is not parenth_lvl was incremented above and
// that's it.
if (isFor && !forFlag) {
forParenthLevel = parenLevel;
forFlag = true;
} else if (isIf) {
writeIndentedLine();
s_tabs[curlyLvl][if_lev] = tabs;
sp_flg[curlyLvl][if_lev] = p_flg[level];
s_ind[curlyLvl][if_lev] = ind[level];
if_lev++;
if_levSafe();
if_flg = true;
}
} // end switch
} // end while not EOF
if (buf.length() > 0) writeIndentedLine();
final String formatted = result.toString();
return formatted.equals(cleanText) ? source : formatted;
}
}

View File

@@ -0,0 +1,404 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2008-12 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 java.io.UnsupportedEncodingException;
import processing.app.*;
import processing.app.contrib.ModeContribution;
import processing.core.PApplet;
import processing.mode.java.runner.*;
/**
* Class to handle running Processing from the command line.
*
* @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 forceArg = "--force";
static final String outputArg = "--output=";
static final String exportApplicationArg = "--export";
static final String noJavaArg = "--no-java";
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 = 4;
Sketch sketch;
PrintStream systemOut;
PrintStream systemErr;
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();
// launch command line handler
new Commander(args);
}
public Commander(String[] args) {
String sketchPath = null;
File sketchFolder = null;
String pdePath = null; // path to the .pde file
String outputPath = null;
File outputFolder = null;
boolean outputSet = false; // set an output folder
boolean force = false; // replace that no good output folder
// String preferencesPath = null;
int platform = PApplet.platform; // default to this platform
// int platformBits = 0;
int platformBits = Base.getNativeBits();
int task = HELP;
boolean embedJava = true;
// Turns out the output goes as MacRoman or something else useless.
// http://code.google.com/p/processing/issues/detail?id=1418
try {
systemOut = new PrintStream(System.out, true, "UTF-8");
systemErr = new PrintStream(System.err, true, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
System.exit(1);
}
// File preferencesFile = Base.getSettingsFile("preferences.txt");
// System.out.println("Preferences file at " + preferencesFile.getAbsolutePath());
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(preprocArg)) {
// task = PREPROCESS;
} else if (arg.equals(buildArg)) {
task = BUILD;
} else if (arg.equals(runArg)) {
task = RUN;
} else if (arg.equals(presentArg)) {
task = PRESENT;
// } else if (arg.equals(exportAppletArg)) {
// task = EXPORT_APPLET;
} else if (arg.equals(exportApplicationArg)) {
task = EXPORT;
} else if (arg.equals(noJavaArg)) {
embedJava = false;
} else if (arg.startsWith(platformArg)) {
complainAndQuit("The --platform option has been removed from Processing 2.1.", false);
// String platformStr = arg.substring(platformArg.length());
// platform = Base.getPlatformIndex(platformStr);
// if (platform == -1) {
// complainAndQuit(platformStr + " should instead be " +
// "'windows', 'macosx', or 'linux'.", true);
// }
} else if (arg.startsWith(bitsArg)) {
complainAndQuit("The --bits option has been removed from Processing 2.1.", false);
// String bitsStr = arg.substring(bitsArg.length());
// if (bitsStr.equals("32")) {
// platformBits = 32;
// } else if (bitsStr.equals("64")) {
// platformBits = 64;
// } else {
// complainAndQuit("Bits should be either 32 or 64, not " + bitsStr, true);
// }
} else if (arg.startsWith(sketchArg)) {
sketchPath = arg.substring(sketchArg.length());
sketchFolder = new File(sketchPath);
if (!sketchFolder.exists()) {
complainAndQuit(sketchFolder + " does not exist.", false);
}
File pdeFile = new File(sketchFolder, sketchFolder.getName() + ".pde");
if (!pdeFile.exists()) {
complainAndQuit("Not a valid sketch folder. " + pdeFile + " does not exist.", true);
}
pdePath = pdeFile.getAbsolutePath();
// } else if (arg.startsWith(preferencesArg)) {
// preferencesPath = arg.substring(preferencesArg.length());
} else if (arg.startsWith(outputArg)) {
outputSet = true;
outputPath = arg.substring(outputArg.length());
} else if (arg.equals(forceArg)) {
force = true;
} else {
complainAndQuit("I don't know anything about " + arg + ".", true);
}
}
// if ((outputPath == null) &&
// (task == PREPROCESS || task == BUILD ||
// task == RUN || task == PRESENT)) {
// complainAndQuit("An output path must be specified when using " +
// preprocArg + ", " + buildArg + ", " +
// runArg + ", or " + presentArg + ".");
// }
if (task == HELP) {
printCommandLine(systemOut);
System.exit(0);
}
if (outputSet) {
if (outputPath == null) {
complainAndQuit("An output path must be specified.", true);
}
outputFolder = new File(outputPath);
if (outputFolder.exists()) {
if (force) {
Base.removeDir(outputFolder);
} else {
complainAndQuit("The output folder already exists. " +
"Use --force to remove it.", false);
}
}
if (!outputFolder.mkdirs()) {
complainAndQuit("Could not create the output folder.", false);
}
}
// // run static initialization that grabs all the prefs
// // (also pass in a prefs path if that was specified)
// if (preferencesPath != null) {
// Preferences.init(preferencesPath);
// }
Preferences.init();
Base.locateSketchbookFolder();
if (sketchPath == null) {
complainAndQuit("No sketch path specified.", true);
// } else if (!pdePath.toLowerCase().endsWith(".pde")) {
// complainAndQuit("Sketch path must point to the main .pde file.", false);
} else {
if (outputSet) {
if (outputPath.equals(sketchPath)) {
complainAndQuit("The sketch path and output path cannot be identical.", false);
}
}
boolean success = false;
// JavaMode javaMode =
// new JavaMode(null, Base.getContentFile("modes/java"));
JavaMode javaMode = (JavaMode)
ModeContribution.load(null, Base.getContentFile("modes/java"),
"processing.mode.java.JavaMode").getMode();
try {
sketch = new Sketch(pdePath, javaMode);
if (!outputSet) {
outputFolder = sketch.makeTempFolder();
}
if (task == BUILD || task == RUN || task == PRESENT) {
JavaBuild build = new JavaBuild(sketch);
File srcFolder = new File(outputFolder, "source");
String className = build.build(srcFolder, outputFolder, true);
// String className = build.build(sketchFolder, outputFolder, true);
if (className != null) {
success = true;
if (task == RUN || task == PRESENT) {
Runner runner = new Runner(build, this);
runner.launch(task == PRESENT);
}
} else {
success = false;
}
} else if (task == EXPORT) {
if (outputPath == null) {
javaMode.handleExportApplication(sketch);
} else {
JavaBuild build = new JavaBuild(sketch);
build.build(true);
if (build != null) {
// if (platformBits == 0) {
// platformBits = Base.getNativeBits();
// }
// if (platformBits == 0 &&
// Library.hasMultipleArch(platform, build.getImportedLibraries())) {
// complainAndQuit("This sketch can be exported for 32- or 64-bit, please specify one.", true);
// }
success = build.exportApplication(outputFolder, platform, platformBits, embedJava);
}
}
}
if (!success) { // error already printed
System.exit(1);
}
systemOut.println("Finished.");
System.exit(0);
} catch (SketchException re) {
statusError(re);
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
public void statusNotice(String message) {
systemErr.println(message);
}
public void statusError(String message) {
systemErr.println(message);
}
public void statusError(Exception exception) {
if (exception instanceof SketchException) {
SketchException re = (SketchException) exception;
int codeIndex = re.getCodeIndex();
if (codeIndex != -1) {
// format the runner exception like emacs
//blah.java:2:10:2:13: Syntax Error: This is a big error message
// Emacs doesn't like the double line thing coming from Java
// https://github.com/processing/processing/issues/2158
String filename = sketch.getCode(codeIndex).getFileName();
int line = re.getCodeLine() + 1;
int column = re.getCodeColumn() + 1;
//if (column == -1) column = 0;
// TODO if column not specified, should just select the whole line.
// But what's the correct syntax for that?
systemErr.println(filename + ":" +
line + ":" + column + ":" +
line + ":" + column + ":" + " " + re.getMessage());
} else { // no line number, pass the trace along to the user
exception.printStackTrace();
}
} else {
exception.printStackTrace();
}
}
void complainAndQuit(String lastWords, boolean schoolEmFirst) {
if (schoolEmFirst) {
printCommandLine(systemErr);
}
systemErr.println(lastWords);
System.exit(1);
}
static void printCommandLine(PrintStream out) {
out.println();
out.println("Command line edition for Processing " + Base.getVersionName() + " (Java Mode)");
out.println();
out.println("--help Show this help text. Congratulations.");
out.println();
out.println("--sketch=<name> Specify the sketch folder (required)");
out.println("--output=<name> Specify the output folder (optional and");
out.println(" cannot be the same as the sketch folder.)");
out.println();
out.println("--force The sketch will not build if the output");
out.println(" folder already exists, because the contents");
out.println(" will be replaced. This option erases the");
out.println(" folder first. Use with extreme caution!");
out.println();
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 Export an application.");
out.println("--no-java Do not embed Java. Use at your own risk!");
// 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();
}
@Override
public void startIndeterminate() {
}
@Override
public void stopIndeterminate() {
}
@Override
public void statusHalt() {
}
@Override
public boolean isHalted() {
return false;
}
}

View File

@@ -0,0 +1,753 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-12 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 java.lang.reflect.Method;
import java.util.HashMap;
//import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
//import org.eclipse.jdt.core.compiler.CompilationProgress;
public class Compiler {
static HashMap<String, String> importSuggestions;
static {
importSuggestions = new HashMap<String, String>();
importSuggestions.put("Arrays", "java.util.Arrays");
importSuggestions.put("Collections", "java.util.Collections");
importSuggestions.put("Date", "java.util.Date");
importSuggestions.put("Frame", "java.awt.Frame");
importSuggestions.put("Iterator", "java.util.Iterator");
}
// 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(JavaBuild 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.6",
"-target", "1.6",
"-classpath", build.getClassPath(),
"-nowarn", // we're not currently interested in warnings (works in ecj)
"-d", build.getBinFolder().getAbsolutePath() // output the classes in the buildPath
};
//PApplet.println(baseCommand);
// make list of code files that need to be compiled
// String[] sourceFiles = new String[sketch.getCodeCount()];
// int sourceCount = 0;
// sourceFiles[sourceCount++] =
// new File(buildPath, primaryClassName + ".java").getAbsolutePath();
//
// for (SketchCode code : sketch.getCode()) {
// if (code.isExtension("java")) {
// String path = new File(buildPath, code.getFileName()).getAbsolutePath();
// sourceFiles[sourceCount++] = path;
// }
// }
String[] sourceFiles = Base.listFiles(build.getSrcFolder(), false, ".java");
// String[] command = new String[baseCommand.length + sourceFiles.length];
// System.arraycopy(baseCommand, 0, command, 0, baseCommand.length);
// // append each of the files to the command string
// System.arraycopy(sourceFiles, 0, command, baseCommand.length, sourceCount);
String[] command = PApplet.concat(baseCommand, sourceFiles);
// PApplet.println(command);
try {
// Load errors into a local StringBuilder
final StringBuilder errorBuffer = new StringBuilder();
// Create single method dummy writer class to slurp errors from ecj
Writer internalWriter = new Writer() {
public void write(char[] buf, int off, int len) {
errorBuffer.append(buf, off, len);
}
public void flush() { }
public void close() { }
};
// Wrap as a PrintWriter since that's what compile() wants
PrintWriter writer = new PrintWriter(internalWriter);
//result = com.sun.tools.javac.Main.compile(command, writer);
PrintWriter outWriter = new PrintWriter(System.out);
// Version that's not dynamically loaded
//CompilationProgress progress = null;
//success = BatchCompiler.compile(command, outWriter, writer, progress);
// Version that *is* dynamically loaded. First gets the mode class loader
// so that it can grab the compiler JAR files from it.
ClassLoader loader = build.mode.getClassLoader();
try {
Class<?> batchClass =
Class.forName("org.eclipse.jdt.core.compiler.batch.BatchCompiler", false, loader);
Class<?> progressClass =
Class.forName("org.eclipse.jdt.core.compiler.CompilationProgress", false, loader);
Class<?>[] compileArgs =
new Class<?>[] { String[].class, PrintWriter.class, PrintWriter.class, progressClass };
Method compileMethod = batchClass.getMethod("compile", compileArgs);
success = (Boolean)
compileMethod.invoke(null, new Object[] { command, outWriter, writer, null });
} catch (Exception e) {
e.printStackTrace();
throw new SketchException("Unknown error inside the compiler.");
}
// Close out the stream for good measure
writer.flush();
writer.close();
String errorString = errorBuffer.toString();
// if (errorString.trim().length() != 0) {
// success = false;
// }
BufferedReader reader =
new BufferedReader(new StringReader(errorString));
//System.err.println(errorBuffer.toString());
String line = null;
while ((line = reader.readLine()) != null) {
//System.out.println("got line " + line); // debug
// get first line, which contains file name, line number,
// and at least the first line of the error message
String errorFormat = "([\\w\\d_]+.java):(\\d+):\\s*(.*):\\s*(.*)\\s*";
String[] pieces = PApplet.match(line, errorFormat);
//PApplet.println(pieces);
// if it's something unexpected, die and print the mess to the console
if (pieces == null) {
exception = new SketchException("Cannot parse error text: " + line);
exception.hideStackTrace();
// Send out the rest of the error message to the console.
System.err.println(line);
while ((line = reader.readLine()) != null) {
System.err.println(line);
}
break;
}
// translate the java filename and line number into a un-preprocessed
// location inside a source file or tab in the environment.
String dotJavaFilename = pieces[1];
// Line numbers are 1-indexed from javac
int dotJavaLineIndex = PApplet.parseInt(pieces[2]) - 1;
String errorMessage = pieces[4];
exception = build.placeException(errorMessage,
dotJavaFilename,
dotJavaLineIndex);
/*
int codeIndex = 0; //-1;
int codeLine = -1;
// first check to see if it's a .java file
for (int i = 0; i < sketch.getCodeCount(); i++) {
SketchCode code = sketch.getCode(i);
if (code.isExtension("java")) {
if (dotJavaFilename.equals(code.getFileName())) {
codeIndex = i;
codeLine = dotJavaLineIndex;
}
}
}
// if it's not a .java file, codeIndex will still be 0
if (codeIndex == 0) { // main class, figure out which tab
//for (int i = 1; i < sketch.getCodeCount(); i++) {
for (int i = 0; i < sketch.getCodeCount(); i++) {
SketchCode code = sketch.getCode(i);
if (code.isExtension("pde")) {
if (code.getPreprocOffset() <= dotJavaLineIndex) {
codeIndex = i;
//System.out.println("i'm thinkin file " + i);
codeLine = dotJavaLineIndex - code.getPreprocOffset();
}
}
}
}
//System.out.println("code line now " + codeLine);
exception = new RunnerException(errorMessage, codeIndex, codeLine, -1, false);
*/
if (exception == null) {
exception = new SketchException(errorMessage);
}
// for a test case once message parsing is implemented,
// use new Font(...) since that wasn't getting picked up properly.
/*
if (errorMessage.equals("cannot find symbol")) {
handleCannotFindSymbol(reader, exception);
} else if (errorMessage.indexOf("is already defined") != -1) {
reader.readLine(); // repeats the line of code w/ error
int codeColumn = caretColumn(reader.readLine());
exception = new RunnerException(errorMessage,
codeIndex, codeLine, codeColumn);
} else if (errorMessage.startsWith("package") &&
errorMessage.endsWith("does not exist")) {
// Because imports are stripped out and re-added to the 0th line of
// the preprocessed code, codeLine will always be wrong for imports.
exception = new RunnerException("P" + errorMessage.substring(1) +
". You might be missing a library.");
} else {
exception = new RunnerException(errorMessage);
}
*/
if (errorMessage.startsWith("The import ") &&
errorMessage.endsWith("cannot be resolved")) {
// The import poo cannot be resolved
//import poo.shoe.blah.*;
//String what = errorMessage.substring("The import ".length());
String[] m = PApplet.match(errorMessage, "The import (.*) cannot be resolved");
//what = what.substring(0, what.indexOf(' '));
if (m != null) {
// System.out.println("'" + m[1] + "'");
if (m[1].equals("processing.xml")) {
exception.setMessage("processing.xml no longer exists, this code needs to be updated for 2.0.");
System.err.println("The processing.xml library has been replaced " +
"with a new 'XML' class that's built-in.");
handleCrustyCode();
} else {
exception.setMessage("The package " +
"\u201C" + m[1] + "\u201D" +
" does not exist. " +
"You might be missing a library.");
System.err.println("Libraries must be " +
"installed in a folder named 'libraries' " +
"inside the 'sketchbook' folder.");
}
}
// // Actually create the folder and open it for the user
// File sketchbookLibraries = Base.getSketchbookLibrariesFolder();
// if (!sketchbookLibraries.exists()) {
// if (sketchbookLibraries.mkdirs()) {
// Base.openFolder(sketchbookLibraries);
// }
// }
} else if (errorMessage.endsWith("cannot be resolved to a type")) {
// xxx cannot be resolved to a type
//xxx c;
String what = errorMessage.substring(0, errorMessage.indexOf(' '));
if (what.equals("BFont") ||
what.equals("BGraphics") ||
what.equals("BImage")) {
exception.setMessage(what + " has been replaced with P" + what.substring(1));
handleCrustyCode();
} else {
exception.setMessage("Cannot find a class or type " +
"named \u201C" + what + "\u201D");
String suggestion = importSuggestions.get(what);
if (suggestion != null) {
System.err.println("You may need to add \"import " + suggestion + ";\" to the top of your sketch.");
System.err.println("To make sketches more portable, imports that are not part of the Processing API have been removed from Processing 2.0.");
System.err.println("See the changes page for more information: http://wiki.processing.org/w/Changes");
}
}
} else if (errorMessage.endsWith("cannot be resolved")) {
// xxx cannot be resolved
//println(xxx);
String what = errorMessage.substring(0, errorMessage.indexOf(' '));
if (what.equals("LINE_LOOP") ||
what.equals("LINE_STRIP")) {
exception.setMessage("LINE_LOOP and LINE_STRIP are not available, " +
"please update your code.");
handleCrustyCode();
} else if (what.equals("framerate")) {
exception.setMessage("framerate should be changed to frameRate.");
handleCrustyCode();
} else if (what.equals("screen")) {
exception.setMessage("Change screen.width and screen.height to " +
"displayWidth and displayHeight.");
handleCrustyCode();
} else if (what.equals("screenWidth") ||
what.equals("screenHeight")) {
exception.setMessage("Change screenWidth and screenHeight to " +
"displayWidth and displayHeight.");
handleCrustyCode();
} else {
exception.setMessage("Cannot find anything " +
"named \u201C" + what + "\u201D");
}
} else if (errorMessage.startsWith("Duplicate")) {
// "Duplicate nested type xxx"
// "Duplicate local variable xxx"
} else {
String[] parts = null;
// The method xxx(String) is undefined for the type Temporary_XXXX_XXXX
//xxx("blah");
// The method xxx(String, int) is undefined for the type Temporary_XXXX_XXXX
//xxx("blah", 34);
// The method xxx(String, int) is undefined for the type PApplet
//PApplet.sub("ding");
String undefined =
"The method (\\S+\\(.*\\)) is undefined for the type (.*)";
parts = PApplet.match(errorMessage, undefined);
if (parts != null) {
if (parts[1].equals("framerate(int)")) {
exception.setMessage("framerate() no longer exists, use frameRate() instead.");
handleCrustyCode();
} else if (parts[1].equals("push()")) {
exception.setMessage("push() no longer exists, use pushMatrix() instead.");
handleCrustyCode();
} else if (parts[1].equals("pop()")) {
exception.setMessage("pop() no longer exists, use popMatrix() instead.");
handleCrustyCode();
} else {
String mess = "The function " + parts[1] + " does not exist.";
exception.setMessage(mess);
}
break;
}
}
if (exception != null) {
// The stack trace just shows that this happened inside the compiler,
// which is a red herring. Don't ever show it for compiler stuff.
exception.hideStackTrace();
break;
}
}
} catch (IOException e) {
String bigSigh = "Error while compiling. (" + e.getMessage() + ")";
exception = new SketchException(bigSigh);
e.printStackTrace();
success = false;
}
// In case there was something else.
if (exception != null) throw exception;
return success;
}
/**
* 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() {
System.err.println("This code needs to be updated " +
"for this version of Processing, " +
"please read the Changes page on the Wiki.");
Editor.showChanges();
}
protected int caretColumn(String caretLine) {
return caretLine.indexOf("^");
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2010-11 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.File;
import java.io.IOException;
import processing.app.*;
import processing.mode.java.runner.Runner;
public class JavaMode extends Mode {
// 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, EditorState state) {
return new JavaEditor(base, path, state, this);
}
public JavaMode(Base base, File folder) {
super(base, folder);
}
public String getTitle() {
return "Java (2.0)";
}
// 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 File[] getExampleCategoryFolders() {
return new File[] {
new File(examplesFolder, "Basics"),
new File(examplesFolder, "Topics"),
new File(examplesFolder, "Demos"),
new File(examplesFolder, "Books")
};
}
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"
};
}
public Library getCoreLibrary() {
if (coreLibrary == null) {
File coreFolder = Base.getContentFile("core");
coreLibrary = new Library(coreFolder);
// try {
// coreLibrary = getLibrary("processing.core");
// System.out.println("core found at " + coreLibrary.getLibraryPath());
// } catch (SketchException e) {
// Base.log("Serious problem while locating processing.core", e);
// }
}
return coreLibrary;
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public Runner handleRun(Sketch sketch, RunnerListener listener) throws SketchException {
JavaBuild build = new JavaBuild(sketch);
String appletClassName = build.build(false);
if (appletClassName != null) {
final Runner runtime = new Runner(build, listener);
new Thread(new Runnable() {
public void run() {
runtime.launch(false); // this blocks until finished
}
}).start();
return runtime;
}
return null;
}
public Runner handlePresent(Sketch sketch, RunnerListener listener) throws SketchException {
JavaBuild build = new JavaBuild(sketch);
String appletClassName = build.build(false);
if (appletClassName != null) {
final Runner runtime = new Runner(build, listener);
new Thread(new Runnable() {
public void run() {
runtime.launch(true);
}
}).start();
return runtime;
}
return null;
}
// public void handleStop() {
// if (runtime != null) {
// runtime.close(); // kills the window
// runtime = null; // will this help?
// }
// }
// public boolean handleExportApplet(Sketch sketch) throws SketchException, IOException {
// JavaBuild build = new JavaBuild(sketch);
// return build.exportApplet();
// }
public boolean handleExportApplication(Sketch sketch) throws SketchException, IOException {
JavaBuild build = new JavaBuild(sketch);
return build.exportApplication();
}
}

View File

@@ -0,0 +1,118 @@
/*
Part of the Processing project - http://processing.org
Copyright (c) 2010-11 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;
import processing.app.Language;
public class JavaToolbar extends EditorToolbar {
static protected final int RUN = 0;
static protected final int STOP = 1;
// static protected final int NEW = 2;
// static protected final int OPEN = 3;
// static protected final int SAVE = 4;
// static protected final int EXPORT = 5;
public JavaToolbar(Editor editor, Base base) {
super(editor, base);
}
public void init() {
Image[][] images = loadImages();
// for (int i = 0; i < 6; i++) {
for (int i = 0; i < 2; i++) {
// addButton(getTitle(i, false), getTitle(i, true), images[i], i == NEW);
addButton(getTitle(i, false), getTitle(i, true), images[i], false);
}
}
static public String getTitle(int index, boolean shift) {
switch (index) {
case RUN: return !shift ? Language.text("toolbar.run") : Language.text("toolbar.present");
case STOP: return Language.text("toolbar.stop");
// case NEW: return Language.text("toolbar.new");
// case OPEN: return Language.text("toolbar.open");
// case SAVE: return Language.text("toolbar.save");
// case EXPORT: return Language.text("toolbar.export_application");
}
return null;
}
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.handleSave(false);
break;
case EXPORT:
// if (shift) {
// jeditor.handleExportApplet();
// } else {
jeditor.handleExportApplication();
// }
break;
*/
}
}
}

View File

@@ -0,0 +1,525 @@
/* -*- 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;
/** 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;
}
/**
* 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) {
char c = event.getKeyChar();
int code = event.getKeyCode();
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() & InputEvent.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() & InputEvent.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() & InputEvent.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() & InputEvent.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() & InputEvent.SHIFT_MASK) != 0) {
textarea.setSelectionStart(caretIndex);
textarea.setSelectionEnd(index);
} else {
textarea.setCaretPosition(index);
}
event.consume();
return true;
}
switch (c) {
case 9: // TAB
if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
// if shift is down, the user always expects an outdent
// http://code.google.com/p/processing/issues/detail?id=458
editor.handleOutdent();
} else if (textarea.isSelectionActive()) {
editor.handleIndent();
} else if (Preferences.getBoolean("editor.tabs.expand")) {
int tabSize = Preferences.getInteger("editor.tabs.size");
textarea.setSelectedText(spaces(tabSize));
event.consume();
return true;
} else if (!Preferences.getBoolean("editor.tabs.expand")) {
textarea.setSelectedText("\t");
event.consume();
}
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");
textarea.setCaretPosition(textarea.getCaretPosition() + extraCount + spaceCount);
} else {
String insertion = "\n" + spaces(spaceCount);
textarea.setSelectedText(insertion);
textarea.setCaretPosition(textarea.getCaretPosition() + extraCount);
}
// 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() & InputEvent.CTRL_MASK) != 0) {
// on linux, ctrl-comma (prefs) being passed through to the editor
if (c == KeyEvent.VK_COMMA) {
event.consume();
return true;
}
}
return false;
}
/**
* Return the index for the first character on this line.
*/
protected int calcLineStart(int index, char contents[]) {
// backup from the current caret position to the last newline,
// so that we can figure out how far this line was indented
/*int spaceCount = 0;*/
boolean finished = false;
while ((index != -1) && (!finished)) {
if ((contents[index] == 10) ||
(contents[index] == 13)) {
finished = true;
//index++; // maybe ?
} else {
index--; // new
}
}
// add one because index is either -1 (the start of the document)
// or it's the newline character for the previous line
return index + 1;
}
/**
* Calculate the number of spaces on this line.
*/
protected int calcSpaceCount(int index, char contents[]) {
index = calcLineStart(index, contents);
int spaceCount = 0;
// now walk forward and figure out how many spaces there are
while ((index < contents.length) && (index >= 0) &&
(contents[index++] == ' ')) {
spaceCount++;
}
return spaceCount;
}
/**
* Walk back from 'index' until the brace that seems to be
* the beginning of the current block, and return the number of
* spaces found on that line.
*/
protected int calcBraceIndent(int index, char contents[]) {
// now that we know things are ok to be indented, walk
// backwards to the last { to see how far its line is indented.
// this isn't perfect cuz it'll pick up commented areas,
// but that's not really a big deal and can be fixed when
// this is all given a more complete (proper) solution.
int braceDepth = 1;
boolean finished = false;
while ((index != -1) && (!finished)) {
if (contents[index] == '}') {
// aww crap, this means we're one deeper
// and will have to find one more extra {
braceDepth++;
//if (braceDepth == 0) {
//finished = true;
//}
index--;
} else if (contents[index] == '{') {
braceDepth--;
if (braceDepth == 0) {
finished = true;
}
index--;
} else {
index--;
}
}
// never found a proper brace, be safe and don't do anything
if (!finished) return -1;
// check how many spaces on the line with the matching open brace
//int pairedSpaceCount = calcSpaceCount(index, contents);
//System.out.println(pairedSpaceCount);
return calcSpaceCount(index, contents);
}
// /**
// * Get the character array and blank out the commented areas.
// * This hasn't yet been tested, the plan was to make auto-indent
// * less gullible (it gets fooled by braces that are commented out).
// */
// protected char[] getCleanedContents() {
// char c[] = textarea.getText().toCharArray();
//
// int index = 0;
// while (index < c.length - 1) {
// if ((c[index] == '/') && (c[index+1] == '*')) {
// c[index++] = 0;
// c[index++] = 0;
// while ((index < c.length - 1) &&
// !((c[index] == '*') && (c[index+1] == '/'))) {
// c[index++] = 0;
// }
//
// } else if ((c[index] == '/') && (c[index+1] == '/')) {
// // clear out until the end of the line
// while ((index < c.length) && (c[index] != 10)) {
// c[index++] = 0;
// }
// if (index != c.length) {
// index++; // skip over the newline
// }
// }
// }
// return c;
// }
static String spaces(int count) {
char[] c = new char[count];
Arrays.fill(c, ' ');
return new String(c);
}
}

View File

@@ -0,0 +1,124 @@
/* -*- 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;
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();
Preferences.setInteger("run.present.display", index + 1);
}
});
}
static public JComboBox getSelector() {
int deviceIndex = Preferences.getInteger("run.present.display") - 1;
selector.setSelectedIndex(deviceIndex);
return selector;
}
/*
static public JFrame create() {
int deviceIndex = PrePreferences.getInteger("run.present.display") - 1;
if ((deviceIndex < 0) || (deviceIndex >= devices.length)) {
Base.showWarning("Display doesn't exist",
"Present Mode is set to use display " +
(deviceIndex+1) +
" but that doesn't seem to exist. \n" +
"This preference will be reset to " +
"use the default display.", null);
deviceIndex = defaultIndex;
}
//GraphicsDevice device = devices[
//JFrame frame = new JFrame(devices[deviceIndex]);
PresentFrame frame = new PresentFrame(devices[deviceIndex]);
}
public void show() {
setUndecorated(true);
setResizable(false);
device.setFullScreenWindow(this);
}
public Window getWindow() {
return device.getFullScreenWindow(); // isn't this just me?
}
public void dispose() {
Window window = device.getFullScreenWindow();
if (window != null) {
window.dispose();
}
device.setFullScreenWindow(null);
}
*/
}

View File

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

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/processing-app/test/src/test/processing/mode/java/ParserTests.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<mapAttribute key="org.eclipse.debug.core.preferred_launchers">
<mapEntry key="[run]" value="org.eclipse.jdt.junit.launchconfig"/>
</mapAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="test.processing.mode.java.ParserTests"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="processing-app"/>
</launchConfiguration>

View File

@@ -0,0 +1,773 @@
/* -*- 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.mode.java.preproc.PdeTokenTypes;
import antlr.CommonASTWithHiddenTokens;
import antlr.CommonHiddenStreamToken;
import antlr.collections.AST;
/* Based on original code copyright (c) 2003 Andy Tripp <atripp@comcast.net>.
* shipped under GPL with permission.
*/
/**
* PDEEmitter: A class that can take an ANTLR Java AST and produce
* reasonably formatted Java code from it. To use it, create a
* PDEEmitter object, call setOut() if you want to print to something
* other than System.out, and then call print(), passing the
* AST. Typically, the AST node that you pass would be the root of a
* tree - the ROOT_ID node that represents a Java file.
*
* Modified March 2010 to support Java 5 type arguments and for loops by
* @author Jonathan Feinberg &lt;jdf@pobox.com&gt;
*/
@SuppressWarnings("serial")
public class PdeEmitter implements PdeTokenTypes {
private final PdePreprocessor pdePreprocessor;
private final PrintWriter out;
private final PrintStream debug = System.err;
private final Stack<AST> stack = new Stack<AST>();
private final static int ROOT_ID = 0;
public PdeEmitter(final PdePreprocessor pdePreprocessor, final PrintWriter out) {
this.pdePreprocessor = pdePreprocessor;
this.out = out;
}
/**
* Find a child of the given AST that has the given type
* @returns a child AST of the given type. If it can't find a child of the
* given type, return null.
*/
private AST getChild(final AST ast, final int childType) {
AST child = ast.getFirstChild();
while (child != null) {
if (child.getType() == childType) {
// debug.println("getChild: found:" + name(ast));
return child;
}
child = child.getNextSibling();
}
return null;
}
/**
* Dump the list of hidden tokens linked to after the AST node passed in.
* Most hidden tokens are dumped from this function.
*/
private void dumpHiddenAfter(final AST ast) {
dumpHiddenTokens(((CommonASTWithHiddenTokens) ast).getHiddenAfter());
}
/**
* Dump the list of hidden tokens linked to before the AST node passed in.
* The only time hidden tokens need to be dumped with this function is when
* dealing parts of the tree where automatic tree construction was
* turned off with the ! operator in the grammar file and the nodes were
* manually constructed in such a way that the usual tokens don't have the
* necessary hiddenAfter links.
*/
private void dumpHiddenBefore(final AST ast) {
antlr.CommonHiddenStreamToken child = null, parent = ((CommonASTWithHiddenTokens) ast)
.getHiddenBefore();
// if there aren't any hidden tokens here, quietly return
//
if (parent == null) {
return;
}
// traverse back to the head of the list of tokens before this node
do {
child = parent;
parent = child.getHiddenBefore();
} while (parent != null);
// dump that list
dumpHiddenTokens(child);
}
/**
* Dump the list of hidden tokens linked to from the token passed in.
*/
private void dumpHiddenTokens(CommonHiddenStreamToken t) {
for (; t != null; t = pdePreprocessor.getHiddenAfter(t)) {
out.print(t.getText());
}
}
/**
* Print the children of the given AST
* @param ast The AST to print
* @returns true iff anything was printed
*/
private boolean printChildren(final AST ast) throws SketchException {
boolean ret = false;
AST child = ast.getFirstChild();
while (child != null) {
ret = true;
print(child);
child = child.getNextSibling();
}
return ret;
}
/**
* Tells whether an AST has any children or not.
* @return true iff the AST has at least one child
*/
private boolean hasChildren(final AST ast) {
return (ast.getFirstChild() != null);
}
/**
* Gets the best node in the subtree for printing. This really means
* the next node which could potentially have hiddenBefore data. It's
* usually the first printable leaf, but not always.
*
* @param includeThisNode Should this node be included in the search?
* If false, only descendants are searched.
*
* @return the first printable leaf node in an AST
*/
private AST getBestPrintableNode(final AST ast, final boolean includeThisNode) {
AST child;
if (includeThisNode) {
child = ast;
} else {
child = ast.getFirstChild();
}
if (child != null) {
switch (child.getType()) {
// the following node types are printing nodes that print before
// any children, but then also recurse over children. So they
// may have hiddenBefore chains that need to be printed first. Many
// statements and all unary expression types qualify. Return these
// nodes directly
case CLASS_DEF:
case ENUM_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);
// }
pdePreprocessor.addMethod(methodName.getText());
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 ENUM_DEF:
case INTERFACE_DEF:
print(getChild(ast, MODIFIERS));
if (ast.getType() == CLASS_DEF) {
out.print("class");
} else if (ast.getType() == ENUM_DEF) {
out.print("enum");
} 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 ENUM_CONSTANT_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 ANNOTATIONS:
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();
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,394 @@
/* -*- 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 == null) ||
( (#e.getType() != STRING_LITERAL) ||
( #t.getType() == LITERAL_boolean ||
#t.getType() == LITERAL_double ||
#t.getType() == LITERAL_float ||
#t.getType() == LITERAL_int ||
#t.getType() == LITERAL_long ||
#t.getType() == LITERAL_short )) }?
// create the node
//
{#constructorCast = #(#[CONSTRUCTOR_CAST,"CONSTRUCTOR_CAST"], t, e);}
;
// A list of types that be used as the destination type in a constructor-style
// cast. Ideally, this would include all class types, not just "String".
// Unfortunately, it's not possible to tell whether Foo(5) is supposed to be
// a method call or a constructor cast without have a table of all valid
// types or methods, which requires semantic analysis (eg processing of import
// statements). So we accept the set of built-in types plus "String".
//
consCastTypeSpec[boolean addImagNode]
// : stringTypeSpec[addImagNode]
// | builtInConsCastTypeSpec[addImagNode]
: builtInConsCastTypeSpec[addImagNode]
// trying to remove String() cast [fry]
;
//stringTypeSpec[boolean addImagNode]
// : id:IDENT { #id.getText().equals("String") }?
// {
// if ( addImagNode ) {
// #stringTypeSpec = #(#[TYPE,"TYPE"],
// #stringTypeSpec);
// }
// }
// ;
builtInConsCastTypeSpec[boolean addImagNode]
: builtInConsCastType
{
if ( addImagNode ) {
#builtInConsCastTypeSpec = #(#[TYPE,"TYPE"],
#builtInConsCastTypeSpec);
}
}
;
// Since "color" tokens are lexed as LITERAL_color now, we need to have a rule
// that can generate a method call from an expression that starts with this
// token
//
colorMethodCall
: c:"color" {#c.setType(IDENT);} // this would default to LITERAL_color
lp:LPAREN^ {#lp.setType(METHOD_CALL);}
argList
RPAREN!
;
// copy of the java.g rule with added constructorCast and colorMethodCall
// alternatives
primaryExpression
: (consCastTypeSpec[false] LPAREN) => constructorCast
{ processing.app.Preferences.getBoolean("preproc.enhanced_casting") }?
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
| constant
| "true"
| "false"
| "null"
| newExpression
| "this"
| "super"
| LPAREN! assignmentExpression RPAREN!
| colorMethodCall
// look for int.class and int[].class
| builtInType
( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
DOT^ "class"
;
// the below variable rule hacks are needed so that it's possible for the
// emitter to correctly output variable declarations of the form "float a, b"
// from the AST. This means that our AST has a somewhat different form in
// these rules than the java one does, and this new form may have its own
// semantic issues. But it seems to fix the comma declaration issues.
//
variableDefinitions![AST mods, AST t]
: vd:variableDeclarator[getASTFactory().dupTree(mods),
getASTFactory().dupTree(t)]
{#variableDefinitions = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods,
t, vd);}
;
variableDeclarator[AST mods, AST t]
: ( id:IDENT (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
v:varInitializer (COMMA!)? )+
;
// java.g builds syntax trees with an inconsistent structure. override one of
// the rules there to fix this.
//
explicitConstructorInvocation!
: (typeArguments)?
t:"this" LPAREN a1:argList RPAREN SEMI
{#explicitConstructorInvocation = #(#[CTOR_CALL, "CTOR_CALL"],
#t, #a1);}
| s:"super" LPAREN a2:argList RPAREN SEMI
{#explicitConstructorInvocation = #(#[SUPER_CTOR_CALL,
"SUPER_CTOR_CALL"],
#s, #a2);}
;
// quick-n-dirty hack to the get the advanced class name. we should
// really be getting it from the AST and not forking this rule from
// the java.g copy at all. Since this is a recursive descent parser, we get
// the last class name in the file so that we don't end up with the classname
// of an inner class. If there is more than one "outer" class in a file,
// this heuristic will fail.
//
classDefinition![AST modifiers]
: "class" i:IDENT
// it _might_ have type paramaters
(tp:typeParameters)?
// it _might_ have a superclass...
sc:superClassClause
// it might implement some interfaces...
ic:implementsClause
// now parse the body of the class
cb:classBlock
{#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"],
modifiers,i,tp,sc,ic,cb);
pp.setAdvClassName(i.getText());}
;
possiblyEmptyField
: classField
| s:SEMI {#s.setType(EMPTY_FIELD);}
;
class PdeLexer extends JavaLexer;
options {
importVocab=PdePartial;
exportVocab=Pde;
}
// We need to preserve whitespace and commentary instead of ignoring
// like the supergrammar does. Otherwise Jikes won't be able to give
// us error messages that point to the equivalent PDE code.
// WS, SL_COMMENT, ML_COMMENT are copies of the original productions,
// but with the SKIP assigment removed.
WS : ( ' '
| '\t'
| '\f'
// handle newlines
| ( options {generateAmbigWarnings=false;}
: "\r\n" // Evil DOS
| '\r' // Macintosh
| '\n' // Unix (the right way)
)
{ newline(); }
)+
;
// Single-line comments
SL_COMMENT
: "//"
(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
{newline();}
;
// multiple-line comments
ML_COMMENT
: "/*"
( /* '\r' '\n' can be matched in one alternative or by matching
'\r' in one iteration and '\n' in another. I am trying to
handle any flavor of newline that comes in, but the language
that allows both "\r\n" and "\r" and "\n" to all be valid
newline is ambiguous. Consequently, the resulting grammar
must be ambiguous. I'm shutting this warning off.
*/
options {
generateAmbigWarnings=false;
}
:
{ LA(2)!='/' }? '*'
| '\r' '\n' {newline();}
| '\r' {newline();}
| '\n' {newline();}
| ~('*'|'\n'|'\r')
)*
"*/"
;
WEBCOLOR_LITERAL
: '#'! (HEX_DIGIT)+
;

View File

@@ -0,0 +1,42 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-06 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.runner;
/**
* Interface for dealing with parser/compiler output.
* <P>
* Different instances of MessageStream need to do different things with
* messages. In particular, a stream instance used for parsing output from
* the compiler compiler has to interpret its messages differently than one
* parsing output from the runtime.
* <P>
* Classes which consume messages and do something with them
* should implement this interface.
*/
public interface MessageConsumer {
public void message(String s);
}

View File

@@ -0,0 +1,92 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-06 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.runner;
import java.io.*;
/**
* Slurps up messages from compiler.
*/
public class MessageSiphon implements Runnable {
BufferedReader streamReader;
Thread thread;
MessageConsumer consumer;
public MessageSiphon(InputStream stream, MessageConsumer consumer) {
this.streamReader = new BufferedReader(new InputStreamReader(stream));
this.consumer = consumer;
thread = new Thread(this);
// don't set priority too low, otherwise exceptions won't
// bubble up in time (i.e. compile errors have a weird delay)
//thread.setPriority(Thread.MIN_PRIORITY);
thread.setPriority(Thread.MAX_PRIORITY-1);
//thread.start();
}
public void run() {
try {
// process data until we hit EOF; this will happily block
// (effectively sleeping the thread) until new data comes in.
// when the program is finally done, null will come through.
//
String currentLine;
while ((currentLine = streamReader.readLine()) != null) {
// \n is added again because readLine() strips it out
//EditorConsole.systemOut.println("messaging in");
consumer.message(currentLine + "\n");
//EditorConsole.systemOut.println("messaging out");
}
//EditorConsole.systemOut.println("messaging thread done");
thread = null;
} catch (NullPointerException npe) {
// Fairly common exception during shutdown
thread = null;
} catch (Exception e) {
// On Linux and sometimes on Mac OS X, a "bad file descriptor"
// message comes up when closing an applet that's run externally.
// That message just gets supressed here..
String mess = e.getMessage();
if ((mess != null) &&
(mess.indexOf("Bad file descriptor") != -1)) {
//if (e.getMessage().indexOf("Bad file descriptor") == -1) {
//System.err.println("MessageSiphon err " + e);
//e.printStackTrace();
} else {
e.printStackTrace();
}
thread = null;
}
}
public Thread getThread() {
return thread;
}
}

View File

@@ -0,0 +1,62 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-08 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.runner;
import java.io.*;
/**
* OutputStream to handle stdout/stderr messages.
* <P>
* This is used by Editor, System.err is set to
* new PrintStream(new MessageStream()).
* It's also used by Compiler.
*/
class MessageStream extends OutputStream {
MessageConsumer messageConsumer;
public MessageStream(MessageConsumer messageConsumer) {
this.messageConsumer = messageConsumer;
}
public void close() { }
public void flush() { }
public void write(byte b[]) {
// this never seems to get called
System.out.println("leech1: " + new String(b));
}
public void write(byte b[], int offset, int length) {
//System.out.println("leech2: " + new String(b));
this.messageConsumer.message(new String(b, offset, length));
}
public void write(int b) {
// this never seems to get called
System.out.println("leech3: '" + ((char)b) + "'");
}
}

View File

@@ -0,0 +1,809 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-13 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 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.runner;
import processing.app.*;
import processing.app.exec.StreamRedirectThread;
import processing.core.*;
import processing.mode.java.JavaBuild;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.io.*;
import java.util.*;
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
/**
* 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;
protected SketchException exception;
protected Editor editor;
protected JavaBuild build;
protected Process process;
protected PrintStream sketchErr;
protected PrintStream sketchOut;
public Runner(JavaBuild build, RunnerListener listener) throws SketchException {
this.listener = listener;
// this.sketch = sketch;
this.build = build;
if (listener instanceof Editor) {
this.editor = (Editor) listener;
sketchErr = editor.getConsole().getErr();
sketchOut = editor.getConsole().getOut();
} else {
sketchErr = System.err;
sketchOut = System.out;
}
// Make sure all the imported libraries will actually run with this setup.
int bits = Base.getNativeBits();
for (Library library : build.getImportedLibraries()) {
if (!library.supportsArch(PApplet.platform, bits)) {
sketchErr.println(library.getName() + " does not run in " + bits + "-bit mode.");
int opposite = (bits == 32) ? 64 : 32;
if (Base.isMacOS()) {
//if (library.supportsArch(PConstants.MACOSX, opposite)) { // should always be true
throw new SketchException("To use " + library.getName() + ", " +
"switch to " + opposite + "-bit mode in Preferences.");
//}
} else {
throw new SketchException(library.getName() + " is only compatible " +
"with the " + opposite + "-bit download of Processing.");
//throw new SketchException(library.getName() + " does not run in " + bits + "-bit mode.");
// "To use this library, switch to 32-bit mode in Preferences." (OS X)
// "To use this library, you must use the 32-bit version of Processing."
}
}
}
}
public void launch(boolean presenting) {
if (launchVirtualMachine(presenting)) {
generateTrace();
}
}
public boolean launchVirtualMachine(boolean presenting) {
String[] vmParams = getMachineParams();
String[] sketchParams = getSketchParams(presenting);
int port = 8000 + (int) (Math.random() * 1000);
String portStr = String.valueOf(port);
// Older (Java 1.5 and earlier) version, go figure
// String jdwpArg = "-Xrunjdwp:transport=dt_socket,address=" + portStr + ",server=y,suspend=y";
// String debugArg = "-Xdebug";
// Newer (Java 1.5+) version that uses JVMTI
String jdwpArg = "-agentlib:jdwp=transport=dt_socket,address=" + portStr + ",server=y,suspend=y";
// Everyone works the same under Java 7 (also on OS X)
String[] commandArgs = new String[] { Base.getJavaPath(), jdwpArg };
commandArgs = PApplet.concat(commandArgs, vmParams);
commandArgs = PApplet.concat(commandArgs, sketchParams);
// PApplet.println(commandArgs);
// commandArg.setValue(commandArgs);
launchJava(commandArgs);
AttachingConnector connector = (AttachingConnector)
findConnector("com.sun.jdi.SocketAttach");
//PApplet.println(connector); // gets the defaults
Map arguments = connector.defaultArguments();
// Connector.Argument addressArg =
// (Connector.Argument)arguments.get("address");
// addressArg.setValue(addr);
Connector.Argument portArg =
(Connector.Argument)arguments.get("port");
portArg.setValue(portStr);
// Connector.Argument timeoutArg =
// (Connector.Argument)arguments.get("timeout");
// timeoutArg.setValue("10000");
//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 {
// boolean available = false;
// while (!available) {
while (true) {
try {
vm = connector.attach(arguments);
// vm = connector.attach(arguments);
if (vm != null) {
// generateTrace();
// available = true;
return true;
}
} catch (IOException e) {
// System.out.println("waiting");
// e.printStackTrace();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace(sketchErr);
}
}
}
// } catch (IOException exc) {
// throw new Error("Unable to launch target VM: " + exc);
} catch (IllegalConnectorArgumentsException exc) {
throw new Error("Internal error: " + exc);
}
}
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(boolean presenting) {
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://processing.org/bugs/bugzilla/1446.html
if (build.getFoundMain()) {
params.add(build.getSketchClassName());
} else {
params.add("processing.core.PApplet");
// get the stored device index (starts at 0)
int runDisplay = Preferences.getInteger("run.display");
// 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
GraphicsDevice editorDevice =
editor.getGraphicsConfiguration().getDevice();
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] devices = ge.getScreenDevices();
// Make sure the display set in Preferences actually exists
GraphicsDevice runDevice = editorDevice;
if (runDisplay >= 0 && runDisplay < devices.length) {
runDevice = devices[runDisplay];
} else {
runDevice = editorDevice;
for (int i = 0; i < devices.length; i++) {
if (devices[i] == runDevice) {
runDisplay = i;
break;
// Don't set the pref, might be a temporary thing. Users can
// open/close Preferences to reset the device themselves.
// Preferences.setInteger("run.display", runDisplay);
}
}
}
Point windowLocation = editor.getSketchLocation();
// if (windowLocation != null) {
// // could check to make sure the sketch location is on the device
// // that's specified in Preferences, but that's going to be annoying
// // if you move a sketch to another window, then it keeps jumping
// // back to the specified window.
//// Rectangle screenRect =
//// runDevice.getDefaultConfiguration().getBounds();
// }
if (windowLocation == null) {
if (editorDevice == runDevice) {
// If sketches are to be shown on the same display as the editor,
// provide the editor location so the sketch's main() can place it.
Point editorLocation = editor.getLocation();
params.add(PApplet.ARGS_EDITOR_LOCATION + "=" +
editorLocation.x + "," + editorLocation.y);
} else {
// The sketch's main() will set a location centered on the new
// display. It has to happen in main() because the width/height
// of the sketch are not known here.
// Set a location centered on the other display
// Rectangle screenRect =
// runDevice.getDefaultConfiguration().getBounds();
// int runX =
// params.add(PApplet.ARGS_LOCATION + "=" + runX + "," + runY);
}
} else {
params.add(PApplet.ARGS_LOCATION + "=" +
windowLocation.x + "," + windowLocation.y);
}
params.add(PApplet.ARGS_EXTERNAL);
}
params.add(PApplet.ARGS_DISPLAY + "=" + runDisplay);
if (presenting) {
params.add(PApplet.ARGS_FULL_SCREEN);
// 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());
params.add(PApplet.ARGS_SKETCH_FOLDER + "=" + build.getSketchPath());
// Adding sketch path in the end coz it's likely to
// contain spaces and things go wrong on UNIX systems.
}
// String outgoing[] = new String[params.size()];
// params.toArray(outgoing);
// return outgoing;
return params.toArray(new String[0]);
}
protected void launchJava(final String[] args) {
new Thread(new Runnable() {
public void run() {
// PApplet.println("java starting");
process = PApplet.exec(args);
try {
// PApplet.println("java waiting");
int result = process.waitFor();
// PApplet.println("java done waiting");
if (result != 0) {
String[] errorStrings = PApplet.loadStrings(process.getErrorStream());
String[] inputStrings = PApplet.loadStrings(process.getInputStream());
// PApplet.println("launchJava stderr:");
// PApplet.println(errorStrings);
// PApplet.println("launchJava stdout:");
PApplet.printArray(inputStrings);
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.", null);
} else {
for (String err : errorStrings) {
sketchErr.println(err);
}
sketchErr.println("Using startup command: " + PApplet.join(args, " "));
}
} else {
//exc.printStackTrace();
sketchErr.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
sketchErr.println("Make sure that you haven't set the maximum available memory too high.");
}
sketchErr.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;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 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() {
//vm.setDebugTraceMode(debugTraceMode);
// vm.setDebugTraceMode(VirtualMachine.TRACE_ALL);
// vm.setDebugTraceMode(VirtualMachine.TRACE_NONE); // formerly, seems to have no effect
// Calling this seems to set something internally to make the
// Eclipse JDI wake up. Without it, an ObjectCollectedException
// is thrown on excReq.enable(). No idea why this works,
// but at least exception handling has returned. (Suspect that it may
// block until all or at least some threads are available, meaning
// that the app has launched and we have legit objects to talk to).
vm.allThreads();
// The bug may not have been noticed because the test suite waits for
// a thread to be available, and queries it by calling allThreads().
// See org.eclipse.debug.jdi.tests.AbstractJDITest for the example.
EventRequestManager mgr = vm.eventRequestManager();
// get only the uncaught exceptions
ExceptionRequest excReq = mgr.createExceptionRequest(null, false, true);
// System.out.println(excReq);
// 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.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
// excReq.setSuspendPolicy(EventRequest.SUSPEND_NONE); // another option?
excReq.enable();
Thread eventThread = new Thread() {
public void run() {
try {
boolean connected = true;
while (connected) {
EventQueue eventQueue = vm.eventQueue();
// remove() blocks until event(s) available
EventSet eventSet = eventQueue.remove();
// listener.vmEvent(eventSet);
for (Event event : eventSet) {
// System.out.println("EventThread.handleEvent -> " + event);
if (event instanceof VMStartEvent) {
vm.resume();
} else if (event instanceof ExceptionEvent) {
// for (ThreadReference thread : vm.allThreads()) {
// System.out.println("thread : " + thread);
//// thread.suspend();
// }
exceptionEvent((ExceptionEvent) event);
} else if (event instanceof VMDisconnectEvent) {
connected = false;
}
}
}
// } catch (VMDisconnectedException e) {
// Logger.getLogger(VMEventReader.class.getName()).log(Level.INFO, "VMEventReader quit on VM disconnect");
} catch (Exception e) {
System.err.println("crashed in event thread due to " + e.getMessage());
// Logger.getLogger(VMEventReader.class.getName()).log(Level.SEVERE, "VMEventReader quit", e);
e.printStackTrace();
}
}
};
eventThread.start();
errThread =
new MessageSiphon(process.getErrorStream(), this).getThread();
outThread = new StreamRedirectThread("JVM stdout Reader",
process.getInputStream(),
sketchOut);
errThread.start();
outThread.start();
// Shutdown begins when event thread terminates
try {
if (eventThread != null) eventThread.join(); // is this the problem?
// 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("finished join for errThread and outThread");
// 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");
}
protected Connector findConnector(String connectorName) {
// List connectors =
// com.sun.jdi.Bootstrap.virtualMachineManager().allConnectors();
List connectors =
org.eclipse.jdi.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());
// }
for (Object c : connectors) {
Connector connector = (Connector) c;
// System.out.println(connector.name());
// }
// Iterator iter = connectors.iterator();
// while (iter.hasNext()) {
// Connector connector = (Connector)iter.next();
if (connector.name().equals(connectorName)) {
return connector;
}
}
Base.showError("Compiler Error",
"findConnector() failed to find " +
connectorName + " inside Runner", null);
return null; // Not reachable
}
public void exceptionEvent(ExceptionEvent 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);
//"java.lang.ArrayIndexOutOfBoundsException"
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();
// First just report the exception and its placement
reportException(message, or, event.thread());
// Then try to pretty it up with a better message
handleCommonErrors(exceptionName, message, listener, sketchErr);
if (editor != null) {
editor.deactivateRun();
}
}
/**
* Provide more useful explanations of common error messages, perhaps with
* a short message in the status area, and (if necessary) a longer message
* in the console.
*
* @param exceptionClass Class name causing the error (with full package name)
* @param message The message from the exception
* @param listener The Editor or command line interface that's listening for errors
* @return true if the error was purtified, false otherwise
*/
public static boolean handleCommonErrors(final String exceptionClass,
final String message,
final RunnerListener listener,
final PrintStream err) {
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.");
err.println("An OutOfMemoryError means that your code is either using up too much memory");
err.println("because of a bug (e.g. creating an array that's too large, or unintentionally");
err.println("loading thousands of images), or simply that it's trying to use more memory");
err.println("than what is supported by the current device.");
} else {
listener.statusError("OutOfMemoryError: You may need to increase the memory setting in Preferences.");
err.println("An OutOfMemoryError means that your code is either using up too much memory");
err.println("because of a bug (e.g. creating an array that's too large, or unintentionally");
err.println("loading thousands of images), or that your sketch may need more memory to run.");
err.println("If your sketch uses a lot of memory (for instance if it loads a lot of data files)");
err.println("you can increase the memory available to your sketch using the Preferences window.");
}
} else if (exceptionClass.equals("java.lang.UnsatisfiedLinkError")) {
listener.statusError("A library used by this sketch is not installed properly.");
err.println("A library relies on native code that's not available.");
err.println("Or only works properly when the sketch is run as a " +
((Base.getNativeBits() == 32) ? "64-bit " : "32-bit ") + " application.");
} else if (exceptionClass.equals("java.lang.StackOverflowError")) {
listener.statusError("StackOverflowError: This sketch is attempting too much recursion.");
err.println("A StackOverflowError means that you have a bug that's causing a function");
err.println("to be called recursively (it's calling itself and going in circles),");
err.println("or you're intentionally calling a recursive function too much,");
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.");
err.println("This version of Processing only supports libraries and JAR files compiled for Java 1.6 or earlier.");
err.println("A library used by this sketch was compiled for Java 1.7 or later, ");
err.println("and needs to be recompiled to be compatible with Java 1.6.");
} 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 {
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, ObjectReference or, ThreadReference thread) {
listener.statusError(findException(message, or, 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.
*/
protected SketchException findException(String message, ObjectReference or, 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(sketchErr);
}
// before giving up, try to extract from the throwable object itself
// since sometimes exceptions are re-thrown from a different context
try {
// assume object reference is Throwable, get stack trace
Method method = ((ClassType) or.referenceType()).concreteMethodByName("getStackTrace", "()[Ljava/lang/StackTraceElement;");
ArrayReference result = (ArrayReference) or.invokeMethod(thread, method, new ArrayList<Value>(), ObjectReference.INVOKE_SINGLE_THREADED);
// iterate through stack frames and pull filename and line number for each
for (Value val: result.getValues()) {
ObjectReference ref = (ObjectReference)val;
method = ((ClassType) ref.referenceType()).concreteMethodByName("getFileName", "()Ljava/lang/String;");
StringReference strref = (StringReference) ref.invokeMethod(thread, method, new ArrayList<Value>(), ObjectReference.INVOKE_SINGLE_THREADED);
String filename = strref == null ? "Unknown Source" : strref.value();
method = ((ClassType) ref.referenceType()).concreteMethodByName("getLineNumber", "()I");
IntegerValue intval = (IntegerValue) ref.invokeMethod(thread, method, new ArrayList<Value>(), ObjectReference.INVOKE_SINGLE_THREADED);
int lineNumber = intval.intValue() - 1;
SketchException rex =
build.placeException(message, filename, lineNumber);
if (rex != null) {
return rex;
}
}
// for (Method m : ((ClassType) or.referenceType()).allMethods()) {
// System.out.println(m + " | " + m.signature() + " | " + m.genericSignature());
// }
// Implemented for 2.0b9, writes a stack trace when there's an internal error inside core.
method = ((ClassType) or.referenceType()).concreteMethodByName("printStackTrace", "()V");
// System.err.println("got method " + method);
or.invokeMethod(thread, method, new ArrayList<Value>(), ObjectReference.INVOKE_SINGLE_THREADED);
} catch (Exception e) {
e.printStackTrace(sketchErr);
}
// 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 0087
// 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 message, since it might not fall under
// the same setup as we're expecting
sketchErr.print(s);
//System.err.println("[" + s.length() + "] " + s);
sketchErr.flush();
}
}