content: /*
   *    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.
   */
  
  /*
   * XMLExperiment.java
   * Copyright (C) 2004 University of Waikato, Hamilton, New Zealand
   */
  
  package weka.experiment.xml;
  
  import java.beans.PropertyDescriptor;
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.util.Vector;
  
  import org.w3c.dom.Element;
  
  import weka.core.RevisionUtils;
  import weka.core.xml.XMLBasicSerialization;
  import weka.core.xml.XMLDocument;
  import weka.experiment.Experiment;
  import weka.experiment.PropertyNode;
  
  /**
   * This class serializes and deserializes an Experiment instance to and
   * fro XML.<br>
   * It omits the <code>options</code> from the Experiment, since these are handled
   * by the get/set-methods. For the <code>Classifier</code> class with all its 
   * derivative classes it stores only <code>debug</code> and <code>options</code>.
   * For <code>SplitEvaluator</code> and <code>ResultProducer</code> only the
   * options are retrieved. The <code>PropertyNode</code> is done manually since
   * it has no get/set-methods for its public fields.<br>
   * Since there's no read-method for <code>m_ClassFirst</code> we always save it
   * as <code>false</code>.
   * 
   * @see Experiment#m_ClassFirst
   * 
   * @author FracPete (fracpete at waikato dot ac dot nz)
   * @version $Revision: 1.6 $ 
   */
  public class XMLExperiment
     extends XMLBasicSerialization {
    
     /** the name of the classFirst property */
     public final static String NAME_CLASSFIRST = "classFirst";
  
     /** PropertyNode member */
     public final static String NAME_PROPERTYNODE_VALUE = "value";
  
     /** PropertyNode member */
     public final static String NAME_PROPERTYNODE_PARENTCLASS = "parentClass";
  
     /** PropertyNode member */
     public final static String NAME_PROPERTYNODE_PROPERTY = "property";
     
     /**
      * initializes the serialization
      * 
      * @throws Exception if initialization fails
      */
     public XMLExperiment() throws Exception {
        super();
     }
     
     /**
      * generates internally a new XML document and clears also the IgnoreList and
      * the mappings for the Read/Write-Methods
      * 
      * @throws Exception if initializing fails
      */
     public void clear() throws Exception {
        super.clear();
  
        // ignore
        m_Properties.addIgnored(VAL_ROOT + ".options");
        m_Properties.addIgnored(Experiment.class, "options");
        
        // allow
        m_Properties.addAllowed(weka.classifiers.Classifier.class, "debug");
        m_Properties.addAllowed(weka.classifiers.Classifier.class, "options");
        // we assume that classes implementing SplitEvaluator also implement OptionHandler
        m_Properties.addAllowed(weka.experiment.SplitEvaluator.class, "options");
        // we assume that classes implementing ResultProducer also implement OptionHandler
        m_Properties.addAllowed(weka.experiment.ResultProducer.class, "options");
        
        // read/write methods
        m_CustomMethods.register(this, PropertyNode.class, "PropertyNode");
     }
     
     /**
      * enables derived classes to add other properties to the DOM tree, e.g.
      * ones that do not apply to the get/set convention of beans. 
      * 
      * @param o the object that is serialized into XML
      * @throws Exception if post-processing fails
      */
     protected void writePostProcess(Object o) throws Exception {
        Element              node;
        Experiment           exp;
  
        exp = (Experiment) o;
        
        // classfirst
        node = addElement(m_Document.getDocument().getDocumentElement(), NAME_CLASSFIRST, Boolean.class.getName(), false);
        node.appendChild(node.getOwnerDocument().createTextNode(new Boolean(false).toString()));   // TODO: get-Method for classFirst in Experiment???
     }
     
     /**
      * additional post-processing can happen in derived classes after reading 
      * from XML. 
      * 
      * @param o the object to perform some additional processing on
      * @return the processed object
      * @throws Exception if post-processing fails
      */
     protected Object readPostProcess(Object o) throws Exception {
        Element              node;
        Experiment           exp;
        int                  i;
        Vector               children;
  
        exp = (Experiment) o;
        
        // classfirst
        children = XMLDocument.getChildTags(m_Document.getDocument().getDocumentElement());
        for (i = 0; i < children.size(); i++) {
           node = (Element) children.get(i);
           if (node.getAttribute(ATT_NAME).equals(NAME_CLASSFIRST)) {
              exp.classFirst(new Boolean(XMLDocument.getContent(node)).booleanValue());
              break;
           }
        }
        
        return o;
     }
     
     /**
      * adds the given PropertyNode to a DOM structure. 
      * 
      * @param parent the parent of this object, e.g. the class this object is a member of
      * @param o the Object to describe in XML
      * @param name the name of the object
      * @return the node that was created
      * @throws Exception if the DOM creation fails
      */
     public Element writePropertyNode(Element parent, Object o, String name) throws Exception {
        Element              node;
        PropertyNode         pnode;
        Vector               children;
        int                  i;
        Element              child;
  
        // for debugging only
        if (DEBUG)
           trace(new Throwable(), name);
        
        m_CurrentNode = parent;
        
        pnode = (PropertyNode) o;
        node  = (Element) parent.appendChild(m_Document.getDocument().createElement(TAG_OBJECT));
        node.setAttribute(ATT_NAME, name);
        node.setAttribute(ATT_CLASS, pnode.getClass().getName());
        node.setAttribute(ATT_PRIMITIVE, VAL_NO);
        node.setAttribute(ATT_ARRAY, VAL_NO);
        
        if (pnode.value != null)
           invokeWriteToXML(node, pnode.value, NAME_PROPERTYNODE_VALUE);
        if (pnode.parentClass != null)
           invokeWriteToXML(node, pnode.parentClass.getName(), NAME_PROPERTYNODE_PARENTCLASS);
        if (pnode.property != null)
           invokeWriteToXML(node, pnode.property.getDisplayName(), NAME_PROPERTYNODE_PROPERTY);
        
        // fix primitive values
        if (    (pnode.value != null) 
             && (pnode.property != null) 
             && (pnode.property.getPropertyType().isPrimitive())) {
           children = XMLDocument.getChildTags(node);
           for (i = 0; i < children.size(); i++) {
              child = (Element) children.get(i);
              if (!child.getAttribute(ATT_NAME).equals(NAME_PROPERTYNODE_VALUE))
                 continue;
              child.setAttribute(ATT_CLASS, pnode.property.getPropertyType().getName());
              child.setAttribute(ATT_PRIMITIVE, VAL_YES);
           }
        }
        
        return node;
     }
  
     /**
      * builds the PropertyNode from the given DOM node. 
      * 
      * @param node the associated XML node
      * @return the instance created from the XML description
      * @throws Exception if instantiation fails 
      * @see javax.swing.DefaultListModel
      */
     public Object readPropertyNode(Element node) throws Exception {
        Object               result;
        Object               value;
        String               parentClass;
        String               property;
        Vector               children;
        Element              child;
        int                  i;
        Class                cls;
  
        // for debugging only
        if (DEBUG)
           trace(new Throwable(), node.getAttribute(ATT_NAME));
  
        m_CurrentNode = node;
        
        result      = null;
  
        children    = XMLDocument.getChildTags(node);
        value       = null;
        parentClass = null;
        property    = null;
        
        for (i = 0; i < children.size(); i++) {
           child = (Element) children.get(i);
           
           if (child.getAttribute(ATT_NAME).equals(NAME_PROPERTYNODE_VALUE)) {
              if (stringToBoolean(child.getAttribute(ATT_PRIMITIVE)))
                 value = getPrimitive(child);
              else
                 value = invokeReadFromXML(child);
           }
           if (child.getAttribute(ATT_NAME).equals(NAME_PROPERTYNODE_PARENTCLASS))
              parentClass = XMLDocument.getContent(child);
           if (child.getAttribute(ATT_NAME).equals(NAME_PROPERTYNODE_PROPERTY))
              property = XMLDocument.getContent(child);
        }
        
        if (parentClass != null)
           cls = Class.forName(parentClass);
        else
           cls = null;
        
        if (cls != null)
           result = new PropertyNode(value, new PropertyDescriptor(property, cls), cls);
        else
           result = new PropertyNode(value);
        
        return result;
     }
     
     /**
      * Returns the revision string.
      * 
      * @return		the revision
      */
     public String getRevision() {
       return RevisionUtils.extract("$Revision: 1.6 $");
     }
  
     /**
      * for testing only. if the first argument is a filename with ".xml"
      * as extension it tries to generate an instance from the XML description
      * and does a <code>toString()</code> of the generated object.
      * Otherwise it loads the binary file, saves the XML representation in a 
      * file with the original filename appended by ".xml" and once again in a
      * binary file with the original filename appended by ".exp".
      * 
      * @param args 	the commandline arguments
      * @throws Exception	if something goes wrong, e.g., file not found
      */
     public static void main(String[] args) throws Exception {
        if (args.length > 0) {
           // read xml and print
           if (args[0].toLowerCase().endsWith(".xml")) {
              System.out.println(new XMLExperiment().read(args[0]).toString());
           }
           // read binary and print generated XML
           else {
              // read
              FileInputStream fi = new FileInputStream(args[0]);
              ObjectInputStream oi = new ObjectInputStream(
                                     new BufferedInputStream(fi));
              Object o = oi.readObject();
              oi.close();
              // print to stdout
              //new XMLExperiment().write(System.out, o);
              // write to XML file
              new XMLExperiment().write(new BufferedOutputStream(new FileOutputStream(args[0] + ".xml")), o);
              // print to binary file
              FileOutputStream fo = new FileOutputStream(args[0] + ".exp");
              ObjectOutputStream oo = new ObjectOutputStream(
                                     new BufferedOutputStream(fo));
              oo.writeObject(o);
              oo.close();
           }
        }
     }
  }
  