From 752492808994e83fb6c83c31bd859c9ea735fb33 Mon Sep 17 00:00:00 2001 From: lonnen Date: Wed, 13 Oct 2010 21:32:37 +0000 Subject: [PATCH] New Core version 0.2.4.0, handles libraries like the PDE. --- .../META-INF/MANIFEST.MF | 2 +- .../plugin/core/ProcessingCore.java | 94 +---- .../core/ProcessingCorePreferences.java | 2 + .../processing/plugin/core/ProcessingLog.java | 2 +- .../plugin/core/ProcessingUtilities.java | 125 ++----- .../plugin/core/builder/SketchBuilder.java | 88 ++--- .../plugin/core/builder/SketchProject.java | 2 +- .../plugin/core/exporter/Exporter.java | 9 +- .../plugin/core/model/LibraryFolder.java | 329 ++++++++++++++++++ .../plugin/core/model/LibraryModel.java | 131 +++++++ .../plugin/ui/ProcessingPlugin.java | 1 - 11 files changed, 559 insertions(+), 226 deletions(-) create mode 100644 editor/processing.plugin.core/src/processing/plugin/core/model/LibraryFolder.java create mode 100644 editor/processing.plugin.core/src/processing/plugin/core/model/LibraryModel.java diff --git a/editor/processing.plugin.core/META-INF/MANIFEST.MF b/editor/processing.plugin.core/META-INF/MANIFEST.MF index 4802bf01e..418a9b4f7 100644 --- a/editor/processing.plugin.core/META-INF/MANIFEST.MF +++ b/editor/processing.plugin.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Processing Plugin Core Bundle-SymbolicName: processing.plugin.core;singleton:=true -Bundle-Version: 0.2.3.3 +Bundle-Version: 0.2.4.0 Bundle-Activator: processing.plugin.core.ProcessingCore Bundle-Vendor: Processing.org Require-Bundle: org.eclipse.core.runtime, 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 ada3a2c39..ca442af03 100644 --- a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCore.java +++ b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCore.java @@ -11,14 +11,12 @@ package processing.plugin.core; 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.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; @@ -26,7 +24,7 @@ import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Plugin; -import processing.plugin.core.ProcessingUtilities; +import processing.plugin.core.model.LibraryModel; /** * The plug-in activator enabling the core (UI-free) support for Processing sketches. @@ -45,8 +43,10 @@ public final class ProcessingCore extends Plugin { /** shared plugin object */ private static ProcessingCore plugin; - /** shared resource bundle */ + // shared objects private ResourceBundle resourceBundle; + private LibraryModel pLibs; + /** * Creates the Processing core plug-in. @@ -68,17 +68,22 @@ public final class ProcessingCore extends Plugin { } } - // special initialization and shutdown goes here + /** Returns the single model instance providing access to Processing libraries */ + public LibraryModel getLibraryModel(){ + return (pLibs == null) ? new LibraryModel() : pLibs; + } + + // any 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 the path results in a bad URL. * * @param path relative path from the Resources folder */ - public URL getPluginResource(String path){ + public URL getPluginResource(String path) { try{ return new URL(this.getBundle().getEntry("/"), "Resources/" + path); } catch (MalformedURLException e){ @@ -92,7 +97,7 @@ public final class ProcessingCore extends Plugin { * * @param path relative from the Resources folder */ - public URL getPluginResource(IPath path){ + public URL getPluginResource(IPath path) { return getPluginResource(path.toOSString()); } @@ -102,7 +107,7 @@ public final class ProcessingCore extends Plugin { * * @return File reference to the core resources */ - public File getPluginResourceFolder(){ + public File getPluginResourceFolder() { URL fileLocation = getPluginResource(""); try { File folder = new File(FileLocator.toFileURL(fileLocation).getPath()); @@ -115,7 +120,7 @@ public final class ProcessingCore extends Plugin { } /** Returns a file handle to the plug-in's local cache folder. */ - public File getBuiltInCacheFolder(){ + public File getBuiltInCacheFolder() { return new File(this.getStateLocation().toString()); } @@ -137,7 +142,7 @@ public final class ProcessingCore extends Plugin { } /** Returns the single instance of the Processing core plug-in runtime class. */ - public static ProcessingCore getProcessingCore(){ + public static ProcessingCore getCore(){ return plugin; } @@ -157,73 +162,6 @@ public final class ProcessingCore extends Plugin { public static boolean isProcessingFile(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); - } - - /** - * 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 boolean isLibrary(File rootFolder, boolean complain){ - if (rootFolder == null) return false; - if(!rootFolder.isDirectory()) return false; - - String name = rootFolder.getName(); - try { - File libraryJar = new File(rootFolder.getCanonicalPath() + - File.separatorChar + "library" + File.separatorChar + - name + ".jar"); - if (libraryJar.exists()) - if (ProcessingUtilities.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 library " + - name + ", could not resolve canonical path.", e); - } - - return false; - } - - /** - * 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 File, not IFile. - * If something goes wrong, logs an error and returns null. - * - * @return File containing the core libraries folder or null - */ - public File getCoreLibsFolder() { - URL fileLocation = getPluginResource("libraries"); - try { - File folder = new File(FileLocator.toFileURL(fileLocation).getPath()); - if (folder.exists()) - return folder; - } catch (Exception e) { - ProcessingLog.logError(e); - } - return null; - } /** * Finds and retrieves core.jar in the resource bundle. diff --git a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCorePreferences.java b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCorePreferences.java index f35be2560..2df9bccb6 100644 --- a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCorePreferences.java +++ b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingCorePreferences.java @@ -52,6 +52,8 @@ public class ProcessingCorePreferences { } catch (BackingStoreException bse){ ProcessingLog.logError("Could not save Processing Core Preferences.", bse); } + // if things have changed, we'll need to rebuild the library list + ProcessingCore.getCore().getLibraryModel().rebuildLibraryList(); } /** Returns the stored sketchbook path as a string. */ diff --git a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingLog.java b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingLog.java index c3825bcee..918dcfba3 100644 --- a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingLog.java +++ b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingLog.java @@ -83,7 +83,7 @@ public class ProcessingLog { * @param status */ public static void log(IStatus status){ - ProcessingCore.getProcessingCore().getLog().log(status); + ProcessingCore.getCore().getLog().log(status); } } diff --git a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingUtilities.java b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingUtilities.java index ce8f0c2be..c0100e9df 100644 --- a/editor/processing.plugin.core/src/processing/plugin/core/ProcessingUtilities.java +++ b/editor/processing.plugin.core/src/processing/plugin/core/ProcessingUtilities.java @@ -36,9 +36,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.IPath; - import processing.core.PConstants; /** @@ -56,6 +53,30 @@ import processing.core.PConstants; */ public class ProcessingUtilities implements PConstants{ + /** + * Current platform in use, one of the + * PConstants WINDOWS, MACOSX, MACOS9, LINUX or OTHER. + */ + static public int platform; + + static { + String osname = System.getProperty("os.name"); + + if (osname.indexOf("Mac") != -1) { + platform = MACOSX; + + } else if (osname.indexOf("Windows") != -1) { + platform = WINDOWS; + + } else if (osname.equals("Linux")) { // true for the ibm vm + platform = LINUX; + + } else { + platform = OTHER; + } + } + + /** This class is not meant to be instantiated. */ private ProcessingUtilities(){} @@ -256,89 +277,6 @@ public class ProcessingUtilities implements PConstants{ } } - /** - * Find the folder containing the users libraries, which should be in the sketchbook. - * Looks in the user's preferences first, then look relative to the sketch location. - * - * @return File containing the Sketch book library folder, or null if it can't be located - */ - public static 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 static IPath findSketchBookLibsFolder(IProject proj) { - try{ - 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; - } - - /** - * If the folder is the root of a Processing library, return a String containing - * the canonical path to the library's Jar. If it is not, return null. - */ - public static String getLibraryJarPath(File folder){ - if( ProcessingCore.isLibrary(folder) ){ - try { - return folder.getCanonicalPath().concat( File.separatorChar + "library" + File.separatorChar + folder.getName() + ".jar" ); - } catch (IOException e) { - ProcessingLog.logError("Could not get the library jar for library " + folder.getName(), e); - } - } - return null; - } - - /** - * Looks in the provided folder for valid libraries and returns a list of paths to them. - * Returns an empty list if there are no valid libraries. - * - * @param folder - * @return - */ - public static ArrayList getLibraryJars(File folder){ - ArrayList libPaths = new ArrayList(); - if(folder == null) return libPaths; - if(!folder.exists()) return libPaths; - - for (File f : folder.listFiles()){ - if ( ProcessingCore.isLibrary(f) ){ - // if it is a library, add the jar - String path = getLibraryJarPath(f); - if (path!= null) - libPaths.add(path); - } else if (f.isDirectory()){ - // if it is not a library, but is a directory, recurse - // and add all libraries in it to our list - libPaths.addAll(getLibraryJars(f)); - } - // we don't care about anything else. - } - return libPaths; - } - /** * Produce a sanitized name that fits our standards for likely to work. *

