001package org.maltparser.core.plugin;
002
003import java.io.File;
004import java.lang.reflect.Constructor;
005import java.lang.reflect.InvocationTargetException;
006import java.util.HashMap;
007import java.util.Iterator;
008import java.util.TreeSet;
009import org.maltparser.core.exception.MaltChainedException;
010
011/**
012Loads MaltParser plug-ins and makes new instances of classes within these plug-ins. 
013
014@author Johan Hall
015
016@since 1.0
017 */
018public class PluginLoader implements Iterable<Plugin> {
019        private HashMap<String, Plugin> plugins;
020        private TreeSet<String> pluginNames;
021        private File[] directories;
022        private JarLoader jarLoader;
023        private static PluginLoader uniqueInstance = new PluginLoader();
024        
025        /**
026         * Creates a PluginLoader
027         * 
028         * @throws PluginException
029         */
030        private PluginLoader() {
031                pluginNames = new TreeSet<String>();
032                plugins = new HashMap<String, Plugin>();
033                jarLoader = null;
034        }
035        
036        /**
037        * Returns a reference to the single instance.
038        */
039        public static PluginLoader instance() {
040                return uniqueInstance;
041        }
042        
043        /**
044         * Loads plug-ins from one directory
045         * 
046         * @param pluginDirectory The directory that contains all plug-ins
047         * @throws MaltChainedException
048         */
049        public void loadPlugins(File pluginDirectory) throws MaltChainedException {
050                this.loadPlugins(new File[] {pluginDirectory});
051        }
052        
053        /**
054         * Loads plug-ins from one or more directories
055         * 
056         * @param pluginDirectories An array of directories that contains all plug-ins
057         * @throws MaltChainedException
058         */
059        public void loadPlugins(File[] pluginDirectories) throws MaltChainedException {
060                directories = new File[pluginDirectories.length];
061                for (int i = 0; i < directories.length; i++) {
062                        directories[i] = pluginDirectories[i];
063                }
064                
065                try {
066                        Class<?> self = Class.forName("org.maltparser.core.plugin.PluginLoader");
067                        jarLoader = new JarLoader(self.getClassLoader());               
068                } catch (ClassNotFoundException e) {
069                        throw new PluginException("The class 'org.maltparser.core.plugin.PluginLoader' not found. ", e);
070                }       
071                traverseDirectories();
072        }
073        
074        /**
075         * Traverse all the plug-in directories
076         * 
077         * @throws MaltChainedException
078         */
079        private void traverseDirectories() throws MaltChainedException {
080                for (int i = 0; i < directories.length; i++) {
081                        traverseDirectory(directories[i]);
082                }
083        }
084        
085        /**
086         * Traverse all plug-ins and sub-directories within one plug-in directory.
087         * 
088         * @param directory The directory that contains plug-ins
089         * @throws MaltChainedException
090         */
091        private void traverseDirectory(File directory) throws MaltChainedException {
092                if (!directory.isDirectory() && directory.getName().endsWith(".jar")) {
093                        pluginNames.add(directory.getAbsolutePath());
094                        Plugin plugin = new Plugin(directory);
095                        plugins.put(directory.getAbsolutePath(), plugin);
096                        if (jarLoader.readJarFile(plugin.getUrl()) == false) {
097                                plugins.remove(directory.getAbsolutePath());
098                        }
099                }
100        
101        if (directory.isDirectory()) {
102            String[] children = directory.list();
103            for (int i=0; i<children.length; i++) {
104                traverseDirectory(new File(directory, children[i]));
105            }
106        }
107        }
108        
109        /**
110         * Returns the Class object for the class with the specified name.
111         * 
112         * @param classname the fully qualified name of the desired class
113         * @return the Class object for the class with the specified name.
114         */
115        public Class<?> getClass(String classname) {
116                if (jarLoader != null) {
117                        return jarLoader.getClass(classname);
118                } else {
119                        return null;
120                }
121        }
122        
123        /**
124         * Creates a new instance of a class within one of the plug-ins
125         * 
126         * @param classname The fully qualified name of the desired class
127         * @param argTypes An array of classes (fully qualified name) that specify the arguments to the constructor 
128         * @param args An array of objects that will be the actual parameters to the constructor (the type should corresponds to the argTypes).
129         * @return a reference to the created instance.
130         * @throws MaltChainedException
131         */
132        public Object newInstance(String classname, Class<?>[] argTypes, Object[] args) throws MaltChainedException {
133                try {
134                        if (jarLoader == null) {
135                                return null;
136                        }
137                        Class<?> clazz = jarLoader.getClass(classname);
138                        Object o = null;
139                        if (clazz == null)
140                                return null;
141                        if (argTypes != null) {
142                                Constructor<?> constructor = clazz.getConstructor(argTypes);
143                                o = constructor.newInstance(args);
144                        } else {
145                                o = clazz.newInstance();
146                        }
147                        return o;
148                } catch (NoSuchMethodException e) {
149                        throw new PluginException("The plugin loader was not able to create an instance of the class '"+classname+"'. ", e);
150                } catch (InstantiationException e) {
151                        throw new PluginException("The plugin loader was not able to create an instance of the class '"+classname+"'. ", e);
152                } catch (IllegalAccessException e) {
153                        throw new PluginException("The plugin loader was not able to create an instance of the class '"+classname+"'. ", e);
154                } catch (InvocationTargetException e) {
155                        throw new PluginException("The plugin loader was not able to create an instance of the class '"+classname+"'. ", e);
156                }
157        }
158        
159        public Iterator<Plugin> iterator() {
160                return plugins.values().iterator();
161        }
162        
163        
164        public String toString() {
165                StringBuilder sb = new StringBuilder();
166                for (Plugin plugin : this) {
167                        sb.append(plugin.toString() + "\n");
168                }
169                return sb.toString();
170        }
171}