New Core version 0.2.4.0, handles libraries like the PDE.

This commit is contained in:
lonnen
2010-10-13 21:32:37 +00:00
parent d25eadcc09
commit 7524928089
11 changed files with 559 additions and 226 deletions
@@ -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,
@@ -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.
@@ -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. */
@@ -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);
}
}
@@ -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<String> getLibraryJars(File folder){
ArrayList<String> libPaths = new ArrayList<String>();
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.
* <p/>
@@ -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;
}
//////////////////////////////////////////////////////////////
@@ -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 <code>"processing.plugin.core.processingMarker"</code>). */
public static final String PROCESSINGMARKER = ProcessingCore.PLUGIN_ID + ".processingMarker";
/** Problem marker for processing preprocessor issues (value <code>"processing.plugin.core.preprocError"</code>). */
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<String> allFoundLibraries = new ArrayList<String>(); // 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<String, IPath> libraryImportToPathTable = new HashMap<String, IPath>();
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.
* <p>
* 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;
}
}
}
/**
@@ -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
@@ -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<IPath> 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;
@@ -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.
* <p>
* 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; // <paragraph length description for site>
int version; // 102
String prettyVersion; // "1.0.2"
HashMap<String,String[]> 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<LibraryFolder> list(File folder) throws IOException {
ArrayList<LibraryFolder> libraries = new ArrayList<LibraryFolder>();
list(folder, libraries);
return libraries;
}
/** Loads the provided ArrayList with the libraries in the directory. */
static protected void list(File folder, ArrayList<LibraryFolder> 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.
* <p>
* 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.
* <p>
* 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<String,String> exportTable = ProcessingUtilities.readSettings(exportSettings);
name = exportTable.get("name");
if (name == null) name = folder.getName();
exportList = new HashMap<String, String[]>();
// 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<LibraryFolder> libraries) {
for (LibraryFolder library : libraries) {
if (library.hasMultipleArch(platform)) {
return true;
}
}
return false;
}
}
@@ -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.
* <p>
* Provides some static methods for detecting libraries and locating library folders.
* <p>
* 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<String, LibraryFolder> 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<LibraryFolder> coreLibraries;
//ArrayList<LibraryFolder> 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<String, LibraryFolder>();
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);
}
}
@@ -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;