@@ -708,9 +646,7 @@ public class ProcessingUtilities implements PConstants{ } - /** - * Spews a buffer of bytes to an OutputStream. - */ + /** Spews a buffer of bytes to an OutputStream. */ static public void saveBytes(OutputStream output, byte buffer[]) { try { output.write(buffer); @@ -774,6 +710,17 @@ public class ProcessingUtilities implements PConstants{ file.getAbsolutePath(), se); } } + + ////////////////////////////////////////////////////////////// + + // ARRAYS + + static public String[] concat(String a[], String b[]) { + String c[] = new String[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } ////////////////////////////////////////////////////////////// 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 33f4ed8ac..561118c2c 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 @@ -14,7 +14,6 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.StringWriter; import java.util.ArrayList; -import java.util.HashMap; import java.util.Map; import org.eclipse.core.resources.IFile; @@ -23,10 +22,7 @@ import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; -//import org.eclipse.core.resources.IResourceChangeListener; -//import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IncrementalProjectBuilder; -//import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; @@ -43,6 +39,7 @@ import processing.app.preproc.PreprocessResult; import processing.plugin.core.ProcessingCore; import processing.plugin.core.ProcessingLog; import processing.plugin.core.ProcessingUtilities; +import processing.plugin.core.model.LibraryFolder; /** * Builder for Processing Sketches. @@ -68,7 +65,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{ /** Parent marker for Processing created markers (value "processing.plugin.core.processingMarker"). */ public static final String PROCESSINGMARKER = ProcessingCore.PLUGIN_ID + ".processingMarker"; - + /** Problem marker for processing preprocessor issues (value "processing.plugin.core.preprocError"). */ public static final String PREPROCMARKER = ProcessingCore.PLUGIN_ID + ".preprocError"; @@ -149,17 +146,17 @@ public class SketchBuilder extends IncrementalProjectBuilder{ IProject project = this.getProject(); SketchProject sketch = SketchProject.forProject(project); 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; // everything falls through to return null + 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; // everything falls through to return null } } - + /** Handles platform auto builds */ protected IProject[] autoBuild(SketchProject sketchProject, IProgressMonitor monitor) throws CoreException{ //System.err.println("Auto Build"); @@ -170,11 +167,11 @@ public class SketchBuilder extends IncrementalProjectBuilder{ fullBuild(sketchProject, monitor); return null; } - + /** Incremental builds are ignored. */ protected IProject[] incrementalBuild(SketchProject sketchProject, IProgressMonitor monitor){ //System.err.println("Incremental Build"); - + // triggered by launching a sketch or explicitly by a user iff auto build is off // if auto build is on, launching a sketch triggers an auto build first // save a few cycles by ignoring these @@ -189,7 +186,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{ * This can be a long running process, so we use a monitor. */ protected IProject[] fullBuild( SketchProject sketchProject, IProgressMonitor monitor) throws CoreException { -// System.err.println("Full Build of " + sketchProject.getProject().getName()); + // System.err.println("Full Build of " + sketchProject.getProject().getName()); clean(monitor); // tabula rasa @@ -205,7 +202,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{ ProcessingLog.logError("Build folder could not be accessed.", null); return null; } - + IFile mainFile = sketchProject.getMainFile(); if (!mainFile.isAccessible()){ reportProblem( @@ -273,7 +270,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{ sketchProject.sketch_width = -1; sketchProject.sketch_height = -1; sketchProject.renderer = ""; - + String scrubbed = ProcessingUtilities.scrubComments(stream.toString()); String[] matches = ProcessingUtilities.match(scrubbed, ProcessingUtilities.SIZE_REGEX); if(matches != null){ @@ -285,7 +282,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{ sketchProject.sketch_width = wide; else ProcessingLog.logInfo("Width cannot be negative. Using default width instead."); - + if (high > 0) sketchProject.sketch_height = high; else @@ -293,7 +290,7 @@ public class SketchBuilder extends IncrementalProjectBuilder{ if(matches.length==4) sketchProject.renderer = matches[3].trim(); // "Actually matches.length should always be 4..." - Processing Sketch.java - + } catch (NumberFormatException e) { ProcessingLog.logInfo( "Found a reference to size, but it didn't seem to contain numbers. " @@ -411,54 +408,44 @@ public class SketchBuilder extends IncrementalProjectBuilder{ monitor.worked(10); if(checkCancel(monitor)) { return null; } + // Library import checking - ArrayList allFoundLibraries = new ArrayList(); // a list of all the libraries that can be found - - allFoundLibraries.addAll( ProcessingUtilities.getLibraryJars(ProcessingCore.getProcessingCore().getCoreLibsFolder()) ); - allFoundLibraries.addAll( ProcessingUtilities.getLibraryJars(ProcessingUtilities.getSketchBookLibsFolder(sketch)) ); - - HashMap libraryImportToPathTable = new HashMap(); - - for (String libraryPath : allFoundLibraries ){ - String[] packages = ProcessingUtilities.packageListFromClassPath(libraryPath); - for (String pkg : packages) libraryImportToPathTable.put(pkg, new Path(libraryPath)); - } - boolean importProblems = false; sketchProject.libraryPaths.clear(); - for (int i=0; i < result.extraImports.size(); i++){ - String importPackage = result.extraImports.get(i); + for (String importPackage : result.extraImports){ int dot = importPackage.lastIndexOf('.'); String entry = (dot == -1) ? importPackage : importPackage.substring(0, dot); - IPath libPath = libraryImportToPathTable.get(entry); - if (libPath != null ){ - libraryJarPathList.add(libPath.makeAbsolute()); // we've got it! - sketchProject.libraryPaths.add(libPath.makeAbsolute()); - } else { + LibraryFolder libFolder = ProcessingCore.getCore().getLibraryModel().getLibraryFolder(entry); + if (libFolder == null ){ // The user is trying to import something we won't be able to find. reportProblem( - "Library import "+ entry +" could not be found. Check the library folder in your sketchbook.", - sketch.getFile( sketch.getName() + ".pde"), i+1, true + "Library import \""+ entry +"\" could not be found. Check the library folder in your sketchbook.", + sketch, -1, true ); importProblems=true; - } + continue; + } + // found what they're looking for! + libraryJarPathList.add( new Path(libFolder.getJarPath()) ); + sketchProject.libraryPaths.add( new Path(libFolder.getJarPath()) ); } + if (importProblems) return null; // bail after all errors are found. monitor.worked(10); if(checkCancel(monitor)) { return null; } - + // Add data folder if there is stuff in it IFolder dataFolder = sketchProject.getDataFolder(); if (dataFolder.isAccessible()){ if (dataFolder.members().length > 0) srcFolderPathList.add(dataFolder.getFullPath()); } - + // Almost there! Set a new classpath using all this stuff we've computed. // Even though the list types are specified, Java still tosses errors when I try - // to cast them. So instead I'm stuck with explicit iteration. + // to cast them. So instead I'm stuck with this idiom. IPath[] libPaths = new IPath[libraryJarPathList.size()]; @@ -491,8 +478,8 @@ public class SketchBuilder extends IncrementalProjectBuilder{ /** * Generates and assigns a processing problem marker. *

