/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.tools; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.lang.reflect.Constructor; import java.util.List; import java.util.logging.Level; import com.rapidminer.example.AttributeRole; import com.rapidminer.example.NominalStatistics; import com.rapidminer.example.NumericalStatistics; import com.rapidminer.example.SimpleAttributes; import com.rapidminer.example.UnknownStatistics; import com.rapidminer.example.WeightedNumericalStatistics; import com.rapidminer.example.table.BinominalAttribute; import com.rapidminer.example.table.BinominalMapping; import com.rapidminer.example.table.NumericalAttribute; import com.rapidminer.example.table.PolynominalAttribute; import com.rapidminer.example.table.PolynominalMapping; import com.rapidminer.operator.IOContainer; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorCreationException; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.operator.performance.AbstractPerformanceEvaluator; import com.rapidminer.operator.performance.PerformanceCriterion; /** * This class handles all kinds in- and output write processes for all kinds of objects * into and from XML. This class must use object streams since memory consumption is too * big otherwise. Hence, string based methods are no longer supported. * * @author Ingo Mierswa */ public class XMLSerialization { private static ClassLoader classLoader; private com.thoughtworks.xstream.XStream xStream; private XMLSerialization(ClassLoader classLoader) { try { Class<?> xStreamClass = Class.forName("com.thoughtworks.xstream.XStream"); Class generalDriverClass = Class.forName("com.thoughtworks.xstream.io.HierarchicalStreamDriver"); Constructor constructor = xStreamClass.getConstructor(new Class[] { generalDriverClass }); Class driverClass = Class.forName("com.thoughtworks.xstream.io.xml.XppDriver"); xStream = (com.thoughtworks.xstream.XStream)constructor.newInstance(driverClass.newInstance()); xStream.setMode(com.thoughtworks.xstream.XStream.ID_REFERENCES); // define default aliases here addAlias("IOContainer", IOContainer.class); addAlias("PolynominalAttribute", PolynominalAttribute.class); addAlias("BinominalAttribute", BinominalAttribute.class); addAlias("NumericalAttribute", NumericalAttribute.class); addAlias("PolynominalMapping", PolynominalMapping.class); addAlias("BinominalMapping", BinominalMapping.class); addAlias("NumericalStatistics", NumericalStatistics.class); addAlias("WeightedNumericalStatistics", WeightedNumericalStatistics.class); addAlias("NominalStatistics", NominalStatistics.class); addAlias("UnknownStatistics", UnknownStatistics.class); addAlias("SimpleAttributes", SimpleAttributes.class); addAlias("AttributeRole", AttributeRole.class); xStream.setClassLoader(classLoader); defineXMLAliasPairs(); } catch (Throwable e) { // TODO: Why are we catching Throwables? LogService.getRoot().log(Level.WARNING, "Cannot initialize XML serialization. Probably the libraries 'xstream.jar' and 'xpp.jar' were not provided. XML serialization will not work!", e); } } public static void init(ClassLoader classLoader) { XMLSerialization.classLoader = classLoader; } public void addAlias(String name, Class clazz) { if (xStream != null) { String alias = name.replaceAll("[^a-zA-Z_0-9-]", "_").replaceAll("_+", "-"); if (alias.endsWith("-")) alias = alias.substring(0, alias.length() -1); xStream.alias(alias, clazz); } } public void writeXML(Object object, OutputStream out) throws IOException { if (xStream != null) { ObjectOutputStream xOut = xStream.createObjectOutputStream(new OutputStreamWriter(out)); xOut.writeObject(object); // xstream requires us to close() stream. see java doc of createObjectOutputStream xOut.close(); } else { LogService.getRoot().warning("Cannot write XML serialization. Probably the libraries 'xstream.jar' and 'xpp.jar' were not provided."); throw new IOException("Cannot write object with XML serialization."); } } public Object fromXML(InputStream in) throws IOException { if (xStream != null) { try { ObjectInputStream xIn = xStream.createObjectInputStream(new InputStreamReader(in)); Object result = null; try { result = xIn.readObject(); } catch (ClassNotFoundException e) { throw new IOException("Class not found: " + e.getMessage(), e); } return result; } catch (Throwable e) { throw new IOException("Cannot read from XML stream, wrong format: " + e.getMessage(), e); } } else { LogService.getRoot().warning("Cannot read object from XML serialization. Probably the libraries 'xstream.jar' and 'xpp.jar' were not provided."); throw new IOException("Cannot read object from XML serialization."); } } /** * Returns the singleton instance. * We have to return a new instance, since the xStream will remember several mappings and causing a huge * memory leak. **/ public static XMLSerialization getXMLSerialization() { return new XMLSerialization(classLoader); } /** * Defines the alias pairs for the {@link XMLSerialization} for all IOObject * pairs. */ private void defineXMLAliasPairs() { // pairs for IOObjects for (String ioObjectName: OperatorService.getIOObjectsNames()) { addAlias(ioObjectName, OperatorService.getIOObjectClass(ioObjectName)); } // pairs for performance criteria for (String key: OperatorService.getOperatorKeys()) { OperatorDescription description = OperatorService.getOperatorDescription(key); // test if operator delivers performance criteria if (AbstractPerformanceEvaluator.class.isAssignableFrom(description.getOperatorClass())) { Operator operator = null; try { operator = OperatorService.createOperator(key); } catch (OperatorCreationException e) { // does nothing } if (operator != null) { AbstractPerformanceEvaluator evaluator = (AbstractPerformanceEvaluator) operator; List<PerformanceCriterion> criteria = evaluator.getCriteria(); for (PerformanceCriterion criterion : criteria) { addAlias(criterion.getName(), criterion.getClass()); } } } } } }