/*
* 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.
*/
/*
* PropertyPath.java
* Copyright (C) 2006 University of Waikato, Hamilton, New Zealand
*/
package weka.core;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* A helper class for accessing properties in nested objects, e.g., accessing
* the "getRidge" method of a LinearRegression classifier part of
* MultipleClassifierCombiner, e.g., Vote. For doing so, one needs to
* supply the object to work on and a property path. The property path is a
* dot delimited path of property names ("getFoo()" and "setFoo(int)" have
* "foo" as property name), indices of arrays are 0-based. E.g.: <p/>
*
* <code>getPropertyDescriptor(vote, "classifiers[1].ridge")</code> will return
* the second classifier (which should be our LinearRegression) of the given
* Vote meta-classifier and there the property descriptor of the "ridge"
* property. <code>getValue(...)</code> will return the actual value of the
* ridge parameter and <code>setValue(...)</code> will set it.
*
* @author fracpete (fracpete at waikato dot ac dot nz)
* @version $Revision: 5953 $
*/
public class PropertyPath
implements RevisionHandler {
/**
* Represents a single element of a property path
*
* @author fracpete (fracpete at waikato dot ac dot nz)
* @version $Revision: 5953 $
*/
public static class PathElement
implements Cloneable, RevisionHandler {
/** the property */
protected String m_Name;
/** the index of the array (-1 for none) */
protected int m_Index;
/**
* initializes the path element with the given property
*
* @param property the property to initialize with
*/
public PathElement(String property) {
super();
if (property.indexOf("[") > -1) {
m_Name = property.replaceAll("\\[.*$", "");
m_Index = Integer.parseInt(
property.replaceAll(".*\\[", "").replaceAll("\\].*", ""));
}
else {
m_Name = property;
m_Index = -1;
}
}
/**
* returns a clone of the current object
*
* @return the clone of the current state
*/
public Object clone() {
return new PathElement(this.toString());
}
/**
* returns the name of the property
*
* @return the name of the property
*/
public String getName() {
return m_Name;
}
/**
* returns whether the property is an index-based one
*
* @return true if the property has an index
*/
public boolean hasIndex() {
return (getIndex() > -1);
}
/**
* returns the index of the property, -1 if the property is not an
* index-based one
*
* @return the index of the property
*/
public int getIndex() {
return m_Index;
}
/**
* returns the element once again as string
*
* @return the property as string
*/
public String toString() {
String result;
result = getName();
if (hasIndex())
result += "[" + getIndex() + "]";
return result;
}
/**
* Returns the revision string.
*
* @return the revision
*/
public String getRevision() {
return RevisionUtils.extract("$Revision: 5953 $");
}
}
/**
* Contains a (property) path structure
*
* @author fracpete (fracpete at waikato dot ac dot nz)
* @version $Revision: 5953 $
*/
public static class Path
implements RevisionHandler {
/** the structure */
protected Vector<PathElement> m_Elements;
/**
* default constructor, only used internally
*/
protected Path() {
super();
m_Elements = new Vector<PathElement>();
}
/**
* uses the given dot-path
*
* @param path path in dot-notation
*/
public Path(String path) {
this();
m_Elements = breakUp(path);
}
/**
* uses the vector with PathElement objects to initialize with
*
* @param elements the PathElements to use
*/
public Path(Vector<PathElement> elements) {
this();
for (int i = 0; i < elements.size(); i++)
m_Elements.add((PathElement) elements.get(i).clone());
}
/**
* uses the given array as elements for the path
*
* @param elements the path elements to use
*/
public Path(String[] elements) {
this();
for (int i = 0; i < elements.length; i++)
m_Elements.add(new PathElement(elements[i]));
}
/**
* breaks up the given path and returns it as vector
*
* @param path the path to break up
* @return the single elements of the path
*/
protected Vector<PathElement> breakUp(String path) {
Vector<PathElement> result;
StringTokenizer tok;
result = new Vector<PathElement>();
tok = new StringTokenizer(path, ".");
while (tok.hasMoreTokens())
result.add(new PathElement(tok.nextToken()));
return result;
}
/**
* returns the element at the given index
*
* @param index the index of the element to return
* @return the specified element
*/
public PathElement get(int index) {
return (PathElement) m_Elements.get(index);
}
/**
* returns the number of path elements of this structure
*
* @return the number of path elements
*/
public int size() {
return m_Elements.size();
}
/**
* returns a path object based on the given path string
*
* @param path path to work on
* @return the path structure
*/
public static Path parsePath(String path) {
return new Path(path);
}
/**
* returns a subpath of the current structure, starting with the specified
* element index up to the end
*
* @param startIndex the first element of the subpath
* @return the new subpath
*/
public Path subpath(int startIndex) {
return subpath(startIndex, size());
}
/**
* returns a subpath of the current structure, starting with the specified
* element index up. The endIndex specifies the element that is not part
* of the new subpath. In other words, the new path contains the elements
* from "startIndex" up to "(endIndex-1)".
*
* @param startIndex the first element of the subpath
* @param endIndex the element that is after the last added element
* @return the new subpath
*/
public Path subpath(int startIndex, int endIndex) {
Vector<PathElement> list;
int i;
list = new Vector<PathElement>();
for (i = startIndex; i < endIndex; i++)
list.add(get(i));
return new Path(list);
}
/**
* returns the structure again as a dot-path
*
* @return the path structure as dot-path
*/
public String toString() {
String result;
int i;
result = "";
for (i = 0; i < m_Elements.size(); i++) {
if (i > 0)
result += ".";
result += m_Elements.get(i);
}
return result;
}
/**
* Returns the revision string.
*
* @return the revision
*/
public String getRevision() {
return RevisionUtils.extract("$Revision: 5953 $");
}
}
// /**
// * A helper class that stores Object and PropertyDescriptor together.
// *
// * @author fracpete (fracpete at waikato dot ac dot nz)
// * @version $Revision: 5953 $
// */
// protected static class PropertyContainer
// implements RevisionHandler {
//
// /** the descriptor */
//// protected PropertyDescriptor m_Descriptor;
//
// /** the associated object */
// protected Object m_Object;
//
// /**
// * initializes the container
// *
// * @param desc the property descriptor
// * @param obj the associated object
// */
// public PropertyContainer(PropertyDescriptor desc, Object obj) {
// super();
//
// m_Descriptor = desc;
// m_Object = obj;
// }
//
// /**
// * returns the stored descriptor
// *
// * @return the stored descriptor
// */
// public PropertyDescriptor getDescriptor() {
// return m_Descriptor;
// }
//
// /**
// * returns the stored object
// *
// * @return the stored object
// */
// public Object getObject() {
// return m_Object;
// }
//
// /**
// * Returns the revision string.
// *
// * @return the revision
// */
// public String getRevision() {
// return RevisionUtils.extract("$Revision: 5953 $");
// }
// }
//
// /**
// * returns the property and object associated with the given path, null if
// * a problem occurred.
// *
// * @param src the object to start from
// * @param path the path to follow
// * @return not null, if the property could be found
// */
// public static PropertyContainer find(Object src, Path path) {
// PropertyContainer result;
// PropertyDescriptor desc;
// Object newSrc;
// PathElement part;
// Method method;
// Object methodResult;
//
// // get descriptor
// part = path.get(0);
// try {
// desc = new PropertyDescriptor(part.getName(), src.getClass());
// }
// catch (Exception e) {
// desc = null;
// e.printStackTrace();
// }
//
// // problem occurred? -> stop
// if (desc == null)
// return null;
//
// // end of path reached?
// if (path.size() == 1) {
// result = new PropertyContainer(desc, src);
// }
// // recurse further
// else {
// try {
// method = desc.getReadMethod();
// methodResult = method.invoke(src, (Object[]) null);
// if (part.hasIndex())
// newSrc = Array.get(methodResult, part.getIndex());
// else
// newSrc = methodResult;
// result = find(newSrc, path.subpath(1));
// }
// catch (Exception e) {
// result = null;
// e.printStackTrace();
// }
// }
//
// return result;
// }
//
// /**
// * returns the property associated with the given path, null if a problem
// * occurred.
// *
// * @param src the object to start from
// * @param path the path to follow
// * @return not null, if the property could be found
// */
// public static PropertyDescriptor getPropertyDescriptor(Object src, Path path) {
// PropertyContainer cont;
//
// cont = find(src, path);
//
// if (cont == null)
// return null;
// else
// return cont.getDescriptor();
// }
//
// /**
// * returns the property associated with the given path
// *
// * @param src the object to start from
// * @param path the path to follow
// * @return not null, if the property could be found
// */
// public static PropertyDescriptor getPropertyDescriptor(Object src, String path) {
// return getPropertyDescriptor(src, new Path(path));
// }
//
// /**
// * returns the value specified by the given path from the object
// *
// * @param src the object to work on
// * @param path the retrieval path
// * @return the value, null if an error occurred
// */
// public static Object getValue(Object src, Path path) {
// Object result;
// PropertyContainer cont;
// Method method;
// Object methodResult;
// PathElement part;
//
// result = null;
//
// cont = find(src, path);
// // problem?
// if (cont == null)
// return null;
//
// // retrieve the value
// try {
// part = path.get(path.size() - 1);
// method = cont.getDescriptor().getReadMethod();
// methodResult = method.invoke(cont.getObject(), (Object[]) null);
// if (part.hasIndex())
// result = Array.get(methodResult, part.getIndex());
// else
// result = methodResult;
// }
// catch (Exception e) {
// result = null;
// e.printStackTrace();
// }
//
// return result;
// }
//
// /**
// * returns the value specified by the given path from the object
// *
// * @param src the object to work on
// * @param path the retrieval path
// * @return the value, null if an error occurred
// */
// public static Object getValue(Object src, String path) {
// return getValue(src, new Path(path));
// }
//
// /**
// * set the given value specified by the given path in the object
// *
// * @param src the object to work on
// * @param path the retrieval path
// * @param value the value to set
// * @return true if the value could be set
// */
// public static boolean setValue(Object src, Path path, Object value) {
// boolean result;
// PropertyContainer cont;
// Method methodRead;
// Method methodWrite;
// Object methodResult;
// PathElement part;
//
// result = false;
//
// cont = find(src, path);
// // problem?
// if (cont == null)
// return result;
//
// // set the value
// try {
// part = path.get(path.size() - 1);
// methodRead = cont.getDescriptor().getReadMethod();
// methodWrite = cont.getDescriptor().getWriteMethod();
// if (part.hasIndex()) {
// methodResult = methodRead.invoke(cont.getObject(), (Object[]) null);
// Array.set(methodResult, part.getIndex(), value);
// methodWrite.invoke(cont.getObject(), new Object[]{methodResult});
// }
// else {
// methodWrite.invoke(cont.getObject(), new Object[]{value});
// }
// result = true;
// }
// catch (Exception e) {
// result = false;
// e.printStackTrace();
// }
//
// return result;
// }
//
// /**
// * set the given value specified by the given path in the object
// *
// * @param src the object to work on
// * @param path the retrieval path
// * @param value the value to set
// */
// public static void setValue(Object src, String path, Object value) {
// setValue(src, new Path(path), value);
// }
/**
* Returns the revision string.
*
* @return the revision
*/
public String getRevision() {
return RevisionUtils.extract("$Revision: 5953 $");
}
/**
* for testing only
*
* @param args the commandline options - ignored
* @throws Exception if something goes wrong
*/
public static void main(String[] args) throws Exception {
// Path
Path path = new Path("hello.world[2].nothing");
System.out.println("Path: " + path);
System.out.println(" -size: " + path.size());
System.out.println(" -elements:");
for (int i = 0; i < path.size(); i++)
System.out.println(
" " + i + ". " + path.get(i).getName()
+ " -> " + path.get(i).getIndex());
/*
// retrieving ridge with path
weka.classifiers.meta.Vote vote = new weka.classifiers.meta.Vote();
vote.setClassifiers(
new weka.classifiers.Classifier[]{
new weka.classifiers.trees.J48(),
new weka.classifiers.functions.LinearRegression()});
path = new Path("classifiers[1].ridge");
System.out.println("path: " + path + " -> " + getValue(vote, path));
// setting ridge with path and retrieving it again
setValue(vote, path.toString(), new Double(0.1));
System.out.println("path: " + path + " -> " + getValue(vote, path));
*/
}
}