/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* GenericPropertiesCreator.java
* Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui;
import weka.core.ClassDiscovery;
import weka.core.Utils;
import weka.core.ClassDiscovery.StringCompare;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* This class can generate the properties object that is normally loaded from
* the GenericObjectEditor.props
file (= PROPERTY_FILE). It takes
* the GenericPropertiesCreator.props
file as a template to
* determine all the derived classes by checking the classes in the given
* packages (a file with the same name in your home directory overrides the
* the one in the weka/gui directory/package).
* E.g. if we want to have all the subclasses of the Classifier
* class then we specify the superclass ("weka.classifiers.Classifier") and the
* packages where to look for ("weka.classifiers.bayes" etc.):
*
*
* * weka.classifiers.Classifier=\ * weka.classifiers.bayes,\ * weka.classifiers.functions,\ * weka.classifiers.lazy,\ * weka.classifiers.meta,\ * weka.classifiers.trees,\ * weka.classifiers.rules * ** * This creates the same list as stored in the *
GenericObjectEditor.props
file, but it will also add
* additional classes, that are not listed in the static list (e.g. a newly
* developed Classifier), but still in the classpath. UseDyanmic
* property in the props file (values: true|false).
*
* @see #CREATOR_FILE
* @see #PROPERTY_FILE
* @see #USE_DYNAMIC
* @see GenericObjectEditor
* @see weka.core.ClassDiscovery
* @author FracPete (fracpete at waikato dot ac dot nz)
* @version $Revision: 5968 $
*/
public class GenericPropertiesCreator {
/** whether to output some debug information */
public final static boolean VERBOSE = false;
/** name of property whether to use the dynamic approach or the old
* GenericObjectEditor.props file */
public final static String USE_DYNAMIC = "UseDynamic";
/** The name of the properties file to use as a template. Contains the
* packages in which to look for derived classes. It has the same structure
* as the PROPERTY_FILE
* @see #PROPERTY_FILE
*/
protected static String CREATOR_FILE = "weka/gui/GenericPropertiesCreator.props";
/** The name of the properties file that lists classes/interfaces/superclasses
* to exclude from being shown in the GUI. See the file for more information.
*/
protected static String EXCLUDE_FILE = "weka/gui/GenericPropertiesCreator.excludes";
/** the prefix for an interface exclusion */
protected static String EXCLUDE_INTERFACE = "I";
/** the prefix for an (exact) class exclusion */
protected static String EXCLUDE_CLASS = "C";
/** the prefix for a superclass exclusion */
protected static String EXCLUDE_SUPERCLASS = "S";
/** The name of the properties file for the static GenericObjectEditor
* (USE_DYNAMIC
= false
)
* @see GenericObjectEditor
* @see #USE_DYNAMIC
*/
protected static String PROPERTY_FILE = "weka/gui/GenericObjectEditor.props";
/** the input file with the packages */
protected String m_InputFilename;
/** the output props file for the GenericObjectEditor */
protected String m_OutputFilename;
/** the "template" properties file with the layout and the packages */
protected Properties m_InputProperties;
/** the output properties file with the filled in classes */
protected Properties m_OutputProperties;
/** whether an explicit input file was given - if false, the Utils class
* is used to locate the props-file */
protected boolean m_ExplicitPropsFile;
/** the hashtable that stores the excludes:
* key -> Hashtable(prefix -> Vector of classnames) */
protected Hashtable m_Excludes;
/**
* initializes the creator, locates the props file with the Utils class.
*
* @throws Exception if loading of CREATOR_FILE fails
* @see #CREATOR_FILE
* @see Utils#readProperties(String)
* @see #loadInputProperties()
*/
public GenericPropertiesCreator() throws Exception {
this(CREATOR_FILE);
m_ExplicitPropsFile = false;
}
/**
* initializes the creator, the given file overrides the props-file search
* of the Utils class
*
* @param filename the file containing the packages to create a props file from
* @throws Exception if loading of the file fails
* @see #CREATOR_FILE
* @see Utils#readProperties(String)
* @see #loadInputProperties()
*/
public GenericPropertiesCreator(String filename) throws Exception {
super();
m_InputFilename = filename;
m_OutputFilename = PROPERTY_FILE;
m_InputProperties = null;
m_OutputProperties = null;
m_ExplicitPropsFile = true;
m_Excludes = new Hashtable();
}
/**
* if FALSE, the locating of a props-file of the Utils-class is used,
* otherwise it's tried to load the specified file
*
* @param value if true the specified file will be loaded not via the
* Utils-class
* @see Utils#readProperties(String)
* @see #loadInputProperties()
*/
public void setExplicitPropsFile(boolean value) {
m_ExplicitPropsFile = value;
}
/**
* returns TRUE, if a file is loaded and not the Utils class used for
* locating the props file.
*
* @return true if the specified file is used and not the one found by
* the Utils class
* @see Utils#readProperties(String)
* @see #loadInputProperties()
*/
public boolean getExplicitPropsFile() {
return m_ExplicitPropsFile;
}
/**
* returns the name of the output file
*
* @return the name of the output file
*/
public String getOutputFilename() {
return m_OutputFilename;
}
/**
* sets the file to output the properties for the GEO to
*
* @param filename the filename for the output
*/
public void setOutputFilename(String filename) {
m_OutputFilename = filename;
}
/**
* returns the name of the input file
*
* @return the name of the input file
*/
public String getInputFilename() {
return m_InputFilename;
}
/**
* sets the file to get the information about the packages from.
* automatically sets explicitPropsFile to TRUE.
*
* @param filename the filename for the input
* @see #setExplicitPropsFile(boolean)
*/
public void setInputFilename(String filename) {
m_InputFilename = filename;
setExplicitPropsFile(true);
}
/**
* returns the input properties object (template containing the packages)
*
* @return the input properties (the template)
*/
public Properties getInputProperties() {
return m_InputProperties;
}
/**
* returns the output properties object (structure like the template, but
* filled with classes instead of packages)
*
* @return the output properties (filled with classes)
*/
public Properties getOutputProperties() {
return m_OutputProperties;
}
/**
* loads the property file containing the layout and the packages of
* the output-property-file. The exlcude property file is also read here.
*
* @see #m_InputProperties
* @see #m_InputFilename
*/
protected void loadInputProperties() {
if (VERBOSE)
System.out.println("Loading '" + getInputFilename() + "'...");
m_InputProperties = new Properties();
try {
File f = new File(getInputFilename());
if (getExplicitPropsFile() && f.exists())
m_InputProperties.load(new FileInputStream(getInputFilename()));
else
m_InputProperties = Utils.readProperties(getInputFilename());
// excludes
m_Excludes.clear();
Properties p = Utils.readProperties(EXCLUDE_FILE);
Enumeration enm = p.propertyNames();
while (enm.hasMoreElements()) {
String name = enm.nextElement().toString();
// new Hashtable for key
Hashtable t = new Hashtable();
m_Excludes.put(name, t);
t.put(EXCLUDE_INTERFACE, new Vector());
t.put(EXCLUDE_CLASS, new Vector());
t.put(EXCLUDE_SUPERCLASS, new Vector());
// process entries
StringTokenizer tok = new StringTokenizer(p.getProperty(name), ",");
while (tok.hasMoreTokens()) {
String item = tok.nextToken();
// get list
Vector list = new Vector();
if (item.startsWith(EXCLUDE_INTERFACE + ":"))
list = (Vector) t.get(EXCLUDE_INTERFACE);
else if (item.startsWith(EXCLUDE_CLASS + ":"))
list = (Vector) t.get(EXCLUDE_CLASS);
else if (item.startsWith(EXCLUDE_SUPERCLASS))
list = (Vector) t.get(EXCLUDE_SUPERCLASS);
// add to list
list.add(item.substring(item.indexOf(":") + 1));
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* gets whether the dynamic approach should be used or not
*
* @return true if the dynamic approach is to be used
*/
public boolean useDynamic() {
if (getInputProperties() == null)
loadInputProperties();
// check our classloader against the system one - if different then
// return false (as dynamic classloading only works for classes discoverable
// in the system classpath
/*if (!ClassLoader.getSystemClassLoader().equals(this.getClass().getClassLoader())) {
if (Boolean.parseBoolean(getInputProperties().getProperty(USE_DYNAMIC, "true")) == true) {
System.out.println("[GenericPropertiesCreator] classloader in use is not the system "
+ "classloader: using static entries in weka/gui/GenericObjectEditor.props rather "
+ "than dynamic class discovery.");
}
return false;
}*/
return Boolean.parseBoolean(
getInputProperties().getProperty(USE_DYNAMIC, "true"));
}
/**
* checks whether the classname is a valid one, i.e., from a public class
*
* @param classname the classname to check
* @return whether the classname is a valid one
*/
protected boolean isValidClassname(String classname) {
return (classname.indexOf("$") == -1);
}
/**
* Checks whether the classname is a valid one for the given key. This is
* based on the settings in the Exclude file.
*
* @param key the property key
* @param classname the classname to check
* @return whether the classname is a valid one
* @see #EXCLUDE_FILE
*/
protected boolean isValidClassname(String key, String classname) {
boolean result;
Class cls;
Class clsCurrent;
Vector list;
int i;
result = true;
// are there excludes for this key?
if (m_Excludes.containsKey(key)) {
try {
clsCurrent = Class.forName(classname);
}
catch (Exception e) {
// we ignore this Exception
clsCurrent = null;
}
// interface
if ((clsCurrent != null) && result) {
list = (Vector) ((Hashtable) m_Excludes.get(key)).get(EXCLUDE_INTERFACE);
for (i = 0; i < list.size(); i++) {
try {
cls = Class.forName(list.get(i).toString());
if (ClassDiscovery.hasInterface(cls, clsCurrent)) {
result = false;
break;
}
}
catch (Exception e) {
// we ignore this Exception
}
}
}
// superclass
if ((clsCurrent != null) && result) {
list = (Vector) ((Hashtable) m_Excludes.get(key)).get(EXCLUDE_SUPERCLASS);
for (i = 0; i < list.size(); i++) {
try {
cls = Class.forName(list.get(i).toString());
if (ClassDiscovery.isSubclass(cls, clsCurrent)) {
result = false;
break;
}
}
catch (Exception e) {
// we ignore this Exception
}
}
}
// class
if ((clsCurrent != null) && result) {
list = (Vector) ((Hashtable) m_Excludes.get(key)).get(EXCLUDE_CLASS);
for (i = 0; i < list.size(); i++) {
try {
cls = Class.forName(list.get(i).toString());
if (cls.getName().equals(clsCurrent.getName()))
result = false;
}
catch (Exception e) {
// we ignore this Exception
}
}
}
}
return result;
}
/**
* fills in all the classes (based on the packages in the input properties
* file) into the output properties file
*
* @throws Exception if something goes wrong
* @see #m_OutputProperties
*/
protected void generateOutputProperties() throws Exception {
Enumeration keys;
String key;
String value;
String pkg;
StringTokenizer tok;
Vector classes;
HashSet names;
int i;
m_OutputProperties = new Properties();
keys = m_InputProperties.propertyNames();
while (keys.hasMoreElements()) {
key = keys.nextElement().toString();
if (key.equals(USE_DYNAMIC))
continue;
tok = new StringTokenizer(m_InputProperties.getProperty(key), ",");
names = new HashSet();
// get classes for all packages
while (tok.hasMoreTokens()) {
pkg = tok.nextToken().trim();
try {
classes = ClassDiscovery.find(Class.forName(key), pkg);
}
catch (Exception e) {
System.out.println("Problem with '" + key + "': " + e);
classes = new Vector();
}
for (i = 0; i < classes.size(); i++) {
// skip non-public classes
if (!isValidClassname(classes.get(i).toString()))
continue;
// some classes should not be listed for some keys
if (!isValidClassname(key, classes.get(i).toString()))
continue;
names.add(classes.get(i));
}
}
// generate list
value = "";
classes = new Vector();
classes.addAll(names);
Collections.sort(classes, new StringCompare());
for (i = 0; i < classes.size(); i++) {
if (!value.equals(""))
value += ",";
value += classes.get(i).toString();
}
if (VERBOSE)
System.out.println(pkg + " -> " + value);
// set value
m_OutputProperties.setProperty(key, value);
}
}
/**
* stores the generated output properties file
*
* @throws Exception if the saving fails
* @see #m_OutputProperties
* @see #m_OutputFilename
*/
protected void storeOutputProperties() throws Exception {
if (VERBOSE)
System.out.println("Saving '" + getOutputFilename() + "'...");
m_OutputProperties.store(
new FileOutputStream(getOutputFilename()),
" Customises the list of options given by the GenericObjectEditor\n# for various superclasses.");
}
/**
* generates the props-file for the GenericObjectEditor and stores it
*
* @throws Exception if something goes wrong
* @see #execute(boolean)
*/
public void execute() throws Exception {
execute(true);
}
/**
* generates the props-file for the GenericObjectEditor and stores it only
* if the the param store
is TRUE. If it is FALSE then the
* generated properties file can be retrieved via the getOutputProperties
* method.
*
* @param store if TRUE then the properties file is stored to the stored
* filename
* @throws Exception if something goes wrong
* @see #getOutputFilename()
* @see #setOutputFilename(String)
* @see #getOutputProperties()
*/
public void execute(boolean store) throws Exception {
// read properties file
loadInputProperties();
// generate the props file
generateOutputProperties();
// write properties file
if (store)
storeOutputProperties();
}
/**
* for generating props file:
*