/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ Franz Wilhelmstötter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) */ package org.jenetics.util; import static org.jenetics.internal.util.JAXBContextCache.context; import static org.jenetics.internal.util.jaxb.adapterFor; import static org.jenetics.internal.util.jaxb.marshal; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.nio.file.Path; import java.util.Arrays; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.adapters.XmlAdapter; import org.jenetics.internal.util.JAXBContextCache; import org.jenetics.internal.util.require; /** * Class for object serialization. The following example shows how to write and * reload a given population. * * <pre>{@code * // Creating result population. * EvolutionResult<DoubleGene, Double> result = stream * .collect(toBestEvolutionResult()); * * // Writing the population to disk. * final File file = new File("population.xml"); * IO.jaxb.write(result.getPopulation(), file); * * // Reading the population from disk. * Population<DoubleGene, Double> population = * (Population<DoubleGene, Double>)IO.jaxb.read(file); * EvolutionStream<DoubleGene, Double> stream = Engine * .build(ff, gtf) * .stream(population, 1); * }</pre> * * The {@code jaxb} marshalling also allows to read and write own classes. For * this you have to register your {@code @XmlType}d class first. * <pre>{@code * // The user defined 'JAXB' model class. * \@XmlRootElement(name = "data-class") * \@XmlType(name = "DataClass") * \@XmlAccessorType(XmlAccessType.FIELD) * public static final class DataClass { * \@XmlAttribute public String name; * \@XmlValue public String value; * } * * // Register the 'JAXB' model class. * IO.JAXB.register(DataClass.class); * final DataClass data = ...; * IO.jaxb.write(data, "data.xml"); * }</pre> * * It is safe to call {@code IO.JAXB.register(DataClass.class)} more than once. * * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> * @since 1.0 * @version 3.5 */ public abstract class IO { protected IO() { } /** * Helper class for <em>JAXB</em> class registering/de-registering. * * <pre>{@code * // The user defined 'JAXB' model class. * \@XmlRootElement(name = "data-class") * \@XmlType(name = "DataClass") * \@XmlAccessorType(XmlAccessType.FIELD) * public static final class DataClass { * \@XmlAttribute public String name; * \@XmlValue public String value; * } * * // Register the 'JAXB' model class. * IO.JAXB.register(DataClass.class); * final DataClass data = ...; * IO.jaxb.write(data, "data.xml"); * }</pre> * * It is safe to call {@code IO.JAXB.register(DataClass.class)} more than * once. * * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> * @since 3.5 * @version 3.5 */ public static final class JAXB { private JAXB() {require.noInstance();} /** * Registers the given <em>JAXB</em> model classes. This allows to use * the {@code IO.jaxb} class with own <em>JAXB</em> marshallings. * <p> * <em>It is safe to call this method more than once for a given class. * The class is registered only once.</em> * * @param classes the <em>JAXB</em> model classes to register * @throws NullPointerException if one of the classes is {@code null} */ public static void register(final Class<?>... classes) { Arrays.asList(classes).forEach(JAXBContextCache::add); } /** * De-registers the given <em>JAXB</em> model classes. * * @param classes the <em>JAXB</em> model classes to register * @throws NullPointerException if one of the classes is {@code null} */ public static void deregister(final Class<?>... classes) { Arrays.asList(classes).forEach(JAXBContextCache::remove); } /** * Check is the given class is already registered. * * @param cls the class to check * @return {@code true} if the given class is already registered, * {@code false} otherwise. */ public static boolean contains(final Class<?> cls) { return JAXBContextCache.contains(cls); } } /** * JAXB for <i>XML</i> serialization. */ public static final IO jaxb = new IO() { @Override public void write(final Object object, final OutputStream out) throws IOException { try { final Marshaller marshaller = context().createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(marshal(object), out); } catch (Exception e) { throw new IOException(e); } } @Override public <T> T read(final Class<T> type, final InputStream in) throws IOException { try { final Unmarshaller unmarshaller = context().createUnmarshaller(); //final XMLInputFactory factory = XMLInputFactory.newInstance(); //final XMLStreamReader reader = factory.createXMLStreamReader(in); //try { final Object object = unmarshaller.unmarshal(in); final XmlAdapter<Object, Object> adapter = adapterFor(object); if (adapter != null) { return type.cast(adapter.unmarshal(object)); } else { return type.cast(object); } //} finally { // reader.close(); //} } catch (Exception e) { throw new IOException(e); } } }; /** * IO implementation for "native" <i>Java</i> serialization. */ public static final IO object = new IO() { @Override public void write(final Object object, final OutputStream out) throws IOException { final ObjectOutputStream oout = new ObjectOutputStream(out); oout.writeObject(object); out.flush(); } @Override public <T> T read(final Class<T> type, final InputStream in) throws IOException { final ObjectInputStream oin = new ObjectInputStream(in); try { return type.cast(oin.readObject()); } catch (ClassNotFoundException | ClassCastException e) { throw new IOException(e); } } }; /** * Write the (serializable) object to the given path. * * @param object the object to serialize. * @param path the path to write the object to. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IOException if the object could not be serialized. */ public void write(final Object object, final String path) throws IOException { write(object, new File(path)); } /** * Write the (serializable) object to the given path. * * @param object the object to serialize. * @param path the path to write the object to. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IOException if the object could not be serialized. */ public void write(final Object object, final Path path) throws IOException { write(object, path.toFile()); } /** * Write the (serializable) object to the given file. * * @param object the object to serialize. * @param file the file to write the object to. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IOException if the object could not be serialized. */ public void write(final Object object, final File file) throws IOException { try (final FileOutputStream out = new FileOutputStream(file)) { write(object, out); } } /** * Write the (serializable) object to the given output stream. * * @param object the object to serialize. * @param out the output stream to write the object to. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IOException if the object could not be serialized. */ public abstract void write(final Object object, final OutputStream out) throws IOException; /** * Reads an object from the given file. * * @param <T> the type of the read object * @param path the path to read from. * @param type the type of the read object. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public <T> T read(final Class<T> type, final String path) throws IOException { try (final FileInputStream in = new FileInputStream(new File(path))) { return read(type, in); } } /** * Reads an object from the given file. * * @param path the path to read from. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public Object read(final String path) throws IOException { return read(Object.class, path); } /** * Reads an object from the given file. * * @param <T> the type of the read object * @param path the path to read from. * @param type the type of the read object. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public <T> T read(final Class<T> type, final Path path) throws IOException { try (final FileInputStream in = new FileInputStream(path.toFile())) { return read(type, in); } } /** * Reads an object from the given file. * * @param path the path to read from. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public Object read(final Path path) throws IOException { return read(Object.class, path); } /** * Reads an object from the given file. * * @param <T> the type of the read object * @param file the file to read from. * @param type the type of the read object. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public <T> T read(final Class<T> type, final File file) throws IOException { try (final FileInputStream in = new FileInputStream(file)) { return read(type, in); } } /** * Reads an object from the given file. * * @param file the file to read from. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public Object read(final File file) throws IOException { return read(Object.class, file); } /** * Reads an object from the given input stream. * * @param <T> the type of the read object * @param in the input stream to read from. * @param type the type of the read object. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public abstract <T> T read(final Class<T> type, final InputStream in) throws IOException; /** * Reads an object from the given input stream. * * @param in the input stream to read from. * @return the de-serialized object. * @throws NullPointerException if the input stream {@code in} is {@code null}. * @throws IOException if the object could not be read. */ public Object read(final InputStream in) throws IOException { return read(Object.class, in); } }