001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.plugins;
003    
004    import java.io.File;
005    import java.io.FileNotFoundException;
006    import java.io.FileOutputStream;
007    import java.io.IOException;
008    import java.io.InputStream;
009    import java.net.URL;
010    import java.net.URLClassLoader;
011    import java.util.List;
012    
013    import org.openstreetmap.josm.Main;
014    import org.openstreetmap.josm.gui.MapFrame;
015    import org.openstreetmap.josm.gui.download.DownloadSelection;
016    import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
017    
018    /**
019     * For all purposes of loading dynamic resources, the Plugin's class loader should be used
020     * (or else, the plugin jar will not be within the class path).
021     *
022     * A plugin may subclass this abstract base class (but it is optional).
023     *
024     * The actual implementation of this class is optional, as all functions will be called
025     * via reflection. This is to be able to change this interface without the need of
026     * recompiling or even breaking the plugins. If your class does not provide a
027     * function here (or does provide a function with a mismatching signature), it will not
028     * be called. That simple.
029     *
030     * Or in other words: See this base class as an documentation of what automatic callbacks
031     * are provided (you can register yourself to more callbacks in your plugin class
032     * constructor).
033     *
034     * Subclassing Plugin and overriding some functions makes it easy for you to keep sync
035     * with the correct actual plugin architecture of JOSM.
036     *
037     * @author Immanuel.Scholz
038     */
039    public abstract class Plugin {
040    
041        /**
042         * This is the info available for this plugin. You can access this from your
043         * constructor.
044         *
045         * (The actual implementation to request the info from a static variable
046         * is a bit hacky, but it works).
047         */
048        private PluginInformation info = null;
049    
050        /**
051         * Creates the plugin
052         *
053         * @param info the plugin information describing the plugin.
054         */
055        public Plugin(PluginInformation info) {
056            this.info = info;
057        }
058    
059        /**
060         * Replies the plugin information object for this plugin
061         *
062         * @return the plugin information object
063         */
064        public PluginInformation getPluginInformation() {
065            return info;
066        }
067    
068        /**
069         * Sets the plugin information object for this plugin
070         *
071         * @parma info the plugin information object
072         */
073        public void setPluginInformation(PluginInformation info) {
074            this.info = info;
075        }
076    
077        /**
078         * @return The directory for the plugin to store all kind of stuff.
079         */
080        public String getPluginDir() {
081            return new File(Main.pref.getPluginsDirectory(), info.name).getPath();
082        }
083    
084        /**
085         * Called after Main.mapFrame is initalized. (After the first data is loaded).
086         * You can use this callback to tweak the newFrame to your needs, as example install
087         * an alternative Painter.
088         */
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 = new FileOutputStream(new File(pluginDirName, to));
113            InputStream in = getClass().getResourceAsStream(from);
114            byte[] buffer = new byte[8192];
115            for(int len = in.read(buffer); len > 0; len = in.read(buffer)) {
116                out.write(buffer, 0, len);
117            }
118            in.close();
119            out.close();
120        }
121    
122        /**
123         * Get a class loader for loading resources from the plugin jar.
124         *
125         * This can be used to avoid getting a file from another plugin that
126         * happens to have a file with the same file name and path.
127         *
128         * Usage: Instead of
129         *   getClass().getResource("/resources/pluginProperties.properties");
130         * write
131         *   getPluginResourceClassLoader().getResource("resources/pluginProperties.properties");
132         *
133         * (Note the missing leading "/".)
134         */
135        public ClassLoader getPluginResourceClassLoader() {
136            File pluginDir = Main.pref.getPluginsDirectory();
137            File pluginJar = new File(pluginDir, info.name + ".jar");
138            URL pluginJarUrl = PluginInformation.fileToURL(pluginJar);
139            URLClassLoader pluginClassLoader = new URLClassLoader(new URL[] { pluginJarUrl } , Main.class.getClassLoader());
140            return pluginClassLoader;
141        }
142    }