source: src/main/java/weka/gui/GenericPropertiesCreator.java @ 10

Last change on this file since 10 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 17.8 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * GenericPropertiesCreator.java
19 * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
20 *
21 */
22package weka.gui;
23
24import weka.core.ClassDiscovery;
25import weka.core.Utils;
26import weka.core.ClassDiscovery.StringCompare;
27
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.FileOutputStream;
31import java.util.Collections;
32import java.util.Enumeration;
33import java.util.HashSet;
34import java.util.Hashtable;
35import java.util.Properties;
36import java.util.StringTokenizer;
37import java.util.Vector;
38
39/**
40 * This class can generate the properties object that is normally loaded from
41 * the <code>GenericObjectEditor.props</code> file (= PROPERTY_FILE). It takes
42 * the <code>GenericPropertiesCreator.props</code> file as a template to
43 * determine all the derived classes by checking the classes in the given
44 * packages (a file with the same name in your home directory overrides the
45 * the one in the weka/gui directory/package). <br>
46 * E.g. if we want to have all the subclasses of the <code>Classifier</code>
47 * class then we specify the superclass ("weka.classifiers.Classifier") and the
48 * packages where to look for ("weka.classifiers.bayes" etc.):
49 *
50 * <pre>
51 *
52 *   weka.classifiers.Classifier=\
53 *     weka.classifiers.bayes,\
54 *     weka.classifiers.functions,\
55 *     weka.classifiers.lazy,\
56 *     weka.classifiers.meta,\
57 *     weka.classifiers.trees,\
58 *     weka.classifiers.rules
59 * 
60 * </pre>
61 *
62 * This creates the same list as stored in the
63 * <code>GenericObjectEditor.props</code> file, but it will also add
64 * additional classes, that are not listed in the static list (e.g. a newly
65 * developed Classifier), but still in the classpath. <br>
66 * <br>
67 * For discovering the subclasses the whole classpath is inspected, which means
68 * that you can have several parallel directories with the same package
69 * structure (e.g. a release directory and a developer directory with additional
70 * classes). <br>
71 * <br>
72 * The dynamic discovery can be turned off via the <code>UseDyanmic</code>
73 * property in the props file (values: true|false).
74 *
75 * @see #CREATOR_FILE
76 * @see #PROPERTY_FILE
77 * @see #USE_DYNAMIC
78 * @see GenericObjectEditor
79 * @see weka.core.ClassDiscovery
80 * @author FracPete (fracpete at waikato dot ac dot nz)
81 * @version $Revision: 5968 $
82 */
83public class GenericPropertiesCreator {
84 
85  /** whether to output some debug information */
86  public final static boolean VERBOSE = false;
87
88  /** name of property whether to use the dynamic approach or the old
89   * GenericObjectEditor.props file */
90  public final static String USE_DYNAMIC = "UseDynamic";
91 
92  /** The name of the properties file to use as a template. Contains the
93   * packages in which to look for derived classes. It has the same structure
94   * as the <code>PROPERTY_FILE</code>
95   * @see #PROPERTY_FILE
96   */
97  protected static String CREATOR_FILE = "weka/gui/GenericPropertiesCreator.props";
98 
99  /** The name of the properties file that lists classes/interfaces/superclasses
100   * to exclude from being shown in the GUI. See the file for more information.
101   */
102  protected static String EXCLUDE_FILE = "weka/gui/GenericPropertiesCreator.excludes";
103
104  /** the prefix for an interface exclusion */
105  protected static String EXCLUDE_INTERFACE = "I";
106
107  /** the prefix for an (exact) class exclusion */
108  protected static String EXCLUDE_CLASS = "C";
109
110  /** the prefix for a superclass exclusion */
111  protected static String EXCLUDE_SUPERCLASS = "S";
112 
113  /** The name of the properties file for the static GenericObjectEditor
114   * (<code>USE_DYNAMIC</code> = <code>false</code>)
115   * @see GenericObjectEditor
116   * @see #USE_DYNAMIC
117   */
118  protected static String PROPERTY_FILE = "weka/gui/GenericObjectEditor.props";
119 
120  /** the input file with the packages */
121  protected String m_InputFilename;
122 
123  /** the output props file for the GenericObjectEditor */
124  protected String m_OutputFilename;
125 
126  /** the "template" properties file with the layout and the packages */
127  protected Properties m_InputProperties;
128 
129  /** the output properties file with the filled in classes */
130  protected Properties m_OutputProperties;
131 
132  /** whether an explicit input file was given - if false, the Utils class
133   * is used to locate the props-file */
134  protected boolean m_ExplicitPropsFile;
135 
136  /** the hashtable that stores the excludes:
137   * key -&gt; Hashtable(prefix -&gt; Vector of classnames) */
138  protected Hashtable m_Excludes;
139 
140  /**
141   * initializes the creator, locates the props file with the Utils class.
142   *
143   * @throws Exception if loading of CREATOR_FILE fails
144   * @see #CREATOR_FILE
145   * @see Utils#readProperties(String)
146   * @see #loadInputProperties()
147   */
148  public GenericPropertiesCreator() throws Exception {
149    this(CREATOR_FILE);
150    m_ExplicitPropsFile = false;
151  }
152
153  /**
154   * initializes the creator, the given file overrides the props-file search
155   * of the Utils class
156   *
157   * @param filename the file containing the packages to create a props file from
158   * @throws Exception if loading of the file fails
159   * @see #CREATOR_FILE
160   * @see Utils#readProperties(String)
161   * @see #loadInputProperties()
162   */
163  public GenericPropertiesCreator(String filename) throws Exception {
164    super();
165    m_InputFilename     = filename;
166    m_OutputFilename    = PROPERTY_FILE;
167    m_InputProperties   = null;
168    m_OutputProperties  = null;
169    m_ExplicitPropsFile = true;
170    m_Excludes          = new Hashtable();
171  }
172
173  /**
174   * if FALSE, the locating of a props-file of the Utils-class is used,
175   * otherwise it's tried to load the specified file
176   *
177   * @param value       if true the specified file will be loaded not via the
178   *                    Utils-class
179   * @see Utils#readProperties(String)
180   * @see #loadInputProperties()
181   */
182  public void setExplicitPropsFile(boolean value) {
183    m_ExplicitPropsFile = value;
184  }
185 
186  /**
187   * returns TRUE, if a file is loaded and not the Utils class used for
188   * locating the props file.
189   *
190   * @return    true if the specified file is used and not the one found by
191   *            the Utils class
192   * @see Utils#readProperties(String)
193   * @see #loadInputProperties()
194   */
195  public boolean getExplicitPropsFile() {
196    return m_ExplicitPropsFile;
197  }
198 
199  /**
200   * returns the name of the output file
201   *
202   * @return the name of the output file
203   */
204  public String getOutputFilename() {
205    return m_OutputFilename;
206  }
207 
208  /**
209   * sets the file to output the properties for the GEO to
210   *
211   * @param filename the filename for the output
212   */
213  public void setOutputFilename(String filename) {
214    m_OutputFilename = filename;
215  }
216
217  /**
218   * returns the name of the input file
219   *
220   * @return the name of the input file
221   */
222  public String getInputFilename() {
223    return m_InputFilename;
224  }
225 
226  /**
227   * sets the file to get the information about the packages from.
228   * automatically sets explicitPropsFile to TRUE.
229   *
230   * @param filename the filename for the input
231   * @see #setExplicitPropsFile(boolean)
232   */
233  public void setInputFilename(String filename) {
234    m_InputFilename = filename;
235    setExplicitPropsFile(true);
236  }
237 
238  /**
239   * returns the input properties object (template containing the packages)
240   *
241   * @return            the input properties (the template)
242   */
243  public Properties getInputProperties() {
244    return m_InputProperties;
245  }
246 
247  /**
248   * returns the output properties object (structure like the template, but
249   * filled with classes instead of packages)
250   *
251   * @return            the output properties (filled with classes)
252   */
253  public Properties getOutputProperties() {
254    return m_OutputProperties;
255  }
256 
257  /**
258   * loads the property file containing the layout and the packages of
259   * the output-property-file. The exlcude property file is also read here.
260   *
261   * @see #m_InputProperties
262   * @see #m_InputFilename
263   */
264  protected void loadInputProperties()  {
265    if (VERBOSE)
266      System.out.println("Loading '" + getInputFilename() + "'...");
267    m_InputProperties = new Properties();
268    try {
269      File f = new File(getInputFilename());
270      if (getExplicitPropsFile() && f.exists())
271        m_InputProperties.load(new FileInputStream(getInputFilename()));
272      else
273        m_InputProperties = Utils.readProperties(getInputFilename());
274     
275      // excludes
276      m_Excludes.clear();
277      Properties p = Utils.readProperties(EXCLUDE_FILE);
278      Enumeration enm = p.propertyNames();
279      while (enm.hasMoreElements()) {
280        String name = enm.nextElement().toString();
281        // new Hashtable for key
282        Hashtable t = new Hashtable();
283        m_Excludes.put(name, t);
284        t.put(EXCLUDE_INTERFACE, new Vector());
285        t.put(EXCLUDE_CLASS, new Vector());
286        t.put(EXCLUDE_SUPERCLASS, new Vector());
287       
288        // process entries
289        StringTokenizer tok = new StringTokenizer(p.getProperty(name), ",");
290        while (tok.hasMoreTokens()) {
291          String item = tok.nextToken();
292          // get list
293          Vector list = new Vector();
294          if (item.startsWith(EXCLUDE_INTERFACE + ":"))
295            list = (Vector) t.get(EXCLUDE_INTERFACE);
296          else if (item.startsWith(EXCLUDE_CLASS + ":"))
297            list = (Vector) t.get(EXCLUDE_CLASS);
298          else if (item.startsWith(EXCLUDE_SUPERCLASS))
299            list = (Vector) t.get(EXCLUDE_SUPERCLASS);
300          // add to list
301          list.add(item.substring(item.indexOf(":") + 1));
302        }
303      }
304    }
305    catch (Exception e) {
306      e.printStackTrace();
307    }
308  }
309
310  /**
311   * gets whether the dynamic approach should be used or not
312   *
313   * @return            true if the dynamic approach is to be used
314   */
315  public boolean useDynamic() {
316    if (getInputProperties() == null)
317      loadInputProperties();
318   
319    // check our classloader against the system one - if different then
320    // return false (as dynamic classloading only works for classes discoverable
321    // in the system classpath
322    /*if (!ClassLoader.getSystemClassLoader().equals(this.getClass().getClassLoader())) {
323      if (Boolean.parseBoolean(getInputProperties().getProperty(USE_DYNAMIC, "true")) == true) {
324        System.out.println("[GenericPropertiesCreator] classloader in use is not the system "
325            + "classloader: using static entries in weka/gui/GenericObjectEditor.props rather "
326            + "than dynamic class discovery.");
327      }
328      return false;
329    }*/
330   
331    return Boolean.parseBoolean(
332        getInputProperties().getProperty(USE_DYNAMIC, "true"));
333  }
334 
335  /**
336   * checks whether the classname is a valid one, i.e., from a public class
337   *
338   * @param classname   the classname to check
339   * @return            whether the classname is a valid one
340   */
341  protected boolean isValidClassname(String classname) {
342    return (classname.indexOf("$") == -1);
343  }
344
345  /**
346   * Checks whether the classname is a valid one for the given key. This is
347   * based on the settings in the Exclude file.
348   *
349   * @param key         the property key
350   * @param classname   the classname to check
351   * @return            whether the classname is a valid one
352   * @see               #EXCLUDE_FILE
353   */
354  protected boolean isValidClassname(String key, String classname) {
355    boolean     result;
356    Class       cls;
357    Class       clsCurrent;
358    Vector      list;
359    int         i;
360
361    result = true;
362
363    // are there excludes for this key?
364    if (m_Excludes.containsKey(key)) {
365      try {
366        clsCurrent = Class.forName(classname);
367      }
368      catch (Exception e) {
369        // we ignore this Exception
370        clsCurrent = null;
371      }
372     
373      // interface
374      if ((clsCurrent != null) && result) {
375        list = (Vector) ((Hashtable) m_Excludes.get(key)).get(EXCLUDE_INTERFACE);
376        for (i = 0; i < list.size(); i++) {
377          try {
378            cls = Class.forName(list.get(i).toString());
379            if (ClassDiscovery.hasInterface(cls, clsCurrent)) {
380              result = false;
381              break;
382            }
383          }
384          catch (Exception e) {
385            // we ignore this Exception
386          }
387        }
388      }
389     
390      // superclass
391      if ((clsCurrent != null) && result) {
392        list = (Vector) ((Hashtable) m_Excludes.get(key)).get(EXCLUDE_SUPERCLASS);
393        for (i = 0; i < list.size(); i++) {
394          try {
395            cls = Class.forName(list.get(i).toString());
396            if (ClassDiscovery.isSubclass(cls, clsCurrent)) {
397              result = false;
398              break;
399            }
400          }
401          catch (Exception e) {
402            // we ignore this Exception
403          }
404        }
405      }
406     
407      // class
408      if ((clsCurrent != null) && result) {
409        list = (Vector) ((Hashtable) m_Excludes.get(key)).get(EXCLUDE_CLASS);
410        for (i = 0; i < list.size(); i++) {
411          try {
412            cls = Class.forName(list.get(i).toString());
413            if (cls.getName().equals(clsCurrent.getName()))
414              result = false;
415          }
416          catch (Exception e) {
417            // we ignore this Exception
418          }
419        }
420      }
421    }
422
423    return result;
424  }
425 
426  /**
427   * fills in all the classes (based on the packages in the input properties
428   * file) into the output properties file
429   *     
430   * @throws Exception  if something goes wrong
431   * @see #m_OutputProperties
432   */
433  protected void generateOutputProperties() throws Exception {
434    Enumeration       keys;
435    String            key;
436    String            value;
437    String            pkg;
438    StringTokenizer   tok;
439    Vector            classes;
440    HashSet           names;
441    int               i;
442   
443    m_OutputProperties = new Properties();
444    keys               = m_InputProperties.propertyNames();
445    while (keys.hasMoreElements()) {
446      key   = keys.nextElement().toString();
447      if (key.equals(USE_DYNAMIC))
448        continue;
449      tok   = new StringTokenizer(m_InputProperties.getProperty(key), ",");
450      names = new HashSet();
451     
452      // get classes for all packages
453      while (tok.hasMoreTokens()) {
454        pkg = tok.nextToken().trim();
455       
456        try {
457          classes = ClassDiscovery.find(Class.forName(key), pkg);
458        }
459        catch (Exception e) {
460          System.out.println("Problem with '" + key + "': " + e);
461          classes = new Vector();
462        }
463       
464        for (i = 0; i < classes.size(); i++) {
465          // skip non-public classes
466          if (!isValidClassname(classes.get(i).toString()))
467            continue;
468          // some classes should not be listed for some keys
469          if (!isValidClassname(key, classes.get(i).toString()))
470            continue;
471          names.add(classes.get(i));
472        }
473      }
474     
475      // generate list
476      value   = "";
477      classes = new Vector();
478      classes.addAll(names);
479      Collections.sort(classes, new StringCompare());
480      for (i = 0; i < classes.size(); i++) {
481        if (!value.equals(""))
482          value += ",";
483        value += classes.get(i).toString();
484      }
485      if (VERBOSE)
486        System.out.println(pkg + " -> " + value);
487     
488      // set value
489      m_OutputProperties.setProperty(key, value);
490    }
491  }
492 
493  /**
494   * stores the generated output properties file
495   *
496   * @throws Exception  if the saving fails
497   * @see #m_OutputProperties
498   * @see #m_OutputFilename
499   */
500  protected void storeOutputProperties() throws Exception {
501    if (VERBOSE)
502      System.out.println("Saving '" + getOutputFilename() + "'...");
503    m_OutputProperties.store(
504        new FileOutputStream(getOutputFilename()), 
505        " Customises the list of options given by the GenericObjectEditor\n# for various superclasses.");
506  }
507 
508  /**
509   * generates the props-file for the GenericObjectEditor and stores it
510   *
511   * @throws Exception  if something goes wrong
512   * @see #execute(boolean)
513   */
514  public void execute() throws Exception {
515    execute(true);
516  }
517 
518  /**
519   * generates the props-file for the GenericObjectEditor and stores it only
520   * if the the param <code>store</code> is TRUE. If it is FALSE then the
521   * generated properties file can be retrieved via the <code>getOutputProperties</code>
522   * method.
523   *
524   * @param store       if TRUE then the properties file is stored to the stored
525   *                    filename
526   * @throws Exception  if something goes wrong
527   * @see #getOutputFilename()
528   * @see #setOutputFilename(String)
529   * @see #getOutputProperties()
530   */
531  public void execute(boolean store) throws Exception {
532    // read properties file
533    loadInputProperties();
534   
535    // generate the props file
536    generateOutputProperties();
537   
538    // write properties file
539    if (store)
540      storeOutputProperties();
541  }
542 
543  /**
544   * for generating props file:
545   * <ul>
546   *   <li>
547   *     no parameter:
548   *     see default constructor
549   *   </li>
550   *   <li>
551   *     1 parameter (i.e., filename):
552   *     see default constructor + setOutputFilename(String)
553   *   </li>
554   *   <li>
555   *     2 parameters (i.e, filenames):
556   *     see constructor with String argument + setOutputFilename(String)
557   *   </li>
558   * </ul>
559   *
560   * @param args        the commandline arguments
561   * @throws Exception  if something goes wrong
562   * @see #GenericPropertiesCreator()
563   * @see #GenericPropertiesCreator(String)
564   * @see #setOutputFilename(String)
565   */
566  public static void main(String[] args) throws Exception {
567    GenericPropertiesCreator   c = null;
568   
569    if (args.length == 0) {
570      c = new GenericPropertiesCreator();
571    }
572    else if (args.length == 1) {
573      c = new GenericPropertiesCreator();
574      c.setOutputFilename(args[0]);
575    }
576    else if (args.length == 2) {
577      c = new GenericPropertiesCreator(args[0]);
578      c.setOutputFilename(args[1]);
579    }
580    else {
581      System.out.println("usage: " + GenericPropertiesCreator.class.getName() + " [<input.props>] [<output.props>]");
582      System.exit(1);
583    }
584   
585    c.execute(true);
586  }
587}
Note: See TracBrowser for help on using the repository browser.