IO.java
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  */
020 package org.jenetics.util;
021 
022 import static org.jenetics.internal.util.JAXBContextCache.context;
023 import static org.jenetics.internal.util.jaxb.adapterFor;
024 import static org.jenetics.internal.util.jaxb.marshal;
025 
026 import java.io.File;
027 import java.io.FileInputStream;
028 import java.io.FileOutputStream;
029 import java.io.IOException;
030 import java.io.InputStream;
031 import java.io.ObjectInputStream;
032 import java.io.ObjectOutputStream;
033 import java.io.OutputStream;
034 import java.nio.file.Path;
035 import java.util.Arrays;
036 
037 import javax.xml.bind.Marshaller;
038 import javax.xml.bind.Unmarshaller;
039 import javax.xml.bind.annotation.adapters.XmlAdapter;
040 
041 import org.jenetics.internal.util.JAXBContextCache;
042 import 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  */
089 public 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 paththrows 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 paththrows 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 filethrows 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 inthrows IOException {
398         return read(Object.class, in);
399     }
400 }