001/*
002 * Java Genetic Algorithm Library (jenetics-9.1.0).
003 * Copyright (c) 2007-2026 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.BaseSeq;
041import io.jenetics.util.ISeq;
042import io.jenetics.xml.stream.Writer;
043import io.jenetics.xml.stream.XML;
044
045/**
046 * This class contains static fields and methods for creating chromosome- and
047 * genotype writers for different gene types.
048 * {@snippet lang="java":
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 * }
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 * {@snippet lang="java":
062 * final List<Genotype<BitGene>> genotypes = null; // @replace substring='null' replacement="..."
063 * try (OutputStream out = Files.newOutputStream(Paths.get("path"))) {
064 *     Writers.write(out, genotypes, Writers.BitChromosome.writer());
065 * }
066 * }
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         * {@snippet lang="java":
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         * }
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(io.jenetics.BitChromosome::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         * {@snippet lang="java":
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         * }
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(BaseSeq::length),
289                                elem(MIN_NAME, alleleWriter.map(io.jenetics.BoundedChromosome::min)),
290                                elem(MAX_NAME, alleleWriter.map(io.jenetics.BoundedChromosome::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         * {@snippet lang="java":
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         * }
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         * {@snippet lang="java":
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         * }
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         * {@snippet lang="java":
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         * }
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         * {@snippet lang="java":
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         * }
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.elem(
716                                ROOT_NAME,
717                                attr(LENGTH_NAME)
718                                        .map(io.jenetics.PermutationChromosome::length),
719                                elem(VALID_ALLELES_NAME,
720                                        attr("type").map(PermutationChromosome::toAlleleTypeName),
721                                        Writer.<A>elems(ALLELE_NAME, alleleWriter)
722                                                .map(io.jenetics.PermutationChromosome::validAlleles)
723                                ),
724                                elem(ORDER_NAME, text())
725                                        .map(ch -> ch.stream()
726                                                .map(g -> Integer.toString(g.alleleIndex()))
727                                                .collect(Collectors.joining(" ")))
728                        );
729                }
730
731                private static String toAlleleTypeName(
732                        final io.jenetics.PermutationChromosome<?> ch
733                ) {
734                        return ch.gene().allele().getClass().getCanonicalName();
735                }
736
737                /**
738                 * Create a writer for permutation-chromosomes. The valid alleles are
739                 * serialized by calling the {@link Object#toString()} method. Calling
740                 * this method is equivalent to:
741                 * {@snippet lang="java":
742                 * final Writer<PermutationChromosome<Double>> writer =
743                 *     PermutationChromosome.write(text().map(Objects::toString));
744                 * }
745                 *
746                 * Example output:
747                 * <pre> {@code
748                 * <permutation-chromosome length="15">
749                 *     <valid-alleles type="java.lang.Double">
750                 *         <allele>0.27251556008507416</allele>
751                 *         <allele>0.003140816229067145</allele>
752                 *         <allele>0.43947528327497376</allele>
753                 *         <allele>0.10654807463069327</allele>
754                 *         <allele>0.19696530915810317</allele>
755                 *         <allele>0.7450003838065538</allele>
756                 *         <allele>0.5594416969271359</allele>
757                 *         <allele>0.02823782430152355</allele>
758                 *         <allele>0.5741102315010789</allele>
759                 *         <allele>0.4533651041367144</allele>
760                 *         <allele>0.811148141800367</allele>
761                 *         <allele>0.5710456351848858</allele>
762                 *         <allele>0.30166768355230955</allele>
763                 *         <allele>0.5455492865240272</allele>
764                 *         <allele>0.21068427527733102</allele>
765                 *     </valid-alleles>
766                 *     <order>13 12 4 6 8 14 7 2 11 5 3 0 9 10 1</order>
767                 * </permutation-chromosome>
768                 * } </pre>
769                 *
770                 * @param <A> the allele type
771                 * @return a new permutation chromosome writer
772                 */
773                public static <A> Writer<io.jenetics.PermutationChromosome<A>> writer() {
774                        return writer(text());
775                }
776
777                /**
778                 * Write the given {@link io.jenetics.PermutationChromosome} to the
779                 * given output stream.
780                 *
781                 * @param <A> the allele type
782                 * @param out the target output stream
783                 * @param data the chromosome to write
784                 * @param indent the XML level indentation
785                 * @throws XMLStreamException if an error occurs while writing the
786                 *         chromosome
787                 * @throws NullPointerException if the {@code chromosome} or output
788                 *         stream is {@code null}
789                 */
790                public static <A> void write(
791                        final OutputStream out,
792                        final io.jenetics.PermutationChromosome<A> data,
793                        final String indent
794                )
795                        throws XMLStreamException
796                {
797                        requireNonNull(data);
798                        requireNonNull(out);
799
800                        try (var writer = XML.writer(out, indent)) {
801                                PermutationChromosome.<A>writer().write(writer, data);
802                        }
803                }
804
805                /**
806                 * Write the given {@link io.jenetics.PermutationChromosome} to the
807                 * given output stream.
808                 *
809                 * @param <A> the allele type
810                 * @param out the target output stream
811                 * @param data the chromosome to write
812                 * @param indent the XML level indentation
813                 * @param alleleWriter the allele writer of the permutation chromosome
814                 * @throws XMLStreamException if an error occurs while writing the
815                 *         chromosome
816                 * @throws NullPointerException if the {@code chromosome} or output
817                 *         stream is {@code null}
818                 */
819                public static <A> void write(
820                        final OutputStream out,
821                        final io.jenetics.PermutationChromosome<A> data,
822                        final String indent,
823                        final Writer<? super A> alleleWriter
824                )
825                        throws XMLStreamException
826                {
827                        requireNonNull(data);
828                        requireNonNull(alleleWriter);
829                        requireNonNull(out);
830
831                        try (var xml = XML.writer(out, indent)) {
832                                PermutationChromosome.<A>writer(alleleWriter)
833                                        .write(xml, data);
834                        }
835                }
836
837                /**
838                 * Write the given {@link io.jenetics.PermutationChromosome} to the
839                 * given output stream.
840                 *
841                 * @param <A> the allele type
842                 * @param out the target output stream
843                 * @param data the chromosome to write
844                 * @throws XMLStreamException if an error occurs while writing the
845                 *         chromosome
846                 * @throws NullPointerException if the {@code chromosome} or output
847                 *         stream is {@code null}
848                 */
849                public static <A> void write(
850                        final OutputStream out,
851                        final io.jenetics.PermutationChromosome<A> data
852                )
853                        throws XMLStreamException
854                {
855                        write(out, data, null, text());
856                }
857
858                /**
859                 * Write the given {@link io.jenetics.PermutationChromosome} to the
860                 * given output stream.
861                 *
862                 * @param <A> the allele type
863                 * @param out the target output stream
864                 * @param data the chromosome to write
865                 * @param alleleWriter the allele writer used to write the chromosome
866                 *         alleles
867                 * @throws XMLStreamException if an error occurs while writing the
868                 *         chromosome
869                 * @throws NullPointerException if the {@code chromosome} or output
870                 *         stream is {@code null}
871                 */
872                public static <A> void write(
873                        final OutputStream out,
874                        final io.jenetics.PermutationChromosome<A> data,
875                        final Writer<? super A> alleleWriter
876                )
877                        throws XMLStreamException
878                {
879                        write(out, data, null, alleleWriter);
880                }
881
882        }
883
884        /**
885         * This class contains static writer methods for
886         * {@link io.jenetics.Genotype} objects.
887         * <p>
888         * <b>Writer code</b>
889         * {@snippet lang="java":
890         * final Genotype<DoubleGene> gt = Genotype.of(
891         *     DoubleChromosome.of(0.0, 1.0, 3),
892         *     DoubleChromosome.of(0.0, 1.0, 2)
893         * );
894         * final Writer<Genotype<DoubleGene>> writer =
895         *     Writers.Genotype.writer(Writers.DoubleChromosome.writer());
896         *
897         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
898         *     writer.write(value, xml);
899         * }
900         * }
901         *
902         * <b>XML output</b>
903         * <pre> {@code
904         * <genotype length="2" ngenes="5">
905         *     <double-chromosome length="3">
906         *         <min>0.0</min>
907         *         <max>1.0</max>
908         *         <alleles>
909         *             <allele>0.27251556008507416</allele>
910         *             <allele>0.003140816229067145</allele>
911         *             <allele>0.43947528327497376</allele>
912         *         </alleles>
913         *     </double-chromosome>
914         *     <double-chromosome length="2">
915         *         <min>0.0</min>
916         *         <max>1.0</max>
917         *         <alleles>
918         *             <allele>0.4026521545744768</allele>
919         *             <allele>0.36137605952663554</allele>
920         *         <alleles>
921         *     </double-chromosome>
922         * </genotype>
923         * } </pre>
924         */
925        public static final class Genotype {
926                private Genotype() {}
927
928                static final String ROOT_NAME = "genotype";
929                static final String LENGTH_NAME = "length";
930                static final String NGENES_NAME = "ngenes";
931
932                /**
933                 * Create a writer for genotypes of arbitrary chromosomes. How to write the
934                 * genotype chromosomes is defined by the given {@link Writer}.
935                 *
936                 * @param writer the chromosome writer
937                 * @param <A> the allele type
938                 * @param <G> the gene type
939                 * @param <C> the chromosome type
940                 * @return a new genotype writer
941                 * @throws NullPointerException if the given chromosome {@code writer} is
942                 *         {@code null}
943                 */
944                public static <
945                        A,
946                        G extends Gene<A, G>,
947                        C extends Chromosome<G>
948                >
949                Writer<io.jenetics.Genotype<G>> writer(final Writer<? super C> writer) {
950                        return elem(
951                                ROOT_NAME,
952                                attr(LENGTH_NAME).map(io.jenetics.Genotype::length),
953                                attr(NGENES_NAME).map(io.jenetics.Genotype::geneCount),
954                                elems(writer).map(gt -> cast(ISeq.of(gt)))
955                        );
956                }
957
958                @SuppressWarnings("unchecked")
959                private static <A, B> B cast(final A value) {
960                        return (B)value;
961                }
962
963                /**
964                 * Write the given {@link io.jenetics.Genotype} to the given output
965                 * stream.
966                 *
967                 * @param <A> the allele type
968                 * @param <G> the gene type
969                 * @param <C> the chromosome type
970                 * @param out the target output stream
971                 * @param data the genotype to write
972                 * @param indent the XML level indentation
973                 * @param chromosomeWriter the chromosome writer used to write the
974                 *        genotypes
975                 * @throws XMLStreamException if an error occurs while writing the
976                 *         chromosome
977                 * @throws NullPointerException if the one of the arguments is
978                 *         {@code null}
979                 */
980                public static <
981                        A,
982                        G extends Gene<A, G>,
983                        C extends Chromosome<G>
984                >
985                void write(
986                        final OutputStream out,
987                        final io.jenetics.Genotype<G> data,
988                        final String indent,
989                        final Writer<? super C> chromosomeWriter
990                )
991                        throws XMLStreamException
992                {
993                        requireNonNull(data);
994                        requireNonNull(chromosomeWriter);
995                        requireNonNull(out);
996
997                        try (var writer = XML.writer(out, indent)) {
998                                Genotype.<A, G, C>writer(chromosomeWriter).write(writer, data);
999                        }
1000                }
1001
1002                /**
1003                 * Write the given {@link io.jenetics.Genotype} to the given output
1004                 * stream.
1005                 *
1006                 * @param <A> the allele type
1007                 * @param <G> the gene type
1008                 * @param <C> the chromosome type
1009                 * @param out the target output stream
1010                 * @param data the genotype to write
1011                 * @param chromosomeWriter the chromosome writer used to write the
1012                 *        genotypes
1013                 * @throws XMLStreamException if an error occurs while writing the
1014                 *         chromosome
1015                 * @throws NullPointerException if the one of the arguments is
1016                 *         {@code null}
1017                 */
1018                public static <
1019                        A,
1020                        G extends Gene<A, G>,
1021                        C extends Chromosome<G>
1022                >
1023                void write(
1024                        final OutputStream out,
1025                        final io.jenetics.Genotype<G> data,
1026                        final Writer<? super C> chromosomeWriter
1027                )
1028                        throws XMLStreamException
1029                {
1030                        requireNonNull(data);
1031                        requireNonNull(chromosomeWriter);
1032                        requireNonNull(out);
1033
1034                        try (var xml = XML.writer(out)) {
1035                                Genotype.<A, G, C>writer(chromosomeWriter).write(xml, data);
1036                        }
1037                }
1038
1039        }
1040
1041        /**
1042         * This class contains static writer methods for
1043         * {@link io.jenetics.Genotype} objects.
1044         * <p>
1045         * <b>Writer code</b>
1046         * {@snippet lang="java":
1047         * final Genotype<DoubleGene> gt = Genotype.of(
1048         *     DoubleChromosome.of(0.0, 1.0, 3),
1049         *     DoubleChromosome.of(0.0, 1.0, 2)
1050         * );
1051         *
1052         * final Writer<Collection<Genotype<DoubleGene>>> writer =
1053         *     Writers.Genotypes.writer(Writers.DoubleChromosome.writer());
1054         *
1055         * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
1056         *     writer.write(asList(value), xml);
1057         * }
1058         * }
1059         *
1060         * <pre> {@code
1061         * <genotypes length="1">
1062         *     <genotype length="2" ngenes="5">
1063         *         <double-chromosome length="3">
1064         *             <min>0.0</min>
1065         *             <max>1.0</max>
1066         *             <alleles>
1067         *                 <allele>0.27251556008507416</allele>
1068         *                 <allele>0.003140816229067145</allele>
1069         *                 <allele>0.43947528327497376</allele>
1070         *             </alleles>
1071         *         </double-chromosome>
1072         *         <double-chromosome length="2">
1073         *             <min>0.0</min>
1074         *             <max>1.0</max>
1075         *             <alleles>
1076         *                 <allele>0.4026521545744768</allele>
1077         *                 <allele>0.36137605952663554</allele>
1078         *             <alleles>
1079         *         </double-chromosome>
1080         *     </genotype>
1081         * </genotypes>
1082         * } </pre>
1083         */
1084        public static final class Genotypes {
1085                private Genotypes() {}
1086
1087                static final String ROOT_NAME = "genotypes";
1088                static final String LENGTH_NAME = "length";
1089
1090                /**
1091                 * Create a writer for genotypes of arbitrary chromosomes. How to write the
1092                 * genotype chromosomes is defined by the given {@link Writer}. The
1093                 * following writer allows writing double-gene chromosomes:
1094                 * {@snippet lang="java":
1095                 * final Writer<Collection<Genotype<DoubleGene>>> writer =
1096                 *     Writers.Genotypes.writer(Writers.DoubleChromosome.writer());
1097                 * }
1098                 *
1099                 * @param writer the chromosome writer
1100                 * @param <A> the allele type
1101                 * @param <G> the gene type
1102                 * @param <C> the chromosome type
1103                 * @return a new genotype writer
1104                 * @throws NullPointerException if the given chromosome {@code writer} is
1105                 *         {@code null}
1106                 */
1107                public static <
1108                        A,
1109                        G extends Gene<A, G>,
1110                        C extends Chromosome<G>
1111                >
1112                Writer<Collection<io.jenetics.Genotype<G>>>
1113                writer(final Writer<? super C> writer) {
1114                        return elem(
1115                                ROOT_NAME,
1116                                attr(LENGTH_NAME).map(Collection::size),
1117                                elems(Genotype.writer(writer))
1118                        );
1119                }
1120
1121                /**
1122                 * Write the given {@link io.jenetics.Genotype} to the given output
1123                 * stream.
1124                 *
1125                 * @param <A> the allele type
1126                 * @param <G> the gene type
1127                 * @param <C> the chromosome type
1128                 * @param out the target output stream
1129                 * @param data the genotypes to write
1130                 * @param indent the XML level indentation
1131                 * @param chromosomeWriter the chromosome writer used to write the
1132                 *        genotypes
1133                 * @throws XMLStreamException if an error occurs while writing the
1134                 *         chromosome
1135                 * @throws NullPointerException if the one of the arguments is
1136                 *         {@code null}
1137                 */
1138                public static <
1139                        A,
1140                        G extends Gene<A, G>,
1141                        C extends Chromosome<G>
1142                >
1143                void write(
1144                        final OutputStream out,
1145                        final Collection<io.jenetics.Genotype<G>> data,
1146                        final String indent,
1147                        final Writer<? super C> chromosomeWriter
1148                )
1149                        throws XMLStreamException
1150                {
1151                        requireNonNull(data);
1152                        requireNonNull(chromosomeWriter);
1153                        requireNonNull(out);
1154
1155                        try (var xml = XML.writer(out, indent)) {
1156                                Genotypes.<A, G, C>writer(chromosomeWriter).write(xml, data);
1157                        }
1158                }
1159
1160                /**
1161                 * Write the given {@link io.jenetics.Genotype} to the given output
1162                 * stream.
1163                 *
1164                 * @param <A> the allele type
1165                 * @param <G> the gene type
1166                 * @param <C> the chromosome type
1167                 * @param out the target output stream
1168                 * @param data the genotypes to write
1169                 * @param chromosomeWriter the chromosome writer used to write the
1170                 *        genotypes
1171                 * @throws XMLStreamException if an error occurs while writing the
1172                 *         chromosome
1173                 * @throws NullPointerException if the one of the arguments is
1174                 *         {@code null}
1175                 */
1176                public static <
1177                        A,
1178                        G extends Gene<A, G>,
1179                        C extends Chromosome<G>
1180                >
1181                void write(
1182                        final OutputStream out,
1183                        final Collection<io.jenetics.Genotype<G>> data,
1184                        final Writer<? super C> chromosomeWriter
1185                )
1186                        throws XMLStreamException
1187                {
1188                        requireNonNull(data);
1189                        requireNonNull(chromosomeWriter);
1190                        requireNonNull(out);
1191
1192                        try (var xml = XML.writer(out)) {
1193                                Genotypes.<A, G, C>writer(chromosomeWriter).write(xml, data);
1194                        }
1195                }
1196
1197        }
1198
1199
1200        /**
1201         * Write the given {@link io.jenetics.Genotype} to the given output
1202         * stream.
1203         *
1204         * @see Genotypes#write(OutputStream, Collection, Writer)
1205         *
1206         * @param <A> the allele type
1207         * @param <G> the gene type
1208         * @param <C> the chromosome type
1209         * @param out the target output stream
1210         * @param data the genotypes to write
1211         * @param chromosomeWriter the chromosome writer used to write the
1212         *        genotypes
1213         * @throws XMLStreamException if an error occurs while writing the
1214         *         chromosome
1215         * @throws NullPointerException if the one of the arguments is
1216         *         {@code null}
1217         */
1218        public static <
1219                A,
1220                G extends Gene<A, G>,
1221                C extends Chromosome<G>
1222        >
1223        void write(
1224                final OutputStream out,
1225                final Collection<io.jenetics.Genotype<G>> data,
1226                final Writer<? super C> chromosomeWriter
1227        )
1228                throws XMLStreamException
1229        {
1230                Genotypes.write(out, data, chromosomeWriter);
1231        }
1232
1233}