Files
processing4/app/PdeSketch.java
2004-01-16 19:12:52 +00:00

523 lines
15 KiB
Java

/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
PdeSketch - stores information about files in the current sketch
Part of the Processing project - http://processing.org
Except where noted, code is written by Ben Fry
Copyright (c) 2001-03 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
*/
/*
Export to: [ Applet (for the web) + ] [ OK ]
[ ] OK to overwrite HTML file <-- only visible if there is one there
remembers previous setting as a pref
> Advanced
Version: [ Java 1.1 + ]
Using a version of Java other than 1.1 will require
your Windows users to install the Java Plug-In,
and your Macintosh users to be running OS X.
Export to: [ Application + ] [ OK ]
> Advanced
Platform: [ Mac OS X + ] <-- defaults to your current platform
Windows
jar file
Exports the application as a double-clickable .app package,
compatible with Mac OS X.
Exports the application as a double-clickable .exe
and a handful of supporting files.
A jar file can be used on any platform that has Java
installed. Simply doube-click the jar to run the application.
It is the simplest (but ugliest) method for exporting.
Version: [ Java 1.3 + ]
Java 1.3 is the recommended setting for exporting
applications. Applications will run on any Windows or
Unix machine that has java installed, and on Mac OS X.
Export to: [ Library + ] [ OK ]
> Advanced
(no settings here?)
*/
public class Sketch {
String name;
File directory;
static final int PDE = 0;
static final int JAVA = 1;
//int current;
Code current;
int codeCount;
Code code[];
int hiddenCount;
Code hidden[];
/*
String names[];
File files[];
int flavor[];
String program[];
boolean modified[];
PdeHistory history[];
*/
//int hiddenCount;
//String hiddenNames[];
//File hiddenFiles[];
/**
* path is location of the main .pde file, because this is also
* simplest to use when opening the file from the finder/explorer.
*/
public Sketch(String path) {
File mainFile = new File(path);
System.out.println("main file is " + mainFile);
directory = new File(path.getParent());
System.out.println("sketch dir is " + directory);
load();
}
/**
* Build the list of files.
*
* Generally his is only done once, rather than
* each time a change is made, because otherwise it gets to be
* a nightmare to keep track of what files went where, because
* not all the data will be saved to disk.
*
* The exception is when an external editor is in use,
* in which case the load happens each time "run" is hit.
*/
public void load() {
// get list of files in the sketch folder
String list[] = directory.list();
for (int i = 0; i < list.length; i++) {
if (list[i].endsWith(".pde")) fileCount++;
else if (list[i].endsWith(".java")) fileCount++;
else if (list[i].endsWith(".pde.x")) hiddenCount++;
else if (list[i].endsWith(".java.x")) hiddenCount++;
}
code = new Code[codeCount];
//names = new String[fileCount];
//files = new File[fileCount];
//flavor = new int[fileCount];
hidden = new Code[hiddenCount];
//hiddenNames = new String[hiddenCount];
//hiddenFiles = new File[hiddenCount];
int fileCounter = 0;
int hiddenCounter = 0;
for (int i = 0; i < list.length; i++) {
if (list[i].endsWith(".pde")) {
code[fileCounter++] =
new Code(list[i].substring(0, list[i].length() - 4),
new File(directory, list[i]),
PDE);
} else if (list[i].endsWith(".java")) {
code[fileCounter++] =
new Code(list[i].substring(0, list[i].length() - 5),
new File(directory, list[i]),
JAVA);
} else if (list[i].endsWith(".pde.x")) {
hidden[hiddenCounter++] =
new Code(list[i].substring(0, list[i].length() - 6),
new File(directory, list[i]),
PDE);
} else if (list[i].endsWith(".java.x")) {
hidden[hiddenCounter++] =
new Code(list[i].substring(0, list[i].length() - 7),
new File(directory, list[i]),
JAVA);
}
}
// remove any entries that didn't load properly
int index = 0;
while (index < codeCount) {
if (code[index].program == null) {
//hide(index); // although will this file be hidable?
for (int i = index+1; i < codeCount; i++) {
code[i-1] = code[i];
}
codeCount--;
} else {
index++;
}
}
}
/**
* Change the file currently being edited.
* 1. store the String for the text of the current file.
* 2. retrieve the String for the text of the new file.
* 3. change the text that's visible in the text area
*/
public void setCurrent(int which) {
// get the text currently being edited
//program[current] = editor.getText();
if (current != null) {
current.program = editor.getText();
}
current = code[which];
// set to the text for this file
// 'true' means to wipe out the undo buffer
// (so they don't undo back to the other file.. whups!)
editor.changeText(current.program, true);
// and i'll personally make a note of the change
//current = which;
}
/**
* This is not Runnable.run(), but a handler for the run() command.
*/
public void run() {
try {
String program = textarea.getText();
current.history.record(program, PdeHistory.RUN);
// if an external editor is being used, need to grab the
// latest version of the code from the file.
if (PdePreferences.getBoolean("editor.external")) {
// history gets screwed by the open..
//String historySaved = history.lastRecorded;
//handleOpen(sketch);
//history.lastRecorded = historySaved;
// nuke previous files and settings, just get things loaded
load();
}
// temporary build folder is inside 'lib'
// this is added to the classpath by default
tempBuildPath = "lib" + File.separator + "build";
File buildDir = new File(tempBuildPath);
if (!buildDir.exists()) {
buildDir.mkdirs();
}
// copy (changed) files from data directory into build folder
//sketch.updateDataDirectory(buildDir);
// copy contents of data dir.. eventually, if the files
// already exist in the target, don't' bother.
//public void updateDataDirectory(File buildDir) {
File dataDir = new File(directory, "data");
if (dataDir.exists()) {
PdeBase.copyDir(dataDir, buildDir);
}
// make up a temporary class name to suggest
// only used if the code is not in ADVANCED mode
String suggestedClassName =
("Temporary_" + String.valueOf((int) (Math.random() * 10000)) +
"_" + String.valueOf((int) (Math.random() * 10000)));
// handle building the code
className = build(program, suggestedClassName, tempBuildPath);
// externalPaths is magically set by build()
// if the compilation worked, run the applet
if (className != null) {
if (externalPaths == null) {
externalPaths =
PdeCompiler.calcClassPath(null) + File.pathSeparator +
tempBuildPath;
} else {
externalPaths =
tempBuildPath + File.pathSeparator +
PdeCompiler.calcClassPath(null) + File.pathSeparator +
externalPaths;
}
// get a useful folder name for the 'code' folder
// so that it can be included in the java.library.path
String codeFolderPath = "";
if (externalCode != null) {
codeFolderPath = externalCode.getCanonicalPath();
}
// create a runtime object
runtime = new PdeRuntime(this, className,
externalRuntime,
codeFolderPath, externalPaths);
// if programType is ADVANCED
// or the code/ folder is not empty -> or just exists (simpler)
// then set boolean for external to true
// include path to build in front, then path for code folder
// when passing the classpath through
// actually, build will already be in there, just prepend code
// use the runtime object to consume the errors now
//messageStream.setMessageConsumer(runtime);
// no need to bother recycling the old guy
PdeMessageStream messageStream = new PdeMessageStream(runtime);
// start the applet
runtime.start(presenting ? presentLocation : appletLocation,
new PrintStream(messageStream));
//leechErr);
// spawn a thread to update PDE GUI state
watcher = new RunButtonWatcher();
} else {
// [dmose] throw an exception here?
// [fry] iirc the exception will have already been thrown
cleanTempFiles(); //tempBuildPath);
}
} catch (PdeException e) {
// if it made it as far as creating a Runtime object,
// call its stop method to unwind its thread
if (runtime != null) runtime.stop();
cleanTempFiles(); //tempBuildPath);
// printing the stack trace may be overkill since it happens
// even on a simple parse error
//e.printStackTrace();
error(e);
} catch (Exception e) { // something more general happened
e.printStackTrace();
// if it made it as far as creating a Runtime object,
// call its stop method to unwind its thread
if (runtime != null) runtime.stop();
cleanTempFiles(); //tempBuildPath);
}
/**
* Have the contents of the currently visible tab been modified.
*/
/*
public boolean isCurrentModified() {
return modified[current];
}
*/
// in an advanced program, the returned classname could be different,
// which is why the className is set based on the return value.
// @return null if compilation failed, className if not
//
protected String build(String program, String suggestedClassName,
String buildPath) throws PdeException, Exception {
// true if this should extend BApplet instead of BAppletGL
//boolean extendsNormal = base.normalItem.getState();
externalRuntime = false;
externalPaths = null;
String externalImports[] = null;
externalCode = new File(sketchDir, "code");
if (externalCode.exists()) {
externalRuntime = true;
externalPaths = PdeCompiler.contentsToClassPath(externalCode);
externalImports = PdeCompiler.magicImports(externalPaths);
} else {
externalCode = null;
}
PdePreprocessor preprocessor = new PdePreprocessor();
try {
className =
preprocessor.write(program, buildPath,
className, imports, false);
} catch (antlr.RecognitionException re) {
// this even returns a column
throw new PdeException(re.getMessage(),
re.getLine() - 1, re.getColumn());
} catch (antlr.TokenStreamRecognitionException tsre) {
// while this seems to store line and column internally,
// there doesn't seem to be a method to grab it..
// so instead it's done using a regexp
PatternMatcher matcher = new Perl5Matcher();
PatternCompiler compiler = new Perl5Compiler();
// line 3:1: unexpected char: 0xA0
String mess = "^line (\\d+):(\\d+):\\s";
Pattern pattern = compiler.compile(mess);
PatternMatcherInput input =
new PatternMatcherInput(tsre.toString());
if (matcher.contains(input, pattern)) {
MatchResult result = matcher.getMatch();
int line = Integer.parseInt(result.group(1).toString());
int column = Integer.parseInt(result.group(2).toString());
throw new PdeException(tsre.getMessage(), line-1, column);
} else {
throw new PdeException(tsre.toString());
}
} catch (PdeException pe) {
throw pe;
} catch (Exception ex) {
System.err.println("Uncaught exception type:" + ex.getClass());
ex.printStackTrace();
throw new PdeException(ex.toString());
}
if (PdePreprocessor.programType == PdePreprocessor.ADVANCED) {
externalRuntime = true; // we in advanced mode now, boy
}
// compile the program
//
PdeCompiler compiler =
new PdeCompiler(buildPath, className, externalCode, this);
// run the compiler, and funnel errors to the leechErr
// which is a wrapped around
// (this will catch and parse errors during compilation
// the messageStream will call message() for 'compiler')
messageStream = new PdeMessageStream(compiler);
boolean success = compiler.compile(new PrintStream(messageStream));
return success ? className : null;
}
/**
* Returns true if this is a read-only sketch. Used for the
* examples directory, or when sketches are loaded from read-only
* volumes or folders without appropraite permissions.
*/
public boolean isReadOnly() {
return false;
}
/**
* Path to the data folder of this sketch.
*/
/*
public File getDataDirectory() {
File dataDir = new File(directory, "data");
return dataDir.exists() ? dataDir : null;
}
*/
/**
* Returns path to the main .pde file for this sketch.
*/
public String getMainFilePath() {
return files[0].getAbsolutePath();
}
class Code {
String name; // pretty name (no extension), not the full file name
File file;
int flavor;
String program;
boolean modified;
History history;
public Code(String name, File file, int flavor) {
this.name = name;
this.file = file;
this.flavor = flavor;
}
public void load() {
program = null;
try {
if (files[i].length() != 0) {
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(files[i])));
StringBuffer buffer = new StringBuffer();
String line = null;
while ((line = reader.readLine()) != null) {
buffer.append(line);
buffer.append('\n');
}
reader.close();
program = buffer.toString();
} else {
// empty code file.. no worries, might be getting filled up
program = "";
}
} catch (IOException e) {
Pdebase.showWarning("Error loading file",
"Error while opening the file\n" +
files[i].getPath(), e);
program = null; // just in case
}
if (program != null) {
history = new History(file);
}
}
}