001/*
002 * Java Genetic Algorithm Library (jenetics-3.8.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         * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
117         * @since 3.5
118         * @version 3.5
119         */
120        public static final class JAXB {
121                private JAXB() {require.noInstance();}
122
123                /**
124                 * Registers the given <em>JAXB</em> model classes. This allows to use
125                 * the {@code IO.jaxb} class with own <em>JAXB</em> marshallings.
126                 * <p>
127                 * <em>It is safe to call this method more than once for a given class.
128                 * The class is registered only once.</em>
129                 *
130                 * @param classes the <em>JAXB</em> model classes to register
131                 * @throws NullPointerException if one of the classes is {@code null}
132                 */
133                public static void register(final Class<?>... classes) {
134                        Arrays.asList(classes).forEach(JAXBContextCache::add);
135                }
136
137                /**
138                 * De-registers the given <em>JAXB</em> model classes.
139                 *
140                 * @param classes the <em>JAXB</em> model classes to register
141                 * @throws NullPointerException if one of the classes is {@code null}
142                 */
143                public static void deregister(final Class<?>... classes) {
144                        Arrays.asList(classes).forEach(JAXBContextCache::remove);
145                }
146
147                /**
148                 * Check is the given class is already registered.
149                 *
150                 * @param cls the class to check
151                 * @return {@code true} if the given class is already registered,
152                 *         {@code false} otherwise.
153                 */
154                public static boolean contains(final Class<?> cls) {
155                        return JAXBContextCache.contains(cls);
156                }
157        }
158
159        /**
160         * JAXB for <i>XML</i> serialization.
161         */
162        public static final IO jaxb = new IO() {
163
164                @Override
165                public void write(final Object object, final OutputStream out)
166                        throws IOException
167                {
168                        try {
169                                final Marshaller marshaller = context().createMarshaller();
170                                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
171                                marshaller.marshal(marshal(object), out);
172                        } catch (Exception e) {
173                                throw new IOException(e);
174                        }
175                }
176
177                @Override
178                public <T> T read(final Class<T> type, final InputStream in)
179                        throws IOException
180                {
181                        try {
182                                final Unmarshaller unmarshaller = context().createUnmarshaller();
183
184                                //final XMLInputFactory factory = XMLInputFactory.newInstance();
185                                //final XMLStreamReader reader = factory.createXMLStreamReader(in);
186                                //try {
187                                        final Object object = unmarshaller.unmarshal(in);
188                                        final XmlAdapter<Object, Object> adapter = adapterFor(object);
189                                        if (adapter != null) {
190                                                return type.cast(adapter.unmarshal(object));
191                                        } else {
192                                                return type.cast(object);
193                                        }
194                                //} finally {
195                                //      reader.close();
196                                //}
197                        } catch (Exception e) {
198                                throw new IOException(e);
199                        }
200                }
201        };
202
203        /**
204         * IO implementation for "native" <i>Java</i> serialization.
205         */
206        public static final IO object = new IO() {
207
208                @Override
209                public void write(final Object object, final OutputStream out)
210                        throws IOException
211                {
212                        final ObjectOutputStream oout = new ObjectOutputStream(out);
213                        oout.writeObject(object);
214                        out.flush();
215                }
216
217                @Override
218                public <T> T read(final Class<T> type, final InputStream in)
219                        throws IOException
220                {
221                        final ObjectInputStream oin = new ObjectInputStream(in);
222                        try {
223                                return type.cast(oin.readObject());
224                        } catch (ClassNotFoundException | ClassCastException e) {
225                                throw new IOException(e);
226                        }
227                }
228        };
229
230
231        /**
232         * Write the (serializable) object to the given path.
233         *
234         * @param object the object to serialize.
235         * @param path the path to write the object to.
236         * @throws NullPointerException if one of the arguments is {@code null}.
237         * @throws IOException if the object could not be serialized.
238         */
239        public void write(final Object object, final String path)
240                throws IOException
241        {
242                write(object, new File(path));
243        }
244
245        /**
246         * Write the (serializable) object to the given path.
247         *
248         * @param object the object to serialize.
249         * @param path the path to write the object to.
250         * @throws NullPointerException if one of the arguments is {@code null}.
251         * @throws IOException if the object could not be serialized.
252         */
253        public void write(final Object object, final Path path)
254                throws IOException
255        {
256                write(object, path.toFile());
257        }
258
259        /**
260         * Write the (serializable) object to the given file.
261         *
262         * @param object the object to serialize.
263         * @param file the file to write the object to.
264         * @throws NullPointerException if one of the arguments is {@code null}.
265         * @throws IOException if the object could not be serialized.
266         */
267        public void write(final Object object, final File file)
268                throws IOException
269        {
270                try (final FileOutputStream out = new FileOutputStream(file)) {
271                        write(object, out);
272                }
273        }
274
275        /**
276         * Write the (serializable) object to the given output stream.
277         *
278         * @param object the object to serialize.
279         * @param out the output stream to write the object to.
280         * @throws NullPointerException if one of the arguments is {@code null}.
281         * @throws IOException if the object could not be serialized.
282         */
283        public abstract void write(final Object object, final OutputStream out)
284                throws IOException;
285
286        /**
287         * Reads an object from the given file.
288         *
289         * @param <T> the type of the read object
290         * @param path the path to read from.
291         * @param type the type of the read object.
292         * @return the de-serialized object.
293         * @throws NullPointerException if the input stream {@code in} is {@code null}.
294         * @throws IOException if the object could not be read.
295         */
296        public <T> T read(final Class<T> type, final String path)
297                throws IOException
298        {
299                try (final FileInputStream in = new FileInputStream(new File(path))) {
300                        return read(type, in);
301                }
302        }
303
304        /**
305         * Reads an object from the given file.
306         *
307         * @param path the path to read from.
308         * @return the de-serialized object.
309         * @throws NullPointerException if the input stream {@code in} is {@code null}.
310         * @throws IOException if the object could not be read.
311         */
312        public Object read(final String path) throws IOException {
313                return read(Object.class, path);
314        }
315
316        /**
317         * Reads an object from the given file.
318         *
319         * @param <T> the type of the read object
320         * @param path the path to read from.
321         * @param type the type of the read object.
322         * @return the de-serialized object.
323         * @throws NullPointerException if the input stream {@code in} is {@code null}.
324         * @throws IOException if the object could not be read.
325         */
326        public <T> T read(final Class<T> type, final Path path)
327                throws IOException
328        {
329                try (final FileInputStream in = new FileInputStream(path.toFile())) {
330                        return read(type, in);
331                }
332        }
333
334        /**
335         * Reads an object from the given file.
336         *
337         * @param path the path to read from.
338         * @return the de-serialized object.
339         * @throws NullPointerException if the input stream {@code in} is {@code null}.
340         * @throws IOException if the object could not be read.
341         */
342        public Object read(final Path path) throws IOException {
343                return read(Object.class, path);
344        }
345
346        /**
347         * Reads an object from the given file.
348         *
349         * @param <T> the type of the read object
350         * @param file the file to read from.
351         * @param type the type of the read object.
352         * @return the de-serialized object.
353         * @throws NullPointerException if the input stream {@code in} is {@code null}.
354         * @throws IOException if the object could not be read.
355         */
356        public <T> T read(final Class<T> type, final File file)
357                throws IOException
358        {
359                try (final FileInputStream in = new FileInputStream(file)) {
360                        return read(type, in);
361                }
362        }
363
364        /**
365         * Reads an object from the given file.
366         *
367         * @param file the file to read from.
368         * @return the de-serialized object.
369         * @throws NullPointerException if the input stream {@code in} is {@code null}.
370         * @throws IOException if the object could not be read.
371         */
372        public Object read(final File file) throws IOException {
373                return read(Object.class, file);
374        }
375
376        /**
377         * Reads an object from the given input stream.
378         *
379         * @param <T> the type of the read object
380         * @param in the input stream to read from.
381         * @param type the type of the read object.
382         * @return the de-serialized object.
383         * @throws NullPointerException if the input stream {@code in} is {@code null}.
384         * @throws IOException if the object could not be read.
385         */
386        public abstract <T> T read(final Class<T> type, final InputStream in)
387                throws IOException;
388
389        /**
390         * Reads an object from the given input stream.
391         *
392         * @param in the input stream to read from.
393         * @return the de-serialized object.
394         * @throws NullPointerException if the input stream {@code in} is {@code null}.
395         * @throws IOException if the object could not be read.
396         */
397        public Object read(final InputStream in) throws IOException {
398                return read(Object.class, in);
399        }
400}