001    /* PAVLOV -- Multiple Choice Study System
002     * Copyright (C) 2000 - 2004 T.J. Willis
003     *
004     * This program is free software; you can redistribute it and/or
005     * modify it under the terms of the GNU General Public License
006     * as published by the Free Software Foundation; either version 2
007     * of the License, or (at your option) any later version.
008     *
009     * This program is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012     * GNU General Public License for more details.
013     *
014     * You should have received a copy of the GNU General Public License
015     * along with this program; if not, write to the Free Software
016     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
017     *
018     * $Header: /cvsroot/pavlov/net/sourceforge/pavlov/pluglets/PlugletLoader.java,v 1.9 2004/07/01 05:50:20 tj_willis Exp $
019     */
020    package net.sourceforge.pavlov.pluglets;
021    
022    
023    
024    
025    import java.io.File;
026    import java.util.*;
027    import java.net.*;
028    import javax.swing.*;
029    import java.awt.Container;
030    import net.sourceforge.pavlov.event.*;
031    import org.apache.log4j.*;
032    
033    
034    /**
035     * <code>PlugletLoader</code> 
036     * 
037     * Generates a vector of objects that are a subclass of a given class.
038     * These objects must have a no-arg constructor and be in a given directory.
039     *
040     * If a serious error occurs (no such directory, can't read directory, no
041     * files) the vector will be null.  Otherwise, individual classes are skipped.
042     * Current implementation prints status and error/exception information
043     * to stdout.
044     *
045     * There is, at this time, little support for JarFiles or subdirectories.
046     *
047     * @author <a href="mailto:tj_willis@users.sourceforge.net"></a>
048     * @version $Revision: 1.9 $
049     */
050    public class PlugletLoader {
051        /**
052         * Vector of pluglets that have been successfully loaded.
053         *
054         */
055        protected Vector<Pluglet> pluglets = null;
056        protected static final Category cat = Category.getInstance(PlugletLoader.class.getName());    
057    
058        protected PlugletLoader()
059        {
060    
061        }
062    
063        /**
064         * Creates a new <code>PlugletLoader</code> instance.
065         *
066         * @param baseClassName a <code>String</code> value
067         * @param directory a <code>String</code> value
068         */
069        public PlugletLoader(String baseClassName, String directory) {
070            cat.setLevel(Level.WARN);
071            if(baseClassName == null)
072                return;
073            if(directory == null)
074                return;
075    
076            File f = new File(directory);
077            if( !f.exists())
078                return;
079            if( !f.canRead())
080                return;
081            if( !f.isDirectory())
082                return;
083    
084            File fs[] = f.listFiles();
085            //List<File> files = Arrays.asList(fs);
086            int nFiles = java.lang.reflect.Array.getLength(fs);
087            if(nFiles<1)
088                return;
089            //if ( files.isEmpty()) return;
090    
091            Vector<URL> urls = new Vector<URL>();//files.size());
092            ArrayList<URL> jars = new ArrayList<URL>();//files.size());
093    
094            URL d;
095    
096            for(File z : fs) {
097                try {
098                    d = z.toURL();
099                    urls.add((URL)d);
100                    if(isAJarFile(z))
101                        jars.add((URL)d);
102                } catch (java.net.MalformedURLException e) {
103                    cat.info("Can't load pluglet: " + e);
104                }
105            }
106    
107            if(urls.isEmpty())
108                return;
109    
110    
111            URL urlsarr[] = new URL[jars.size()+1];
112            URL base = null;
113            try {
114                base = new URL("file:" + directory);
115                cat.info("Base URL for pluglets is :" + base);
116            } catch (Exception e) {
117                cat.error("Exception creating base directory : " + base);
118            }
119    
120            if(!jars.isEmpty()) {
121                jars.add(0,base);
122                //urlsarr = (URL[])jars.toArray(((Object[])urlsarr));
123                urlsarr = (URL[])jars.toArray(urlsarr);
124            } else {
125                //urlsarr = new URL[1];
126                urlsarr[0] = base;
127            }
128    
129            URLClassLoader cl = null;
130            try {
131                cl = URLClassLoader.newInstance(urlsarr);
132            } catch (Exception e) {
133                return;
134            }
135    
136    
137            pluglets = new Vector<Pluglet>(nFiles);//files.size());
138    
139            for(URL u : urls) {
140                try {
141                    String t = u.toString();
142                    String s = mungeName(t);
143                    if (s==null)
144                        continue;
145                    cat.info("Loading pluglet: " + s);
146                    Class lala = Class.forName(baseClassName);
147                    Class c = cl.loadClass(s);
148                    if( lala.isAssignableFrom (c) && lala!=c ) {
149                        try {
150                            //Object bar = (AbstractFeedbackApplet)c.newInstance();
151                            Pluglet bar = (Pluglet)c.newInstance();
152                            pluglets.add(bar);
153                        } catch (java.lang.InstantiationException e) {
154                            cat.warn("Cannot load pluglet " ,e);
155                        } catch (java.lang.IllegalAccessException e) {
156                            cat.warn("Cannot load pluglet " ,e);
157                        }
158                    }
159                } catch (Exception e) {
160                    cat.warn( "Cannot load pluglet " , e );
161                } catch (Error er) {
162                    cat.warn( "Cannot load pluglet " , er );
163                }
164            }
165        }
166    
167        /**
168         * Given a JMenu, will add to it each loaded pluglet as a 
169         * JCheckboxMenuItem.  The item's name will be the pluglet's getText()
170         * and the item's tooltip will be the pluglet's getDescription().
171         *
172         * @param menu a <code>JMenu</code> value
173         */
174        public void addToJMenuAsCheckboxes(JMenu menu) {
175            if(pluglets==null)
176                return;
177            if(menu==null)
178                return;
179            for(Pluglet ap: pluglets) {
180                JCheckBoxMenuItem mi = new JCheckBoxMenuItem(ap.getName());
181                mi.setToolTipText(ap.getDescription());
182                menu.add(mi);
183                //addInternalFrame(ap);
184                mi.addActionListener(ap);
185            }
186        }
187    
188        /**
189         * Given a JMenu, will add to it each loaded pluglet as a 
190         * JRadioButtonMenuItem.  The item's name will be the pluglet's getText()
191         * and the item's tooltip will be the pluglet's getDescription().
192         * Returns the ButtonGroup to which the items belong.
193         * @param menu a <code>JMenu</code> value
194         * @return a <code>ButtonGroup</code> value
195         */
196        public ButtonGroup addToJMenuAsRadioButtons(JMenu menu) {
197            if(pluglets==null)
198                return null;
199            if(menu==null)
200                return null;
201            ButtonGroup radioButtons = new ButtonGroup();
202            for(Pluglet ap: pluglets) {
203                JRadioButtonMenuItem mi 
204                    = new JRadioButtonMenuItem(ap.getName());
205                mi.setToolTipText(ap.getDescription());
206                menu.add(mi);
207                //addInternalFrame(ap);
208                mi.addActionListener(ap);
209            }
210            return radioButtons;
211        }
212    
213    
214        /**
215         * Add each of my pluglets that are a subclass of java.awt.Component,
216         * to the given java.awt.Container.
217         *
218         * @param cont a <code>Container</code> value
219         */
220        public void addToContainer(Container cont) {
221            if(pluglets==null)
222                return;
223            if(cont==null)
224                return;
225            for(Pluglet ap: pluglets) {
226                try {
227                    Class comp = Class.forName("java.awt.Component");
228                    Class wind = Class.forName("java.awt.Component");
229                    Class c = ap.getClass();
230                    if( comp.isAssignableFrom (c) && !wind.isAssignableFrom(c) )
231                        cont.add( (java.awt.Component)ap ); 
232                } catch (Exception e) {
233                    cat.warn("Can't add pluglet " + ap + " to container " + cont,e);
234                    return;
235                }
236            }
237        }
238    
239    
240        // FIXME: if large number of pluglets, reimp with hashtable vs. vector
241        /**
242         * Returns the named plugin if it exists, otherwise null.
243         *
244         * @param name a <code>String</code> value
245         * @return a <code>Pluglet</code> value
246         */
247        public Pluglet getPluginByName(String name) {
248            if(name==null)
249                return null;
250            for(Pluglet ap: pluglets) {
251                String targ = ap.getName();
252                if(targ==null)
253                    continue;
254                if(targ.equals(name))
255                    return ap;
256            }
257            return null;
258        }
259    
260        // TOFIX: this should really be in a subclass...
261        /**
262         * Describe <code>addToAnswerEventProducer</code> method here.
263         *
264         * @param cont an <code>AnswerEventProducer</code> value
265         * @deprecated This should be in FeedbackPluglet
266         */
267        @Deprecated public void addToAnswerEventProducer(AnswerEventProducer cont) {
268            if(pluglets==null)
269                return;
270            if(cont==null)
271                return;
272            for(Pluglet ap: pluglets) {
273                cont.addAnswerListener( (AnswerListener)ap ); //addInternalFrame(ap);
274            }
275        }
276    
277    
278    
279        /**
280         * Returns the filename, sans extention, of a given string.
281         * FIXME: this can be implemented with java.io.File methods
282         *
283         * @param in a <code>String</code> value
284         * @return a <code>String</code> value
285         */
286        protected String mungeName(String in) {
287            String f = new String(in);
288            int x = f.lastIndexOf('/')+1;
289            if(x==-1)
290                return null;
291            int y = f.indexOf('.');
292            if(y==-1)
293                return null;
294            String g = f.substring(x,y);
295            return g;
296        }
297    
298        /**
299         * Returns true if the file is recognized as a Jar file.
300         *
301         * @param f a <code>File</code> value
302         * @return a <code>boolean</code> value
303         */
304        public boolean isAJarFile(File f) {
305            if(f==null)
306                return false;
307            if(f.toString().endsWith(".jar"))
308                return true;
309            if(f.toString().endsWith(".JAR"))
310                return true;
311            if(f.toString().endsWith(".Jar"))
312                return true;
313            return false;
314        }
315    
316    
317    }
318