// License: GPL. Copyright 2007 by Immanuel Scholz and others package org.openstreetmap.josm.plugins; import static org.openstreetmap.josm.tools.I18n.tr; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.jar.Attributes; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import org.openstreetmap.josm.Main; /** * Encapsulate general information about a plugin. This information is available * without the need of loading any class from the plugin jar file. * * @author imi */ public class PluginInformation { public File file = null; public String name = null; public int mainversion = 0; public String className = null; public String requires = null; public String link = null; public String description = null; public boolean early = false; public String author = null; public int stage = 50; public String version = null; public String downloadlink = null; public List libraries = new LinkedList(); public final Map attr = new TreeMap(); /** * Used in the Plugin constructor to make the information of the plugin * that is currently initializing available. * * If you think this is hacky, you are probably right. But it is * convinient anyway ;-) */ static PluginInformation currentPluginInitialization = null; /** * @param file the plugin jar file. */ public PluginInformation(File file) { this(file, file.getName().substring(0, file.getName().length()-4)); } public PluginInformation(File file, String name) { this.name = name; this.file = file; try { JarInputStream jar = new JarInputStream(new FileInputStream(file)); Manifest manifest = jar.getManifest(); if (manifest == null) throw new IOException(file+" contains no manifest."); scanManifest(manifest); libraries.add(0, fileToURL(file)); jar.close(); } catch (IOException e) { throw new PluginException(null, name, e); } } public PluginInformation(InputStream manifestStream, String name) { this.name = name; try { Manifest manifest = new Manifest(); manifest.read(manifestStream); scanManifest(manifest); } catch (IOException e) { throw new PluginException(null, name, e); } } private void scanManifest(Manifest manifest) { String lang = Main.getLanguageCode()+"_"; Attributes attr = manifest.getMainAttributes(); className = attr.getValue("Plugin-Class"); String s = attr.getValue(lang+"Plugin-Link"); if(s == null) s = attr.getValue("Plugin-Link"); link = s; requires = attr.getValue("Plugin-Requires"); s = attr.getValue(lang+"Plugin-Description"); if(s == null) { s = attr.getValue("Plugin-Description"); if(s != null) s = tr(s); } description = s; early = Boolean.parseBoolean(attr.getValue("Plugin-Early")); String stageStr = attr.getValue("Plugin-Stage"); stage = stageStr == null ? 50 : Integer.parseInt(stageStr); version = attr.getValue("Plugin-Version"); try { mainversion = Integer.parseInt(attr.getValue("Plugin-Mainversion")); } catch(NumberFormatException e) { e.printStackTrace(); } author = attr.getValue("Author"); String classPath = attr.getValue(Attributes.Name.CLASS_PATH); if (classPath != null) { for (String entry : classPath.split(" ")) { File entryFile; if (new File(entry).isAbsolute()) entryFile = new File(entry); else entryFile = new File(file.getParent(), entry); libraries.add(fileToURL(entryFile)); } } for (Object o : attr.keySet()) this.attr.put(o.toString(), attr.getValue(o.toString())); } public String getLinkDescription() { String d = description == null ? tr("no description available") : description; if(link != null) d += " "+tr("More details")+""; return d; } /** * Load and instantiate the plugin */ public PluginProxy load(Class klass) { try { currentPluginInitialization = this; return new PluginProxy(klass.newInstance(), this); } catch (Exception e) { throw new PluginException(null, name, e); } } /** * Load the class of the plugin */ public Class loadClass(ClassLoader classLoader) { if (className == null) return null; try { Class realClass = Class.forName(className, true, classLoader); return realClass; } catch (Exception e) { throw new PluginException(null, name, e); } } public static URL fileToURL(File f) { try { return f.toURI().toURL(); } catch (MalformedURLException ex) { return null; } } /** * Try to find a plugin after some criterias. Extract the plugin-information * from the plugin and return it. The plugin is searched in the following way: * *
  • first look after an MANIFEST.MF in the package org.openstreetmap.josm.plugins. * (After removing all fancy characters from the plugin name). * If found, the plugin is loaded using the bootstrap classloader. *
  • If not found, look for a jar file in the user specific plugin directory * (~/.josm/plugins/.jar) *
  • If not found and the environment variable JOSM_RESSOURCES + "/plugins/" exist, look there. *
  • Try for the java property josm.ressources + "/plugins/" (set via java -Djosm.plugins.path=...) *
  • If the environment variable ALLUSERSPROFILE and APPDATA exist, look in * ALLUSERSPROFILE//JOSM/plugins. * (*sic* There is no easy way under Windows to get the All User's application * directory) *
  • Finally, look in some typical unix paths:
      *
    • /usr/local/share/josm/plugins/ *
    • /usr/local/lib/josm/plugins/ *
    • /usr/share/josm/plugins/ *
    • /usr/lib/josm/plugins/ * * If a plugin class or jar file is found earlier in the list but seem not to * be working, an PluginException is thrown rather than continuing the search. * This is so JOSM can detect broken user-provided plugins and do not go silently * ignore them. * * The plugin is not initialized. If the plugin is a .jar file, it is not loaded * (only the manifest is extracted). In the classloader-case, the class is * bootstraped (e.g. static {} - declarations will run. However, nothing else is done. * * @param pluginName The name of the plugin (in all lowercase). E.g. "lang-de" * @return Information about the plugin or null, if the plugin * was nowhere to be found. * @throws PluginException In case of broken plugins. */ public static PluginInformation findPlugin(String pluginName) throws PluginException { String name = pluginName; name = name.replaceAll("[-. ]", ""); InputStream manifestStream = PluginInformation.class.getResourceAsStream("/org/openstreetmap/josm/plugins/"+name+"/MANIFEST.MF"); if (manifestStream != null) return new PluginInformation(manifestStream, pluginName); Collection locations = getPluginLocations(); for (String s : locations) { File pluginFile = new File(s, pluginName + ".jar"); if (pluginFile.exists()) { PluginInformation info = new PluginInformation(pluginFile); return info; } } return null; } public static Collection getPluginLocations() { Collection locations = Main.pref.getAllPossiblePreferenceDirs(); Collection all = new ArrayList(locations.size()); for (String s : locations) all.add(s+"plugins"); return all; } }