001/*
002 * Java Genetic Algorithm Library (jenetics-6.2.0).
003 * Copyright (c) 2007-2021 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@gmail.com)
019 */
020package io.jenetics.xml;
021
022import static java.util.Objects.requireNonNull;
023import static io.jenetics.xml.stream.Writer.attr;
024import static io.jenetics.xml.stream.Writer.elem;
025import static io.jenetics.xml.stream.Writer.elems;
026import static io.jenetics.xml.stream.Writer.text;
027
028import java.io.OutputStream;
029import java.util.Collection;
030import java.util.stream.Collectors;
031
032import javax.xml.stream.XMLStreamException;
033
034import io.jenetics.BoundedGene;
035import io.jenetics.Chromosome;
036import io.jenetics.DoubleGene;
037import io.jenetics.Gene;
038import io.jenetics.IntegerGene;
039import io.jenetics.LongGene;
040import io.jenetics.util.ISeq;
041import io.jenetics.xml.stream.Writer;
042import io.jenetics.xml.stream.XML;
043
044/**
045 * This class contains static fields and methods, for creating chromosome- and
046 * genotype writers for different gene types.
047 *
048 * <pre>{@code
049 * final Writer<Genotype<BitGene> bgw =
050 *     Writers.Genotype.writer(Writers.BitChromosome.writer()));
051 *
052 * final Writer<Genotype<IntegerGene>> igw =
053 *     Writers.Genotype.writer(Writers.IntegerChromosome.writer()));
054 *
055 * final Writer<Genotype<DoubleGene>> dgw =
056 *     Writers.Genotype.writer(Writers.DoubleChromosome.writer()));
057 * }</pre>
058 *
059 * This class also contains some helper methods, which makes it easier to write
060 * Jenetics domain objects to a given output stream.
061 * <pre>{@code
062 * final List<Genotype<BitGene>> genotypes = ...;
063 * try (OutputStream out = Files.newOutputStream(Paths.get("path"))) {
064 *     Writers.write(out, genotypes, Writers.BitChromosome.writer());
065 * }
066 * }</pre>
067 *
068 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
069 * @version 3.9
070 * @since 3.9
071 */
072public final class Writers {
073        private Writers() {}
074
075        /**
076         * This class contains static writer methods for
077         * {@link io.jenetics.BitChromosome} objects.
078         * <p>
079         * <b>Writer code</b>
080         * <pre>{@code
081         * final BitChromosome value = BitChromosome.of(20, 0.5);
082         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
083         *     Writers.BitChromosome.writer().write(value, xml);
084         * }
085         * }</pre>
086         *
087         * <b>XML output</b>
088         * <pre> {@code
089         * <bit-chromosome length="20" ones-probability="0.5">11100011101011001010</bit-chromosome>
090         * }</pre>
091         *
092         * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
093         * @version 3.9
094         * @since 3.9
095         */
096        public static final class BitChromosome {
097                private BitChromosome() {}
098
099                static final String ROOT_NAME = "bit-chromosome";
100                static final String LENGTH_NAME = "length";
101                static final String ONES_PROBABILITY_NAME = "ones-probability";
102
103                /**
104                 * Return a {@link Writer} for {@link io.jenetics.BitChromosome}
105                 * objects.
106                 *
107                 * @return a chromosome writer
108                 */
109                public static Writer<io.jenetics.BitChromosome> writer() {
110                        return elem(ROOT_NAME,
111                                attr(LENGTH_NAME).map(io.jenetics.BitChromosome::length),
112                                attr(ONES_PROBABILITY_NAME).map(ch -> ch.oneProbability()),
113                                text().map(io.jenetics.BitChromosome::toCanonicalString)
114                        );
115                }
116
117                /**
118                 * Write the given {@link io.jenetics.BitChromosome} to the given
119                 * output stream.
120                 *
121                 * @param out the target output stream
122                 * @param data the bit-chromosome to write
123                 * @throws XMLStreamException if an error occurs while writing the
124                 *         chromosome
125                 * @throws NullPointerException if one of the given arguments is
126                 *         {@code null}
127                 */
128                public static void write(
129                        final OutputStream out,
130                        final io.jenetics.BitChromosome data
131                )
132                        throws XMLStreamException
133                {
134                        requireNonNull(data);
135                        requireNonNull(out);
136
137                        try (var xml = XML.writer(out)) {
138                                writer().write(xml, data);
139                        }
140                }
141        }
142
143
144        /**
145         * This class contains static writer methods for
146         * {@link io.jenetics.CharacterChromosome} objects.
147         * <p>
148         * <b>Writer code</b>
149         * <pre>{@code
150         * final CharacterChromosome value = CharacterChromosome.of("ASDF", CharSeq.of("A-Z"));
151         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
152         *     Writers.CharacterChromosome.writer().write(value, xml);
153         * }
154         * }</pre>
155         *
156         * <b>XML output</b>
157         * <pre> {@code
158         * <character-chromosome length="4">
159         *     <valid-alleles>ABCDEFGHIJKLMNOPQRSTUVWXYZ<valid-alleles>
160         *     <alleles>ASDF</alleles>
161         * </character-chromosome>
162         * }</pre>
163         */
164        public static final class CharacterChromosome {
165                private CharacterChromosome() {}
166
167                static final String ROOT_NAME = "character-chromosome";
168                static final String LENGTH_NAME = "length";
169                static final String VALID_ALLELES_NAME = "valid-alleles";
170                static final String ALLELES_NAME = "alleles";
171
172                /**
173                 * Return a {@link Writer} for {@link io.jenetics.CharacterChromosome}
174                 * objects.
175                 *
176                 * @return a chromosome writer
177                 */
178                public static Writer<io.jenetics.CharacterChromosome> writer() {
179                        return elem(ROOT_NAME,
180                                attr(LENGTH_NAME).map(io.jenetics.CharacterChromosome::length),
181                                elem(VALID_ALLELES_NAME,
182                                        text().map(ch -> ch.gene().validChars())),
183                                elem(ALLELES_NAME,
184                                        text().map(io.jenetics.CharacterChromosome::toString))
185                        );
186                }
187
188                /**
189                 * Write the given {@link io.jenetics.CharacterChromosome} to the given
190                 * output stream.
191                 *
192                 * @param out the target output stream
193                 * @param data the chromosome to write
194                 * @param indent the XML level indentation
195                 * @throws XMLStreamException if an error occurs while writing the
196                 *         chromosome
197                 * @throws NullPointerException if the {@code chromosome} or output
198                 *         stream is {@code null}
199                 */
200                public static void write(
201                        final OutputStream out,
202                        final io.jenetics.CharacterChromosome data,
203                        final String indent
204                )
205                        throws XMLStreamException
206                {
207                        requireNonNull(data);
208                        requireNonNull(out);
209
210                        try (var xml = XML.writer(out, indent)) {
211                                writer().write(xml, data);
212                        }
213                }
214
215                /**
216                 * Write the given {@link io.jenetics.CharacterChromosome} to the given
217                 * output stream.
218                 *
219                 * @param out the target output stream
220                 * @param data the chromosome to write
221                 * @throws XMLStreamException if an error occurs while writing the
222                 *         chromosome
223                 * @throws NullPointerException if the {@code chromosome} or output
224                 *         stream is {@code null}
225                 */
226                public static void write(
227                        final OutputStream out,
228                        final io.jenetics.CharacterChromosome data
229                )
230                        throws XMLStreamException
231                {
232                        write(out, data, null);
233                }
234
235        }
236
237        /**
238         * This class contains static writer methods for
239         * {@link io.jenetics.BoundedChromosome} objects.
240         *
241         * <p>
242         * <b>XML template</b>
243         * <pre> {@code
244         * <root-name length="3">
245         *     <min>aaa</min>
246         *     <max>zzz</max>
247         *     <alleles>
248         *         <allele>iii</allele>
249         *         <allele>fff</allele>
250         *         <allele>ggg</allele>
251         *     </alleles>
252         * </root-name>
253         * }</pre>
254         */
255        public static final class BoundedChromosome {
256                private BoundedChromosome() {}
257
258                static final String LENGTH_NAME = "length";
259                static final String MIN_NAME = "min";
260                static final String MAX_NAME = "max";
261                static final String ALLELE_NAME = "allele";
262                static final String ALLELES_NAME = "alleles";
263
264                /**
265                 * Create a bounded chromosome writer with the given configuration.
266                 *
267                 * @param rootName the name of the root element. E.g. {@code int-chromosome}
268                 * @param alleleWriter the XML writer used for the alleles
269                 * @param <A> the allele type
270                 * @param <G> the bounded gene type
271                 * @param <C> the bounded chromosome type
272                 * @return a bounded chromosome XML writer
273                 * @throws NullPointerException if one of the arguments is {@code null}
274                 */
275                public static <
276                        A extends Comparable<? super A>,
277                        G extends BoundedGene<A, G>,
278                        C extends io.jenetics.BoundedChromosome<A, G>
279                >
280                Writer<C> writer(
281                        final String rootName,
282                        final Writer<? super A> alleleWriter
283                ) {
284                        requireNonNull(rootName);
285                        requireNonNull(alleleWriter);
286
287                        return elem(rootName,
288                                attr(LENGTH_NAME).map(ch -> ch.length()),
289                                elem(MIN_NAME, alleleWriter.map(ch -> ch.min())),
290                                elem(MAX_NAME, alleleWriter.map(ch -> ch.max())),
291                                elem(ALLELES_NAME,
292                                        elems(ALLELE_NAME, alleleWriter)
293                                                .map(ch -> ISeq.of(ch).map(G::allele))
294                                )
295                        );
296                }
297        }
298
299        /**
300         * This class contains static writer methods for
301         * {@link io.jenetics.IntegerChromosome} objects.
302         * <p>
303         * <b>Writer code</b>
304         * <pre>{@code
305         * final IntegerChromosome value = IntegerChromosome
306         *     .of(Integer.MIN_VALUE, Integer.MAX_VALUE, 3);
307         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
308         *     Writers.IntegerChromosome.writer().write(value, xml);
309         * }
310         * }</pre>
311         *
312         * <b>XML output</b>
313         * <pre> {@code
314         * <int-chromosome length="3">
315         *     <min>-2147483648</min>
316         *     <max>2147483647</max>
317         *     <alleles>
318         *         <allele>-1878762439</allele>
319         *         <allele>-957346595</allele>
320         *         <allele>-88668137</allele>
321         *     </alleles>
322         * </int-chromosome>
323         * }</pre>
324         */
325        public static final class IntegerChromosome {
326                private IntegerChromosome() {}
327
328                static final String ROOT_NAME = "int-chromosome";
329
330                /**
331                 * Return the default integer allele writer for the
332                 * {@code IntegerChromosome}.
333                 *
334                 * @return the default integer allele writer
335                 */
336                public static Writer<Integer> alleleWriter() {
337                        return text();
338                }
339
340                /**
341                 * Return a {@link Writer} for {@link io.jenetics.IntegerChromosome}
342                 * objects.
343                 *
344                 * @param alleleWriter the allele writer used for writing the integer
345                 *        allele. Might be useful for using different integer
346                 *        <i>encodings</i>.
347                 * @return a chromosome writer
348                 * @throws NullPointerException if the given {@code alleleWriter} is
349                 *         {@code null}
350                 */
351                public static Writer<io.jenetics.IntegerChromosome>
352                writer(final Writer<? super Integer> alleleWriter) {
353                        requireNonNull(alleleWriter);
354
355                        return BoundedChromosome.<
356                                Integer,
357                                IntegerGene,
358                                io.jenetics.IntegerChromosome
359                        >writer(ROOT_NAME, alleleWriter);
360                }
361
362                /**
363                 * Return a {@link Writer} for {@link io.jenetics.IntegerChromosome}
364                 * objects.
365                 *
366                 * @return a chromosome writer
367                 */
368                public static Writer<io.jenetics.IntegerChromosome> writer() {
369                        return writer(alleleWriter());
370                }
371
372                /**
373                 * Write the given {@link io.jenetics.IntegerChromosome} to the given
374                 * output stream.
375                 *
376                 * @param out the target output stream
377                 * @param data the chromosome to write
378                 * @param indent the XML level indentation
379                 * @throws XMLStreamException if an error occurs while writing the
380                 *         chromosome
381                 * @throws NullPointerException if the {@code chromosome} or output
382                 *         stream is {@code null}
383                 */
384                public static void write(
385                        final OutputStream out,
386                        final io.jenetics.IntegerChromosome data,
387                        final String indent
388                )
389                        throws XMLStreamException
390                {
391                        requireNonNull(data);
392                        requireNonNull(out);
393
394                        try (var xml = XML.writer(out, indent)) {
395                                writer().write(xml, data);
396                        }
397                }
398
399                /**
400                 * Write the given {@link io.jenetics.IntegerChromosome} to the given
401                 * output stream.
402                 *
403                 * @param out the target output stream
404                 * @param data the chromosome to write
405                 * @throws XMLStreamException if an error occurs while writing the
406                 *         chromosome
407                 * @throws NullPointerException if the {@code chromosome} or output
408                 *         stream is {@code null}
409                 */
410                public static void write(
411                        final OutputStream out,
412                        final io.jenetics.IntegerChromosome data
413                )
414                        throws XMLStreamException
415                {
416                        write(out, data, null);
417                }
418        }
419
420        /**
421         * This class contains static writer methods for
422         * {@link io.jenetics.LongChromosome} objects.
423         * <p>
424         * <b>Writer code</b>
425         * <pre>{@code
426         * final LongChromosome value = LongChromosome
427         *     .of(Long.MIN_VALUE, Long.MAX_VALUE, 3);
428         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
429         *     Writers.LongChromosome.writer().write(value, xml);
430         * }
431         * }</pre>
432         *
433         * <b>XML output</b>
434         * <pre> {@code
435         * <long-chromosome length="3">
436         *     <min>-9223372036854775808</min>
437         *     <max>9223372036854775807</max>
438         *     <alleles>
439         *         <allele>-1345217698116542402</allele>
440         *         <allele>-7144755673073475303</allele>
441         *         <allele>6053786736809578435</allele>
442         *     </alleles>
443         * </long-chromosome>
444         * }</pre>
445         */
446        public static final class LongChromosome {
447                private LongChromosome() {}
448
449                static final String ROOT_NAME = "long-chromosome";
450
451                /**
452                 * Return the default long allele writer for the
453                 * {@code IntegerChromosome}.
454                 *
455                 * @return the default long allele writer
456                 */
457                public static Writer<Long> alleleWriter() {
458                        return Writer.text();
459                }
460
461                /**
462                 * Return a {@link Writer} for {@link io.jenetics.LongChromosome}
463                 * objects.
464                 *
465                 * @param alleleWriter the allele writer used for writing the long
466                 *        allele. Might be useful for using different long
467                 *        <i>encodings</i>.
468                 * @return a chromosome writer
469                 * @throws NullPointerException if the given {@code alleleWriter} is
470                 *         {@code null}
471                 */
472                public static Writer<io.jenetics.LongChromosome>
473                writer(final Writer<? super Long> alleleWriter) {
474                        return BoundedChromosome.<
475                                Long,
476                                LongGene,
477                                io.jenetics.LongChromosome
478                        >writer(ROOT_NAME, alleleWriter);
479                }
480
481                /**
482                 * Return a {@link Writer} for {@link io.jenetics.LongChromosome}
483                 * objects.
484                 *
485                 * @return a chromosome writer
486                 */
487                public static Writer<io.jenetics.LongChromosome> writer() {
488                        return writer(alleleWriter());
489                }
490
491                /**
492                 * Write the given {@link io.jenetics.LongChromosome} to the given
493                 * output stream.
494                 *
495                 * @param out the target output stream
496                 * @param data the chromosome to write
497                 * @param indent the XML level indentation
498                 * @throws XMLStreamException if an error occurs while writing the
499                 *         chromosome
500                 * @throws NullPointerException if the {@code chromosome} or output
501                 *         stream is {@code null}
502                 */
503                public static void write(
504                        final OutputStream out,
505                        final io.jenetics.LongChromosome data,
506                        final String indent
507                )
508                        throws XMLStreamException
509                {
510                        requireNonNull(data);
511                        requireNonNull(out);
512
513                        try (var xml = XML.writer(out, indent)) {
514                                writer().write(xml, data);
515                        }
516                }
517
518                /**
519                 * Write the given {@link io.jenetics.LongChromosome} to the given
520                 * output stream.
521                 *
522                 * @param out the target output stream
523                 * @param data the chromosome to write
524                 * @throws XMLStreamException if an error occurs while writing the
525                 *         chromosome
526                 * @throws NullPointerException if the {@code chromosome} or output
527                 *         stream is {@code null}
528                 */
529                public static void write(
530                        final OutputStream out,
531                        final io.jenetics.LongChromosome data
532                )
533                        throws XMLStreamException
534                {
535                        write(out, data, null);
536                }
537        }
538
539        /**
540         * This class contains static writer methods for
541         * {@link io.jenetics.DoubleChromosome} objects.
542         * <p>
543         * <b>Writer code</b>
544         * <pre>{@code
545         * final DoubleChromosome value = DoubleChromosome.of(0.0, 1.0, 3);
546         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
547         *     Writers.DoubleChromosome.writer().write(value, xml);
548         * }
549         * }</pre>
550         *
551         * <b>XML output</b>
552         * <pre> {@code
553         * <double-chromosome length="3">
554         *     <min>0.0</min>
555         *     <max>1.0</max>
556         *     <alleles>
557         *         <allele>0.27251556008507416</allele>
558         *         <allele>0.003140816229067145</allele>
559         *         <allele>0.43947528327497376</allele>
560         *     </alleles>
561         * </double-chromosome>
562         * }</pre>
563         */
564        public static final class DoubleChromosome
565                //extends WriterProvider<io.jenetics.DoubleChromosome>
566        {
567                private DoubleChromosome() {}
568
569                static final String ROOT_NAME = "double-chromosome";
570
571                /**
572                 * Return the default double allele writer for the
573                 * {@code DoubleChromosome}.
574                 *
575                 * @return the default double allele writer
576                 */
577                public static Writer<Double> alleleWriter() {
578                        return text().map(Object::toString);
579                }
580
581                /**
582                 * Return a {@link Writer} for {@link io.jenetics.DoubleChromosome}
583                 * objects.
584                 *
585                 * @param alleleWriter the allele writer used for writing the long
586                 *        allele. Might be useful for using different long
587                 *        <i>encodings</i>.
588                 * @return a chromosome writer
589                 * @throws NullPointerException if the given {@code alleleWriter} is
590                 *         {@code null}
591                 */
592                public static Writer<io.jenetics.DoubleChromosome>
593                writer(final Writer<? super Double> alleleWriter) {
594                        return BoundedChromosome.<
595                                Double,
596                                DoubleGene,
597                                io.jenetics.DoubleChromosome
598                        >writer(ROOT_NAME, alleleWriter);
599                }
600
601                /**
602                 * Return a {@link Writer} for {@link io.jenetics.DoubleChromosome}
603                 * objects.
604                 *
605                 * @return a chromosome writer
606                 */
607                public static Writer<io.jenetics.DoubleChromosome> writer() {
608                        return writer(alleleWriter());
609                }
610
611                public Class<io.jenetics.DoubleChromosome> type() {
612                        return io.jenetics.DoubleChromosome.class;
613                }
614
615                /**
616                 * Write the given {@link io.jenetics.DoubleChromosome} to the given
617                 * output stream.
618                 *
619                 * @param out the target output stream
620                 * @param data the chromosome to write
621                 * @param indent the XML level indentation
622                 * @throws XMLStreamException if an error occurs while writing the
623                 *         chromosome
624                 * @throws NullPointerException if the {@code chromosome} or output
625                 *         stream is {@code null}
626                 */
627                public static void write(
628                        final OutputStream out,
629                        final io.jenetics.DoubleChromosome data,
630                        final String indent
631                )
632                        throws XMLStreamException
633                {
634                        requireNonNull(data);
635                        requireNonNull(out);
636
637                        try (var xml = XML.writer(out, indent)) {
638                                writer().write(xml, data);
639                        }
640                }
641
642                /**
643                 * Write the given {@link io.jenetics.DoubleChromosome} to the given
644                 * output stream.
645                 *
646                 * @param out the target output stream
647                 * @param data the chromosome to write
648                 * @throws XMLStreamException if an error occurs while writing the
649                 *         chromosome
650                 * @throws NullPointerException if the {@code chromosome} or output
651                 *         stream is {@code null}
652                 */
653                public static void write(
654                        final OutputStream out,
655                        final io.jenetics.DoubleChromosome data
656                )
657                        throws XMLStreamException
658                {
659                        write(out, data, null);
660                }
661        }
662
663        /**
664         * This class contains static writer methods for
665         * {@link io.jenetics.PermutationChromosome} objects.
666         * <p>
667         * <b>Writer code</b>
668         * <pre>{@code
669         * final PermutationChromosome<Integer> value =
670         *     PermutationChromosome.ofInteger(5)
671         *
672         * final Writer<PermutationChromosome<Integer> writer =
673         *     Writers.PermutationChromosome.writer();
674         *
675         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
676         *     Writers.PermutationChromosome.writer().write(value, xml);
677         * }
678         * }</pre>
679         *
680         * <b>XML output</b>
681         * <pre> {@code
682         * <permutation-chromosome length="5">
683         *     <valid-alleles type="java.lang.Integer">
684         *         <allele>0</allele>
685         *         <allele>1</allele>
686         *         <allele>2</allele>
687         *         <allele>3</allele>
688         *         <allele>4</allele>
689         *     </valid-alleles>
690         *     <order>2 1 3 5 4</order>
691         * </permutation-chromosome>
692         * }</pre>
693         */
694        public static final class PermutationChromosome {
695                private PermutationChromosome() {}
696
697                static final String ROOT_NAME = "permutation-chromosome";
698                static final String LENGTH_NAME = "length";
699                static final String VALID_ALLELES_NAME = "valid-alleles";
700                static final String ALLELE_NAME = "allele";
701                static final String ORDER_NAME = "order";
702
703                /**
704                 * Create a writer for permutation-chromosomes. How to write the valid
705                 * alleles is defined by the given {@link Writer}.
706                 *
707                 * @param alleleWriter the allele writer
708                 * @param <A> the allele type
709                 * @return a new permutation chromosome writer
710                 * @throws NullPointerException if the given allele {@code writer} is
711                 *         {@code null}
712                 */
713                public static <A> Writer<io.jenetics.PermutationChromosome<A>>
714                writer(final Writer<? super A> alleleWriter) {
715                        return Writer.<io.jenetics.PermutationChromosome<A>>elem(
716                                ROOT_NAME,
717                                attr(LENGTH_NAME).map(io.jenetics.PermutationChromosome::length),
718                                elem(VALID_ALLELES_NAME,
719                                        attr("type").map(PermutationChromosome::toAlleleTypeName),
720                                        Writer.<A>elems(ALLELE_NAME, alleleWriter)
721                                                .map(io.jenetics.PermutationChromosome::validAlleles)
722                                ),
723                                elem(ORDER_NAME, text())
724                                        .map(ch -> ch.stream()
725                                                .map(g -> Integer.toString(g.alleleIndex()))
726                                                .collect(Collectors.joining(" ")))
727                        );
728                }
729
730                private static String toAlleleTypeName(
731                        final io.jenetics.PermutationChromosome<?> ch
732                ) {
733                        return ch.gene().allele().getClass().getCanonicalName();
734                }
735
736                /**
737                 * Create a writer for permutation-chromosomes. The valid alleles are
738                 * serialized by calling the {@link Object#toString()} method. Calling
739                 * this method is equivalent with:
740                 * <pre>{@code
741                 * final Writer<PermutationChromosome<Double> writer =
742                 *     PermutationChromosome.write(text().map(Objects::toString));
743                 * }</pre>
744                 *
745                 * Example output:
746                 * <pre> {@code
747                 * <permutation-chromosome length="15">
748                 *     <valid-alleles type="java.lang.Double">
749                 *         <allele>0.27251556008507416</allele>
750                 *         <allele>0.003140816229067145</allele>
751                 *         <allele>0.43947528327497376</allele>
752                 *         <allele>0.10654807463069327</allele>
753                 *         <allele>0.19696530915810317</allele>
754                 *         <allele>0.7450003838065538</allele>
755                 *         <allele>0.5594416969271359</allele>
756                 *         <allele>0.02823782430152355</allele>
757                 *         <allele>0.5741102315010789</allele>
758                 *         <allele>0.4533651041367144</allele>
759                 *         <allele>0.811148141800367</allele>
760                 *         <allele>0.5710456351848858</allele>
761                 *         <allele>0.30166768355230955</allele>
762                 *         <allele>0.5455492865240272</allele>
763                 *         <allele>0.21068427527733102</allele>
764                 *     </valid-alleles>
765                 *     <order>13 12 4 6 8 14 7 2 11 5 3 0 9 10 1</order>
766                 * </permutation-chromosome>
767                 * }</pre>
768                 *
769                 * @param <A> the allele type
770                 * @return a new permutation chromosome writer
771                 */
772                public static <A> Writer<io.jenetics.PermutationChromosome<A>> writer() {
773                        return writer(text());
774                }
775
776                /**
777                 * Write the given {@link io.jenetics.PermutationChromosome} to the
778                 * given output stream.
779                 *
780                 * @param <A> the allele type
781                 * @param out the target output stream
782                 * @param data the chromosome to write
783                 * @param indent the XML level indentation
784                 * @throws XMLStreamException if an error occurs while writing the
785                 *         chromosome
786                 * @throws NullPointerException if the {@code chromosome} or output
787                 *         stream is {@code null}
788                 */
789                public static <A> void write(
790                        final OutputStream out,
791                        final io.jenetics.PermutationChromosome<A> data,
792                        final String indent
793                )
794                        throws XMLStreamException
795                {
796                        requireNonNull(data);
797                        requireNonNull(out);
798
799                        try (var writer = XML.writer(out, indent)) {
800                                PermutationChromosome.<A>writer().write(writer, data);
801                        }
802                }
803
804                /**
805                 * Write the given {@link io.jenetics.PermutationChromosome} to the
806                 * given output stream.
807                 *
808                 * @param <A> the allele type
809                 * @param out the target output stream
810                 * @param data the chromosome to write
811                 * @param indent the XML level indentation
812                 * @param alleleWriter the allele writer of the permutation chromosome
813                 * @throws XMLStreamException if an error occurs while writing the
814                 *         chromosome
815                 * @throws NullPointerException if the {@code chromosome} or output
816                 *         stream is {@code null}
817                 */
818                public static <A> void write(
819                        final OutputStream out,
820                        final io.jenetics.PermutationChromosome<A> data,
821                        final String indent,
822                        final Writer<? super A> alleleWriter
823                )
824                        throws XMLStreamException
825                {
826                        requireNonNull(data);
827                        requireNonNull(alleleWriter);
828                        requireNonNull(out);
829
830                        try (var xml = XML.writer(out, indent)) {
831                                PermutationChromosome.<A>writer(alleleWriter)
832                                        .write(xml, data);
833                        }
834                }
835
836                /**
837                 * Write the given {@link io.jenetics.PermutationChromosome} to the
838                 * given output stream.
839                 *
840                 * @param <A> the allele type
841                 * @param out the target output stream
842                 * @param data the chromosome to write
843                 * @throws XMLStreamException if an error occurs while writing the
844                 *         chromosome
845                 * @throws NullPointerException if the {@code chromosome} or output
846                 *         stream is {@code null}
847                 */
848                public static <A> void write(
849                        final OutputStream out,
850                        final io.jenetics.PermutationChromosome<A> data
851                )
852                        throws XMLStreamException
853                {
854                        write(out, data, null, text());
855                }
856
857                /**
858                 * Write the given {@link io.jenetics.PermutationChromosome} to the
859                 * given output stream.
860                 *
861                 * @param <A> the allele type
862                 * @param out the target output stream
863                 * @param data the chromosome to write
864                 * @param alleleWriter the allele writer used to write the chromosome
865                 *         alleles
866                 * @throws XMLStreamException if an error occurs while writing the
867                 *         chromosome
868                 * @throws NullPointerException if the {@code chromosome} or output
869                 *         stream is {@code null}
870                 */
871                public static <A> void write(
872                        final OutputStream out,
873                        final io.jenetics.PermutationChromosome<A> data,
874                        final Writer<? super A> alleleWriter
875                )
876                        throws XMLStreamException
877                {
878                        write(out, data, null, alleleWriter);
879                }
880
881        }
882
883        /**
884         * This class contains static writer methods for
885         * {@link io.jenetics.Genotype} objects.
886         * <p>
887         * <b>Writer code</b>
888         * <pre>{@code
889         * final Genotype<DoubleGene> gt = Genotype.of(
890         *     DoubleChromosome.of(0.0, 1.0, 3),
891         *     DoubleChromosome.of(0.0, 1.0, 2)
892         * );
893         * final Writer<Genotype<DoubleGene>> writer =
894         *     Writers.Genotype.writer(Writers.DoubleChromosome.writer());
895         *
896         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
897         *     writer.write(value, xml);
898         * }
899         * }</pre>
900         *
901         * <b>XML output</b>
902         * <pre> {@code
903         * <genotype length="2" ngenes="5">
904         *     <double-chromosome length="3">
905         *         <min>0.0</min>
906         *         <max>1.0</max>
907         *         <alleles>
908         *             <allele>0.27251556008507416</allele>
909         *             <allele>0.003140816229067145</allele>
910         *             <allele>0.43947528327497376</allele>
911         *         </alleles>
912         *     </double-chromosome>
913         *     <double-chromosome length="2">
914         *         <min>0.0</min>
915         *         <max>1.0</max>
916         *         <alleles>
917         *             <allele>0.4026521545744768</allele>
918         *             <allele>0.36137605952663554</allele>
919         *         <alleles>
920         *     </double-chromosome>
921         * </genotype>
922         * }</pre>
923         */
924        public static final class Genotype {
925                private Genotype() {}
926
927                static final String ROOT_NAME = "genotype";
928                static final String LENGTH_NAME = "length";
929                static final String NGENES_NAME = "ngenes";
930
931                /**
932                 * Create a writer for genotypes of arbitrary chromosomes. How to write the
933                 * genotypes chromosomes is defined by the given {@link Writer}.
934                 *
935                 * @param writer the chromosome writer
936                 * @param <A> the allele type
937                 * @param <G> the gene type
938                 * @param <C> the chromosome type
939                 * @return a new genotype writer
940                 * @throws NullPointerException if the given chromosome {@code writer} is
941                 *         {@code null}
942                 */
943                public static <
944                        A,
945                        G extends Gene<A, G>,
946                        C extends Chromosome<G>
947                >
948                Writer<io.jenetics.Genotype<G>> writer(final Writer<? super C> writer) {
949                        return elem(
950                                ROOT_NAME,
951                                attr(LENGTH_NAME).map(io.jenetics.Genotype::length),
952                                attr(NGENES_NAME).map(io.jenetics.Genotype::geneCount),
953                                elems(writer).map(gt -> cast(ISeq.of(gt)))
954                        );
955                }
956
957                @SuppressWarnings("unchecked")
958                private static <A, B> B cast(final A value) {
959                        return (B)value;
960                }
961
962                /**
963                 * Write the given {@link io.jenetics.Genotype} to the given output
964                 * stream.
965                 *
966                 * @param <A> the allele type
967                 * @param <G> the gene type
968                 * @param <C> the chromosome type
969                 * @param out the target output stream
970                 * @param data the genotype to write
971                 * @param indent the XML level indentation
972                 * @param chromosomeWriter the chromosome writer used to write the
973                 *        genotypes
974                 * @throws XMLStreamException if an error occurs while writing the
975                 *         chromosome
976                 * @throws NullPointerException if the one of the arguments is
977                 *         {@code null}
978                 */
979                public static <
980                        A,
981                        G extends Gene<A, G>,
982                        C extends Chromosome<G>
983                >
984                void write(
985                        final OutputStream out,
986                        final io.jenetics.Genotype<G> data,
987                        final String indent,
988                        final Writer<? super C> chromosomeWriter
989                )
990                        throws XMLStreamException
991                {
992                        requireNonNull(data);
993                        requireNonNull(chromosomeWriter);
994                        requireNonNull(out);
995
996                        try (var writer = XML.writer(out, indent)) {
997                                Genotype.<A, G, C>writer(chromosomeWriter).write(writer, data);
998                        }
999                }
1000
1001                /**
1002                 * Write the given {@link io.jenetics.Genotype} to the given output
1003                 * stream.
1004                 *
1005                 * @param <A> the allele type
1006                 * @param <G> the gene type
1007                 * @param <C> the chromosome type
1008                 * @param out the target output stream
1009                 * @param data the genotype to write
1010                 * @param chromosomeWriter the chromosome writer used to write the
1011                 *        genotypes
1012                 * @throws XMLStreamException if an error occurs while writing the
1013                 *         chromosome
1014                 * @throws NullPointerException if the one of the arguments is
1015                 *         {@code null}
1016                 */
1017                public static <
1018                        A,
1019                        G extends Gene<A, G>,
1020                        C extends Chromosome<G>
1021                >
1022                void write(
1023                        final OutputStream out,
1024                        final io.jenetics.Genotype<G> data,
1025                        final Writer<? super C> chromosomeWriter
1026                )
1027                        throws XMLStreamException
1028                {
1029                        requireNonNull(data);
1030                        requireNonNull(chromosomeWriter);
1031                        requireNonNull(out);
1032
1033                        try (var xml = XML.writer(out)) {
1034                                Genotype.<A, G, C>writer(chromosomeWriter).write(xml, data);
1035                        }
1036                }
1037
1038        }
1039
1040        /**
1041         * This class contains static writer methods for
1042         * {@link io.jenetics.Genotype} objects.
1043         * <p>
1044         * <b>Writer code</b>
1045         * <pre>{@code
1046         * final Genotype<DoubleGene> gt = Genotype.of(
1047         *     DoubleChromosome.of(0.0, 1.0, 3),
1048         *     DoubleChromosome.of(0.0, 1.0, 2)
1049         * );
1050         *
1051         * final Writer<Collection<Genotype<DoubleGene>>> writer =
1052         *     Writers.Genotypes.writer(Writers.DoubleChromosome.writer());
1053         *
1054         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
1055         *     writer.write(asList(value), xml);
1056         * }
1057         * }</pre>
1058         *
1059         * <pre> {@code
1060         * <genotypes length="1">
1061         *     <genotype length="2" ngenes="5">
1062         *         <double-chromosome length="3">
1063         *             <min>0.0</min>
1064         *             <max>1.0</max>
1065         *             <alleles>
1066         *                 <allele>0.27251556008507416</allele>
1067         *                 <allele>0.003140816229067145</allele>
1068         *                 <allele>0.43947528327497376</allele>
1069         *             </alleles>
1070         *         </double-chromosome>
1071         *         <double-chromosome length="2">
1072         *             <min>0.0</min>
1073         *             <max>1.0</max>
1074         *             <alleles>
1075         *                 <allele>0.4026521545744768</allele>
1076         *                 <allele>0.36137605952663554</allele>
1077         *             <alleles>
1078         *         </double-chromosome>
1079         *     </genotype>
1080         * </genotypes>
1081         * }</pre>
1082         */
1083        public static final class Genotypes {
1084                private Genotypes() {}
1085
1086                static final String ROOT_NAME = "genotypes";
1087                static final String LENGTH_NAME = "length";
1088
1089                /**
1090                 * Create a writer for genotypes of arbitrary chromosomes. How to write the
1091                 * genotypes chromosomes is defined by the given {@link Writer}. The
1092                 * following writer allows to write double-gene chromosomes:
1093                 * <pre>{@code
1094                 * final Writer<Collection<Genotype<DoubleGene>>> writer =
1095                 *     Writers.Genotypes.writer(Writers.DoubleChromosome.writer());
1096                 * }</pre>
1097                 *
1098                 * @param writer the chromosome writer
1099                 * @param <A> the allele type
1100                 * @param <G> the gene type
1101                 * @param <C> the chromosome type
1102                 * @return a new genotype writer
1103                 * @throws NullPointerException if the given chromosome {@code writer} is
1104                 *         {@code null}
1105                 */
1106                public static <
1107                        A,
1108                        G extends Gene<A, G>,
1109                        C extends Chromosome<G>
1110                >
1111                Writer<Collection<io.jenetics.Genotype<G>>>
1112                writer(final Writer<? super C> writer) {
1113                        return elem(
1114                                ROOT_NAME,
1115                                attr(LENGTH_NAME).map(Collection::size),
1116                                elems(Genotype.writer(writer))
1117                        );
1118                }
1119
1120                /**
1121                 * Write the given {@link io.jenetics.Genotype} to the given output
1122                 * stream.
1123                 *
1124                 * @param <A> the allele type
1125                 * @param <G> the gene type
1126                 * @param <C> the chromosome type
1127                 * @param out the target output stream
1128                 * @param data the genotypes to write
1129                 * @param indent the XML level indentation
1130                 * @param chromosomeWriter the chromosome writer used to write the
1131                 *        genotypes
1132                 * @throws XMLStreamException if an error occurs while writing the
1133                 *         chromosome
1134                 * @throws NullPointerException if the one of the arguments is
1135                 *         {@code null}
1136                 */
1137                public static <
1138                        A,
1139                        G extends Gene<A, G>,
1140                        C extends Chromosome<G>
1141                >
1142                void write(
1143                        final OutputStream out,
1144                        final Collection<io.jenetics.Genotype<G>> data,
1145                        final String indent,
1146                        final Writer<? super C> chromosomeWriter
1147                )
1148                        throws XMLStreamException
1149                {
1150                        requireNonNull(data);
1151                        requireNonNull(chromosomeWriter);
1152                        requireNonNull(out);
1153
1154                        try (var xml = XML.writer(out, indent)) {
1155                                Genotypes.<A, G, C>writer(chromosomeWriter).write(xml, data);
1156                        }
1157                }
1158
1159                /**
1160                 * Write the given {@link io.jenetics.Genotype} to the given output
1161                 * stream.
1162                 *
1163                 * @param <A> the allele type
1164                 * @param <G> the gene type
1165                 * @param <C> the chromosome type
1166                 * @param out the target output stream
1167                 * @param data the genotypes to write
1168                 * @param chromosomeWriter the chromosome writer used to write the
1169                 *        genotypes
1170                 * @throws XMLStreamException if an error occurs while writing the
1171                 *         chromosome
1172                 * @throws NullPointerException if the one of the arguments is
1173                 *         {@code null}
1174                 */
1175                public static <
1176                        A,
1177                        G extends Gene<A, G>,
1178                        C extends Chromosome<G>
1179                >
1180                void write(
1181                        final OutputStream out,
1182                        final Collection<io.jenetics.Genotype<G>> data,
1183                        final Writer<? super C> chromosomeWriter
1184                )
1185                        throws XMLStreamException
1186                {
1187                        requireNonNull(data);
1188                        requireNonNull(chromosomeWriter);
1189                        requireNonNull(out);
1190
1191                        try (var xml = XML.writer(out)) {
1192                                Genotypes.<A, G, C>writer(chromosomeWriter).write(xml, data);
1193                        }
1194                }
1195
1196        }
1197
1198
1199        /**
1200         * Write the given {@link io.jenetics.Genotype} to the given output
1201         * stream.
1202         *
1203         * @see Genotypes#write(OutputStream, Collection, Writer)
1204         *
1205         * @param <A> the allele type
1206         * @param <G> the gene type
1207         * @param <C> the chromosome type
1208         * @param out the target output stream
1209         * @param data the genotypes to write
1210         * @param chromosomeWriter the chromosome writer used to write the
1211         *        genotypes
1212         * @throws XMLStreamException if an error occurs while writing the
1213         *         chromosome
1214         * @throws NullPointerException if the one of the arguments is
1215         *         {@code null}
1216         */
1217        public static <
1218                A,
1219                G extends Gene<A, G>,
1220                C extends Chromosome<G>
1221        >
1222        void write(
1223                final OutputStream out,
1224                final Collection<io.jenetics.Genotype<G>> data,
1225                final Writer<? super C> chromosomeWriter
1226        )
1227                throws XMLStreamException
1228        {
1229                Genotypes.write(out, data, chromosomeWriter);
1230        }
1231
1232}