Support for tools in the contribution manager.

This commit is contained in:
pesckal
2011-07-25 03:07:55 +00:00
parent 904a8c01b4
commit 98da801fa0
6 changed files with 387 additions and 177 deletions

View File

@@ -1683,6 +1683,11 @@ public class Base {
}
public File getSketchbookToolsFolder() {
return new File(sketchbookFolder, "tools");
}
static protected File getDefaultSketchbookFolder() {
File sketchbookFolder = null;
try {

View File

@@ -100,21 +100,13 @@ public class ContributionListPanel extends JPanel implements Scrollable, Contrib
public void contributionAdded(ContributionInfo contributionInfo) {
if (setupProgressBar.isVisible()) {
setupProgressBar.setVisible(false);
}
setupProgressBar.setVisible(false);
if (contributionPanelsByInfo.containsKey(contributionInfo)) {
return;
}
ContributionPanel newPanel = null;
if (contributionInfo.getType() == ContributionType.LIBRARY) {
newPanel = new ContributionPanel();
} else if (contributionInfo.getType() == ContributionType.LIBRARY_COMPILATION) {
newPanel = new ContributionPanel();
}
ContributionPanel newPanel = new ContributionPanel();
synchronized (contributionPanelsByInfo) {
contributionPanelsByInfo.put(contributionInfo, newPanel);

View File

@@ -37,6 +37,7 @@ import processing.app.Contribution.ContributionInfo.Author;
import processing.app.Contribution.ContributionInfo.ContributionType;
import processing.app.Library.LibraryInfo;
import processing.app.LibraryCompilation.LibraryCompilationInfo;
import processing.app.ToolContribution.ToolInfo;
public class ContributionListing {
@@ -141,21 +142,21 @@ public class ContributionListing {
notifyChange(oldLib, newLib);
}
public void addContribution(ContributionInfo libInfo) {
public void addContribution(ContributionInfo info) {
if (librariesByCategory.containsKey(libInfo.category)) {
List<ContributionInfo> list = librariesByCategory.get(libInfo.category);
list.add(libInfo);
if (librariesByCategory.containsKey(info.category)) {
List<ContributionInfo> list = librariesByCategory.get(info.category);
list.add(info);
Collections.sort(list);
} else {
ArrayList<ContributionInfo> libs = new ArrayList<ContributionInfo>();
libs.add(libInfo);
librariesByCategory.put(libInfo.category, libs);
ArrayList<ContributionInfo> list = new ArrayList<ContributionInfo>();
list.add(info);
librariesByCategory.put(info.category, list);
}
allLibraries.add(libInfo);
allLibraries.add(info);
notifyAdd(libInfo);
notifyAdd(info);
Collections.sort(allLibraries);
}
@@ -361,7 +362,12 @@ public class ContributionListing {
*/
private static class ContributionXmlParser extends DefaultHandler {
ArrayList<ContributionInfo> libraries;
final static String LIBRARY_TAG = "library";
final static String LIBRARY_COMPILATION_TAG = "librarycompilation";
final static String TOOL_TAG = "tool";
//final static String MODE_TAG = "mode";
ArrayList<ContributionInfo> contributions;
String currentCategoryName;
@@ -376,7 +382,7 @@ public class ContributionListing {
InputSource input = new InputSource(new FileReader(xmlFile));
libraries = new ArrayList<ContributionInfo>();
contributions = new ArrayList<ContributionInfo>();
sp.parse(input, this); // throws SAXException
} catch (ParserConfigurationException e) {
@@ -393,12 +399,12 @@ public class ContributionListing {
"The list of libraries downloaded from Processing.org\n" +
"appears to be malformed. You can still install libraries\n" +
"manually while we work on fixing this.", e);
libraries = null;
contributions = null;
}
}
public ArrayList<ContributionInfo> getLibraries() {
return libraries;
return contributions;
}
@Override
@@ -408,11 +414,11 @@ public class ContributionListing {
if ("category".equals(qName)) {
currentCategoryName = attributes.getValue("name");
} else if ("library".equals(qName)) {
} else if (LIBRARY_TAG.equals(qName)) {
currentInfo = new LibraryInfo();
setCommonAttributes(attributes);
} else if ("librarycompilation".equals(qName)) {
} else if (LIBRARY_COMPILATION_TAG.equals(qName)) {
LibraryCompilationInfo compilationInfo = new LibraryCompilationInfo();
String[] names = attributes.getValue("libraryNames").split(";");
for (int i = 0; i < names.length; i++) {
@@ -422,6 +428,10 @@ public class ContributionListing {
currentInfo = compilationInfo;
setCommonAttributes(attributes);
} else if (TOOL_TAG.equals(qName)) {
currentInfo = new ToolInfo();
setCommonAttributes(attributes);
} else if ("author".equals(qName)) {
Author author = new Author();
author.name = attributes.getValue("name");
@@ -454,8 +464,9 @@ public class ContributionListing {
public void endElement(String uri, String localName, String qName)
throws SAXException {
if ("library".equals(qName) || "librarycompilation".equals(qName)) {
libraries.add(currentInfo);
if (LIBRARY_TAG.equals(qName) || LIBRARY_COMPILATION_TAG.equals(qName)
|| TOOL_TAG.equals(qName)) {
contributions.add(currentInfo);
currentInfo = null;
}
}

View File

@@ -309,18 +309,21 @@ public class ContributionManager {
ArrayList<Library> libraries = editor.getMode().contribLibraries;
ArrayList<LibraryCompilation> compilations = LibraryCompilation.list(libraries);
// Remove libraries from the list that are part of a compilations
for (LibraryCompilation compilation : compilations) {
for (Library lib : compilation.libraries) {
libraries.remove(lib);
}
}
ArrayList<Contribution> contributions = new ArrayList<Contribution>();
contributions.addAll(editor.contribTools);
contributions.addAll(libraries);
contributions.addAll(compilations);
ArrayList<ContributionInfo> infoList = new ArrayList<ContributionInfo>();
for (Library library : libraries) {
infoList.add(library.info);
}
for (LibraryCompilation compilation : compilations) {
infoList.add(compilation.info);
for (Contribution contribution : contributions) {
infoList.add(contribution.getInfo());
}
contributionListing.updateInstalledList(infoList);
@@ -372,19 +375,22 @@ public class ContributionManager {
public void run() {
File libFile = downloader.getFile();
File contributionFile = downloader.getFile();
if (libFile != null) {
if (contributionFile != null) {
installProgressMonitor.startTask("Installing",
ProgressMonitor.UNKNOWN);
Contribution contribution = null;
switch (info.getType()) {
case LIBRARY:
contribution = installLibrary(libFile, false);
contribution = installLibrary(contributionFile, false);
break;
case LIBRARY_COMPILATION:
contribution = installLibraryCompilation(libFile);
contribution = installLibraryCompilation(contributionFile);
break;
case TOOL:
contribution = installTool(contributionFile);
break;
}
@@ -531,6 +537,71 @@ public class ContributionManager {
return fileName;
}
protected ToolContribution installTool(File zippedToolFile) {
File tempDir = unzipFileToTemp(zippedToolFile);
ArrayList<ToolContribution> discoveredTools = ToolContribution.list(tempDir);
if (discoveredTools.isEmpty()) {
// Sometimes tool 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.
discoveredTools = ToolContribution.list(tempDir.getParentFile());
}
if (discoveredTools != null && discoveredTools.size() == 1) {
ToolContribution discoveredTool = discoveredTools.get(0);
return installTool(discoveredTool);
} else {
// Diagnose the problem and notify the user
if (discoveredTools == null || discoveredTools.isEmpty()) {
Base.showWarning(DISCOVERY_ERROR_TITLE,
DISCOVERY_INTERNAL_ERROR_MESSAGE, null);
} else {
Base.showWarning("Too many tools",
"We found more than one tool in the file we just\n"
+ "downloaded. That shouldn't happen, so we're going\n"
+ "to ignore this file.", null);
}
}
return null;
}
protected ToolContribution installTool(ToolContribution newTool) {
ArrayList<ToolContribution> oldTools = editor.contribTools;
String toolFolderName = newTool.folder.getName();
File toolDestination = editor.getBase().getSketchbookToolsFolder();
File newToolDest = new File(toolDestination, toolFolderName);
for (ToolContribution oldTool : oldTools) {
// XXX: Handle other cases when installing libraries.
// -What if a library by the same name is already installed?
// -What if newLibDest exists, but isn't used by an existing library?
if (oldTool.folder.exists() && oldTool.folder.equals(newToolDest)) {
if (!backupContribution(oldTool)) {
return null;
}
}
}
// Move newLib to the sketchbook library folder
if (newTool.folder.renameTo(newToolDest)) {
return ToolContribution.getTool(newToolDest);
} else {
Base.showWarning("Trouble moving new tool to the sketchbook",
"Could not move tool \"" + newTool.info.name + "\" to "
+ newToolDest.getAbsolutePath() + ".\n", null);
}
return null;
}
protected Library installLibrary(File libFile, boolean confirmReplace) {
File tempDir = unzipFileToTemp(libFile);
@@ -575,7 +646,6 @@ public class ContributionManager {
}
/**
*
* @param confirmReplace
* if true and the library is already installed, opens a prompt to
* ask the user if it's okay to replace the library. If false, the
@@ -639,30 +709,31 @@ public class ContributionManager {
}
public void refreshInstalled() {
editor.getMode().rebuildLibraryList();
editor.getMode().rebuildImportMenu();
editor.rebuildToolMenu();
}
/**
* Moves the given contribution to a backup folder.
*/
private boolean backupContribution(Contribution lib) {
private boolean backupContribution(Contribution contribution) {
File backupFolder = null;
switch (lib.getInfo().getType()) {
switch (contribution.getInfo().getType()) {
case LIBRARY:
case LIBRARY_COMPILATION:
backupFolder = createLibraryBackupFolder();
break;
case MODE:
case TOOL:
backupFolder = createToolBackupFolder();
break;
}
if (backupFolder == null) return false;
String libFolderName = lib.getFolder().getName();
String libFolderName = contribution.getFolder().getName();
String prefix = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
final String backupName = prefix + "_" + libFolderName;
@@ -671,21 +742,17 @@ public class ContributionManager {
// try {
// FileUtils.moveDirectory(lib.folder, backupFolderForLib);
// return true;
if (lib.getFolder().renameTo(backupFolderForLib)) {
if (contribution.getFolder().renameTo(backupFolderForLib)) {
return true;
} else {
// } catch (IOException e) {
Base.showWarning("Trouble creating backup of old \"" + lib.getInfo().name + "\" library",
Base.showWarning("Trouble creating backup of old \"" + contribution.getInfo().name + "\" library",
"Could not move library to backup folder:\n"
+ backupFolderForLib.getAbsolutePath(), null);
return false;
}
}
/**
* @return false if there was an error creating the backup folder, true if it
* already exists or was created successfully
*/
private File createLibraryBackupFolder() {
File libraryBackupFolder = new File(editor.getBase()
@@ -705,6 +772,26 @@ public class ContributionManager {
return libraryBackupFolder;
}
private File createToolBackupFolder() {
File toolsBackupFolder = new File(editor.getBase()
.getSketchbookToolsFolder(), "old");
if (!toolsBackupFolder.exists() || !toolsBackupFolder.isDirectory()) {
if (!toolsBackupFolder.mkdirs()) {
Base.showWarning("Trouble creating folder to store old libraries in",
"Could not create folder "
+ toolsBackupFolder.getAbsolutePath()
+ ".\n"
+ "That's gonna prevent us from replacing the library.",
null);
return null;
}
}
return toolsBackupFolder;
}
/**
* Returns a file in the parent folder that does not exist yet. If

View File

@@ -31,9 +31,7 @@ import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.print.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import javax.swing.event.*;
@@ -98,8 +96,11 @@ public abstract class Editor extends JFrame implements RunnerListener {
private final Stack<Integer> caretRedoStack = new Stack<Integer>();
private FindReplace find;
JMenu toolsMenu;
JMenu modeMenu;
ArrayList<ToolContribution> coreTools;
ArrayList<ToolContribution> contribTools;
protected Editor(final Base base, String path, int[] location, final Mode mode) {
super("Processing");
@@ -451,7 +452,9 @@ public abstract class Editor extends JFrame implements RunnerListener {
menubar.add(fileMenu);
menubar.add(buildEditMenu());
menubar.add(buildSketchMenu());
menubar.add(buildToolsMenu());
rebuildToolList();
rebuildToolMenu();
menubar.add(getToolMenu());
JMenu modeMenu = buildModeMenu();
if (modeMenu != null) {
@@ -804,109 +807,51 @@ public abstract class Editor extends JFrame implements RunnerListener {
abstract public void handleImportLibrary(String jarPath);
protected JMenu buildToolsMenu() {
JMenu menu = new JMenu("Tools");
addInternalTools(menu);
addTools(menu, base.getToolsFolder());
File sketchbookTools = new File(base.getSketchbookFolder(), "tools");
addTools(menu, sketchbookTools);
return menu;
public JMenu getToolMenu() {
if (toolsMenu == null) {
rebuildToolMenu();
}
return toolsMenu;
}
protected void addTools(JMenu menu, File sourceFolder) {
protected void rebuildToolList() {
coreTools = ToolContribution.list(base.getToolsFolder());
contribTools = ToolContribution.list(base.getSketchbookToolsFolder());
}
protected void rebuildToolMenu() {
if (toolsMenu == null) {
toolsMenu = new JMenu("Tools");
} else {
toolsMenu.removeAll();
}
rebuildToolList();
addInternalTools(toolsMenu);
addTools(toolsMenu, coreTools);
addTools(toolsMenu, contribTools);
}
protected void addTools(JMenu menu, ArrayList<ToolContribution> tools) {
HashMap<String, JMenuItem> toolItems = new HashMap<String, JMenuItem>();
File[] folders = sourceFolder.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();
for (final ToolContribution tool : tools) {
String title = tool.getMenuTitle();
JMenuItem item = new JMenuItem(title);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(tool);
//new Thread(tool).start();
}
return false;
}
});
if (folders == null || folders.length == 0) {
return;
}
for (int i = 0; i < folders.length; i++) {
File toolDirectory = new File(folders[i], "tool");
try {
// add dir to classpath for .classes
//urlList.add(toolDirectory.toURL());
// add .jar files to classpath
File[] archives = toolDirectory.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".jar") ||
name.toLowerCase().endsWith(".zip"));
}
});
URL[] urlList = new URL[archives.length];
for (int j = 0; j < urlList.length; j++) {
urlList[j] = archives[j].toURI().toURL();
}
URLClassLoader loader = new URLClassLoader(urlList);
String className = null;
for (int j = 0; j < archives.length; j++) {
className = findClassInZipFile(folders[i].getName(), archives[j]);
if (className != null) break;
}
/*
// 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];
}
}
}
*/
// If no class name found, just move on.
if (className == null) continue;
Class<?> toolClass = Class.forName(className, true, loader);
final Tool tool = (Tool) toolClass.newInstance();
tool.init(Editor.this);
String title = tool.getMenuTitle();
JMenuItem item = new JMenuItem(title);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(tool);
//new Thread(tool).start();
}
});
//menu.add(item);
toolItems.put(title, item);
} catch (Exception e) {
e.printStackTrace();
}
});
//menu.add(item);
toolItems.put(title, item);
}
ArrayList<String> toolList = new ArrayList<String>(toolItems.keySet());
if (toolList.size() == 0) return;
@@ -925,37 +870,6 @@ public abstract class Editor extends JFrame implements RunnerListener {
return null;
}
protected String findClassInZipFile(String base, File file) {
// Class file to search for
String classFileName = "/" + base + ".class";
try {
ZipFile zipFile = new ZipFile(file);
Enumeration<?> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
if (!entry.isDirectory()) {
String name = entry.getName();
//System.out.println("entry: " + name);
if (name.endsWith(classFileName)) {
//int slash = name.lastIndexOf('/');
//String packageName = (slash == -1) ? "" : name.substring(0, slash);
// Remove .class and convert slashes to periods.
return name.substring(0, name.length() - 6).replace('/', '.');
}
}
}
} catch (IOException e) {
//System.err.println("Ignoring " + filename + " (" + e.getMessage() + ")");
e.printStackTrace();
}
return null;
}
protected JMenuItem createToolMenuItem(String className) {
try {
Class<?> toolClass = Class.forName(className);

View File

@@ -0,0 +1,201 @@
package processing.app;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.zip.*;
import processing.app.tools.Tool;
public class ToolContribution extends Contribution implements Tool {
Tool tool;
ToolInfo info;
File folder;
static public ToolContribution getTool(File folder) {
try {
ToolContribution tool = new ToolContribution(folder);
if (tool.tool != null)
return tool;
} catch (Exception e) {
}
return null;
}
private ToolContribution(File folder) throws Exception {
this.folder = folder;
// XXX: This is repeated in LibraryCompilcation.java
File propertiesFile = new File(folder, "properties.txt");
info = new ToolInfo();
info.tool = this;
HashMap<String,String> propertiesTable = Base.readSettings(propertiesFile);
readProperties(propertiesTable, info);
if (info.name == null) {
info.name = folder.getName();
}
File toolDirectory = new File(folder, "tool");
// add dir to classpath for .classes
//urlList.add(toolDirectory.toURL());
// add .jar files to classpath
File[] archives = toolDirectory.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".jar") ||
name.toLowerCase().endsWith(".zip"));
}
});
URL[] urlList = new URL[archives.length];
for (int j = 0; j < urlList.length; j++) {
urlList[j] = archives[j].toURI().toURL();
}
URLClassLoader loader = new URLClassLoader(urlList);
String className = null;
for (int j = 0; j < archives.length; j++) {
className = findClassInZipFile(folder.getName(), archives[j]);
if (className != null) break;
}
/*
// 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];
}
}
}
*/
// If no class name found, just move on.
if (className == null) return;
Class<?> toolClass = Class.forName(className, true, loader);
tool = (Tool) toolClass.newInstance();
}
protected String findClassInZipFile(String base, File file) {
// Class file to search for
String classFileName = "/" + base + ".class";
try {
ZipFile zipFile = new ZipFile(file);
Enumeration<?> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
if (!entry.isDirectory()) {
String name = entry.getName();
//System.out.println("entry: " + name);
if (name.endsWith(classFileName)) {
//int slash = name.lastIndexOf('/');
//String packageName = (slash == -1) ? "" : name.substring(0, slash);
// Remove .class and convert slashes to periods.
zipFile.close();
return name.substring(0, name.length() - 6).replace('/', '.');
}
}
}
zipFile.close();
} catch (IOException e) {
//System.err.println("Ignoring " + filename + " (" + e.getMessage() + ")");
e.printStackTrace();
}
return null;
}
static protected ArrayList<ToolContribution> list(File folder) {
ArrayList<ToolContribution> tools = new ArrayList<ToolContribution>();
list(folder, tools);
return tools;
}
static protected void list(File folder, ArrayList<ToolContribution> tools) {
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;
}
});
if (folders == null || folders.length == 0) {
return;
}
for (int i = 0; i < folders.length; i++) {
try {
final ToolContribution tool = getTool(folders[i]);
if (tool != null) {
tools.add(tool);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
ContributionInfo getInfo() {
return info;
}
File getFolder() {
return folder;
}
public void init(Editor editor) {
tool.init(editor);
}
public void run() {
tool.run();
}
public String getMenuTitle() {
return tool.getMenuTitle();
}
public static class ToolInfo extends ContributionInfo {
ToolContribution tool;
public ContributionType getType() {
return ContributionType.TOOL;
}
public boolean isInstalled() {
return tool != null;
}
public Contribution getContribution() {
return tool;
}
}
}