/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.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.operator; import com.rapidminer.operator.ports.OutputPort; import com.rapidminer.operator.ports.ProcessingStep; import com.rapidminer.tools.LogService; import com.rapidminer.tools.LoggingHandler; import com.rapidminer.tools.XMLSerialization; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.zip.GZIPInputStream; /** * This is an abstract superclass for all IOObject. It provides basic implementations for all * methods of the IOObject interface. In addition, it also provides static methods which can be used * for reading IOObjects from XML strings and input streams / files containing the XML * serialization. * * @author Ingo Mierswa */ public abstract class AbstractIOObject implements IOObject { private static final long serialVersionUID = 7131412868947165460L; /** The source of this IOObect. Might be null. */ private String source = null; /** The current working operator. */ private transient LoggingHandler loggingHandler; private transient LinkedList<ProcessingStep> processingHistory = new LinkedList<>(); private transient HashMap<String, Object> userData = new HashMap<>(); /** Sets the source of this IOObject. */ @Override public void setSource(String sourceName) { this.source = sourceName; } /** Returns the source of this IOObject (might return null if the source is unknown). */ @Override public String getSource() { return source; } @Override public void appendOperatorToHistory(Operator operator, OutputPort port) { if (processingHistory == null) { processingHistory = new LinkedList<>(); if (operator.getProcess() != null) { processingHistory.add(new ProcessingStep(operator, port)); } } ProcessingStep newStep = new ProcessingStep(operator, port); if (operator.getProcess() != null && (processingHistory.isEmpty() || !processingHistory.getLast().equals(newStep))) { processingHistory.add(newStep); } } @Override public List<ProcessingStep> getProcessingHistory() { if (processingHistory == null) { processingHistory = new LinkedList<>(); } return processingHistory; } /** * Gets the logging associated with the operator currently working on this IOObject or the * global log service if no operator was set. */ @SuppressWarnings("deprecation") @Override public LoggingHandler getLog() { if (this.loggingHandler != null) { return this.loggingHandler; } else { return LogService.getGlobal(); } } /** * Sets the current working operator, i.e. the operator which is currently working on this * IOObject. This might be used for example for logging. */ @Override public void setLoggingHandler(LoggingHandler loggingHandler) { this.loggingHandler = loggingHandler; } /** * Returns not a copy but the very same object. This is ok for IOObjects which cannot be altered * after creation. However, IOObjects which might be changed (e.g. * {@link com.rapidminer.example.ExampleSet}s) should overwrite this method and return a proper * copy. */ @Override public IOObject copy() { return this; } /** * Initializes the writing of this object. This method is invoked before the actual writing is * performed. The default implementation does nothing. * * This method should also be used for clean up processes which should be performed before the * actual writing is done. For example, models might decide to keep the example set information * directly after learning (e.g. for visualization reasons) but not to write them down. Please * note that all fields will be written into files unless they are set to null in this method or * they are marked as transient. */ protected void initWriting() {} /** * Just serializes this object with help of a {@link XMLSerialization}. Initializes * {@link #initWriting()} before the actual writing is performed. */ @Override public final void write(OutputStream out) throws IOException { initWriting(); XMLSerialization.getXMLSerialization().writeXML(this, out); } @Override public Object getUserData(String key) { if (userData == null) { userData = new HashMap<>(); } return userData.get(key); } @Override public Object setUserData(String key, Object value) { return userData.put(key, value); } /** * Deserializes an IOObect from the given XML stream. TODO: Make private and remove deprecated * annotation * * @throws IOException * if any IO error occurs. * @throws IllegalStateException * if {@link XMLSerialization#init(ClassLoader)} has never been called. * @deprecated Use {@link #read(InputStreamProvider, String)} to be able to read all formats * (xml zipped/not zipped and binary) */ @Deprecated public static IOObject read(InputStream in) throws IOException { final XMLSerialization serializer = XMLSerialization.getXMLSerialization(); if (serializer == null) { throw new IllegalStateException( "XMLSerialization not initialized, please invoke XMLSerialization.init(ClassLoader) before using this method."); } return (IOObject) serializer.fromXML(in); } /** This interface is needed since we must reset the stream in case of an exception. */ public static interface InputStreamProvider { public InputStream getInputStream() throws IOException; } public static IOObject read(final File file) throws IOException { return read(new InputStreamProvider() { @Override public InputStream getInputStream() throws IOException { return new FileInputStream(file); } }); } public static IOObject read(final byte[] buf) throws IOException { return read(new InputStreamProvider() { @Override public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(buf); } }); } public static IOObject read(InputStreamProvider inProvider) throws IOException { ObjectInputStream objectIn = null; try { // try if the object was written as a serializable object objectIn = new ObjectInputStream(inProvider.getInputStream()); IOObject object = (IOObject) objectIn.readObject(); objectIn.close(); // done in finally return object; } catch (Exception e) { // if not serialized, then try the usual serialization (xml) InputStream in = null; try { in = new GZIPInputStream(inProvider.getInputStream()); } catch (IOException e1) { // do nothing and simply use the given input stream in = inProvider.getInputStream(); } try { return read(in); } finally { if (in != null) { try { in.close(); } catch (IOException e1) { } } } } finally { if (objectIn != null) { try { objectIn.close(); } catch (IOException e) { } } } } }