001/*
002 * Java Genetic Algorithm Library (jenetics-3.9.0).
003 * Copyright (c) 2007-2017 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
019 */
020package org.jenetics.util;
021
022import static org.jenetics.internal.util.JAXBContextCache.context;
023import static org.jenetics.internal.util.jaxb.adapterFor;
024import static org.jenetics.internal.util.jaxb.marshal;
025
026import java.io.File;
027import java.io.FileInputStream;
028import java.io.FileOutputStream;
029import java.io.IOException;
030import java.io.InputStream;
031import java.io.ObjectInputStream;
032import java.io.ObjectOutputStream;
033import java.io.OutputStream;
034import java.nio.file.Path;
035import java.util.Arrays;
036
037import javax.xml.bind.Marshaller;
038import javax.xml.bind.Unmarshaller;
039import javax.xml.bind.annotation.adapters.XmlAdapter;
040
041import org.jenetics.internal.util.JAXBContextCache;
042import org.jenetics.internal.util.require;
043
044/**
045 * Class for object serialization. The following example shows how to write and
046 * reload a given population.
047 *
048 * <pre>{@code
049 * // Creating result population.
050 * EvolutionResult<DoubleGene, Double> result = stream
051 *     .collect(toBestEvolutionResult());
052 *
053 * // Writing the population to disk.
054 * final File file = new File("population.xml");
055 * IO.jaxb.write(result.getPopulation(), file);
056 *
057 * // Reading the population from disk.
058 * Population<DoubleGene, Double> population =
059 *     (Population<DoubleGene, Double>)IO.jaxb.read(file);
060 * EvolutionStream<DoubleGene, Double> stream = Engine
061 *     .build(ff, gtf)
062 *     .stream(population, 1);
063 * }</pre>
064 *
065 * The {@code jaxb} marshalling also allows to read and write own classes. For
066 * this you have to register your {@code @XmlType}d class first.
067 * <pre>{@code
068 * // The user defined 'JAXB' model class.
069 * \@XmlRootElement(name = "data-class")
070 * \@XmlType(name = "DataClass")
071 * \@XmlAccessorType(XmlAccessType.FIELD)
072 * public static final class DataClass {
073 *     \@XmlAttribute public String name;
074 *     \@XmlValue public String value;
075 * }
076 *
077 * // Register the 'JAXB' model class.
078 * IO.JAXB.register(DataClass.class);
079 * final DataClass data = ...;
080 * IO.jaxb.write(data, "data.xml");
081 * }</pre>
082 *
083 * It is safe to call {@code IO.JAXB.register(DataClass.class)} more than once.
084 *
085 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
086 * @since 1.0
087 * @version 3.5
088 */
089public abstract class IO {
090
091        protected IO() {
092        }
093
094        /**
095         * Helper class for <em>JAXB</em> class registering/de-registering.
096         *
097         * <pre>{@code
098         * // The user defined 'JAXB' model class.
099         * \@XmlRootElement(name = "data-class")
100         * \@XmlType(name = "DataClass")
101         * \@XmlAccessorType(XmlAccessType.FIELD)
102         * public static final class DataClass {
103         *     \@XmlAttribute public String name;
104         *     \@XmlValue public String value;
105         * }
106         *
107         * // Register the 'JAXB' model class.
108         * IO.JAXB.register(DataClass.class);
109         * final DataClass data = ...;
110         * IO.jaxb.write(data, "data.xml");
111         * }</pre>
112         *
113         * It is safe to call {@code IO.JAXB.register(DataClass.class)} more than
114         * once.
115         *
116         * @deprecated Since the {@code javax.xml.bind} is marked as deprecated for
117         *             removal in Java 9, the JAXB marshalling will be removed as
118         *             well. Use the {@code org.jenetics.xml} module when writing
119         *             and reading Jenetics data-objects as XML.
120         *
121         * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
122         * @since 3.5
123         * @version 3.5
124         */
125        @Deprecated
126        public static final class JAXB {
127                private JAXB() {require.noInstance();}
128
129                /**
130                 * Registers the given <em>JAXB</em> model classes. This allows to use
131                 * the {@code IO.jaxb} class with own <em>JAXB</em> marshallings.
132                 * <p>
133                 * <em>It is safe to call this method more than once for a given class.
134                 * The class is registered only once.</em>
135                 *
136                 * @param classes the <em>JAXB</em> model classes to register
137                 * @throws NullPointerException if one of the classes is {@code null}
138                 */
139                public static void register(final Class<?>... classes) {
140                        Arrays.asList(classes).forEach(JAXBContextCache::add);
141                }
142
143                /**
144                 * De-registers the given <em>JAXB</em> model classes.
145                 *
146                 * @param classes the <em>JAXB</em> model classes to register
147                 * @throws NullPointerException if one of the classes is {@code null}
148                 */
149                public static void deregister(final Class<?>... classes) {
150                        Arrays.asList(classes).forEach(JAXBContextCache::remove);
151                }
152
153                /**
154                 * Check is the given class is already registered.
155                 *
156                 * @param cls the class to check
157                 * @return {@code true} if the given class is already registered,
158                 *         {@code false} otherwise.
159                 */
160                public static boolean contains(final Class<?> cls) {
161                        return JAXBContextCache.contains(cls);
162                }
163        }
164
165        /**
166         * JAXB for <i>XML</i> serialization.
167         *
168         * @deprecated Since the {@code javax.xml.bind} is marked as deprecated for
169         *             removal in Java 9, the JAXB marshalling will be removed as
170         *             well. Use the {@code org.jenetics.xml} module when writing
171         *             and reading Jenetics data-objects as XML.
172         */
173        @Deprecated
174        public static final IO jaxb = new IO() {
175
176                @Override
177                public void write(final Object object, final OutputStream out)
178                        throws IOException
179                {
180                        try {
181                                final Marshaller marshaller = context().createMarshaller();
182                                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
183                                marshaller.marshal(marshal(object), out);
184                        } catch (Exception e) {
185                                throw new IOException(e);
186                        }
187                }
188
189                @Override
190                public <T> T read(final Class<T> type, final InputStream in)
191                        throws IOException
192                {
193                        try {
194                                final Unmarshaller unmarshaller = context().createUnmarshaller();
195
196                                //final XMLInputFactory factory = XMLInputFactory.newInstance();
197                                //final XMLStreamReader reader = factory.createXMLStreamReader(in);
198                                //try {
199                                        final Object object = unmarshaller.unmarshal(in);
200                                        final XmlAdapter<Object, Object> adapter = adapterFor(object);
201                                        if (adapter != null) {
202                                                return type.cast(adapter.unmarshal(object));
203                                        } else {
204                                                return type.cast(object);
205                                        }
206                                //} finally {
207                                //      reader.close();
208                                //}
209                        } catch (Exception e) {
210                                throw new IOException(e);
211                        }
212                }
213        };
214
215        /**
216         * IO implementation for "native" <i>Java</i> serialization.
217         */
218        public static final IO object = new IO() {
219
220                @Override
221                public void write(final Object object, final OutputStream out)
222                        throws IOException
223                {
224                        final ObjectOutputStream oout = new ObjectOutputStream(out);
225                        oout.writeObject(object);
226                        out.flush();
227                }
228
229                @Override
230                public <T> T read(final Class<T> type, final InputStream in)
231                        throws IOException
232                {
233                        final ObjectInputStream oin = new ObjectInputStream(in);
234                        try {
235                                return type.cast(oin.readObject());
236                        } catch (ClassNotFoundException | ClassCastException e) {
237                                throw new IOException(e);
238                        }
239                }
240        };
241
242
243        /**
244         * Write the (serializable) object to the given path.
245         *
246         * @param object the object to serialize.
247         * @param path the path to write the object to.
248         * @throws NullPointerException if one of the arguments is {@code null}.
249         * @throws IOException if the object could not be serialized.
250         */
251        public void write(final Object object, final String path)
252                throws IOException
253        {
254                write(object, new File(path));
255        }
256
257        /**
258         * Write the (serializable) object to the given path.
259         *
260         * @param object the object to serialize.
261         * @param path the path to write the object to.
262         * @throws NullPointerException if one of the arguments is {@code null}.
263         * @throws IOException if the object could not be serialized.
264         */
265        public void write(final Object object, final Path path)
266                throws IOException
267        {
268                write(object, path.toFile());
269        }
270
271        /**
272         * Write the (serializable) object to the given file.
273         *
274         * @param object the object to serialize.
275         * @param file the file to write the object to.
276         * @throws NullPointerException if one of the arguments is {@code null}.
277         * @throws IOException if the object could not be serialized.
278         */
279        public void write(final Object object, final File file)
280                throws IOException
281        {
282                try (final FileOutputStream out = new FileOutputStream(file)) {
283                        write(object, out);
284                }
285        }
286
287        /**
288         * Write the (serializable) object to the given output stream.
289         *
290         * @param object the object to serialize.
291         * @param out the output stream to write the object to.
292         * @throws NullPointerException if one of the arguments is {@code null}.
293         * @throws IOException if the object could not be serialized.
294         */
295        public abstract void write(final Object object, final OutputStream out)
296                throws IOException;
297
298        /**
299         * Reads an object from the given file.
300         *
301         * @param <T> the type of the read object
302         * @param path the path to read from.
303         * @param type the type of the read object.
304         * @return the de-serialized object.
305         * @throws NullPointerException if the input stream {@code in} is {@code null}.
306         * @throws IOException if the object could not be read.
307         */
308        public <T> T read(final Class<T> type, final String path)
309                throws IOException
310        {
311                try (final FileInputStream in = new FileInputStream(new File(path))) {
312                        return read(type, in);
313                }
314        }
315
316        /**
317         * Reads an object from the given file.
318         *
319         * @param path the path to read from.
320         * @return the de-serialized object.
321         * @throws NullPointerException if the input stream {@code in} is {@code null}.
322         * @throws IOException if the object could not be read.
323         */
324        public Object read(final String path) throws IOException {
325                return read(Object.class, path);
326        }
327
328        /**
329         * Reads an object from the given file.
330         *
331         * @param <T> the type of the read object
332         * @param path the path to read from.
333         * @param type the type of the read object.
334         * @return the de-serialized object.
335         * @throws NullPointerException if the input stream {@code in} is {@code null}.
336         * @throws IOException if the object could not be read.
337         */
338        public <T> T read(final Class<T> type, final Path path)
339                throws IOException
340        {
341                try (final FileInputStream in = new FileInputStream(path.toFile())) {
342                        return read(type, in);
343                }
344        }
345
346        /**
347         * Reads an object from the given file.
348         *
349         * @param path the path to read from.
350         * @return the de-serialized object.
351         * @throws NullPointerException if the input stream {@code in} is {@code null}.
352         * @throws IOException if the object could not be read.
353         */
354        public Object read(final Path path) throws IOException {
355                return read(Object.class, path);
356        }
357
358        /**
359         * Reads an object from the given file.
360         *
361         * @param <T> the type of the read object
362         * @param file the file to read from.
363         * @param type the type of the read object.
364         * @return the de-serialized object.
365         * @throws NullPointerException if the input stream {@code in} is {@code null}.
366         * @throws IOException if the object could not be read.
367         */
368        public <T> T read(final Class<T> type, final File file)
369                throws IOException
370        {
371                try (final FileInputStream in = new FileInputStream(file)) {
372                        return read(type, in);
373                }
374        }
375
376        /**
377         * Reads an object from the given file.
378         *
379         * @param file the file to read from.
380         * @return the de-serialized object.
381         * @throws NullPointerException if the input stream {@code in} is {@code null}.
382         * @throws IOException if the object could not be read.
383         */
384        public Object read(final File file) throws IOException {
385                return read(Object.class, file);
386        }
387
388        /**
389         * Reads an object from the given input stream.
390         *
391         * @param <T> the type of the read object
392         * @param in the input stream to read from.
393         * @param type the type of the read object.
394         * @return the de-serialized object.
395         * @throws NullPointerException if the input stream {@code in} is {@code null}.
396         * @throws IOException if the object could not be read.
397         */
398        public abstract <T> T read(final Class<T> type, final InputStream in)
399                throws IOException;
400
401        /**
402         * Reads an object from the given input stream.
403         *
404         * @param in the input stream to read from.
405         * @return the de-serialized object.
406         * @throws NullPointerException if the input stream {@code in} is {@code null}.
407         * @throws IOException if the object could not be read.
408         */
409        public Object read(final InputStream in) throws IOException {
410                return read(Object.class, in);
411        }
412}