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     */
021    package csheets.ext;
022    
023    import java.io.File;
024    import java.io.FileInputStream;
025    import java.io.IOException;
026    import java.io.InputStream;
027    import java.net.MalformedURLException;
028    import java.net.URL;
029    import java.net.URLClassLoader;
030    import java.util.Collection;
031    import java.util.Map;
032    import java.util.Properties;
033    import java.util.SortedMap;
034    import java.util.TreeMap;
035    
036    import csheets.CleanSheets;
037    
038    /**
039     * The class that manages extensions to the CleanSheets application.
040     * @author Einar Pehrson
041     */
042    public 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    }