/* * 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)); */ } }