diff --git a/app/src/processing/app/Library.java b/app/src/processing/app/Library.java index c31823b69..fbe3d82e6 100644 --- a/app/src/processing/app/Library.java +++ b/app/src/processing/app/Library.java @@ -6,6 +6,7 @@ import java.util.*; import processing.app.contrib.*; import processing.core.*; + public class Library extends InstalledContribution { static final String[] platformNames = PConstants.platformNames; @@ -14,7 +15,10 @@ public class Library extends InstalledContribution { protected File examplesFolder; // shortname/examples protected File referenceFile; // shortname/reference/index.html - /** Subfolder for grouping libraries in a menu. */ + /** + * Subfolder for grouping libraries in a menu. Basic subfolder support + * is provided so that basic organization can be done in the import menu. + */ protected String group; /** Packages provided by this library. */ diff --git a/app/src/processing/app/contrib/AdvertisedContribution.java b/app/src/processing/app/contrib/AdvertisedContribution.java new file mode 100644 index 000000000..250c3d92c --- /dev/null +++ b/app/src/processing/app/contrib/AdvertisedContribution.java @@ -0,0 +1,197 @@ +package processing.app.contrib; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; + +import processing.app.Base; +import processing.app.Editor; +import processing.core.PApplet; + +class AdvertisedContribution implements Contribution { + protected final String name; // "pdf" or "PDF Export" + protected final ContributionType type; // Library, tool, etc. + protected final String category; // "Sound" + protected final String authorList; // [Ben Fry](http://benfry.com/) + protected final String url; // http://processing.org + protected final String sentence; // Write graphics to PDF files. + protected final String paragraph; // + protected final int version; // 102 + protected final String prettyVersion; // "1.0.2" + protected final String link; // Direct link to download the file + + + public AdvertisedContribution(ContributionType type, HashMap exports) { + + this.type = type; + name = exports.get("name"); + category = ContributionListing.getCategory(exports.get("category")); + authorList = exports.get("authorList"); + + url = exports.get("url"); + sentence = exports.get("sentence"); + paragraph = exports.get("paragraph"); + + int v = 0; + try { + v = Integer.parseInt(exports.get("version")); + } catch (NumberFormatException e) { + } + version = v; + + prettyVersion = exports.get("prettyVersion"); + + this.link = exports.get("download"); + } + + + /** + * @param contribArchive + * a zip file containing the library to install + * @param ad + * the advertised version of this library, if it was downloaded + * through the Contribution Manager. This is used to check the type + * of library being installed, and to replace the .properties file in + * the zip + * @param confirmReplace + * true to open a dialog asking the user to confirm removing/moving + * the library when a library by the same name already exists + * @return + */ + public InstalledContribution install(Editor editor, File contribArchive, + boolean confirmReplace, + ErrorWidget statusBar) { + + // Unzip the file into the modes, tools, or libraries folder inside the + // sketchbook. Unzipping to /tmp is problematic because it may be on + // another file system, so move/rename operations will break. + File sketchbookContribFolder = + ContributionManager.getSketchbookContribFolder(editor.getBase(), type); + File tempFolder = null; + + try { + tempFolder = + Base.createTempFolder(type.toString(), "tmp", sketchbookContribFolder); + } catch (IOException e) { + statusBar.setErrorMessage("Could not create a temporary folder to install."); + return null; + } + ContributionManager.unzip(contribArchive, tempFolder); + + // Now go looking for a legit contrib inside what's been unpacked. + File contribFolder = null; + + // Sometimes contrib authors place all their folders in the base directory + // of the .zip file instead of in single folder as the guidelines suggest. + if (InstalledContribution.isCandidate(tempFolder, type)) { + contribFolder = tempFolder; + } + + if (contribFolder == null) { + // Find the first legitimate looking folder in what we just unzipped + contribFolder = InstalledContribution.findCandidate(tempFolder, type); + } + + InstalledContribution outgoing = null; + + if (contribFolder == null) { + statusBar.setErrorMessage("Could not find a " + type + " in the downloaded file."); + + } else { + File propFile = new File(contribFolder, type + ".properties"); + + if (writePropertiesFile(propFile)) { + InstalledContribution newContrib = + ContributionManager.load(editor.getBase(), contribFolder, type); + outgoing = + newContrib.install(editor, confirmReplace, statusBar); + + } else { + statusBar.setErrorMessage("Error overwriting .properties file."); + } + } + + // Remove any remaining boogers + if (tempFolder.exists()) { + Base.removeDir(tempFolder); + } + return outgoing; + } + + + public boolean isInstalled() { + return false; + } + + public ContributionType getType() { + return type; + } + + public String getTypeName() { + return type.toString(); + } + + public String getCategory() { + return category; + } + + public String getName() { + return name; + } + + public String getAuthorList() { + return authorList; + } + + public String getUrl() { + return url; + } + + public String getSentence() { + return sentence; + } + + public String getParagraph() { + return paragraph; + } + + public int getVersion() { + return version; + } + + public String getPrettyVersion() { + return prettyVersion; + } + + + public boolean writePropertiesFile(File propFile) { + try { + if (propFile.delete() && propFile.createNewFile() && propFile.setWritable(true)) { + //BufferedWriter bw = new BufferedWriter(new FileWriter(propFile)); + PrintWriter writer = PApplet.createWriter(propFile); + + writer.println("name=" + getName()); + writer.println("category=" + getCategory()); + writer.println("authorList=" + getAuthorList()); + writer.println("url=" + getUrl()); + writer.println("sentence=" + getSentence()); + writer.println("paragraph=" + getParagraph()); + writer.println("version=" + getVersion()); + writer.println("prettyVersion=" + getPrettyVersion()); + + writer.flush(); + writer.close(); + } + return true; + + } catch (FileNotFoundException e) { + e.printStackTrace(); + + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } +} \ No newline at end of file diff --git a/app/src/processing/app/contrib/ContributionListPanel.java b/app/src/processing/app/contrib/ContributionListPanel.java index 2cf0290db..c899e2ec8 100644 --- a/app/src/processing/app/contrib/ContributionListPanel.java +++ b/app/src/processing/app/contrib/ContributionListPanel.java @@ -40,7 +40,6 @@ import java.io.File; import java.net.*; import processing.app.Base; -import processing.app.contrib.ContributionListing.AdvertisedContribution; import processing.app.contrib.ContributionListing.ContributionChangeListener; public class ContributionListPanel extends JPanel implements Scrollable, ContributionChangeListener { diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index 07549c24f..d457879dc 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -80,7 +80,7 @@ public class ContributionListing { listingFile = file; advertisedContributions.clear(); - advertisedContributions.addAll(getLibraries(listingFile)); + advertisedContributions.addAll(parseContribList(listingFile)); for (Contribution contribution : advertisedContributions) { addContribution(contribution); } @@ -246,12 +246,13 @@ public class ContributionListing { // Chances are the person is still typing the property, so rather than // make the list flash empty (because nothing contains "is:" or "has:", // just return true. - if (!isProperty(property)) + if (!isProperty(property)) { return true; + } if ("is".equals(isText) || "has".equals(isText)) { return hasProperty(contrib, filter.substring(colon + 1)); - } else if ("not".equals(isText)) { + } else if ("not".equals(isText)) { return !hasProperty(contrib, filter.substring(colon + 1)); } } @@ -324,6 +325,7 @@ public class ContributionListing { } } + public void addContributionListener(ContributionChangeListener listener) { for (Contribution contrib : allContributions) { listener.contributionAdded(contrib); @@ -331,14 +333,17 @@ public class ContributionListing { listeners.add(listener); } + public void removeContributionListener(ContributionChangeListener listener) { listeners.remove(listener); } + public ArrayList getContributionListeners() { return new ArrayList(listeners); } + /** * Starts a new thread to download the advertised list of contributions. * Only one instance will run at a time. @@ -360,7 +365,7 @@ public class ContributionListing { } if (!progressMonitor.isFinished()) { - FileDownloader.downloadFile(url, listingFile, progressMonitor); + download(url, listingFile, progressMonitor); if (!progressMonitor.isCanceled() && !progressMonitor.isError()) { hasDownloadedLatestList = true; setAdvertisedList(listingFile); @@ -370,6 +375,56 @@ public class ContributionListing { } }).start(); } + + + /** + * Blocks until the file is downloaded or an error occurs. + * Returns true if the file was successfully downloaded, false otherwise. + * + * @param source + * the URL of the file to download + * @param dest + * the file on the local system where the file will be written. This + * must be a file (not a directory), and must already exist. + * @param progress + * @throws FileNotFoundException + * if an error occurred downloading the file + */ + static boolean download(URL source, File dest, ProgressMonitor progress) { + boolean success = false; + try { +// System.out.println("downloading file " + source); + URLConnection conn = source.openConnection(); + conn.setConnectTimeout(1000); + conn.setReadTimeout(5000); + + // TODO this is often -1, may need to set progress to indeterminate + int fileSize = conn.getContentLength(); +// System.out.println("file size is " + fileSize); + progress.startTask("Downloading", fileSize); + + InputStream in = conn.getInputStream(); + FileOutputStream out = new FileOutputStream(dest); + + byte[] b = new byte[8192]; + int amount; + int total = 0; + while (!progress.isCanceled() && (amount = in.read(b)) != -1) { + out.write(b, 0, amount); + total += amount; + progress.setProgress(total); + } + out.flush(); + out.close(); + success = true; + + } catch (IOException ioe) { + progress.error(ioe); + ioe.printStackTrace(); + } + progress.finished(); + return success; + } public boolean hasUpdates() { @@ -434,7 +489,7 @@ public class ContributionListing { } - public ArrayList getLibraries(File f) { + public ArrayList parseContribList(File f) { ArrayList outgoing = new ArrayList(); if (f != null && f.exists()) { @@ -476,116 +531,6 @@ public class ContributionListing { } - static class AdvertisedContribution implements Contribution { - protected final String name; // "pdf" or "PDF Export" - protected final ContributionType type; // Library, tool, etc. - protected final String category; // "Sound" - protected final String authorList; // [Ben Fry](http://benfry.com/) - protected final String url; // http://processing.org - protected final String sentence; // Write graphics to PDF files. - protected final String paragraph; // - protected final int version; // 102 - protected final String prettyVersion; // "1.0.2" - protected final String link; // Direct link to download the file - - public AdvertisedContribution(ContributionType type, HashMap exports) { - - this.type = type; - name = exports.get("name"); - category = ContributionListing.getCategory(exports.get("category")); - authorList = exports.get("authorList"); - - url = exports.get("url"); - sentence = exports.get("sentence"); - paragraph = exports.get("paragraph"); - - int v = 0; - try { - v = Integer.parseInt(exports.get("version")); - } catch (NumberFormatException e) { - } - version = v; - - prettyVersion = exports.get("prettyVersion"); - - this.link = exports.get("download"); - } - - public boolean isInstalled() { - return false; - } - - public ContributionType getType() { - return type; - } - - public String getTypeName() { - return type.toString(); - } - - public String getCategory() { - return category; - } - - public String getName() { - return name; - } - - public String getAuthorList() { - return authorList; - } - - public String getUrl() { - return url; - } - - public String getSentence() { - return sentence; - } - - public String getParagraph() { - return paragraph; - } - - public int getVersion() { - return version; - } - - public String getPrettyVersion() { - return prettyVersion; - } - - public boolean writePropertiesFile(File propFile) { - try { - if (propFile.delete() && propFile.createNewFile() && propFile.setWritable(true)) { - //BufferedWriter bw = new BufferedWriter(new FileWriter(propFile)); - PrintWriter writer = PApplet.createWriter(propFile); - - writer.println("name=" + getName()); - writer.println("category=" + getCategory()); - writer.println("authorList=" + getAuthorList()); - writer.println("url=" + getUrl()); - writer.println("sentence=" + getSentence()); - writer.println("paragraph=" + getParagraph()); - writer.println("version=" + getVersion()); - writer.println("prettyVersion=" + getPrettyVersion()); - - writer.flush(); - writer.close(); - } - return true; - - } catch (FileNotFoundException e) { - e.printStackTrace(); - - } catch (IOException e) { - e.printStackTrace(); - } - return false; - } - } - - public boolean isDownloadingListing() { return downloadingListingLock.isLocked(); } diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index dd162d120..dfc7478d0 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -6,13 +6,10 @@ import java.text.SimpleDateFormat; import java.util.*; import java.util.zip.*; -import javax.swing.JOptionPane; - import processing.app.Base; import processing.app.Editor; import processing.app.Library; import processing.app.Preferences; -import processing.app.contrib.ContributionListing.AdvertisedContribution; interface ErrorWidget { @@ -102,26 +99,40 @@ public class ContributionManager { static public void downloadAndInstall(final Editor editor, final URL url, final AdvertisedContribution ad, - final JProgressMonitor downloadProgressMonitor, - final JProgressMonitor installProgressMonitor, + final JProgressMonitor downloadProgress, + final JProgressMonitor installProgress, final ErrorWidget statusBar) { new Thread(new Runnable() { public void run() { - final File libArchive = createTemporaryFile(url, statusBar); - FileDownloader.downloadFile(url, libArchive, downloadProgressMonitor); - if (!downloadProgressMonitor.isCanceled() && !downloadProgressMonitor.isError()) { - installProgressMonitor.startTask("Installing", ProgressMonitor.UNKNOWN); - InstalledContribution contribution = null; - contribution = install(editor, libArchive, ad, false, statusBar); + String filename = url.getFile(); + filename = filename.substring(filename.lastIndexOf('/') + 1); + try { + File contribZip = File.createTempFile("download", filename); + contribZip.setWritable(true); // necessary? - if (contribution != null) { - contribListing.replaceContribution(ad, contribution); - refreshInstalled(editor); + try { + ContributionListing.download(url, contribZip, downloadProgress); + + if (!downloadProgress.isCanceled() && !downloadProgress.isError()) { + installProgress.startTask("Installing", ProgressMonitor.UNKNOWN); + InstalledContribution contribution = null; + contribution = ad.install(editor, contribZip, false, statusBar); + + if (contribution != null) { + contribListing.replaceContribution(ad, contribution); + refreshInstalled(editor); + } + installProgress.finished(); + } + contribZip.delete(); + + } catch (Exception e) { + statusBar.setErrorMessage("Error during download and install."); } - installProgressMonitor.finished(); + } catch (IOException e) { + statusBar.setErrorMessage("Could not write to temporary directory."); } - libArchive.delete(); } }).start(); } @@ -133,23 +144,23 @@ public class ContributionManager { } - /** - * Used after unpacking a contrib download do determine the file contents. - */ - static List discover(ContributionType type, File tempDir) { - switch (type) { - case LIBRARY: - return Library.discover(tempDir); -// case LIBRARY_COMPILATION: -// // XXX Implement -// return null; - case TOOL: - return ToolContribution.discover(tempDir); - case MODE: - return ModeContribution.discover(tempDir); - } - return null; - } +// /** +// * Used after unpacking a contrib download do determine the file contents. +// */ +// static List discover(ContributionType type, File tempDir) { +// switch (type) { +// case LIBRARY: +// return Library.discover(tempDir); +//// case LIBRARY_COMPILATION: +//// // XXX Implement +//// return null; +// case TOOL: +// return ToolContribution.discover(tempDir); +// case MODE: +// return ModeContribution.discover(tempDir); +// } +// return null; +// } // static String getPropertiesFileName(Type type) { @@ -182,7 +193,7 @@ public class ContributionManager { } - static InstalledContribution create(Base base, File folder, ContributionType type) { + static InstalledContribution load(Base base, File folder, ContributionType type) { switch (type) { case LIBRARY: return new Library(folder); @@ -239,31 +250,55 @@ public class ContributionManager { * the library when a library by the same name already exists * @return */ + /* static public InstalledContribution install(Editor editor, File libFile, AdvertisedContribution ad, boolean confirmReplace, ErrorWidget statusBar) { + + ContributionType type = ad.getType(); + + // Unzip the file into the modes, tools, or libraries folder inside the + // sketchbook. Unzipping to /tmp is problematic because it may be on + // another file system, so move/rename operations will break. + File sketchbookContribFolder = + getSketchbookContribFolder(editor.getBase(), type); + File tempFolder = null; + + try { + tempFolder = + Base.createTempFolder(type.toString(), "tmp", sketchbookContribFolder); + } catch (IOException e) { + statusBar.setErrorMessage("Could not create a temporary folder to install."); + return null; + } + ContributionManager.unzip(libFile, tempFolder); - File tempDir = ContributionManager.unzipFileToTemp(libFile, statusBar); - List libFolders = ContributionManager.discover(ad.getType(), tempDir); - InstalledContribution outgoing = null; - - if (libFolders.isEmpty()) { - // Sometimes library authors place all their folders in the base - // directory of a zip file instead of in single folder as the - // guidelines suggest. If this is the case, we might be able to find the - // library by stepping up a directory and searching for libraries again. - libFolders = ContributionManager.discover(ad.getType(), tempDir.getParentFile()); + // Now go looking for a legit contrib inside what's been unpacked. + File contribFolder = null; + + // Sometimes contrib authors place all their folders in the base directory + // of the .zip file instead of in single folder as the guidelines suggest. + if (InstalledContribution.isCandidate(tempFolder, type)) { + contribFolder = tempFolder; } - if (libFolders != null && libFolders.size() == 1) { - File libFolder = libFolders.get(0); -// File propFile = new File(libFolder, getPropertiesFileName(ad.getType())); - File propFile = new File(libFolder, ad.getTypeName() + ".properties"); + if (contribFolder == null) { + // Find the first legitimate looking folder in what we just unzipped + contribFolder = InstalledContribution.findCandidate(tempFolder, type); + } + + InstalledContribution outgoing = null; + + if (contribFolder == null) { + statusBar.setErrorMessage("Could not find a " + type + " in the downloaded file."); + + } else { + File propFile = new File(contribFolder, type + ".properties"); if (ad.writePropertiesFile(propFile)) { InstalledContribution newContrib = - ContributionManager.create(editor.getBase(), libFolder, ad.getType()); + ContributionManager.create(editor.getBase(), contribFolder, type); outgoing = ContributionManager.installContribution(editor, newContrib, confirmReplace, @@ -271,24 +306,15 @@ public class ContributionManager { } else { statusBar.setErrorMessage("Error overwriting .properties file."); } - } else { - // Diagnose the problem and notify the user - if (libFolders == null) { - statusBar.setErrorMessage("An internal error occured while searching " - + "for contributions in the downloaded file."); - } else if (libFolders.isEmpty()) { - statusBar.setErrorMessage("Maybe it's just me, but it looks like " + - "there are no contributions in the file " + - "for \"" + ad.getName() + ".\""); - } else { - statusBar.setErrorMessage("There were multiple libraries in the file, " + - "so we're ignoring it."); - } } - Base.removeDir(tempDir); + // Remove any remaining boogers + if (tempFolder.exists()) { + Base.removeDir(tempFolder); + } return outgoing; } + */ /** @@ -297,19 +323,21 @@ public class ContributionManager { * ask the user if it's okay to replace the library. If false, the * library is always replaced with the new copy. */ + /* static public InstalledContribution installContribution(Editor editor, InstalledContribution newContrib, boolean confirmReplace, ErrorWidget statusBar) { + ArrayList oldContribs = + getContributions(newContrib.getType(), editor); + + String contribFolderName = newContrib.getFolder().getName(); - ArrayList oldContribs = getContributions(newContrib.getType(), editor); - String libFolderName = newContrib.getFolder().getName(); - - File libraryDestination = - ContributionManager.getSketchbookContribFolder(editor.getBase(), newContrib.getType()); - File newContribDest = new File(libraryDestination, libFolderName); + File contribTypeFolder = + getSketchbookContribFolder(editor.getBase(), newContrib.getType()); + File contribFolder = new File(contribTypeFolder, contribFolderName); for (InstalledContribution oldContrib : oldContribs) { - if ((oldContrib.getFolder().exists() && oldContrib.getFolder().equals(newContribDest)) || + if ((oldContrib.getFolder().exists() && oldContrib.getFolder().equals(contribFolder)) || (oldContrib.getId() != null && oldContrib.getId().equals(newContrib.getId()))) { if (ContributionManager.requiresRestart(oldContrib)) { @@ -352,15 +380,15 @@ public class ContributionManager { } } - if (newContribDest.exists()) { - Base.removeDir(newContribDest); + if (contribFolder.exists()) { + Base.removeDir(contribFolder); } // Move newLib to the sketchbook library folder - if (newContrib.getFolder().renameTo(newContribDest)) { + if (newContrib.getFolder().renameTo(contribFolder)) { Base base = editor.getBase(); - /* InstalledContribution contrib = */ - ContributionManager.create(base, newContribDest, newContrib.getType()); + // InstalledContribution contrib = + ContributionManager.create(base, contribFolder, newContrib.getType()); // try { // initialize(contrib, base); // return contrib; @@ -391,6 +419,7 @@ public class ContributionManager { } return null; } + */ /* @@ -531,52 +560,52 @@ public class ContributionManager { } - static protected File createTemporaryFile(URL url, ErrorWidget statusBar) { - try { -// //File tmpFolder = Base.createTempFolder("library", "download", Base.getSketchbookLibrariesFolder()); -// String[] segments = url.getFile().split("/"); -// File libFile = new File(tmpFolder, segments[segments.length - 1]); - String filename = url.getFile(); - filename = filename.substring(filename.lastIndexOf('/') + 1); - File libFile = File.createTempFile("download", filename, Base.getSketchbookLibrariesFolder()); - libFile.setWritable(true); - return libFile; - - } catch (IOException e) { - statusBar.setErrorMessage("Could not create a temp folder for download."); - } - return null; - } +// static protected File createTemporaryFile(URL url, ErrorWidget statusBar) { +// try { +//// //File tmpFolder = Base.createTempFolder("library", "download", Base.getSketchbookLibrariesFolder()); +//// String[] segments = url.getFile().split("/"); +//// File libFile = new File(tmpFolder, segments[segments.length - 1]); +// String filename = url.getFile(); +// filename = filename.substring(filename.lastIndexOf('/') + 1); +// File libFile = File.createTempFile("download", filename, Base.getSketchbookLibrariesFolder()); +// libFile.setWritable(true); +// return libFile; +// +// } catch (IOException e) { +// statusBar.setErrorMessage("Could not create a temp folder for download."); +// } +// return null; +// } - /** - * Creates a temporary folder and unzips a file to a subdirectory of the temp - * folder. The subdirectory is the only file of the tempo folder. - * - * e.g. if the contents of foo.zip are /hello and /world, then the resulting - * files will be - * /tmp/foo9432423uncompressed/foo/hello - * /tmp/foo9432423uncompress/foo/world - * ...and "/tmp/id9432423uncompress/foo/" will be returned. - * - * @return the folder where the zips contents have been unzipped to (the - * subdirectory of the temp folder). - */ - static public File unzipFileToTemp(File libFile, ErrorWidget statusBar) { - String fileName = ContributionManager.getFileName(libFile); - File tmpFolder = null; - - try { - tmpFolder = Base.createTempFolder(fileName, "uncompressed", Base.getSketchbookLibrariesFolder()); -// tmpFolder = new File(tmpFolder, fileName); // don't make another subdirectory -// tmpFolder.mkdirs(); - } catch (IOException e) { - statusBar.setErrorMessage("Could not create temp folder to uncompress zip file."); - } - - ContributionManager.unzip(libFile, tmpFolder); - return tmpFolder; - } +// /** +// * Creates a temporary folder and unzips a file to a subdirectory of the temp +// * folder. The subdirectory is the only file of the tempo folder. +// * +// * e.g. if the contents of foo.zip are /hello and /world, then the resulting +// * files will be +// * /tmp/foo9432423uncompressed/foo/hello +// * /tmp/foo9432423uncompress/foo/world +// * ...and "/tmp/id9432423uncompress/foo/" will be returned. +// * +// * @return the folder where the zips contents have been unzipped to (the +// * subdirectory of the temp folder). +// */ +// static public File unzipFileToTemp(File libFile, ErrorWidget statusBar) { +// String fileName = ContributionManager.getFileName(libFile); +// File tmpFolder = null; +// +// try { +// tmpFolder = Base.createTempFolder(fileName, "uncompressed", Base.getSketchbookLibrariesFolder()); +//// tmpFolder = new File(tmpFolder, fileName); // don't make another subdirectory +//// tmpFolder.mkdirs(); +// } catch (IOException e) { +// statusBar.setErrorMessage("Could not create temp folder to uncompress zip file."); +// } +// +// ContributionManager.unzip(libFile, tmpFolder); +// return tmpFolder; +// } /** diff --git a/app/src/processing/app/contrib/ContributionType.java b/app/src/processing/app/contrib/ContributionType.java index 43ac5b3d8..e6916c114 100644 --- a/app/src/processing/app/contrib/ContributionType.java +++ b/app/src/processing/app/contrib/ContributionType.java @@ -33,6 +33,11 @@ public enum ContributionType { } return null; // should be unreachable } + + +// public String getPropertiesName() { +// return toString() + ".properties"; +// } static public ContributionType fromName(String s) { diff --git a/app/src/processing/app/contrib/FileDownloader.java b/app/src/processing/app/contrib/FileDownloader.java deleted file mode 100644 index 005780514..000000000 --- a/app/src/processing/app/contrib/FileDownloader.java +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2004-11 Ben Fry and Casey Reas - Copyright (c) 2001-04 Massachusetts Institute of Technology - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package processing.app.contrib; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; - -public class FileDownloader { - - /** - * Blocks until the file is downloaded or an error occurs. - * Returns true if the file was successfully downloaded, false otherwise. - * - * @param source - * the URL of the file to download - * @param dest - * the file on the local system where the file will be written. This - * must be a file (not a directory), and must already exist. - * @param progress - * @throws FileNotFoundException - * if an error occurred downloading the file - */ - static public void downloadFile(URL source, File dest, - ProgressMonitor progress) { - try { -// System.out.println("downloading file " + source); - URLConnection conn = source.openConnection(); - conn.setConnectTimeout(1000); - conn.setReadTimeout(5000); - - // TODO this is often -1, may need to set progress to indeterminate - int fileSize = conn.getContentLength(); -// System.out.println("file size is " + fileSize); - progress.startTask("Downloading", fileSize); - - InputStream in = conn.getInputStream(); - FileOutputStream out = new FileOutputStream(dest); - - byte[] b = new byte[8192]; - int amount; - int total = 0; - while (!progress.isCanceled() && (amount = in.read(b)) != -1) { - out.write(b, 0, amount); - total += amount; - progress.setProgress(total); - } - out.flush(); - out.close(); - - } catch (IOException ioe) { - progress.error(ioe); - ioe.printStackTrace(); - } - progress.finished(); -// System.out.println("done downloading"); - } -} diff --git a/app/src/processing/app/contrib/InstalledContribution.java b/app/src/processing/app/contrib/InstalledContribution.java index b10ae77bf..518ec2dc4 100644 --- a/app/src/processing/app/contrib/InstalledContribution.java +++ b/app/src/processing/app/contrib/InstalledContribution.java @@ -23,17 +23,17 @@ package processing.app.contrib; import java.io.*; -import java.lang.reflect.Field; import java.net.URL; import java.net.URLClassLoader; import java.util.*; import java.util.zip.*; +import javax.swing.JOptionPane; + import processing.app.*; public abstract class InstalledContribution implements Contribution { - protected String name; // "pdf" or "PDF Export" protected String id; // 1 protected String category; // "Sound" @@ -112,7 +112,7 @@ public abstract class InstalledContribution implements Contribution { if (archives != null && archives.length > 0) { URL[] urlList = new URL[archives.length]; for (int j = 0; j < urlList.length; j++) { - Base.log("found lib: " + archives[j] + " for " + getName()); + Base.log("Found archive " + archives[j] + " for " + getName()); urlList[j] = archives[j].toURI().toURL(); } // loader = new URLClassLoader(urlList, Thread.currentThread().getContextClassLoader()); @@ -131,6 +131,7 @@ public abstract class InstalledContribution implements Contribution { } + /* // doesn't work with URLClassLoader, but works with the system CL static void listClasses(ClassLoader loader) { // loader = Thread.currentThread().getContextClassLoader(); @@ -145,23 +146,117 @@ public abstract class InstalledContribution implements Contribution { e.printStackTrace(); } } + */ + static protected boolean isCandidate(File potential, final ContributionType type) { + return (potential.isDirectory() && + new File(potential, type.getFolderName()).exists()); + } + + /** * Return a list of directories that have the necessary subfolder for this * contribution type. For instance, a list of folders that have a 'mode' * subfolder if this is a ModeContribution. */ - static protected File[] listCandidates(File folder, final String typeName) { + static protected File[] listCandidates(File folder, final ContributionType type) { return folder.listFiles(new FileFilter() { public boolean accept(File potential) { - return (potential.isDirectory() && - new File(potential, typeName).exists()); + return isCandidate(potential, type); } }); } + /** + * Return the first directory that has the necessary subfolder for this + * contribution type. For instance, the first folder that has a 'mode' + * subfolder if this is a ModeContribution. + */ + static protected File findCandidate(File folder, final ContributionType type) { + File[] folders = listCandidates(folder, type); + + if (folders.length == 0) { + return null; + + } else if (folders.length > 1) { + Base.log("More than one " + type.toString() + " found inside " + folder.getAbsolutePath()); + } + return folders[0]; + } + + + InstalledContribution install(Editor editor, + boolean confirmReplace, + ErrorWidget statusBar) { + ArrayList oldContribs = + ContributionManager.getContributions(getType(), editor); + + String contribFolderName = getFolder().getName(); + + File contribTypeFolder = + ContributionManager.getSketchbookContribFolder(editor.getBase(), getType()); + File contribFolder = new File(contribTypeFolder, contribFolderName); + + for (InstalledContribution oldContrib : oldContribs) { + if ((oldContrib.getFolder().exists() && oldContrib.getFolder().equals(contribFolder)) || + (oldContrib.getId() != null && oldContrib.getId().equals(getId()))) { + + if (ContributionManager.requiresRestart(oldContrib)) { + // XXX: We can't replace stuff, soooooo.... do something different + if (!ContributionManager.backupContribution(editor, oldContrib, false, statusBar)) { + return null; + } + } else { + int result = 0; + boolean doBackup = Preferences.getBoolean("contribution.backup.on_install"); + if (confirmReplace) { + if (doBackup) { + result = Base.showYesNoQuestion(editor, "Replace", + "Replace pre-existing \"" + oldContrib.getName() + "\" library?", + "A pre-existing copy of the \"" + oldContrib.getName() + "\" library
"+ + "has been found in your sketchbook. Clicking “Yes”
"+ + "will move the existing library to a backup folder
" + + " in libraries/old before replacing it."); + if (result != JOptionPane.YES_OPTION || !ContributionManager.backupContribution(editor, oldContrib, true, statusBar)) { + return null; + } + } else { + result = Base.showYesNoQuestion(editor, "Replace", + "Replace pre-existing \"" + oldContrib.getName() + "\" library?", + "A pre-existing copy of the \"" + oldContrib.getName() + "\" library
"+ + "has been found in your sketchbook. Clicking “Yes”
"+ + "will permanently delete this library and all of its contents
"+ + "before replacing it."); + if (result != JOptionPane.YES_OPTION || !oldContrib.getFolder().delete()) { + return null; + } + } + } else { + if ((doBackup && !ContributionManager.backupContribution(editor, oldContrib, true, statusBar)) || + (!doBackup && !oldContrib.getFolder().delete())) { + return null; + } + } + } + } + } + + // At this point it should be safe to replace this fella + if (contribFolder.exists()) { + Base.removeDir(contribFolder); + } + + if (!getFolder().renameTo(contribFolder)) { + statusBar.setErrorMessage("Could not move " + getTypeName() + + " \"" + getName() + "\" to the sketchbook."); + return null; + } + return ContributionManager.load(editor.getBase(), contribFolder, getType()); + } + + public File getFolder() { return folder; } diff --git a/app/src/processing/app/contrib/ModeContribution.java b/app/src/processing/app/contrib/ModeContribution.java index 6affb57f6..b45195b65 100644 --- a/app/src/processing/app/contrib/ModeContribution.java +++ b/app/src/processing/app/contrib/ModeContribution.java @@ -25,49 +25,22 @@ package processing.app.contrib; import java.io.File; //import java.io.FileFilter; import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.*; import java.util.HashMap; -import java.util.List; import processing.app.Base; import processing.app.Mode; public class ModeContribution extends InstalledContribution { -// static final String propertiesFileName = "mode.properties"; - - /** Class name with package declaration. */ -// private String className; private Mode mode; -// Base base; -// static public Mode getCoreMode(Base base, String className, File folder) { -// try { -// Class c = Thread.currentThread().getContextClassLoader().loadClass(className); -//// Class c = Class.forName(classname); -// Constructor cc = c.getConstructor(Base.class, File.class); -// return (Mode) cc.newInstance(base, folder); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// return null; -// } - - -// static public ModeContribution getContributedMode(Base base, File folder) { -// ModeContribution mode = new ModeContribution(base, folder); -// return mode.isValid() ? mode : null; -// } - -// static public ModeContribution load(Base base, File folder) { static public ModeContribution load(Base base, File folder) { return load(base, folder, null); } -// static public ModeContribution load(Base base, File folder, String searchName) { static public ModeContribution load(Base base, File folder, String searchName) { try { @@ -109,69 +82,30 @@ public class ModeContribution extends InstalledContribution { mode.setupGUI(); } } - -// // Class name already found above, go ahead and instantiate -// if (className != null) { -//// try { -// System.out.println("instantiating " + className + " using loader " + loader); -//// Class modeClass = Class.forName(className, true, loader); -// modeClass = loader.loadClass(className); -//// return true; -//// } catch (Exception e) { -//// e.printStackTrace(); -//// } -//// return false; -//// } -// -// } else { // className == null, might be a built-in fella -// System.out.println("class name null while looking for " + searchName); -//// try { -// // Probably a contributed mode, check to see if it's already available -// if (loader.loadClass(searchName) != null) { -// System.out.println(" found " + searchName + " after all"); -// className = searchName; -// } -//// } catch (ClassNotFoundException e) { -//// e.printStackTrace(); -//// } -// } -// -// if (modeClass != null) { -// Constructor con = modeClass.getConstructor(Base.class, File.class); -// mode = (Mode) con.newInstance(base, folder); -// mode.setupGUI(); -// } } -// private boolean isValid() { -// return className != null; -// } + static public void loadMissing(Base base) { + File modesFolder = Base.getSketchbookModesFolder(); + ArrayList contribModes = base.getModeContribs(); - -// /** -// * Creates an instance of the Mode object. Warning: this makes it impossible -// * (on Windows) to move the files in the mode's classpath without restarting -// * the PDE. -// */ -// public boolean instantiateModeClass(Base base) { -// new Exception().printStackTrace(System.out); -// try { -// System.out.println("instantiating " + className + " using loader " + loader); -//// Class modeClass = Class.forName(className, true, loader); -// Class modeClass = loader != null ? -// loader.loadClass(className) : -// Thread.currentThread().getContextClassLoader().loadClass(className); -// Constructor contr = modeClass.getConstructor(Base.class, File.class); -// mode = (Mode) contr.newInstance(base, folder); -// mode.setupGUI(); -// return true; -// } catch (Exception e) { -// e.printStackTrace(); -// } -// -// return false; -// } + HashMap existing = new HashMap(); + for (ModeContribution contrib : contribModes) { + existing.put(contrib.getFolder(), contrib); + } + File[] potential = listCandidates(modesFolder, ContributionType.MODE); + for (File folder : potential) { + if (!existing.containsKey(folder)) { + try { + contribModes.add(new ModeContribution(base, folder, null)); + } catch (IgnorableException ig) { + Base.log(ig.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } public Mode getMode() { @@ -189,80 +123,16 @@ public class ModeContribution extends InstalledContribution { return false; } ModeContribution other = (ModeContribution) o; -// return loader.equals(other.loader) && className.equals(other.className); return loader.equals(other.loader) && mode.equals(other.getMode()); } - static public void loadMissing(Base base) { - File modesFolder = Base.getSketchbookModesFolder(); - ArrayList contribModes = base.getModeContribs(); - - HashMap existing = new HashMap(); - for (ModeContribution contrib : contribModes) { - existing.put(contrib.getFolder(), contrib); - } - File[] potential = listCandidates(modesFolder, "mode"); - for (File folder : potential) { - if (!existing.containsKey(folder)) { - try { - contribModes.add(new ModeContribution(base, folder, null)); - } catch (IgnorableException ig) { - Base.log(ig.getMessage()); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - - -// static public ArrayList loadAll(Base base, File folder) { -// ArrayList modes = new ArrayList(); -// ArrayList modeFolders = discover(folder); -// -// for (File potentialModeFolder : modeFolders) { -// //ModeContribution contrib = getContributedMode(base, potentialModeFolder); -// ModeContribution contrib = load(base, potentialModeFolder); -// if (contrib != null) { -// modes.add(contrib); -// } -// } -// return modes; -// } - - -// static protected ArrayList discover(File folder) { -// ArrayList modeFolders = new ArrayList(); -//// discover(folder, modeFolders); -//// return modeFolders; -//// } -//// -//// -//// static protected void discover(File folder, ArrayList modeFolders) { +// static protected List discover(File folder) { // File[] folders = listCandidates(folder, "mode"); -//// File[] folders = folder.listFiles(new FileFilter() { -//// public boolean accept(File potentialModeFolder) { -//// return (potentialModeFolder.isDirectory() && -//// new File(potentialModeFolder, "mode").exists()); -//// } -//// }); -// -// if (folders != null && folders.length > 0) { -// for (File potentialModeFolder : folders) { -// modeFolders.add(potentialModeFolder); -// } +// if (folders == null) { +// return new ArrayList(); +// } else { +// return Arrays.asList(folders); // } -// return modeFolders; // } - - - static protected List discover(File folder) { - File[] folders = listCandidates(folder, "mode"); - if (folders == null) { - return new ArrayList(); - } else { - return Arrays.asList(folders); - } - } } diff --git a/app/src/processing/app/contrib/ToolContribution.java b/app/src/processing/app/contrib/ToolContribution.java index a8f156740..e1ae33ca4 100644 --- a/app/src/processing/app/contrib/ToolContribution.java +++ b/app/src/processing/app/contrib/ToolContribution.java @@ -33,9 +33,6 @@ import processing.app.tools.Tool; public class ToolContribution extends InstalledContribution implements Tool { -// static String propertiesFileName = "tool.properties"; - -// private String className; private Tool tool; @@ -59,73 +56,21 @@ public class ToolContribution extends InstalledContribution implements Tool { Class toolClass = loader.loadClass(className); tool = (Tool) toolClass.newInstance(); } - -// File toolDirectory = new File(folder, "tool"); -// // add dir to classpath for .classes -// //urlList.add(toolDirectory.toURL()); -// -// // add .jar files to classpath -// File[] archives = Base.listJarFiles(toolDirectory); -//// File[] archives = toolDirectory.listFiles(new FilenameFilter() { -//// public boolean accept(File dir, String name) { -//// return (name.toLowerCase().endsWith(".jar") || -//// name.toLowerCase().endsWith(".zip")); -//// } -//// }); -// -// if (archives != null && archives.length > 0) { -// try { -// URL[] urlList = new URL[archives.length]; -// for (int j = 0; j < urlList.length; j++) { -// urlList[j] = archives[j].toURI().toURL(); -// } -// loader = new URLClassLoader(urlList); -// -// for (int j = 0; j < archives.length; j++) { -// className = findClassInZipFile(folder.getName(), archives[j]); -// if (className != null) break; -// } -// } catch (MalformedURLException e) { } -// } -// -// /* -// // Alternatively, could use manifest files with special attributes: -// // http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html -// // Example code for loading from a manifest file: -// // http://forums.sun.com/thread.jspa?messageID=3791501 -// File infoFile = new File(toolDirectory, "tool.txt"); -// if (!infoFile.exists()) continue; -// -// String[] info = PApplet.loadStrings(infoFile); -// //Main-Class: org.poo.shoe.AwesomerTool -// //String className = folders[i].getName(); -// String className = null; -// for (int k = 0; k < info.length; k++) { -// if (info[k].startsWith(";")) continue; -// -// String[] pieces = PApplet.splitTokens(info[k], ": "); -// if (pieces.length == 2) { -// if (pieces[0].equals("Main-Class")) { -// className = pieces[1]; -// } -// } -// } -// */ } - static protected List discover(File folder) { - File[] folders = listCandidates(folder, "tool"); - if (folders == null) { - return new ArrayList(); - } else { - return Arrays.asList(folders); - } - } +// static protected List discover(File folder) { +// File[] folders = listCandidates(folder, "tool"); +// if (folders == null) { +// return new ArrayList(); +// } else { +// return Arrays.asList(folders); +// } +// } static public ArrayList loadAll(File toolsFolder) { - List list = discover(toolsFolder); + File[] list = listCandidates(toolsFolder, ContributionType.TOOL); ArrayList outgoing = new ArrayList(); for (File folder : list) { try { @@ -141,94 +86,6 @@ public class ToolContribution extends InstalledContribution implements Tool { } -// /** -// * @return true if a Tool class of the expected name was found in this tool's -// * classpath -// */ -// private boolean isValid() { -// return className != null; -// } - - -// /** -// * Loads the tool, making it impossible (on Windows) to move the files in the -// * classpath without restarting the PDE. -// */ -// public void initializeToolClass() throws Exception { -// Class toolClass = Class.forName(className, true, loader); -// tool = (Tool) toolClass.newInstance(); -// } - - -// /** -// * Searches and returns a list of tools found in the immediate children of the -// * given folder. -// * @param doInitializeToolClass -// * true if tools should be initialized before they are returned. -// * Tools that failed to initialize for whatever reason are not -// * returned -// */ -// public static ArrayList list(File folder, boolean doInitializeToolClass) { -// ArrayList toolsFolders = ToolContribution.discover(folder); -// -// ArrayList tools = new ArrayList(); -// for (File toolFolder : toolsFolders) { -// final ToolContribution tool = ToolContribution.load(toolFolder); -// if (tool != null) { -// try { -// if (doInitializeToolClass) { -// tool.initializeToolClass(); -// } -// tools.add(tool); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// } -// } -// return tools; -// } - - -// static protected ArrayList discover(File folder) { -// ArrayList tools = new ArrayList(); -// discover(folder, tools); -// return tools; -// } -// -// -// static protected void discover(File folder, ArrayList toolFolders) { -// File[] folders = folder.listFiles(new FileFilter() { -// public boolean accept(File folder) { -// if (folder.isDirectory()) { -// //System.out.println("checking " + folder); -// File subfolder = new File(folder, "tool"); -// return subfolder.exists(); -// } -// return false; -// } -// -//// private boolean toolAlreadyExists(File folder) { -//// boolean exists = true; -//// for (ToolContribution contrib : tools) { -//// if (contrib.getFolder().equals(folder)) { -//// exists = false; -//// } -//// } -//// return exists; -//// } -// }); -// -// if (folders != null) { -// for (int i = 0; i < folders.length; i++) { -// Tool tool = ToolContribution.load(folders[i]); -// -// if (tool != null) -// toolFolders.add(folders[i]); -// } -// } -// } - - public void init(Editor editor) { tool.init(editor); } diff --git a/app/src/processing/mode/java/JavaMode.java b/app/src/processing/mode/java/JavaMode.java index 65e9995d2..4e7aff444 100644 --- a/app/src/processing/mode/java/JavaMode.java +++ b/app/src/processing/mode/java/JavaMode.java @@ -100,7 +100,7 @@ public class JavaMode extends Mode { public Library getCoreLibrary() { if (coreLibrary == null) { File coreFolder = Base.getContentFile("core"); - coreLibrary = new Library(coreFolder, null); + coreLibrary = new Library(coreFolder); } return coreLibrary; }