- * Tags the whole line and adds an issue to the Problems box. If the problem could not be tied - * to a specific file it will be marked against the project and the line will not be marked. + * A negative line number indicates that the problem could not be tied back to a specific line. + * Message strings generated by the preprocessor will be translated into a more readable form. */ private void reportProblem(String message, IResource problemFile, int lineNumber, boolean isError){ // translate error messages to a friendlier form @@ -511,12 +498,11 @@ public class SketchBuilder extends IncrementalProjectBuilder{ IMarker marker = problemFile.createMarker(SketchBuilder.PREPROCMARKER); marker.setAttribute(IMarker.MESSAGE, message); marker.setAttribute(IMarker.SEVERITY, isError ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING); - if( lineNumber != -1) - marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + if(lineNumber > -1) marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); } 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 index 72e82b3a7..8b9bf1632 100644 --- a/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchProject.java +++ b/editor/processing.plugin.core/src/processing/plugin/core/builder/SketchProject.java @@ -358,7 +358,7 @@ public class SketchProject implements IProjectNature { entries.add(JavaCore.newContainerEntry(vmPath.makeAbsolute())); // JVM // Processing Libraries - IPath plibs = new Path(ProcessingCore.getProcessingCore().getCoreJarFile().getAbsolutePath()); + IPath plibs = new Path(ProcessingCore.getCore().getCoreJarFile().getAbsolutePath()); entries.add(JavaCore.newLibraryEntry( plibs, null, null, false )); // if we were given a list of source folders, add them to the list diff --git a/editor/processing.plugin.core/src/processing/plugin/core/exporter/Exporter.java b/editor/processing.plugin.core/src/processing/plugin/core/exporter/Exporter.java index e40889243..f47a8cc16 100644 --- a/editor/processing.plugin.core/src/processing/plugin/core/exporter/Exporter.java +++ b/editor/processing.plugin.core/src/processing/plugin/core/exporter/Exporter.java @@ -28,6 +28,7 @@ import processing.plugin.core.ProcessingCore; import processing.plugin.core.ProcessingLog; import processing.plugin.core.ProcessingUtilities; import processing.plugin.core.builder.SketchProject; +import processing.plugin.core.model.LibraryModel; /** Static export functions. */ public class Exporter { @@ -129,7 +130,7 @@ public class Exporter { // This will happen when the copy fails, which we expect if there is no // image file. It isn't worth reporting. try { - File exportResourcesFolder = new File(ProcessingCore.getProcessingCore().getPluginResourceFolder().getCanonicalPath(), "export"); + File exportResourcesFolder = new File(ProcessingCore.getCore().getPluginResourceFolder().getCanonicalPath(), "export"); File loadingImageCoreResource = new File(exportResourcesFolder, LOADING_IMAGE); ProcessingUtilities.copyFile(loadingImageCoreResource, new File(exportFolder.getLocation().toFile(), LOADING_IMAGE)); } catch (Exception ex) { @@ -176,9 +177,9 @@ public class Exporter { } // snag the opengl library path so we can test for it later - File openglLibrary = new File(ProcessingCore.getProcessingCore().getCoreLibsFolder(), "opengl/library/opengl.jar"); + File openglLibrary = new File(LibraryModel.getCoreLibsFolder(), "opengl/library/opengl.jar"); String openglLibraryPath = openglLibrary.getAbsolutePath(); - boolean openglApplet = false; + boolean openglApplet = false; // add the library jar files to the folder and detect if opengl is in use ArrayList sketchLibraryImportPaths = sp.getLibraryPaths(); @@ -188,7 +189,7 @@ public class Exporter { openglApplet = true; // for each exportFile in library.getAppletExports() - + // File libraryFolder = new File(path.toOSString()); // if (path.toOSString().equalsIgnoreCase(openglLibraryPath)) openglApplet=true; diff --git a/editor/processing.plugin.core/src/processing/plugin/core/model/LibraryFolder.java b/editor/processing.plugin.core/src/processing/plugin/core/model/LibraryFolder.java new file mode 100644 index 000000000..50ed3e2f0 --- /dev/null +++ b/editor/processing.plugin.core/src/processing/plugin/core/model/LibraryFolder.java @@ -0,0 +1,329 @@ +/** + * Copyright (c) 2010 Chris Lonnen. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Chris Lonnen - initial API and implementation + */ +package processing.plugin.core.model; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import processing.core.PConstants; +import processing.plugin.core.ProcessingLog; +import processing.plugin.core.ProcessingUtilities; + +/** + * Provides a model of a Processing library folder for easy extraction of + * files required for different exports on different platforms. + *

+ * This is a rather unimaginative rewrite of the Processing.app.LibraryFolder + * class to work with the LibraryModel instead of Processing.app.Base. + */ +public class LibraryFolder implements PConstants { + + File folder; // root folder + File libraryFolder; // name/library + File examplesFolder; // name/examples + String name; // "pdf" or "PDF Export" + String author; // Ben Fry + String authorURL; // http://processing.org + String sentence; // Write graphics to PDF files. + String paragraph; // + int version; // 102 + String prettyVersion; // "1.0.2" + + HashMap exportList; + String[] appletExportList; + boolean[] multipleArch = new boolean[platformNames.length]; + + /** + * For runtime, the native library path for this platform. e.g. on Windows 64, + * this might be the windows64 subfolder with the library. + */ + String nativeLibraryPath; + + /** How many bits this machine is */ + static int nativeBits; + static { + nativeBits = 32; // perhaps start with 32 + String bits = System.getProperty("sun.arch.data.model"); + if (bits != null) { + if (bits.equals("64")) { + nativeBits = 64; + } + } else { + // if some other strange vm, maybe try this instead + if (System.getProperty("java.vm.name").contains("64")) { + nativeBits = 64; + } + } + } + + /** Filter to pull out just files and no directories */ + FilenameFilter simpleFilter = new FilenameFilter() { + public boolean accept(File dir, String name) { + // skip .DS_Store files, .svn folders, etc + if (name.charAt(0) == '.') return false; + if (name.equals("CVS")) return false; + File file = new File(dir, name); + return (!file.isDirectory()); + } + }; + + /** Filter to pull out just jars*/ + FilenameFilter jarFilter = new FilenameFilter() { + public boolean accept(File dir, String name) { + if (name.charAt(0) == '.') return false; // skip ._blah.jar crap on OS X + if (new File(dir, name).isDirectory()) return false; + String lc = name.toLowerCase(); + return lc.endsWith(".jar") || lc.endsWith(".zip"); + } + }; + + /** Returns an ArrayList of LibraryFolders for each valid library in the folder. */ + static protected ArrayList list(File folder) throws IOException { + ArrayList libraries = new ArrayList(); + list(folder, libraries); + return libraries; + } + + /** Loads the provided ArrayList with the libraries in the directory. */ + static protected void list(File folder, ArrayList libraries) throws IOException { + if (folder == null) return; + if (folder.isDirectory()) { + String[] list = folder.list(new FilenameFilter() { + public boolean accept(File dir, String name) { + // skip .DS_Store files, .svn folders, etc + if (name.charAt(0) == '.') return false; + if (name.equals("CVS")) return false; + return (new File(dir, name).isDirectory()); + } + }); + // if a bad folder or something like that, this might come back null + if (list != null) { + for (String potentialName : list) { + File baseFolder = new File(folder, potentialName); + File libraryFolder = new File(baseFolder, "library"); + File libraryJar = new File(libraryFolder, potentialName + ".jar"); + // If a .jar file of the same prefix as the folder exists + // inside the 'library' subfolder of the sketch + if (libraryJar.exists()) { + String sanityCheck = ProcessingUtilities.sanitizeName(potentialName); + if (sanityCheck.equals(potentialName)) { + libraries.add(new LibraryFolder(baseFolder)); + } else { + ProcessingLog.logInfo( + "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)" + ); + continue; + } + } + } + } + } + } + + /** + * Create a Library Folder from a file folder. + *

+ * The constructor assumes that the provided folder is the root of a valid library + * and does very little checking to ensure that. Invalid libraries may be logged and + * ignored, but more likely will cause a null pointer or something nasty to that end. + *

+ * Creating a library this way updates the LibraryModel's lookup table as a side effect. + */ + public LibraryFolder(File folder) { + this.folder = folder; + libraryFolder = new File(folder, "library"); + examplesFolder = new File(folder, "examples"); + + File exportSettings = new File(libraryFolder, "export.txt"); + HashMap exportTable = ProcessingUtilities.readSettings(exportSettings); + + name = exportTable.get("name"); + if (name == null) name = folder.getName(); + + exportList = new HashMap(); + + // get the list of files just in the library root + String[] baseList = folder.list(simpleFilter); + + String appletExportStr = exportTable.get("applet"); + if (appletExportStr != null) { + appletExportList = ProcessingUtilities.splitTokens(appletExportStr, ", "); + } else { + appletExportList = baseList; + } + + // for the host platform, need to figure out what's available + File nativeLibraryFolder = libraryFolder; + String hostPlatform = platformNames[ProcessingUtilities.platform]; + // see if there's a 'windows', 'macosx', or 'linux' folder + File hostLibrary = new File(libraryFolder, hostPlatform); + if (hostLibrary.exists()) nativeLibraryFolder = hostLibrary; + // check for bit-specific version, e.g. on windows, check if there + // is a window32 or windows64 folder (on windows) + hostLibrary = new File(libraryFolder, hostPlatform + nativeBits); + if (hostLibrary.exists()) nativeLibraryFolder = hostLibrary; + // save that folder for later use + nativeLibraryPath = nativeLibraryFolder.getAbsolutePath(); + + // for each individual platform that this library supports, figure out what's around + for (int i = 1; i < platformNames.length; i++) { + String platformName = platformNames[i]; + String platformName32 = platformName + "32"; + String platformName64 = platformName + "64"; + + String platformAll = exportTable.get("application." + platformName); + String[] platformList = platformAll == null ? null : ProcessingUtilities.splitTokens(platformAll, ", "); + + String platform32 = exportTable.get("application." + platformName + "32"); + String[] platformList32 = platform32 == null ? null : ProcessingUtilities.splitTokens(platform32, ", "); + + String platform64 = exportTable.get("application." + platformName + "64"); + String[] platformList64 = platform64 == null ? null : ProcessingUtilities.splitTokens(platform64, ", "); + + if (platformAll == null) { + File folderAll = new File(libraryFolder, platformName); + if (folderAll.exists()) + platformList = ProcessingUtilities.concat(baseList, folderAll.list(simpleFilter)); + } + if (platform32 == null) { + File folder32 = new File(libraryFolder, platformName32); + if (folder32.exists()) + platformList32 = ProcessingUtilities.concat(baseList, folder32.list(simpleFilter)); + } + if (platform64 == null) { + File folder64 = new File(libraryFolder, platformName64); + if (folder64.exists()) + platformList64 = ProcessingUtilities.concat(baseList, folder64.list(simpleFilter)); + } + + if (platformList32 != null || platformList64 != null) multipleArch[i] = true; + + // if there aren't any relevant imports specified or in their own folders, + // then use the baseList (root of the library folder) as the default. + if (platformList == null && platformList32 == null && platformList64 == null) { + exportList.put(platformName, baseList); + } else { + // once we've figured out which side our bread is buttered on, save it. + // (also concatenate the list of files in the root folder as well + if (platformList != null) exportList.put(platformName, platformList); + if (platformList32 != null) exportList.put(platformName32, platformList32); + if (platformList64 != null) exportList.put(platformName64, platformList64); + } + } + + // get the path for all .jar files in this code folder + String[] packages = ProcessingUtilities.packageListFromClassPath(getClassPath()); + for (String pkg : packages) { + LibraryFolder library = LibraryModel.importToLibraryTable.get(pkg); + if (library != null) { + ProcessingLog.logInfo( + "The library found in " + getPath() + + "conflicts with " + library.getPath() + + "which already defines the package " + pkg + " -- " + + "the original library will be used." + ); + } else { + LibraryModel.importToLibraryTable.put(pkg, this); + } + } + } + + /** Answers the library name */ + public String getName() { + return name; + } + + /** Answers the absolute path to the root of the 'library' folder */ + public String getPath() { + return folder.getAbsolutePath(); + } + + /** Answers the absolute path to the 'library' folder in the root of the library */ + public String getLibraryPath() { + return libraryFolder.getAbsolutePath(); + } + + /** Answers the absolute path to the library jar file */ + public String getJarPath() { + return new File(folder, "library" + File.separatorChar + name + ".jar").getAbsolutePath(); + } + + + // this prepends a colon so that it can be appended to other paths safely + public String getClassPath() { + StringBuilder cp = new StringBuilder(); + + String[] jarHeads = libraryFolder.list(jarFilter); + for (String jar : jarHeads) { + cp.append(File.pathSeparatorChar); + cp.append(new File(libraryFolder, jar).getAbsolutePath()); + } + jarHeads = new File(nativeLibraryPath).list(jarFilter); + for (String jar : jarHeads) { + cp.append(File.pathSeparatorChar); + cp.append(new File(nativeLibraryPath, jar).getAbsolutePath()); + } + return cp.toString(); + } + + public String getNativePath() { return nativeLibraryPath; } + + protected File[] wrapFiles(String[] list) { + File[] outgoing = new File[list.length]; + for (int i = 0; i < list.length; i++) { + outgoing[i] = new File(libraryFolder, list[i]); + } + return outgoing; + } + + public File[] getAppletExports() { + return wrapFiles(appletExportList); + } + + public File[] getApplicationExports(int platform, int bits) { + String[] list = getApplicationExportList(platform, bits); + return wrapFiles(list); + } + + /** + * Returns the necessary exports for the specified platform. + * If no 32 or 64-bit version of the exports exists, it returns the version + * that doesn't specify bit depth. + */ + public String[] getApplicationExportList(int platform, int bits) { + String platformName = platformNames[platform]; + if (bits == 32) { + String[] pieces = exportList.get(platformName + "32"); + if (pieces != null) return pieces; + } else if (bits == 64) { + String[] pieces = exportList.get(platformName + "64"); + if (pieces != null) return pieces; + } + return exportList.get(platformName); + } + + public boolean hasMultipleArch(int platform) { + return multipleArch[platform]; + } + + static boolean hasMultipleArch(int platform, ArrayList libraries) { + for (LibraryFolder library : libraries) { + if (library.hasMultipleArch(platform)) { + return true; + } + } + return false; + } +} diff --git a/editor/processing.plugin.core/src/processing/plugin/core/model/LibraryModel.java b/editor/processing.plugin.core/src/processing/plugin/core/model/LibraryModel.java new file mode 100644 index 000000000..6a0ff948a --- /dev/null +++ b/editor/processing.plugin.core/src/processing/plugin/core/model/LibraryModel.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2010 Chris Lonnen. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.opensource.org/licenses/eclipse-1.0.php + * + * Contributors: + * Chris Lonnen - initial API and implementation + */ +package processing.plugin.core.model; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; + +import processing.plugin.core.ProcessingCore; +import processing.plugin.core.ProcessingCorePreferences; +import processing.plugin.core.ProcessingLog; +import processing.plugin.core.ProcessingUtilities; + +/** + * Provides access to Processing libraries. + *

+ * Provides some static methods for detecting libraries and locating library folders. + *

+ * Instantiation of the model is handled by the ProcessingCore, and clients requiring + * access to the LibraryModel should use that singleton instance to get to things to + * keep things from getting out of sync. An instance of the model provides a convenient + * lookup table for the packages. + */ +public class LibraryModel { // naming is hard. + + /** Maps imported packages to LibraryFolder objects */ + static HashMap importToLibraryTable; + + /** + * Try to get the user defined library folder from the sketchbook location in preferences. + * + * @return a File handle to the sketchbook library folder or null if it doesn't exist + */ + public static File getSketchBookLibsFolder() { + IPath sketchbook = ProcessingCorePreferences.current().getSketchbookPath(); + if (sketchbook == null) return null; + File userLibs = new File(sketchbook.append("libraries").toOSString()); + return (userLibs.exists()) ? userLibs: null; + } + + /** + * Returns the folder containing the Processing core libraries, which are bundled + * with the plugin. If they cannot be found, log an exception and return null. + * This indicates something has gone very wrong, and we should be wary. + * + * @return File containing the core libraries folder or null + */ + public static File getCoreLibsFolder() { + URL fileLocation = ProcessingCore.getCore().getPluginResource("libraries"); + try { + File folder = new File(FileLocator.toFileURL(fileLocation).getPath()); + if (folder.exists()) return folder; + } catch (Exception e) { + ProcessingLog.logError("Couldn't get Core libraries folder",e); + } + return null; + } + + /** @return true if the folder is the root of a valid Processing library folder structure. */ + public static boolean isLibrary(File rootFolder){ + if (rootFolder == null) return false; + if (!rootFolder.isDirectory()) return false; + + String name = rootFolder.getName(); + + File libraryFolder = new File(rootFolder, "library"); + File libraryJar = new File( libraryFolder, name + ".jar" ); + + if (!libraryJar.exists()) return false; + if (!ProcessingUtilities.sanitizeName(name).equals(name)) { + ProcessingLog.logInfo( + "The library \"" + name + "\" is being ignored. " + + "Library names must contain only basic letters and numbers. " + + "(ASCII only and no spaces, and it cannot start with a number)" + ); + return false; + } + return true; + } + + // I'm not sure these are used right now. + // In the PDE they are used for GUI stuff that isn't in place here + //ArrayList coreLibraries; + //ArrayList contribLibraries; + + /** Creating the model builds the library list. */ + public LibraryModel(){ + this.rebuildLibraryList(); + } + + /** Rebuild the library import tables from scratch. */ + public void rebuildLibraryList(){ + importToLibraryTable = new HashMap(); + try{ + // LibraryFolder.list() updates the import table as a side affect + + //coreLibraries = LibraryFolder.list(LibraryModel.getCoreLibsFolder()); + //contribLibraries = LibraryFolder.list(LibraryModel.getSketchBookLibsFolder()); + LibraryFolder.list(LibraryModel.getCoreLibsFolder()); + LibraryFolder.list(LibraryModel.getSketchBookLibsFolder()); + } catch (IOException e){ + ProcessingLog.logError("Unhappiness! " + + "An error occured while loading libraries, " + + " not all the books will be in place.", e + ); + } + } + + /** + * Access to the internal lookup table. + * + * @param pkg a String containing a package name + * @return LibraryFolder for that package, or null if it can't be found + */ + public LibraryFolder getLibraryFolder(String pkg){ + return importToLibraryTable.get(pkg); + } + +} diff --git a/editor/processing.plugin.ui/src/processing/plugin/ui/ProcessingPlugin.java b/editor/processing.plugin.ui/src/processing/plugin/ui/ProcessingPlugin.java index e332a7709..5c4f7284c 100644 --- a/editor/processing.plugin.ui/src/processing/plugin/ui/ProcessingPlugin.java +++ b/editor/processing.plugin.ui/src/processing/plugin/ui/ProcessingPlugin.java @@ -34,7 +34,6 @@ public class ProcessingPlugin extends AbstractUIPlugin { /** The ID of the processing */ public static final String PROCESSING_PARTITIONING = "__processing_partitioning"; - /** The shared plugin instance */ private static ProcessingPlugin plugin;