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