/*
 *   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 3 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, see <http://www.gnu.org/licenses/>.
 */

/*
 * KOML.java
 * Copyright (C) 2004-2012 University of Waikato, Hamilton, New Zealand
 */

package weka.core.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import weka.core.RevisionHandler;
import weka.core.RevisionUtils;

/**
 * This class is a helper class for XML serialization using <a href="http://koala.ilog.fr/XML/serialization/" target="_blank">KOML</a> .
 * KOML does not need to be present, since the class-calls are done generically via Reflection.
 *
 * @author FracPete (fracpete at waikato dot ac dot nz)
 * @version $Revision 1.0$
 */
public class KOML
   implements RevisionHandler {
  
   /**
    * indicates whether <a href="http://koala.ilog.fr/XML/serialization/" target="_blank">KOML</a> 
    * (Koala Object Markup Language) is present
    */
   protected static boolean m_Present = false;

   /** the extension for KOML files (including '.') */
   public final static String FILE_EXTENSION = ".koml";
   
   /** check for KOML statically (needs only to be done once) */
   static {
      checkForKOML();
   }

   /**
    * checks whether the KOML is present in the class path
    */
   private static void checkForKOML() {
      try {
         Class.forName("fr.dyade.koala.xml.koml.KOMLSerializer");
         m_Present = true;
      }
      catch (Exception e) {
         m_Present = false;
      }
   }
  
   /**
    * returns whether KOML is present or not, i.e. whether the classes are in the
    * classpath or not
    *
    * @return whether KOML is available
    */
   public static boolean isPresent() {
      return m_Present;
   }
   
   /**
    * reads the XML-serialized object from the given file
    * @param filename the file to deserialize the object from
    * @return the deserialized object
    * @throws Exception if something goes wrong while reading from the file
    */
   public static Object read(String filename) throws Exception {
      return read(new FileInputStream(filename));
   }
   
   /**
    * reads the XML-serialized object from the given file
    * @param file the file to deserialize the object from
    * @return the deserialized object
    * @throws Exception if something goes wrong while reading from the file
    */
   public static Object read(File file) throws Exception {
      return read(new FileInputStream(file));
   }
   
   /**
    * reads the XML-serialized object from a stream
    * @param stream the stream to deserialize the object from
    * @return the deserialized object
    * @throws Exception if something goes wrong while reading from the stream
    */
   public static Object read(InputStream stream) throws Exception {
      Class<?>                            komlClass;
      Class[]                          komlClassArgs;
      Object[]                         komlArgs;
      java.lang.reflect.Constructor    constructor;
      Object                           koml;
      java.lang.reflect.Method         methodRead;
      java.lang.reflect.Method         methodClose;
      Class[]                          readArgsClasses;
      Class[]                          closeArgsClasses;
      Object[]                         readArgs;
      Object[]                         closeArgs;
      Object                           result;

      result = null;
      
      // get Deserializer
      komlClass        = Class.forName("fr.dyade.koala.xml.koml.KOMLDeserializer");
      komlClassArgs    = new Class[2];
      komlClassArgs[0] = java.io.InputStream.class;
      komlClassArgs[1] = Boolean.TYPE;
      komlArgs         = new Object[2];
      komlArgs[0]      = stream;
      komlArgs[1]      = new Boolean(false);
      constructor      = komlClass.getConstructor(komlClassArgs);
      koml             = constructor.newInstance(komlArgs);
      readArgsClasses  = new Class[0];
      methodRead       = komlClass.getMethod("readObject", readArgsClasses);
      readArgs         = new Object[0];
      closeArgsClasses = new Class[0];
      methodClose      = komlClass.getMethod("close", closeArgsClasses);
      closeArgs        = new Object[0];

      // execute it
      try {
         result = methodRead.invoke(koml, readArgs);
      }
      catch (Exception e) {
         result = null;
      } 
      finally {
         methodClose.invoke(koml, closeArgs);
      }
      
      return result;
   }
   
   /**
    * writes the XML-serialized object to the given file
    * @param filename the file to serialize the object to
    * @param o the object to write to the file
    * @return whether writing was successful or not
    * @throws Exception if something goes wrong while writing to the file
    */
   public static boolean write(String filename, Object o) throws Exception {
      return write(new FileOutputStream(filename), o);
   }
   
   /**
    * write the XML-serialized object to the given file
    * @param file the file to serialize the object to
    * @param o the object to write to the file
    * @return whether writing was successful or not
    * @throws Exception if something goes wrong while writing to the file
    */
   public static boolean write(File file, Object o) throws Exception {
      return write(new FileOutputStream(file), o);
   }
   
   /**
    * writes the XML-serialized object to a stream
    * @param stream the stream to serialize the object to
    * @param o the object to write to the stream
    * @return whether writing was successful or not
    * @throws Exception if something goes wrong while writing to the stream
    */
   public static boolean write(OutputStream stream, Object o) throws Exception {
      Class<?>                            komlClass;
      Class[]                          komlClassArgs;
      Object[]                         komlArgs;
      java.lang.reflect.Constructor    constructor;
      Object                           koml;
      java.lang.reflect.Method         methodAdd;
      java.lang.reflect.Method         methodClose;
      Class[]                          addArgsClasses;
      Class[]                          closeArgsClasses;
      Object[]                         addArgs;
      Object[]                         closeArgs;
      boolean                          result;
      
      result = false;

      // get Deserializer
      komlClass        = Class.forName("fr.dyade.koala.xml.koml.KOMLSerializer");
      komlClassArgs    = new Class[2];
      komlClassArgs[0] = java.io.OutputStream.class;
      komlClassArgs[1] = Boolean.TYPE;
      komlArgs         = new Object[2];
      komlArgs[0]      = stream;
      komlArgs[1]      = new Boolean(false);
      constructor      = komlClass.getConstructor(komlClassArgs);
      koml             = constructor.newInstance(komlArgs);
      addArgsClasses   = new Class[1];
      addArgsClasses[0] = Object.class;
      methodAdd        = komlClass.getMethod("addObject", addArgsClasses);
      addArgs          = new Object[1];
      addArgs[0]       = o;
      closeArgsClasses = new Class[0];
      methodClose      = komlClass.getMethod("close", closeArgsClasses);
      closeArgs        = new Object[0];

      // execute it
      try {
         methodAdd.invoke(koml, addArgs);
         result = true;
      }
      catch (Exception e) {
         result = false;
      } 
      finally {
         methodClose.invoke(koml, closeArgs);
      }
      
      return result;
   }
   
   /**
    * Returns the revision string.
    * 
    * @return		the revision
    */
   public String getRevision() {
     return RevisionUtils.extract("$Revision: 8034 $");
   }
}
