-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCore.java b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCore.java
index 773f5aca5..11e22ee25 100644
--- a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCore.java
+++ b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCore.java
@@ -1,61 +1,55 @@
+/*******************************************************************************
+ * This program and the accompanying materials are made available under the
+ * terms of the Common Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.opensource.org/licenses/cpl1.0.php
+ *
+ * Contributors:
+ * Chris Lonnen - initial API and implementation
+ *******************************************************************************/
+
package processing.plugin.core;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
-import org.eclipse.core.resources.ICommand;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IProjectDescription;
-import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Plugin;
+import processing.plugin.core.builder.Utilities;
+
/**
* The plug-in runtime class containing the core (UI-free) support for Processing
* sketches.
*
* Like all plug-in runtime classes (subclasses of Plugin), this class
* is automatically instantiated by the platform when the plug-in gets activated.
- * Clients must not attempt to instantiate plug-in runtime classes directly.
*
*
* The single instance of this class can be accessed from any plug-in declaring the
* Processing core plug-in as a prerequisite via
- * ProcessingCore.getProcessingCore(). The Processing model plug-in
- * will be activated automatically if it is not already active.
+ * ProcessingCore.getProcessingCore(). The Processing core plug-in
+ * will be activated and instantiated automatically if it is not already active.
*
*/
public final class ProcessingCore extends Plugin {
- /* The shared instance */
- private static Plugin PROCESSING_CORE_PLUGIN = null;
-
- /**
- * The plug-in identifier of the Processing core support
- * (value "processing.plugin.core").
- */
+ /** The plug-in identifier of the Processing core support */
public static final String PLUGIN_ID = "processing.plugin.core";
-
- /**
- * The identifier for the Processing builder
- * (value "processing.plugin.core.processingbuilder").
- */
- public static final String BUILDER_ID = PLUGIN_ID + ".sketchBuilder";
-
- /**
- * The identifier for the Processing nature
- * (value "processing.plugin.core.processingnature").
- * The presence of this nature indicates that it is a Processing Sketch.
- */
- public static final String NATURE_ID = PLUGIN_ID + ".sketchNature";
-
- /**
- * Problem marker for processing build chain issues
- * (value "processing.plugin.core.p5marker").
- * These are just vanilla problem markers wrapped for easy identification.
- */
- public static final String MARKER_ID = ProcessingCore.PLUGIN_ID + ".p5marker";
-
+
+ /** shared plugin object */
+ private static ProcessingCore plugin;
+
+ /** shared resource bundle */
+ private ResourceBundle resourceBundle;
+
/**
* Creates the Processing core plug-in.
*
@@ -68,112 +62,119 @@ public final class ProcessingCore extends Plugin {
*/
public ProcessingCore(){
super();
- PROCESSING_CORE_PLUGIN = this;
+ plugin = this;
+ try{
+ this.resourceBundle = ResourceBundle.getBundle(PLUGIN_ID + ".CorePluginResources");
+ } catch (MissingResourceException x){
+ this.resourceBundle = null;
+ }
}
-
- /* public void start(BundleContext context) throws Exception { */
- /* public void stop(BundleContext context) throws Exception { */
- /** Returns the single instance of the Processing core plug-in runtime class.
+ // special initialization and shutdown goes here
+ /* public void start(BundleContext context) throws Exception {} */
+ /* public void stop(BundleContext context) throws Exception {} */
+
+
+ /**
+ * Gets a URL to a file or folder in the plug-in's Resources folder.
+ * Returns null if something went wrong.
+ */
+ public URL getPluginResource(String fileName){
+ try{
+ return new URL(this.getBundle().getEntry("/"), "Resources/" + fileName);
+ } catch (MalformedURLException e){
+ return null;
+ }
+ }
+
+ /** Returns a file handle to the plug-in's local cache folder. */
+ public File getBuiltInCacheFolder(){
+ return new File(this.getStateLocation().toString());
+ }
+
+ /** Returns the plug-in's resource bundle */
+ public ResourceBundle getResourceBundle(){
+ return resourceBundle;
+ }
+
+ /**
+ * Get the workspace the platform workspace.
+ */
+ public static IWorkspace getWorkspace() {
+ return ResourcesPlugin.getWorkspace();
+ }
+
+ /**
+ * Returns the single instance of the Processing core plug-in runtime class.
*
- * @return the single instance of the Processing core plug-in runtime class
+ * @return the single instance of the Processing core plug-in runtime class
*/
public static ProcessingCore getProcessingCore(){
- return (ProcessingCore) PROCESSING_CORE_PLUGIN;
+ return plugin;
+ }
+
+ /** Returns true if the resource is a Processing file */
+ public static boolean isPDEFile(IResource resource){
+ if (resource.getType() == IResource.FILE)
+ return isPDEFilename(resource.getName());
+ return false;
+ }
+
+ /** Returns true if the file is a Processing file */
+ public static boolean isPDEFile(IFile resource) {
+ return isPDEFilename(resource.getName());
+ }
+
+ /** Returns true if the file has a Processing extension */
+ public static boolean isPDEFilename(String filename){
+ return filename.endsWith(".pde");
+ }
+
+ /** Returns true if the IFolder is a Processing library root folder */
+ public static boolean isLibrary(IFolder rootFolder){
+ return isLibrary(rootFolder.getFullPath().toFile());
+ }
+
+ /**
+ * Returns true if the folder is a Processing library root folder and
+ * only complains if there is an error.
+ */
+ public static boolean isLibrary(File rootFolder){
+ return isLibrary(rootFolder, false);
}
/**
- * Adds the Sketch builder to a project
- *
- * The preferred way to do this is with the Sketch nature. Even though this is
- * public clients should consider using the Sketch nature instead of directly
- * adding the builder.
- *
- * @param project the project having the builder added to it
+ * Returns true if the folder is a Processing library root folder.
+ * When complain is false only errors are logged and reported. When
+ * complain is true the standard PDE warning for improperly named
+ * libraries will also be reported.
*/
- public static void addBuilderToProject(IProject project){
-
- if (!project.isOpen())
- return;
-
- IProjectDescription description;
- try{
- description = project.getDescription();
- } catch (Exception e){
- ProcessingLog.logError(e);
- return;
- }
-
- // Look for builders already associated with the project
- ICommand[] cmds = description.getBuildSpec();
- for (int j = 0; j < cmds.length; j++){
- if (cmds[j].getBuilderName().equals(BUILDER_ID))
- return;
- }
-
- //Associate builder with project.
- ICommand newCmd = description.newCommand();
- newCmd.setBuilderName(BUILDER_ID);
- List newCmds = new ArrayList();
- newCmds.addAll(Arrays.asList(cmds));
- newCmds.add(newCmd);
- description.setBuildSpec(
- (ICommand[]) newCmds.toArray(new ICommand[newCmds.size()]));
- try{
- project.setDescription(description,null);
- } catch (CoreException e){
- ProcessingLog.logError(e);
- }
- }
-
- /**
- * Remove the Sketch builder from the project
- *
- * If the builder is being managed by the sketch nature, calling this may cause
- * problems with the nature life cycle.
- *
- *
- * @param project the project whose build spec we are to modify
- * @see processing.plugin.core.builder.SketchNature
- */
- public static void removeBuilderFromProject(IProject project){
-
- if (!project.isOpen())
- return;
-
- IProjectDescription description;
- try{
- description = project.getDescription();
- } catch (Exception e){
- ProcessingLog.logError(e);
- return;
- }
-
- // Look for the builder
- int index = -1;
- ICommand[] cmds = description.getBuildSpec();
- for (int j = 0; j < cmds.length; j++){
- if (cmds[j].getBuilderName().equals(BUILDER_ID)){
- index = j;
- break;
+ public static boolean isLibrary(File rootFolder, boolean complain){
+ if (rootFolder != null){
+ String name = rootFolder.getName();
+ try {
+ File libraryJar = new File(rootFolder.getCanonicalPath() +
+ File.separatorChar + "library" + File.separatorChar +
+ name + ".jar");
+ if (libraryJar.exists())
+ if (Utilities.sanitizeName(name).equals(name)){
+ return true;
+ } else {
+ if(complain){
+ String mess =
+ "The library \"" + name + "\" cannot be used.\n" +
+ "Library names must contain only basic letters and numbers.\n" +
+ "(ASCII only and no spaces, and it cannot start with a number)";
+ ProcessingLog.logInfo("Ignoring bad library " + name + "\n" + mess);
+ }
+ }
+ } catch (IOException e) {
+ ProcessingLog.logError("Problem checking librarary " +
+ name + ", could not resolve canonical path.", e);
}
}
- if (index == -1)
- return;
-
- //Remove builder with project.
- List newCmds = new ArrayList();
- newCmds.addAll(Arrays.asList(cmds));
- newCmds.remove(index);
- description.setBuildSpec(
- (ICommand[]) newCmds.toArray(new ICommand[newCmds.size()]));
- try{
- project.setDescription(description,null);
- } catch (CoreException e){
- ProcessingLog.logError(e);
- }
+ return false;
}
-
-
-
+
+
}
diff --git a/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchBuilder.java b/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchBuilder.java
index c781291eb..fa94429b8 100644
--- a/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchBuilder.java
+++ b/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchBuilder.java
@@ -1,14 +1,9 @@
package processing.plugin.core.builder;
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
@@ -24,18 +19,26 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.jdt.launching.JavaRuntime;
import org.osgi.framework.Bundle;
import processing.app.Preferences;
import processing.app.Sketch;
+import processing.app.debug.RunnerException;
import processing.app.preproc.PdePreprocessor;
import processing.app.preproc.PreprocessResult;
import processing.core.PApplet;
import processing.plugin.core.ProcessingCore;
+import processing.plugin.core.ProcessingCorePreferences;
import processing.plugin.core.ProcessingLog;
/**
@@ -61,6 +64,24 @@ import processing.plugin.core.ProcessingLog;
*/
public class SketchBuilder extends IncrementalProjectBuilder{
+ /**
+ * The identifier for the Processing builder
+ * (value "processing.plugin.core.processingbuilder").
+ */
+ public static final String BUILDER_ID = ProcessingCore.PLUGIN_ID + ".sketchBuilder";
+
+ /**
+ * Problem marker for processing preprocessor issues
+ * (value "processing.plugin.core.preprocError").
+ */
+ public static final String PREPROCMARKER = ProcessingCore.PLUGIN_ID + ".preprocError";
+
+ /**
+ * Problem marker for processing compile issues
+ * value "processing.plugin.core.compileError"
+ */
+ public static final String COMPILEMARKER = ProcessingCore.PLUGIN_ID + ".compileError";
+
/** All of these need to be set for the Processing.app classes. */
static{
Preferences.set("editor.tabs.size", "4");
@@ -81,8 +102,10 @@ public class SketchBuilder extends IncrementalProjectBuilder{
///** For testing */
//public boolean DEBUG = true;
- /** Sketch folder that contains the sketch */
- private IProject sketch;
+ // TODO the builder is maintaining too much state. move this all to a model
+ // Right now it is building an ad-hoc model on the fly using the implied
+ // structure of a Sketch project. Minimally a model should manage these state
+ // objects, manage markers, and be controlled by the SketchProject object.
/** Data folder, located in the sketch folder, may not exist */
private IFolder dataFolder;
@@ -116,27 +139,64 @@ public class SketchBuilder extends IncrementalProjectBuilder{
/** Supplied as a build argument, usually empty */
private String packageName = "";
-
- /**
- * Build the sketch.
- *
- * 1. Prepare the Sketch.
- * 2. Preprocess the files to Java
- * 3. Shuffle the generated Java and supporting libs into the proper folders
- *
- * Unlike the PDE build chain, the Sketch Builder stops short of compiling the generated files.
- * In a properly configured project the Java builder will run after the Sketch builder finishes
- * and automagically handle the heavy compiling work.
- *
- *
- * @param kind the build type
- * @param args build arguments
- * @param monitor let the user know things are happening
- * @throws CoreException for I/O problems, the Eclipse UI should handle these automatically
- */
- protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
- this.sketch = getProject();
+ /** Clean any leftover state from previous builds. */
+ protected void clean(SketchProject sketch, IProgressMonitor monitor) throws CoreException{
+ deleteP5ProblemMarkers(sketch);
+ if (buildFolder != null && buildFolder.exists()){
+ for( IResource r : buildFolder.members()){
+ r.delete(IResource.FORCE, monitor);
+ }
+ }
+ // any other cleaning stuff goes here
+ // Eventually, a model should control the markers, and once a model is
+ // written it should be controlled by the SketchProject. Cleaning the model
+ // should be in a method called beCleaned() in the SketchProject.
+ // This method will then look something like:
+ // SketchProject sketch = SketchProject.forProject(this.getProject());
+ // sketch.beCleaned();
+ }
+
+ /**
+ * Build the sketch project.
+ *
+ * This usually means grabbing all the Processing files and compiling them
+ * to Java source files and moving them into a designated folder where the
+ * JDT will grab them and build them.
+ *
+ *
+ * For now all builds are full builds because the preprocessor does not
+ * handle incremental builds.
+ *
+ *
+ */
+ protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException{
+ SketchProject sketch = SketchProject.forProject(this.getProject());
+ // Eventually we may distinguish between build types.
+ // switch (kind){
+ // case FULL_BUILD:
+ // return this.fullBuild(sketch, monitor);
+ // case AUTO_BUILD:
+ // return this.autoBuild(sketch, monitor);
+ // case INCREMENTAL_BUILD:
+ // return this.incrementalBuild(sketch, monitor);
+ // default:
+ // return null
+ // }
+ return this.fullBuild(sketch, monitor);
+ }
+
+ /**
+ * Full build from scratch.
+ *
+ * Try to clean out and old markers from derived files. They may not be present,
+ * but if they are wipe 'em out and get along with the business of building.
+ * This can be a long running process, so we use a monitor.
+ */
+ protected IProject[] fullBuild( SketchProject sketchProject, IProgressMonitor monitor) throws CoreException {
+ this.clean(sketchProject, monitor);
+ IProject sketch = sketchProject.getProject();
+
if ( sketch == null || !sketch.isAccessible() ){
System.out.println("Sketch is null!");
return null;
@@ -152,13 +212,6 @@ public class SketchBuilder extends IncrementalProjectBuilder{
if(!sketch.isOpen()) { return null; } // has to be open to access it
if(checkCancel(monitor)) { return null; }
- deleteP5ProblemMarkers(sketch);
-
- // save time on autobuilds, but respect clean and full builds
- if (kind == IncrementalProjectBuilder.CLEAN_BUILD || kind == IncrementalProjectBuilder.FULL_BUILD){
- removeDerived(monitor); // delete all the old state
- }
-
// 1 . PREPARE
if (!buildFolder.exists())
@@ -195,12 +248,12 @@ public class SketchBuilder extends IncrementalProjectBuilder{
for( IResource file : sketch.members()){
if(file.getFileExtension() != null && file.getFileExtension().equalsIgnoreCase("pde")){
- file.setSessionProperty(new QualifiedName(ProcessingCore.BUILDER_ID, "preproc start"), bigCount);
+ file.setSessionProperty(new QualifiedName(BUILDER_ID, "preproc start"), bigCount);
String content = Utilities.readFile((IFile) file);
bigCode.append(content);
bigCode.append("\n");
bigCount += Utilities.getLineCount(content);
- file.setSessionProperty(new QualifiedName(ProcessingCore.BUILDER_ID, "preproc end"), bigCount);
+ file.setSessionProperty(new QualifiedName(BUILDER_ID, "preproc end"), bigCount);
}
}
@@ -212,7 +265,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{
IFile outputFile = buildFolder.getFile(sketch.getName() + ".java");
StringWriter stream = new StringWriter();
result = preproc.write(stream, bigCode.toString(), codeFolderPackages);
-
+
// Eclipse idiom for generating the java file and marking it as a generated file
ByteArrayInputStream inStream = new ByteArrayInputStream(stream.toString().getBytes());
try{
@@ -233,8 +286,8 @@ public class SketchBuilder extends IncrementalProjectBuilder{
for( IResource file : sketch.members()){
if(file.getFileExtension() != null && file.getFileExtension().equalsIgnoreCase("pde")){
- int low = (Integer) file.getSessionProperty(new QualifiedName(ProcessingCore.BUILDER_ID, "preproc start"));
- int high = (Integer) file.getSessionProperty(new QualifiedName(ProcessingCore.BUILDER_ID, "preproc end"));
+ int low = (Integer) file.getSessionProperty(new QualifiedName(BUILDER_ID, "preproc start"));
+ int high = (Integer) file.getSessionProperty(new QualifiedName(BUILDER_ID, "preproc end"));
if( low <= errorLine && high > errorLine){
errorFile = file;
errorLine -= low;
@@ -283,8 +336,8 @@ public class SketchBuilder extends IncrementalProjectBuilder{
for( IResource file : sketch.members()){
if(file.getFileExtension() != null && file.getFileExtension().equalsIgnoreCase("pde")){
- int low = (Integer) file.getSessionProperty(new QualifiedName(ProcessingCore.BUILDER_ID, "preproc start"));
- int high = (Integer) file.getSessionProperty(new QualifiedName(ProcessingCore.BUILDER_ID, "preproc end"));
+ int low = (Integer) file.getSessionProperty(new QualifiedName(BUILDER_ID, "preproc start"));
+ int high = (Integer) file.getSessionProperty(new QualifiedName(BUILDER_ID, "preproc end"));
if( low <= errorLine && high > errorLine){
errorFile = file;
errorLine -= low;
@@ -302,8 +355,55 @@ public class SketchBuilder extends IncrementalProjectBuilder{
errorLine = -1;
}
- reportProblem(tsre.getMessage(), errorFile, errorLine, true);
+ reportProblem(tsre.getMessage(), errorFile, errorLine, true);
return null;
+ } catch (RunnerException re){
+ /*
+ * This error is not addressed in the PDE. I've only seen it correspond to
+ * an unclosed, double quote mark ("). The runner reports 1 line behind where
+ * it occurs, so we add 1 to its line.
+ */
+ IResource errorFile = null; // if this remains null, the error is reported back on the sketch itself with no line
+ int errorLine = re.getCodeLine() + 1;
+
+ for( IResource file : sketch.members()){
+ if(file.getFileExtension() != null && file.getFileExtension().equalsIgnoreCase("pde")){
+ int low = (Integer) file.getSessionProperty(new QualifiedName(BUILDER_ID, "preproc start"));
+ int high = (Integer) file.getSessionProperty(new QualifiedName(BUILDER_ID, "preproc end"));
+ if( low <= errorLine && high > errorLine){
+ errorFile = file;
+ errorLine -= low;
+ break;
+ }
+ }
+ }
+
+ // mark the whole project if no file will step forward.
+ if (errorFile == null){
+ errorFile = sketch;
+ errorLine = -1;
+ }
+
+ //DEBUG
+ //System.out.println("error line - error file - offset");
+ //System.out.println(errorLine + " - " + errorFile + " - " + getPreprocOffset((IFile) folderContents[errorFile]));
+
+ String msg = re.getMessage();
+
+ //TODO better remapping of errors, matching errors often get put after the document end. see highlightLine() inside editor.
+ if (msg.equals("expecting RCURLY, found 'null'"))
+ msg = "Found one too many { characters without a } to match it.";
+ if (msg.indexOf("expecting RBRACK") != -1)
+ msg = "Syntax error, maybe a missing right ] character?";
+ if (msg.indexOf("expecting SEMI") != -1)
+ msg = "Syntax error, maybe a missing semicolon?";
+ if (msg.indexOf("expecting RPAREN") != -1)
+ msg = "Syntax error, maybe a missing right parenthesis?";
+ if (msg.indexOf("preproc.web_colors") != -1)
+ msg = "A web color (such as #ffcc00) must be six digits.";
+
+ reportProblem(msg, errorFile, errorLine, true);
+ return null; // exit the build
} catch (CoreException e){
ProcessingLog.logError(e); // logging the error is a better
return null;
@@ -318,11 +418,13 @@ public class SketchBuilder extends IncrementalProjectBuilder{
// Get the imports from the code that was preproc'd
importedLibraries = new ArrayList();
- coreLibs = getCoreLibsFolder();
- sketchBookLibs = getSketchBookLibsFolder();
+ coreLibs = getCoreLibsFolder().getAbsoluteFile();
+ sketchBookLibs = getSketchBookLibsFolder(sketch).getAbsoluteFile();
// Clean the library table and rebuild it
importToLibraryTable = new HashMap();
+
+ // addLibraries internally checks for null folders
try{
addLibraries(coreLibs);
addLibraries(sketchBookLibs);
@@ -364,12 +466,12 @@ public class SketchBuilder extends IncrementalProjectBuilder{
if(pkg == null){
pkg = new String[] { packageName };
// add the package name to the source
- program = "package " + packageName + ";" + program;
+ program = "package " + packageName + ";" + program;
}
IFolder packageFolder = buildFolder.getFolder(pkg[0].replace('.', '/'));
if (!packageFolder.exists())
packageFolder.create(IResource.NONE, true, null);
-
+
IFile modFile = packageFolder.getFile(file.getName() + ".java");
ByteArrayInputStream inStream = new ByteArrayInputStream(program.getBytes());
@@ -389,37 +491,41 @@ public class SketchBuilder extends IncrementalProjectBuilder{
} else if (file.getFileExtension() != null && file.getFileExtension().equalsIgnoreCase("pde")){
// The compiler will need this to have a proper offset
// not sure why every file gets the same offset, but ok I'll go with it... [lonnen] aug 20 2011
- file.setSessionProperty(new QualifiedName(ProcessingCore.BUILDER_ID, "preproc start"), result.headerOffset);
+ file.setSessionProperty(new QualifiedName(BUILDER_ID, "preproc start"), result.headerOffset);
}
}
- boolean foundMain = preproc.getFoundMain(); // is this still necessary?
-
monitor.worked(10);
if(checkCancel(monitor)) { return null; }
//COMPILE
+
+ // setup the VM
+ IPath containerPath = new Path(JavaRuntime.JRE_CONTAINER);
+ IVMInstall vm = JavaRuntime.getDefaultVMInstall();
+ IPath vmPath = containerPath.append(vm.getVMInstallType().getId()).append(vm.getName());
+
+ System.out.println("classPath entries:");
+ for( String s : classPath.split(File.pathSeparator)){
+ if (!s.isEmpty())
+ System.out.println(s);
+ }
+
+ System.out.println("IClasspathEntry[] items:");
+ // Build the classpath entries
+ IClasspathEntry[] newClasspath = {
+ JavaCore.newSourceEntry(buildFolder.getFullPath()),
+ //JavaCore.newLibraryEntry(),
+ JavaCore.newContainerEntry(vmPath)
+ };
+ for (IClasspathEntry s : newClasspath){
+ System.out.println(s.toString());
+ }
return null;
}
- /**
- * Removes all the derived files and markers generated by previous builds.
- * If there are issues expect a CoreException with details. Deletion can be
- * a long running operation, so give it access to the monitor.
- *
- * @param monitor
- * @throws CoreException
- */
- private void removeDerived(IProgressMonitor monitor) throws CoreException {
- if (buildFolder.exists()){
- for( IResource r : buildFolder.members()){
- r.delete(IResource.FORCE, monitor);
- }
- }
- }
-
/**
* Try to delete all of the existing P5 problem markers
*
@@ -431,7 +537,16 @@ public class SketchBuilder extends IncrementalProjectBuilder{
*/
protected static void deleteP5ProblemMarkers(IResource resource) throws CoreException{
if(resource != null && resource.exists())
- resource.deleteMarkers(ProcessingCore.MARKER_ID, true, IResource.DEPTH_INFINITE);
+ resource.deleteMarkers(SketchBuilder.PREPROCMARKER, true, IResource.DEPTH_INFINITE);
+ resource.deleteMarkers(SketchBuilder.COMPILEMARKER, true, IResource.DEPTH_INFINITE);
+ }
+
+ protected static void deleteP5ProblemMarkers(SketchProject sketch)throws CoreException{
+ if(sketch != null){
+ IProject proj = sketch.getProject();
+ if (proj.exists())
+ deleteP5ProblemMarkers(proj);
+ }
}
/**
@@ -447,7 +562,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{
*/
private void reportProblem( String msg, IResource file, int lineNumber, boolean isError){
try{
- IMarker marker = file.createMarker(ProcessingCore.MARKER_ID);
+ IMarker marker = file.createMarker(SketchBuilder.PREPROCMARKER);
marker.setAttribute(IMarker.MESSAGE, msg);
marker.setAttribute(IMarker.SEVERITY, isError ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING);
if( lineNumber != -1)
@@ -480,16 +595,16 @@ public class SketchBuilder extends IncrementalProjectBuilder{
/**
* Finds any Processing Libraries in the folder and loads them into the HashMap
* importToLibraryTable so they can be imported later. Based on the Base.addLibraries,
- * but adapted to avoid GUI elements
+ * but adapted to avoid GUI elements
*
* @param folder the folder containing libraries
* @return true if libraries were imported, false otherwise
*/
- public boolean addLibraries(File folder) throws IOException{
+ public boolean addLibraries(File folder) throws IOException{
if (folder == null) return false;
if (!folder.isDirectory()) return false;
- String list[] = folder.list(new FilenameFilter() {
+ File list[] = folder.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
// skip .DS_Store files, .svn folders, etc
if (name.charAt(0) == '.') return false;
@@ -501,86 +616,85 @@ public class SketchBuilder extends IncrementalProjectBuilder{
// if a bad folder or something like that, this might come back null
if (list == null) return false;
- // alphabetize list, since it's not always alpha order
- Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
-
boolean ifound = false;
- for (String potentialName : list) {
- File subfolder = new File(folder, potentialName);
- File libraryFolder = new File(subfolder, "library");
- File libraryJar = new File(libraryFolder, potentialName + ".jar");
- // look for the .jar inside //library/.jar
- if (libraryJar.exists()) {
- String sanityCheck = Sketch.sanitizeName(potentialName); // TODO remove this processing.app class dependency
- if (!sanityCheck.equals(potentialName)) {
- String mess =
- "The library \"" + potentialName + "\" cannot be used.\n" +
- "Library names must contain only basic letters and numbers.\n" +
- "(ASCII only and no spaces, and it cannot start with a number)";
- ProcessingLog.logInfo("Ignoring bad library name \n" + mess);
- continue;
- }
-
- // get the path for all .jar files in this code folder
+ for (File potentialFile : list){
+ if(ProcessingCore.isLibrary(potentialFile, true)){
+ File libraryFolder = new File(potentialFile, "library");
+ // get the path of all .jar files in this code folder
String libraryClassPath = Utilities.contentsToClassPath(libraryFolder);
-
- // the librariesClassPath is not used in the main build process
- //librariesClassPath += File.pathSeparatorChar + libraryClassPath;
-
- // need to associate each import with a library folder
- String packages[] = Utilities.packageListFromClassPath(libraryClassPath);
- for (String pkg : packages) {
+ // associate each import with a library folder
+ String packages[] = Utilities.packageListFromClassPath(libraryClassPath);
+ for (String pkg:packages){
importToLibraryTable.put(pkg, libraryFolder);
}
-
ifound = true;
-
- } else {
- // not a library, but is still a folder, so recurse
- boolean found = addLibraries(subfolder);
- if (found) ifound = true; // it was found in the subfolder
+ } else {
+ if (addLibraries(potentialFile)) // recurse!
+ ifound = true;
}
}
+
return ifound;
}
/**
* Finds the folder containing the Processing core libraries, which are bundled with the
- * plugin. This folder doesn't exist in the workspace, so we return it as a plain File.
- * If a CoreException is thrown it will be logged and this will return null.
+ * plugin. This folder doesn't exist in the workspace, so we return it as a File, not IFile.
+ * If something goes wrong, logs an error and returns null.
*
- * @return File containing the core libraries folder or null if it cannot be found
+ * @return File containing the core libraries folder or null
*/
public File getCoreLibsFolder() {
- Bundle bundle = ProcessingCore.getProcessingCore().getBundle();
- URL fileLocation;
+ URL fileLocation = ProcessingCore.getProcessingCore().getPluginResource("libraries");
try {
- fileLocation = FileLocator.toFileURL(bundle.getEntry("/Resources" + File.pathSeparatorChar + "libraries"));
- File folder = new File(fileLocation.getPath());
- folder.isDirectory(); // throw an error if the folder does not exist
- return folder;
+ File folder = new File(FileLocator.toFileURL(fileLocation).getPath());
+ if (folder.exists())
+ return folder;
} catch (Exception e) {
ProcessingLog.logError(e);
- return null;
}
+ return null;
}
/**
- * Find the folder containing the Processing sketchbook libraries, which should be contained
- * in the same directory as the sketch itself. This only returns a handle to the folder. If
- * the folder does not exist it will be logged and returned as null
+ * Find the folder containing the users libraries, which should be in the sketchbook.
+ * Looks in the user's preferences first and if that is null it checks for the appropriate
+ * folder relative to the sketch location.
*
- * @return File containing the Sketch book library folder, or null if it doesn't exist
+ * @return File containing the Sketch book library folder, or null if it can't be located
*/
- public File getSketchBookLibsFolder() {
+ public File getSketchBookLibsFolder(IProject proj) {
+ IPath sketchbook = ProcessingCorePreferences.current().getSketchbookPath();
+ if (sketchbook == null)
+ sketchbook = findSketchBookLibsFolder(proj);
+ if (sketchbook == null)
+ return null;
+ return new File(sketchbook.toOSString());
+ }
+
+ /**
+ * Tries to locate the sketchbook library folder relative to the project path
+ * based on the default sketch / sketchbook setup. If such a folder exists, loop
+ * through its contents until a valid library is found and then return the path
+ * to the sketchbook. If no valid libraries are found (empty folder, improper
+ * sketchbook setup), or if no valid folder is found, return null.
+ *
+ * @return IPath containing the location of the new library folder, or null
+ */
+ public IPath findSketchBookLibsFolder(IProject proj) {
try{
- File folder = sketch.getLocation().removeLastSegments(1).append("libraries").toFile();
- folder.isDirectory(); // throw an error if the folder does not exist
- return folder;
+ IPath guess = proj.getLocation().removeLastSegments(1).append("libraries");
+ File folder = new File(guess.toOSString());
+ if(folder.isDirectory())
+ for( File file : folder.listFiles()){
+ if(file.isDirectory())
+ if (ProcessingCore.isLibrary(file))
+ return guess;
+ }
} catch (Exception e){
ProcessingLog.logError(e);
- return null;
}
+ return null;
}
}
diff --git a/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchNature.java b/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchNature.java
deleted file mode 100644
index 0cedc0f9e..000000000
--- a/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchNature.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package processing.plugin.core.builder;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IProjectDescription;
-import org.eclipse.core.resources.IProjectNature;
-import org.eclipse.core.resources.IncrementalProjectBuilder;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.Job;
-
-import processing.plugin.core.ProcessingCore;
-import processing.plugin.core.ProcessingLog;
-
-public class SketchNature implements IProjectNature {
-
- /** The project this SketchNature is modifying */
- protected IProject project;
-
- /** Access method for this natures project */
- public IProject getProject() {
- return project;
- }
-
- /** Set the project this nature is managing */
- public void setProject(IProject project) {
- this.project = project;
- }
-
- /** Associate the sketch builder with this nature's project */
- public void configure() throws CoreException {
- ProcessingCore.addBuilderToProject(project);
- new Job("Build Sketch"){
- protected IStatus run(IProgressMonitor monitor){
- try{
- project.build(
- IncrementalProjectBuilder.FULL_BUILD,
- ProcessingCore.BUILDER_ID,
- null,
- monitor);
- } catch (CoreException e){
- ProcessingLog.logError(e);
- }
- return Status.OK_STATUS;
- }
- }.schedule();
- }
-
- /**
- * Dissociate the processing sketch builder from this nature's
- * project and remove the markers it generated.
- */
- public void deconfigure() throws CoreException {
- ProcessingCore.removeBuilderFromProject(project);
- SketchBuilder.deleteP5ProblemMarkers(project);
- }
-
- /**
- * Adds the sketch nature to the project if it is accessible and
- * does not already have the sketch nature.
- *
- * @param project
- */
- public static void addNature(IProject project){
- // Cannot modify closed projects.
- if (!project.isOpen())
- return;
-
- try {
- // If it has the nature already, we're done
- if (project.hasNature(ProcessingCore.NATURE_ID))
- return;
-
- IProjectDescription description = project.getDescription();;
-
- List newIds = new ArrayList();
- newIds.addAll(Arrays.asList(description.getNatureIds()));
- newIds.add(ProcessingCore.NATURE_ID);
- description.setNatureIds(newIds.toArray(new String[newIds.size()]));
-
- project.setDescription(description, null);
-
- } catch(CoreException e){
- ProcessingLog.logError(e);
- }
- }
-
- /**
- * Tests a project to see if it has the Processing nature
- *
- * IProject project to test
- * returns true if the project has the processing nature
- */
- public static boolean hasNature(IProject project){
- try{
- return project.hasNature(ProcessingCore.NATURE_ID);
- } catch (CoreException e){
- // project doesn't exist or is not open
- return false;
- }
- }
-
- /**
- * Removes the nature from the project if it has it
- *
- * @param project
- */
- public static void removeNature(IProject project){
- // Cannot modify closed projects
- if (!project.isOpen())
- return;
- try{
- // doesn't have the nature, we're done
- if (!project.hasNature(ProcessingCore.NATURE_ID))
- return;
-
- IProjectDescription description = project.getDescription();
-
- List newIds = new ArrayList();
- newIds.addAll(Arrays.asList(description.getNatureIds()));
- newIds.remove(newIds.indexOf(ProcessingCore.NATURE_ID));
- description.setNatureIds(newIds.toArray(new String[newIds.size()]));
-
- project.setDescription(description,null);
-
- } catch (CoreException e){
- ProcessingLog.logError(e);
- return;
- }
- }
-}
diff --git a/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchProject.java b/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchProject.java
new file mode 100644
index 000000000..ea4ccc998
--- /dev/null
+++ b/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchProject.java
@@ -0,0 +1,241 @@
+package processing.plugin.core.builder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.jdt.launching.JavaRuntime;
+
+import processing.plugin.core.ProcessingCore;
+import processing.plugin.core.ProcessingLog;
+
+public class SketchProject implements IProjectNature {
+
+ /** value: "processing.plugin.core.processingnature" */
+ public static final String NATURE_ID = ProcessingCore.PLUGIN_ID + ".sketchNature";
+
+ /** The basic project entry being managed */
+ protected IProject project;
+
+ /** The Java project underlying the SketchProject */
+ protected IJavaProject jproject;
+
+ /**
+ * Return the SketchProject associated with the given IProject, or null
+ * if the project is not associated with a SketchProject.
+ */
+ public static SketchProject forProject(IProject project){
+ IProjectNature nature = null;
+ try{
+ nature = project.getNature(NATURE_ID);
+ } catch (CoreException e){
+ return null;
+ }
+ return (SketchProject) nature;
+ }
+
+ /** True if the project has the SketchProject nature, false otherwise. */
+ public static boolean isSketchProject(IProject project){
+ return SketchProject.forProject(project) != null;
+ }
+
+ /**
+ * Add the Sketch Project nature to the front of the nature list if the
+ * project doesn't already have it as a nature, or move it to the front
+ * of the nature list if it does.
+ */
+ public static void addSketchNature(IProject project) throws CoreException{
+ if (!project.isOpen())
+ return;
+
+ if (SketchProject.isSketchProject(project)){
+ IProjectDescription description = project.getDescription();
+ List newIds = new ArrayList();
+ newIds.addAll(Arrays.asList(description.getNatureIds()));
+ newIds.remove(NATURE_ID);
+ newIds.add(0,NATURE_ID);
+ description.setNatureIds(newIds.toArray(new String[newIds.size()]));
+ return;
+ }
+
+ IProjectDescription description = project.getDescription();
+
+ List newIds = new ArrayList();
+ newIds.add(NATURE_ID); // front of the line
+ newIds.add(JavaCore.NATURE_ID); // add the nature ID afterwards
+ newIds.addAll(Arrays.asList(description.getNatureIds()));
+ description.setNatureIds(newIds.toArray(new String[newIds.size()]));
+
+ project.setDescription(description, null);
+ }
+
+ /** Removes the nature from the project if it has it */
+ public static void removeSketchNature(IProject project) throws CoreException{
+ if (!project.isOpen())
+ return;
+
+ // doesn't have the nature, we're done
+ if (!SketchProject.isSketchProject(project))
+ return;
+
+ IProjectDescription description = project.getDescription();
+
+ List newIds = new ArrayList();
+ newIds.addAll(Arrays.asList(description.getNatureIds()));
+ newIds.remove(newIds.indexOf(NATURE_ID));
+ description.setNatureIds(newIds.toArray(new String[newIds.size()]));
+
+ project.setDescription(description,null);
+
+ }
+
+ /** Access method for this nature's project */
+ public IProject getProject() {
+ return project;
+ }
+
+ /**
+ * Should not be triggered by clients.
+ * Sent by the NatureManager when the nature is added to a project.
+ */
+ public void setProject(IProject project) {
+ this.project = project;
+ }
+
+ /** Access method for this nature's java project */
+ public IJavaProject getJavaProject(){
+ return jproject;
+ }
+
+ /** Associate the sketch builder with this nature's project */
+ public void configure() throws CoreException {
+ if (!project.isOpen())
+ return;
+
+ // Setup the folders
+ IFolder codeFolder = project.getFolder("code");
+ IFolder dataFolder = project.getFolder("data");
+ IFolder buildFolder = project.getFolder("bin"); // TODO relocate to MyPlugin.getPlugin().getStateLocation().getFolder("bin")
+ IFolder appletFolder = project.getFolder("applet");
+
+ if(!codeFolder.exists())
+ buildFolder.create(IResource.NONE, true, null);
+ if(!dataFolder.exists())
+ dataFolder.create(IResource.NONE, true, null);
+ if(!buildFolder.exists())
+ buildFolder.create(IResource.NONE, true, null);
+ if(!appletFolder.exists())
+ appletFolder.create(IResource.NONE, true, null);
+
+ // Setup the Java project underlying the Sketch
+ jproject = JavaCore.create(project);
+
+ // Check the description to see if it already has the builder
+ IProjectDescription description = this.project.getDescription();
+ List newCmds = new ArrayList();
+ newCmds.addAll(Arrays.asList(description.getBuildSpec()));
+
+ int ploc = -1; // builder ID location
+ for (int i = 0; i < newCmds.size(); i++){
+ if (newCmds.get(i).getBuilderName().equals(SketchBuilder.BUILDER_ID))
+ ploc = i;
+ }
+
+ if (ploc == 0) // its there and where we want it
+ return;
+
+ if (ploc > 0)
+ newCmds.remove(ploc); // its not where we want it, remove it and add to the beggining
+
+ ICommand command = description.newCommand();
+ command.setBuilderName(SketchBuilder.BUILDER_ID);
+ newCmds.add(0, command);
+ description.setBuildSpec(
+ (ICommand[]) newCmds.toArray(new ICommand[newCmds.size()]));
+ project.setDescription(description,null);
+
+ // refresh the local space, folders were created
+ project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+
+ // schedule and run a build once its added.
+ /*new Job("Build Sketch"){
+ protected IStatus run(IProgressMonitor monitor){
+ try{
+ project.build(
+ IncrementalProjectBuilder.FULL_BUILD,
+ SketchBuilder.BUILDER_ID,
+ null,
+ monitor);
+ } catch (CoreException e){
+ ProcessingLog.logError(e);
+ }
+ return Status.OK_STATUS;
+ }
+ }.schedule();*/
+
+ }
+
+ /**
+ * Dissociate the processing sketch builder from this nature's
+ * project and remove the markers it generated.
+ */
+ public void deconfigure() throws CoreException {
+ if (!project.isOpen())
+ return;
+
+ IProjectDescription description = this.project.getDescription();
+ ICommand[] cmds = description.getBuildSpec();
+ for (int i=0; i newCmds = new ArrayList();
+ newCmds.addAll(Arrays.asList(cmds));
+ newCmds.remove(i);
+ description.setBuildSpec(newCmds.toArray(new ICommand[newCmds.size()]));
+ }
+ }
+
+ // clean up your tokens
+ SketchBuilder.deleteP5ProblemMarkers(project);
+ }
+
+ /** Returns a qualified name for the property relative to this plug-in */
+ public QualifiedName getPropertyName(String localName){
+ return new QualifiedName(ProcessingCore.PLUGIN_ID, localName);
+ }
+
+ /**
+ * Sets a persistent property in the pojects property store
+ * If there is an exception it will be reported and the project
+ * will not persist.
+ **/
+ public void setPersistentProperty(String localName, String value){
+ try{
+ this.project.setPersistentProperty(this.getPropertyName(localName), value);
+ } catch (CoreException e){
+ ProcessingLog.logError(e);
+ }
+ }
+
+ /** Trigger a full build of the project being managed */
+ public void fullBuild(IProgressMonitor monitor) throws CoreException{
+ project.build(IncrementalProjectBuilder.FULL_BUILD, monitor);
+ }
+
+}
diff --git a/editor/processing.plugin.ui/META-INF/MANIFEST.MF b/editor/processing.plugin.ui/META-INF/MANIFEST.MF
index a5498a991..f5c5b2b0a 100644
--- a/editor/processing.plugin.ui/META-INF/MANIFEST.MF
+++ b/editor/processing.plugin.ui/META-INF/MANIFEST.MF
@@ -8,10 +8,12 @@ Bundle-Vendor: Processing.org
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.core.filebuffers,
+ org.eclipse.jdt.debug.ui,
org.eclipse.jface.text;bundle-version="3.6.0",
org.eclipse.ui.editors;bundle-version="3.6.0",
org.eclipse.core.resources;bundle-version="3.6.0",
processing.plugin.core;bundle-version="0.1.0"
+
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Import-Package: org.eclipse.ui.actions
diff --git a/editor/processing.plugin.ui/plugin.xml b/editor/processing.plugin.ui/plugin.xml
index 0fe7be4c3..b31fdaed6 100644
--- a/editor/processing.plugin.ui/plugin.xml
+++ b/editor/processing.plugin.ui/plugin.xml
@@ -18,10 +18,6 @@
extensions="pde">
-