001/*
002 * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>.
003 *
004 * This file is part of
005 * CleanSheets - a spreadsheet application for the Java platform.
006 *
007 * CleanSheets is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or
010 * (at your option) any later version.
011 *
012 * CleanSheets is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with CleanSheets; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
020 */
021package csheets.ext;
022
023import java.io.File;
024import java.io.FileInputStream;
025import java.io.IOException;
026import java.io.InputStream;
027import java.net.MalformedURLException;
028import java.net.URL;
029import java.net.URLClassLoader;
030import java.util.Collection;
031import java.util.Map;
032import java.util.Properties;
033import java.util.SortedMap;
034import java.util.TreeMap;
035
036import csheets.CleanSheets;
037
038/**
039 * The class that manages extensions to the CleanSheets application.
040 * @author Einar Pehrson
041 */
042public class ExtensionManager {
043
044        /** The singleton instance */
045        private static final ExtensionManager instance = new ExtensionManager();
046
047        /** The name of the files in which extension properties are stored */
048        private static final String PROPERTIES_FILENAME = "extensions.props";
049
050        /** The extensions that have been loaded */
051        private SortedMap<String, Extension> extensionMap
052                = new TreeMap<String, Extension>();
053
054        /** The class loader used to load extensions */
055        private Loader loader = new Loader();
056
057        /**
058         * Creates the extension manager.
059         */
060        private ExtensionManager() {
061                // Loads default extension properties
062                Properties extProps = new Properties();
063                InputStream stream = CleanSheets.class.getResourceAsStream("res/" + PROPERTIES_FILENAME);
064                if (stream != null)
065                        try {
066                                extProps.load(stream);
067                        } catch (IOException e) {
068                                System.err.println("Could not load default extension properties from: "
069                                        + PROPERTIES_FILENAME);
070                        } finally {
071                                try {
072                                        if (stream != null)
073                                                stream.close();
074                                } catch (IOException e) {}
075                        }
076
077                // Loads user extension properties
078                File userExtPropsFile = new File(PROPERTIES_FILENAME);
079                if (userExtPropsFile.exists())
080                        stream = null;
081                        try {
082                                stream = new FileInputStream(userExtPropsFile);
083                                extProps.load(stream);
084                        } catch (IOException e) {
085                        } finally {
086                                try {
087                                        if (stream != null)
088                                                stream.close();
089                                } catch (IOException e) {}
090                        }
091
092                // Loads extensions
093                for (Map.Entry<Object, Object> entry : extProps.entrySet()) {
094                        // Resolves class path
095                        String classPathProp = (String)entry.getValue();
096                        URL classPath = null;
097                        if (classPathProp.length() > 0) {
098                                // Looks for resource
099                                classPath = ExtensionManager.class.getResource(classPathProp);
100                                if (classPath == null) {
101                                        // Looks for file
102                                        File classPathFile = new File(classPathProp);
103                                        if (classPathFile.exists())
104                                                try {
105                                                        classPath = classPathFile.toURL();
106                                                } catch (MalformedURLException e) {}
107                                }
108                        }
109
110                        // Loads class
111                        String className = (String)entry.getKey();
112                        if (classPath == null)
113                                load(className);
114                        else
115                                load(className, classPath);
116                }
117        }
118
119        /**
120         * Returns the singleton instance.
121         * @return the singleton instance
122         */
123        public static ExtensionManager getInstance() {
124                return instance;
125        }
126
127        /**
128         * Returns the extensions that have been loaded.
129         * @return the extensions that have been loaded
130         */
131        public Extension[] getExtensions() {
132                Collection<Extension> extensions = extensionMap.values();
133                return extensions.toArray(new Extension[extensions.size()]);
134        }
135
136        /**
137         * Returns the extension with the given name.
138         * @return the extension with the given name or null if none was found
139         */
140        public Extension getExtension(String name) {
141                return extensionMap.get(name);
142        }
143
144        /**
145         * Adds the given url to the class path, and loads the extension with the
146         * given class name.
147         * @param className the complete class name of a class that extends
148         * the abstract Extension class
149         * @param url the URL of the JAR-file or directory that contains the class
150         * @return the extension that was loaded, or null if none was found.
151         */
152        public Extension load(String className, URL url) {
153                loader.addURL(url);
154                try {
155                        Class extensionClass = Class.forName(className, true, loader);
156                        return load(extensionClass);
157                } catch (Exception e) {
158                        System.err.println("Failed to load extension class " + className + ".");
159                        return null;
160                }
161        }
162
163        /**
164         * Loads the extension with the given class name.
165         * @param className the complete class name of a class that extends
166         * the abstract Extension class
167         * @return the extension that was loaded, or null if none was found.
168         */
169        public Extension load(String className) {
170                try {
171                        Class extensionClass = Class.forName(className);
172                        return load(extensionClass);
173                } catch (Exception e) {
174                        System.err.println("Failed to load extension class " + className + ".");
175                        return null;
176                }
177        }
178
179        /**
180         * Instantiates the given extension class.
181         * @param extensionClass a class that extends the abstract Extension class
182         * @return the extension that was loaded, or null if none was found.
183         */
184        public Extension load(Class extensionClass) {
185                try {
186                        Extension extension = (Extension)extensionClass.newInstance();
187                        extensionMap.put(extension.getName(), extension);
188                        return extension;
189                } catch (IllegalAccessException iae) {
190                        System.err.println("Could not access extension " + extensionClass.getName() + ".");
191                        return null;
192                } catch (InstantiationException ie) {
193                        System.err.println("Could not load extension from " + extensionClass.getName() + ".");
194                        ie.printStackTrace();
195                        return null;
196                }
197        }
198
199        /**
200         * Returns the class loader used to load extensions.
201         * @return the class loader used to load extensions
202         */
203        public ClassLoader getLoader() {
204                return loader;
205        }
206
207        /**
208         * The class loader used to load extensions.
209         */
210        public static class Loader extends URLClassLoader {
211
212                /**
213                 * Creates a new extension loader.
214                 */
215                public Loader() {
216                        super(new URL[]{}, Loader.class.getClassLoader());
217                }
218
219                /**
220                 * Appends the specified URL to the list of URLs to search for classes
221                 * and resources.
222                 * @param url the URL to be added to the search path of URL:s
223                 */
224                protected void addURL(URL url) {
225                        super.addURL(url);
226                }
227        }
228}