001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.plugins; 003 004import java.io.File; 005import java.io.FileNotFoundException; 006import java.io.FileOutputStream; 007import java.io.IOException; 008import java.io.InputStream; 009import java.net.URL; 010import java.net.URLClassLoader; 011import java.security.AccessController; 012import java.security.PrivilegedAction; 013import java.util.List; 014 015import org.openstreetmap.josm.Main; 016import org.openstreetmap.josm.gui.MapFrame; 017import org.openstreetmap.josm.gui.MapFrameListener; 018import org.openstreetmap.josm.gui.download.DownloadSelection; 019import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 020import org.openstreetmap.josm.tools.Utils; 021 022/** 023 * For all purposes of loading dynamic resources, the Plugin's class loader should be used 024 * (or else, the plugin jar will not be within the class path). 025 * 026 * A plugin may subclass this abstract base class (but it is optional). 027 * 028 * The actual implementation of this class is optional, as all functions will be called 029 * via reflection. This is to be able to change this interface without the need of 030 * recompiling or even breaking the plugins. If your class does not provide a 031 * function here (or does provide a function with a mismatching signature), it will not 032 * be called. That simple. 033 * 034 * Or in other words: See this base class as an documentation of what automatic callbacks 035 * are provided (you can register yourself to more callbacks in your plugin class 036 * constructor). 037 * 038 * Subclassing Plugin and overriding some functions makes it easy for you to keep sync 039 * with the correct actual plugin architecture of JOSM. 040 * 041 * @author Immanuel.Scholz 042 */ 043public abstract class Plugin implements MapFrameListener { 044 045 /** 046 * This is the info available for this plugin. You can access this from your 047 * constructor. 048 * 049 * (The actual implementation to request the info from a static variable 050 * is a bit hacky, but it works). 051 */ 052 private PluginInformation info = null; 053 054 /** 055 * Creates the plugin 056 * 057 * @param info the plugin information describing the plugin. 058 */ 059 public Plugin(PluginInformation info) { 060 this.info = info; 061 } 062 063 /** 064 * Replies the plugin information object for this plugin 065 * 066 * @return the plugin information object 067 */ 068 public PluginInformation getPluginInformation() { 069 return info; 070 } 071 072 /** 073 * Sets the plugin information object for this plugin 074 * 075 * @param info the plugin information object 076 */ 077 public void setPluginInformation(PluginInformation info) { 078 this.info = info; 079 } 080 081 /** 082 * @return The directory for the plugin to store all kind of stuff. 083 */ 084 public String getPluginDir() { 085 return new File(Main.pref.getPluginsDirectory(), info.name).getPath(); 086 } 087 088 @Override 089 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {} 090 091 /** 092 * Called in the preferences dialog to create a preferences page for the plugin, 093 * if any available. 094 */ 095 public PreferenceSetting getPreferenceSetting() { return null; } 096 097 /** 098 * Called in the download dialog to give the plugin a chance to modify the list 099 * of bounding box selectors. 100 */ 101 public void addDownloadSelection(List<DownloadSelection> list) {} 102 103 /** 104 * Copies the resource 'from' to the file in the plugin directory named 'to'. 105 */ 106 public void copy(String from, String to) throws FileNotFoundException, IOException { 107 String pluginDirName = getPluginDir(); 108 File pluginDir = new File(pluginDirName); 109 if (!pluginDir.exists()) { 110 pluginDir.mkdirs(); 111 } 112 FileOutputStream out = null; 113 InputStream in = null; 114 try { 115 out = new FileOutputStream(new File(pluginDirName, to)); 116 in = getClass().getResourceAsStream(from); 117 if (in == null) { 118 throw new IOException("Resource not found: "+from); 119 } 120 byte[] buffer = new byte[8192]; 121 for(int len = in.read(buffer); len > 0; len = in.read(buffer)) { 122 out.write(buffer, 0, len); 123 } 124 } finally { 125 Utils.close(in); 126 Utils.close(out); 127 } 128 } 129 130 /** 131 * Get a class loader for loading resources from the plugin jar. 132 * 133 * This can be used to avoid getting a file from another plugin that 134 * happens to have a file with the same file name and path. 135 * 136 * Usage: Instead of 137 * getClass().getResource("/resources/pluginProperties.properties"); 138 * write 139 * getPluginResourceClassLoader().getResource("resources/pluginProperties.properties"); 140 * 141 * (Note the missing leading "/".) 142 */ 143 public ClassLoader getPluginResourceClassLoader() { 144 File pluginDir = Main.pref.getPluginsDirectory(); 145 File pluginJar = new File(pluginDir, info.name + ".jar"); 146 final URL pluginJarUrl = PluginInformation.fileToURL(pluginJar); 147 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 148 public ClassLoader run() { 149 return new URLClassLoader(new URL[] {pluginJarUrl}, Main.class.getClassLoader()); 150 } 151 }); 152 } 153}