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