Writers.java
0001 /*
0002  * Java Genetic Algorithm Library (jenetics-6.2.0).
0003  * Copyright (c) 2007-2021 Franz Wilhelmstötter
0004  *
0005  * Licensed under the Apache License, Version 2.0 (the "License");
0006  * you may not use this file except in compliance with the License.
0007  * You may obtain a copy of the License at
0008  *
0009  *      http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  *
0017  * Author:
0018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
0019  */
0020 package io.jenetics.xml;
0021 
0022 import static java.util.Objects.requireNonNull;
0023 import static io.jenetics.xml.stream.Writer.attr;
0024 import static io.jenetics.xml.stream.Writer.elem;
0025 import static io.jenetics.xml.stream.Writer.elems;
0026 import static io.jenetics.xml.stream.Writer.text;
0027 
0028 import java.io.OutputStream;
0029 import java.util.Collection;
0030 import java.util.stream.Collectors;
0031 
0032 import javax.xml.stream.XMLStreamException;
0033 
0034 import io.jenetics.BoundedGene;
0035 import io.jenetics.Chromosome;
0036 import io.jenetics.DoubleGene;
0037 import io.jenetics.Gene;
0038 import io.jenetics.IntegerGene;
0039 import io.jenetics.LongGene;
0040 import io.jenetics.util.ISeq;
0041 import io.jenetics.xml.stream.Writer;
0042 import io.jenetics.xml.stream.XML;
0043 
0044 /**
0045  * This class contains static fields and methods, for creating chromosome- and
0046  * genotype writers for different gene types.
0047  *
0048  <pre>{@code
0049  * final Writer<Genotype<BitGene> bgw =
0050  *     Writers.Genotype.writer(Writers.BitChromosome.writer()));
0051  *
0052  * final Writer<Genotype<IntegerGene>> igw =
0053  *     Writers.Genotype.writer(Writers.IntegerChromosome.writer()));
0054  *
0055  * final Writer<Genotype<DoubleGene>> dgw =
0056  *     Writers.Genotype.writer(Writers.DoubleChromosome.writer()));
0057  * }</pre>
0058  *
0059  * This class also contains some helper methods, which makes it easier to write
0060  * Jenetics domain objects to a given output stream.
0061  <pre>{@code
0062  * final List<Genotype<BitGene>> genotypes = ...;
0063  * try (OutputStream out = Files.newOutputStream(Paths.get("path"))) {
0064  *     Writers.write(out, genotypes, Writers.BitChromosome.writer());
0065  * }
0066  * }</pre>
0067  *
0068  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
0069  @version 3.9
0070  @since 3.9
0071  */
0072 public final class Writers {
0073     private Writers() {}
0074 
0075     /**
0076      * This class contains static writer methods for
0077      {@link io.jenetics.BitChromosome} objects.
0078      <p>
0079      <b>Writer code</b>
0080      <pre>{@code
0081      * final BitChromosome value = BitChromosome.of(20, 0.5);
0082      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
0083      *     Writers.BitChromosome.writer().write(value, xml);
0084      * }
0085      * }</pre>
0086      *
0087      <b>XML output</b>
0088      <pre> {@code
0089      <bit-chromosome length="20" ones-probability="0.5">11100011101011001010</bit-chromosome>
0090      * }</pre>
0091      *
0092      @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
0093      @version 3.9
0094      @since 3.9
0095      */
0096     public static final class BitChromosome {
0097         private BitChromosome() {}
0098 
0099         static final String ROOT_NAME = "bit-chromosome";
0100         static final String LENGTH_NAME = "length";
0101         static final String ONES_PROBABILITY_NAME = "ones-probability";
0102 
0103         /**
0104          * Return a {@link Writer} for {@link io.jenetics.BitChromosome}
0105          * objects.
0106          *
0107          @return a chromosome writer
0108          */
0109         public static Writer<io.jenetics.BitChromosome> writer() {
0110             return elem(ROOT_NAME,
0111                 attr(LENGTH_NAME).map(io.jenetics.BitChromosome::length),
0112                 attr(ONES_PROBABILITY_NAME).map(ch -> ch.oneProbability()),
0113                 text().map(io.jenetics.BitChromosome::toCanonicalString)
0114             );
0115         }
0116 
0117         /**
0118          * Write the given {@link io.jenetics.BitChromosome} to the given
0119          * output stream.
0120          *
0121          @param out the target output stream
0122          @param data the bit-chromosome to write
0123          @throws XMLStreamException if an error occurs while writing the
0124          *         chromosome
0125          @throws NullPointerException if one of the given arguments is
0126          *         {@code null}
0127          */
0128         public static void write(
0129             final OutputStream out,
0130             final io.jenetics.BitChromosome data
0131         )
0132             throws XMLStreamException
0133         {
0134             requireNonNull(data);
0135             requireNonNull(out);
0136 
0137             try (var xml = XML.writer(out)) {
0138                 writer().write(xml, data);
0139             }
0140         }
0141     }
0142 
0143 
0144     /**
0145      * This class contains static writer methods for
0146      {@link io.jenetics.CharacterChromosome} objects.
0147      <p>
0148      <b>Writer code</b>
0149      <pre>{@code
0150      * final CharacterChromosome value = CharacterChromosome.of("ASDF", CharSeq.of("A-Z"));
0151      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
0152      *     Writers.CharacterChromosome.writer().write(value, xml);
0153      * }
0154      * }</pre>
0155      *
0156      <b>XML output</b>
0157      <pre> {@code
0158      <character-chromosome length="4">
0159      *     <valid-alleles>ABCDEFGHIJKLMNOPQRSTUVWXYZ<valid-alleles>
0160      *     <alleles>ASDF</alleles>
0161      </character-chromosome>
0162      * }</pre>
0163      */
0164     public static final class CharacterChromosome {
0165         private CharacterChromosome() {}
0166 
0167         static final String ROOT_NAME = "character-chromosome";
0168         static final String LENGTH_NAME = "length";
0169         static final String VALID_ALLELES_NAME = "valid-alleles";
0170         static final String ALLELES_NAME = "alleles";
0171 
0172         /**
0173          * Return a {@link Writer} for {@link io.jenetics.CharacterChromosome}
0174          * objects.
0175          *
0176          @return a chromosome writer
0177          */
0178         public static Writer<io.jenetics.CharacterChromosome> writer() {
0179             return elem(ROOT_NAME,
0180                 attr(LENGTH_NAME).map(io.jenetics.CharacterChromosome::length),
0181                 elem(VALID_ALLELES_NAME,
0182                     text().map(ch -> ch.gene().validChars())),
0183                 elem(ALLELES_NAME,
0184                     text().map(io.jenetics.CharacterChromosome::toString))
0185             );
0186         }
0187 
0188         /**
0189          * Write the given {@link io.jenetics.CharacterChromosome} to the given
0190          * output stream.
0191          *
0192          @param out the target output stream
0193          @param data the chromosome to write
0194          @param indent the XML level indentation
0195          @throws XMLStreamException if an error occurs while writing the
0196          *         chromosome
0197          @throws NullPointerException if the {@code chromosome} or output
0198          *         stream is {@code null}
0199          */
0200         public static void write(
0201             final OutputStream out,
0202             final io.jenetics.CharacterChromosome data,
0203             final String indent
0204         )
0205             throws XMLStreamException
0206         {
0207             requireNonNull(data);
0208             requireNonNull(out);
0209 
0210             try (var xml = XML.writer(out, indent)) {
0211                 writer().write(xml, data);
0212             }
0213         }
0214 
0215         /**
0216          * Write the given {@link io.jenetics.CharacterChromosome} to the given
0217          * output stream.
0218          *
0219          @param out the target output stream
0220          @param data the chromosome to write
0221          @throws XMLStreamException if an error occurs while writing the
0222          *         chromosome
0223          @throws NullPointerException if the {@code chromosome} or output
0224          *         stream is {@code null}
0225          */
0226         public static void write(
0227             final OutputStream out,
0228             final io.jenetics.CharacterChromosome data
0229         )
0230             throws XMLStreamException
0231         {
0232             write(out, data, null);
0233         }
0234 
0235     }
0236 
0237     /**
0238      * This class contains static writer methods for
0239      {@link io.jenetics.BoundedChromosome} objects.
0240      *
0241      <p>
0242      <b>XML template</b>
0243      <pre> {@code
0244      <root-name length="3">
0245      *     <min>aaa</min>
0246      *     <max>zzz</max>
0247      *     <alleles>
0248      *         <allele>iii</allele>
0249      *         <allele>fff</allele>
0250      *         <allele>ggg</allele>
0251      *     </alleles>
0252      </root-name>
0253      * }</pre>
0254      */
0255     public static final class BoundedChromosome {
0256         private BoundedChromosome() {}
0257 
0258         static final String LENGTH_NAME = "length";
0259         static final String MIN_NAME = "min";
0260         static final String MAX_NAME = "max";
0261         static final String ALLELE_NAME = "allele";
0262         static final String ALLELES_NAME = "alleles";
0263 
0264         /**
0265          * Create a bounded chromosome writer with the given configuration.
0266          *
0267          @param rootName the name of the root element. E.g. {@code int-chromosome}
0268          @param alleleWriter the XML writer used for the alleles
0269          @param <A> the allele type
0270          @param <G> the bounded gene type
0271          @param <C> the bounded chromosome type
0272          @return a bounded chromosome XML writer
0273          @throws NullPointerException if one of the arguments is {@code null}
0274          */
0275         public static <
0276             extends Comparable<? super A>,
0277             extends BoundedGene<A, G>,
0278             extends io.jenetics.BoundedChromosome<A, G>
0279         >
0280         Writer<C> writer(
0281             final String rootName,
0282             final Writer<? super A> alleleWriter
0283         ) {
0284             requireNonNull(rootName);
0285             requireNonNull(alleleWriter);
0286 
0287             return elem(rootName,
0288                 attr(LENGTH_NAME).map(ch -> ch.length()),
0289                 elem(MIN_NAME, alleleWriter.map(ch -> ch.min())),
0290                 elem(MAX_NAME, alleleWriter.map(ch -> ch.max())),
0291                 elem(ALLELES_NAME,
0292                     elems(ALLELE_NAME, alleleWriter)
0293                         .map(ch -> ISeq.of(ch).map(G::allele))
0294                 )
0295             );
0296         }
0297     }
0298 
0299     /**
0300      * This class contains static writer methods for
0301      {@link io.jenetics.IntegerChromosome} objects.
0302      <p>
0303      <b>Writer code</b>
0304      <pre>{@code
0305      * final IntegerChromosome value = IntegerChromosome
0306      *     .of(Integer.MIN_VALUE, Integer.MAX_VALUE, 3);
0307      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
0308      *     Writers.IntegerChromosome.writer().write(value, xml);
0309      * }
0310      * }</pre>
0311      *
0312      <b>XML output</b>
0313      <pre> {@code
0314      <int-chromosome length="3">
0315      *     <min>-2147483648</min>
0316      *     <max>2147483647</max>
0317      *     <alleles>
0318      *         <allele>-1878762439</allele>
0319      *         <allele>-957346595</allele>
0320      *         <allele>-88668137</allele>
0321      *     </alleles>
0322      </int-chromosome>
0323      * }</pre>
0324      */
0325     public static final class IntegerChromosome {
0326         private IntegerChromosome() {}
0327 
0328         static final String ROOT_NAME = "int-chromosome";
0329 
0330         /**
0331          * Return the default integer allele writer for the
0332          * {@code IntegerChromosome}.
0333          *
0334          @return the default integer allele writer
0335          */
0336         public static Writer<Integer> alleleWriter() {
0337             return text();
0338         }
0339 
0340         /**
0341          * Return a {@link Writer} for {@link io.jenetics.IntegerChromosome}
0342          * objects.
0343          *
0344          @param alleleWriter the allele writer used for writing the integer
0345          *        allele. Might be useful for using different integer
0346          *        <i>encodings</i>.
0347          @return a chromosome writer
0348          @throws NullPointerException if the given {@code alleleWriter} is
0349          *         {@code null}
0350          */
0351         public static Writer<io.jenetics.IntegerChromosome>
0352         writer(final Writer<? super Integer> alleleWriter) {
0353             requireNonNull(alleleWriter);
0354 
0355             return BoundedChromosome.<
0356                 Integer,
0357                 IntegerGene,
0358                 io.jenetics.IntegerChromosome
0359             >writer(ROOT_NAME, alleleWriter);
0360         }
0361 
0362         /**
0363          * Return a {@link Writer} for {@link io.jenetics.IntegerChromosome}
0364          * objects.
0365          *
0366          @return a chromosome writer
0367          */
0368         public static Writer<io.jenetics.IntegerChromosome> writer() {
0369             return writer(alleleWriter());
0370         }
0371 
0372         /**
0373          * Write the given {@link io.jenetics.IntegerChromosome} to the given
0374          * output stream.
0375          *
0376          @param out the target output stream
0377          @param data the chromosome to write
0378          @param indent the XML level indentation
0379          @throws XMLStreamException if an error occurs while writing the
0380          *         chromosome
0381          @throws NullPointerException if the {@code chromosome} or output
0382          *         stream is {@code null}
0383          */
0384         public static void write(
0385             final OutputStream out,
0386             final io.jenetics.IntegerChromosome data,
0387             final String indent
0388         )
0389             throws XMLStreamException
0390         {
0391             requireNonNull(data);
0392             requireNonNull(out);
0393 
0394             try (var xml = XML.writer(out, indent)) {
0395                 writer().write(xml, data);
0396             }
0397         }
0398 
0399         /**
0400          * Write the given {@link io.jenetics.IntegerChromosome} to the given
0401          * output stream.
0402          *
0403          @param out the target output stream
0404          @param data the chromosome to write
0405          @throws XMLStreamException if an error occurs while writing the
0406          *         chromosome
0407          @throws NullPointerException if the {@code chromosome} or output
0408          *         stream is {@code null}
0409          */
0410         public static void write(
0411             final OutputStream out,
0412             final io.jenetics.IntegerChromosome data
0413         )
0414             throws XMLStreamException
0415         {
0416             write(out, data, null);
0417         }
0418     }
0419 
0420     /**
0421      * This class contains static writer methods for
0422      {@link io.jenetics.LongChromosome} objects.
0423      <p>
0424      <b>Writer code</b>
0425      <pre>{@code
0426      * final LongChromosome value = LongChromosome
0427      *     .of(Long.MIN_VALUE, Long.MAX_VALUE, 3);
0428      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
0429      *     Writers.LongChromosome.writer().write(value, xml);
0430      * }
0431      * }</pre>
0432      *
0433      <b>XML output</b>
0434      <pre> {@code
0435      <long-chromosome length="3">
0436      *     <min>-9223372036854775808</min>
0437      *     <max>9223372036854775807</max>
0438      *     <alleles>
0439      *         <allele>-1345217698116542402</allele>
0440      *         <allele>-7144755673073475303</allele>
0441      *         <allele>6053786736809578435</allele>
0442      *     </alleles>
0443      </long-chromosome>
0444      * }</pre>
0445      */
0446     public static final class LongChromosome {
0447         private LongChromosome() {}
0448 
0449         static final String ROOT_NAME = "long-chromosome";
0450 
0451         /**
0452          * Return the default long allele writer for the
0453          * {@code IntegerChromosome}.
0454          *
0455          @return the default long allele writer
0456          */
0457         public static Writer<Long> alleleWriter() {
0458             return Writer.text();
0459         }
0460 
0461         /**
0462          * Return a {@link Writer} for {@link io.jenetics.LongChromosome}
0463          * objects.
0464          *
0465          @param alleleWriter the allele writer used for writing the long
0466          *        allele. Might be useful for using different long
0467          *        <i>encodings</i>.
0468          @return a chromosome writer
0469          @throws NullPointerException if the given {@code alleleWriter} is
0470          *         {@code null}
0471          */
0472         public static Writer<io.jenetics.LongChromosome>
0473         writer(final Writer<? super Long> alleleWriter) {
0474             return BoundedChromosome.<
0475                 Long,
0476                 LongGene,
0477                 io.jenetics.LongChromosome
0478             >writer(ROOT_NAME, alleleWriter);
0479         }
0480 
0481         /**
0482          * Return a {@link Writer} for {@link io.jenetics.LongChromosome}
0483          * objects.
0484          *
0485          @return a chromosome writer
0486          */
0487         public static Writer<io.jenetics.LongChromosome> writer() {
0488             return writer(alleleWriter());
0489         }
0490 
0491         /**
0492          * Write the given {@link io.jenetics.LongChromosome} to the given
0493          * output stream.
0494          *
0495          @param out the target output stream
0496          @param data the chromosome to write
0497          @param indent the XML level indentation
0498          @throws XMLStreamException if an error occurs while writing the
0499          *         chromosome
0500          @throws NullPointerException if the {@code chromosome} or output
0501          *         stream is {@code null}
0502          */
0503         public static void write(
0504             final OutputStream out,
0505             final io.jenetics.LongChromosome data,
0506             final String indent
0507         )
0508             throws XMLStreamException
0509         {
0510             requireNonNull(data);
0511             requireNonNull(out);
0512 
0513             try (var xml = XML.writer(out, indent)) {
0514                 writer().write(xml, data);
0515             }
0516         }
0517 
0518         /**
0519          * Write the given {@link io.jenetics.LongChromosome} to the given
0520          * output stream.
0521          *
0522          @param out the target output stream
0523          @param data the chromosome to write
0524          @throws XMLStreamException if an error occurs while writing the
0525          *         chromosome
0526          @throws NullPointerException if the {@code chromosome} or output
0527          *         stream is {@code null}
0528          */
0529         public static void write(
0530             final OutputStream out,
0531             final io.jenetics.LongChromosome data
0532         )
0533             throws XMLStreamException
0534         {
0535             write(out, data, null);
0536         }
0537     }
0538 
0539     /**
0540      * This class contains static writer methods for
0541      {@link io.jenetics.DoubleChromosome} objects.
0542      <p>
0543      <b>Writer code</b>
0544      <pre>{@code
0545      * final DoubleChromosome value = DoubleChromosome.of(0.0, 1.0, 3);
0546      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
0547      *     Writers.DoubleChromosome.writer().write(value, xml);
0548      * }
0549      * }</pre>
0550      *
0551      <b>XML output</b>
0552      <pre> {@code
0553      <double-chromosome length="3">
0554      *     <min>0.0</min>
0555      *     <max>1.0</max>
0556      *     <alleles>
0557      *         <allele>0.27251556008507416</allele>
0558      *         <allele>0.003140816229067145</allele>
0559      *         <allele>0.43947528327497376</allele>
0560      *     </alleles>
0561      </double-chromosome>
0562      * }</pre>
0563      */
0564     public static final class DoubleChromosome
0565         //extends WriterProvider<io.jenetics.DoubleChromosome>
0566     {
0567         private DoubleChromosome() {}
0568 
0569         static final String ROOT_NAME = "double-chromosome";
0570 
0571         /**
0572          * Return the default double allele writer for the
0573          * {@code DoubleChromosome}.
0574          *
0575          @return the default double allele writer
0576          */
0577         public static Writer<Double> alleleWriter() {
0578             return text().map(Object::toString);
0579         }
0580 
0581         /**
0582          * Return a {@link Writer} for {@link io.jenetics.DoubleChromosome}
0583          * objects.
0584          *
0585          @param alleleWriter the allele writer used for writing the long
0586          *        allele. Might be useful for using different long
0587          *        <i>encodings</i>.
0588          @return a chromosome writer
0589          @throws NullPointerException if the given {@code alleleWriter} is
0590          *         {@code null}
0591          */
0592         public static Writer<io.jenetics.DoubleChromosome>
0593         writer(final Writer<? super Double> alleleWriter) {
0594             return BoundedChromosome.<
0595                 Double,
0596                 DoubleGene,
0597                 io.jenetics.DoubleChromosome
0598             >writer(ROOT_NAME, alleleWriter);
0599         }
0600 
0601         /**
0602          * Return a {@link Writer} for {@link io.jenetics.DoubleChromosome}
0603          * objects.
0604          *
0605          @return a chromosome writer
0606          */
0607         public static Writer<io.jenetics.DoubleChromosome> writer() {
0608             return writer(alleleWriter());
0609         }
0610 
0611         public Class<io.jenetics.DoubleChromosome> type() {
0612             return io.jenetics.DoubleChromosome.class;
0613         }
0614 
0615         /**
0616          * Write the given {@link io.jenetics.DoubleChromosome} to the given
0617          * output stream.
0618          *
0619          @param out the target output stream
0620          @param data the chromosome to write
0621          @param indent the XML level indentation
0622          @throws XMLStreamException if an error occurs while writing the
0623          *         chromosome
0624          @throws NullPointerException if the {@code chromosome} or output
0625          *         stream is {@code null}
0626          */
0627         public static void write(
0628             final OutputStream out,
0629             final io.jenetics.DoubleChromosome data,
0630             final String indent
0631         )
0632             throws XMLStreamException
0633         {
0634             requireNonNull(data);
0635             requireNonNull(out);
0636 
0637             try (var xml = XML.writer(out, indent)) {
0638                 writer().write(xml, data);
0639             }
0640         }
0641 
0642         /**
0643          * Write the given {@link io.jenetics.DoubleChromosome} to the given
0644          * output stream.
0645          *
0646          @param out the target output stream
0647          @param data the chromosome to write
0648          @throws XMLStreamException if an error occurs while writing the
0649          *         chromosome
0650          @throws NullPointerException if the {@code chromosome} or output
0651          *         stream is {@code null}
0652          */
0653         public static void write(
0654             final OutputStream out,
0655             final io.jenetics.DoubleChromosome data
0656         )
0657             throws XMLStreamException
0658         {
0659             write(out, data, null);
0660         }
0661     }
0662 
0663     /**
0664      * This class contains static writer methods for
0665      {@link io.jenetics.PermutationChromosome} objects.
0666      <p>
0667      <b>Writer code</b>
0668      <pre>{@code
0669      * final PermutationChromosome<Integer> value =
0670      *     PermutationChromosome.ofInteger(5)
0671      *
0672      * final Writer<PermutationChromosome<Integer> writer =
0673      *     Writers.PermutationChromosome.writer();
0674      *
0675      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
0676      *     Writers.PermutationChromosome.writer().write(value, xml);
0677      * }
0678      * }</pre>
0679      *
0680      <b>XML output</b>
0681      <pre> {@code
0682      <permutation-chromosome length="5">
0683      *     <valid-alleles type="java.lang.Integer">
0684      *         <allele>0</allele>
0685      *         <allele>1</allele>
0686      *         <allele>2</allele>
0687      *         <allele>3</allele>
0688      *         <allele>4</allele>
0689      *     </valid-alleles>
0690      *     <order>2 1 3 5 4</order>
0691      </permutation-chromosome>
0692      * }</pre>
0693      */
0694     public static final class PermutationChromosome {
0695         private PermutationChromosome() {}
0696 
0697         static final String ROOT_NAME = "permutation-chromosome";
0698         static final String LENGTH_NAME = "length";
0699         static final String VALID_ALLELES_NAME = "valid-alleles";
0700         static final String ALLELE_NAME = "allele";
0701         static final String ORDER_NAME = "order";
0702 
0703         /**
0704          * Create a writer for permutation-chromosomes. How to write the valid
0705          * alleles is defined by the given {@link Writer}.
0706          *
0707          @param alleleWriter the allele writer
0708          @param <A> the allele type
0709          @return a new permutation chromosome writer
0710          @throws NullPointerException if the given allele {@code writer} is
0711          *         {@code null}
0712          */
0713         public static <A> Writer<io.jenetics.PermutationChromosome<A>>
0714         writer(final Writer<? super A> alleleWriter) {
0715             return Writer.<io.jenetics.PermutationChromosome<A>>elem(
0716                 ROOT_NAME,
0717                 attr(LENGTH_NAME).map(io.jenetics.PermutationChromosome::length),
0718                 elem(VALID_ALLELES_NAME,
0719                     attr("type").map(PermutationChromosome::toAlleleTypeName),
0720                     Writer.<A>elems(ALLELE_NAME, alleleWriter)
0721                         .map(io.jenetics.PermutationChromosome::validAlleles)
0722                 ),
0723                 elem(ORDER_NAME, text())
0724                     .map(ch -> ch.stream()
0725                         .map(g -> Integer.toString(g.alleleIndex()))
0726                         .collect(Collectors.joining(" ")))
0727             );
0728         }
0729 
0730         private static String toAlleleTypeName(
0731             final io.jenetics.PermutationChromosome<?> ch
0732         ) {
0733             return ch.gene().allele().getClass().getCanonicalName();
0734         }
0735 
0736         /**
0737          * Create a writer for permutation-chromosomes. The valid alleles are
0738          * serialized by calling the {@link Object#toString()} method. Calling
0739          * this method is equivalent with:
0740          <pre>{@code
0741          * final Writer<PermutationChromosome<Double> writer =
0742          *     PermutationChromosome.write(text().map(Objects::toString));
0743          * }</pre>
0744          *
0745          * Example output:
0746          <pre> {@code
0747          <permutation-chromosome length="15">
0748          *     <valid-alleles type="java.lang.Double">
0749          *         <allele>0.27251556008507416</allele>
0750          *         <allele>0.003140816229067145</allele>
0751          *         <allele>0.43947528327497376</allele>
0752          *         <allele>0.10654807463069327</allele>
0753          *         <allele>0.19696530915810317</allele>
0754          *         <allele>0.7450003838065538</allele>
0755          *         <allele>0.5594416969271359</allele>
0756          *         <allele>0.02823782430152355</allele>
0757          *         <allele>0.5741102315010789</allele>
0758          *         <allele>0.4533651041367144</allele>
0759          *         <allele>0.811148141800367</allele>
0760          *         <allele>0.5710456351848858</allele>
0761          *         <allele>0.30166768355230955</allele>
0762          *         <allele>0.5455492865240272</allele>
0763          *         <allele>0.21068427527733102</allele>
0764          *     </valid-alleles>
0765          *     <order>13 12 4 6 8 14 7 2 11 5 3 0 9 10 1</order>
0766          </permutation-chromosome>
0767          * }</pre>
0768          *
0769          @param <A> the allele type
0770          @return a new permutation chromosome writer
0771          */
0772         public static <A> Writer<io.jenetics.PermutationChromosome<A>> writer() {
0773             return writer(text());
0774         }
0775 
0776         /**
0777          * Write the given {@link io.jenetics.PermutationChromosome} to the
0778          * given output stream.
0779          *
0780          @param <A> the allele type
0781          @param out the target output stream
0782          @param data the chromosome to write
0783          @param indent the XML level indentation
0784          @throws XMLStreamException if an error occurs while writing the
0785          *         chromosome
0786          @throws NullPointerException if the {@code chromosome} or output
0787          *         stream is {@code null}
0788          */
0789         public static <A> void write(
0790             final OutputStream out,
0791             final io.jenetics.PermutationChromosome<A> data,
0792             final String indent
0793         )
0794             throws XMLStreamException
0795         {
0796             requireNonNull(data);
0797             requireNonNull(out);
0798 
0799             try (var writer = XML.writer(out, indent)) {
0800                 PermutationChromosome.<A>writer().write(writer, data);
0801             }
0802         }
0803 
0804         /**
0805          * Write the given {@link io.jenetics.PermutationChromosome} to the
0806          * given output stream.
0807          *
0808          @param <A> the allele type
0809          @param out the target output stream
0810          @param data the chromosome to write
0811          @param indent the XML level indentation
0812          @param alleleWriter the allele writer of the permutation chromosome
0813          @throws XMLStreamException if an error occurs while writing the
0814          *         chromosome
0815          @throws NullPointerException if the {@code chromosome} or output
0816          *         stream is {@code null}
0817          */
0818         public static <A> void write(
0819             final OutputStream out,
0820             final io.jenetics.PermutationChromosome<A> data,
0821             final String indent,
0822             final Writer<? super A> alleleWriter
0823         )
0824             throws XMLStreamException
0825         {
0826             requireNonNull(data);
0827             requireNonNull(alleleWriter);
0828             requireNonNull(out);
0829 
0830             try (var xml = XML.writer(out, indent)) {
0831                 PermutationChromosome.<A>writer(alleleWriter)
0832                     .write(xml, data);
0833             }
0834         }
0835 
0836         /**
0837          * Write the given {@link io.jenetics.PermutationChromosome} to the
0838          * given output stream.
0839          *
0840          @param <A> the allele type
0841          @param out the target output stream
0842          @param data the chromosome to write
0843          @throws XMLStreamException if an error occurs while writing the
0844          *         chromosome
0845          @throws NullPointerException if the {@code chromosome} or output
0846          *         stream is {@code null}
0847          */
0848         public static <A> void write(
0849             final OutputStream out,
0850             final io.jenetics.PermutationChromosome<A> data
0851         )
0852             throws XMLStreamException
0853         {
0854             write(out, data, null, text());
0855         }
0856 
0857         /**
0858          * Write the given {@link io.jenetics.PermutationChromosome} to the
0859          * given output stream.
0860          *
0861          @param <A> the allele type
0862          @param out the target output stream
0863          @param data the chromosome to write
0864          @param alleleWriter the allele writer used to write the chromosome
0865          *         alleles
0866          @throws XMLStreamException if an error occurs while writing the
0867          *         chromosome
0868          @throws NullPointerException if the {@code chromosome} or output
0869          *         stream is {@code null}
0870          */
0871         public static <A> void write(
0872             final OutputStream out,
0873             final io.jenetics.PermutationChromosome<A> data,
0874             final Writer<? super A> alleleWriter
0875         )
0876             throws XMLStreamException
0877         {
0878             write(out, data, null, alleleWriter);
0879         }
0880 
0881     }
0882 
0883     /**
0884      * This class contains static writer methods for
0885      {@link io.jenetics.Genotype} objects.
0886      <p>
0887      <b>Writer code</b>
0888      <pre>{@code
0889      * final Genotype<DoubleGene> gt = Genotype.of(
0890      *     DoubleChromosome.of(0.0, 1.0, 3),
0891      *     DoubleChromosome.of(0.0, 1.0, 2)
0892      * );
0893      * final Writer<Genotype<DoubleGene>> writer =
0894      *     Writers.Genotype.writer(Writers.DoubleChromosome.writer());
0895      *
0896      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
0897      *     writer.write(value, xml);
0898      * }
0899      * }</pre>
0900      *
0901      <b>XML output</b>
0902      <pre> {@code
0903      <genotype length="2" ngenes="5">
0904      *     <double-chromosome length="3">
0905      *         <min>0.0</min>
0906      *         <max>1.0</max>
0907      *         <alleles>
0908      *             <allele>0.27251556008507416</allele>
0909      *             <allele>0.003140816229067145</allele>
0910      *             <allele>0.43947528327497376</allele>
0911      *         </alleles>
0912      *     </double-chromosome>
0913      *     <double-chromosome length="2">
0914      *         <min>0.0</min>
0915      *         <max>1.0</max>
0916      *         <alleles>
0917      *             <allele>0.4026521545744768</allele>
0918      *             <allele>0.36137605952663554</allele>
0919      *         <alleles>
0920      *     </double-chromosome>
0921      </genotype>
0922      * }</pre>
0923      */
0924     public static final class Genotype {
0925         private Genotype() {}
0926 
0927         static final String ROOT_NAME = "genotype";
0928         static final String LENGTH_NAME = "length";
0929         static final String NGENES_NAME = "ngenes";
0930 
0931         /**
0932          * Create a writer for genotypes of arbitrary chromosomes. How to write the
0933          * genotypes chromosomes is defined by the given {@link Writer}.
0934          *
0935          @param writer the chromosome writer
0936          @param <A> the allele type
0937          @param <G> the gene type
0938          @param <C> the chromosome type
0939          @return a new genotype writer
0940          @throws NullPointerException if the given chromosome {@code writer} is
0941          *         {@code null}
0942          */
0943         public static <
0944             A,
0945             extends Gene<A, G>,
0946             extends Chromosome<G>
0947         >
0948         Writer<io.jenetics.Genotype<G>> writer(final Writer<? super C> writer) {
0949             return elem(
0950                 ROOT_NAME,
0951                 attr(LENGTH_NAME).map(io.jenetics.Genotype::length),
0952                 attr(NGENES_NAME).map(io.jenetics.Genotype::geneCount),
0953                 elems(writer).map(gt -> cast(ISeq.of(gt)))
0954             );
0955         }
0956 
0957         @SuppressWarnings("unchecked")
0958         private static <A, B> B cast(final A value) {
0959             return (B)value;
0960         }
0961 
0962         /**
0963          * Write the given {@link io.jenetics.Genotype} to the given output
0964          * stream.
0965          *
0966          @param <A> the allele type
0967          @param <G> the gene type
0968          @param <C> the chromosome type
0969          @param out the target output stream
0970          @param data the genotype to write
0971          @param indent the XML level indentation
0972          @param chromosomeWriter the chromosome writer used to write the
0973          *        genotypes
0974          @throws XMLStreamException if an error occurs while writing the
0975          *         chromosome
0976          @throws NullPointerException if the one of the arguments is
0977          *         {@code null}
0978          */
0979         public static <
0980             A,
0981             extends Gene<A, G>,
0982             extends Chromosome<G>
0983         >
0984         void write(
0985             final OutputStream out,
0986             final io.jenetics.Genotype<G> data,
0987             final String indent,
0988             final Writer<? super C> chromosomeWriter
0989         )
0990             throws XMLStreamException
0991         {
0992             requireNonNull(data);
0993             requireNonNull(chromosomeWriter);
0994             requireNonNull(out);
0995 
0996             try (var writer = XML.writer(out, indent)) {
0997                 Genotype.<A, G, C>writer(chromosomeWriter).write(writer, data);
0998             }
0999         }
1000 
1001         /**
1002          * Write the given {@link io.jenetics.Genotype} to the given output
1003          * stream.
1004          *
1005          @param <A> the allele type
1006          @param <G> the gene type
1007          @param <C> the chromosome type
1008          @param out the target output stream
1009          @param data the genotype to write
1010          @param chromosomeWriter the chromosome writer used to write the
1011          *        genotypes
1012          @throws XMLStreamException if an error occurs while writing the
1013          *         chromosome
1014          @throws NullPointerException if the one of the arguments is
1015          *         {@code null}
1016          */
1017         public static <
1018             A,
1019             extends Gene<A, G>,
1020             extends Chromosome<G>
1021         >
1022         void write(
1023             final OutputStream out,
1024             final io.jenetics.Genotype<G> data,
1025             final Writer<? super C> chromosomeWriter
1026         )
1027             throws XMLStreamException
1028         {
1029             requireNonNull(data);
1030             requireNonNull(chromosomeWriter);
1031             requireNonNull(out);
1032 
1033             try (var xml = XML.writer(out)) {
1034                 Genotype.<A, G, C>writer(chromosomeWriter).write(xml, data);
1035             }
1036         }
1037 
1038     }
1039 
1040     /**
1041      * This class contains static writer methods for
1042      {@link io.jenetics.Genotype} objects.
1043      <p>
1044      <b>Writer code</b>
1045      <pre>{@code
1046      * final Genotype<DoubleGene> gt = Genotype.of(
1047      *     DoubleChromosome.of(0.0, 1.0, 3),
1048      *     DoubleChromosome.of(0.0, 1.0, 2)
1049      * );
1050      *
1051      * final Writer<Collection<Genotype<DoubleGene>>> writer =
1052      *     Writers.Genotypes.writer(Writers.DoubleChromosome.writer());
1053      *
1054      * try (AutoCloseableXMLStreamWriter xml = XML.writer(System.out, "    ")) {
1055      *     writer.write(asList(value), xml);
1056      * }
1057      * }</pre>
1058      *
1059      <pre> {@code
1060      <genotypes length="1">
1061      *     <genotype length="2" ngenes="5">
1062      *         <double-chromosome length="3">
1063      *             <min>0.0</min>
1064      *             <max>1.0</max>
1065      *             <alleles>
1066      *                 <allele>0.27251556008507416</allele>
1067      *                 <allele>0.003140816229067145</allele>
1068      *                 <allele>0.43947528327497376</allele>
1069      *             </alleles>
1070      *         </double-chromosome>
1071      *         <double-chromosome length="2">
1072      *             <min>0.0</min>
1073      *             <max>1.0</max>
1074      *             <alleles>
1075      *                 <allele>0.4026521545744768</allele>
1076      *                 <allele>0.36137605952663554</allele>
1077      *             <alleles>
1078      *         </double-chromosome>
1079      *     </genotype>
1080      </genotypes>
1081      * }</pre>
1082      */
1083     public static final class Genotypes {
1084         private Genotypes() {}
1085 
1086         static final String ROOT_NAME = "genotypes";
1087         static final String LENGTH_NAME = "length";
1088 
1089         /**
1090          * Create a writer for genotypes of arbitrary chromosomes. How to write the
1091          * genotypes chromosomes is defined by the given {@link Writer}. The
1092          * following writer allows to write double-gene chromosomes:
1093          <pre>{@code
1094          * final Writer<Collection<Genotype<DoubleGene>>> writer =
1095          *     Writers.Genotypes.writer(Writers.DoubleChromosome.writer());
1096          * }</pre>
1097          *
1098          @param writer the chromosome writer
1099          @param <A> the allele type
1100          @param <G> the gene type
1101          @param <C> the chromosome type
1102          @return a new genotype writer
1103          @throws NullPointerException if the given chromosome {@code writer} is
1104          *         {@code null}
1105          */
1106         public static <
1107             A,
1108             extends Gene<A, G>,
1109             extends Chromosome<G>
1110         >
1111         Writer<Collection<io.jenetics.Genotype<G>>>
1112         writer(final Writer<? super C> writer) {
1113             return elem(
1114                 ROOT_NAME,
1115                 attr(LENGTH_NAME).map(Collection::size),
1116                 elems(Genotype.writer(writer))
1117             );
1118         }
1119 
1120         /**
1121          * Write the given {@link io.jenetics.Genotype} to the given output
1122          * stream.
1123          *
1124          @param <A> the allele type
1125          @param <G> the gene type
1126          @param <C> the chromosome type
1127          @param out the target output stream
1128          @param data the genotypes to write
1129          @param indent the XML level indentation
1130          @param chromosomeWriter the chromosome writer used to write the
1131          *        genotypes
1132          @throws XMLStreamException if an error occurs while writing the
1133          *         chromosome
1134          @throws NullPointerException if the one of the arguments is
1135          *         {@code null}
1136          */
1137         public static <
1138             A,
1139             extends Gene<A, G>,
1140             extends Chromosome<G>
1141         >
1142         void write(
1143             final OutputStream out,
1144             final Collection<io.jenetics.Genotype<G>> data,
1145             final String indent,
1146             final Writer<? super C> chromosomeWriter
1147         )
1148             throws XMLStreamException
1149         {
1150             requireNonNull(data);
1151             requireNonNull(chromosomeWriter);
1152             requireNonNull(out);
1153 
1154             try (var xml = XML.writer(out, indent)) {
1155                 Genotypes.<A, G, C>writer(chromosomeWriter).write(xml, data);
1156             }
1157         }
1158 
1159         /**
1160          * Write the given {@link io.jenetics.Genotype} to the given output
1161          * stream.
1162          *
1163          @param <A> the allele type
1164          @param <G> the gene type
1165          @param <C> the chromosome type
1166          @param out the target output stream
1167          @param data the genotypes to write
1168          @param chromosomeWriter the chromosome writer used to write the
1169          *        genotypes
1170          @throws XMLStreamException if an error occurs while writing the
1171          *         chromosome
1172          @throws NullPointerException if the one of the arguments is
1173          *         {@code null}
1174          */
1175         public static <
1176             A,
1177             extends Gene<A, G>,
1178             extends Chromosome<G>
1179         >
1180         void write(
1181             final OutputStream out,
1182             final Collection<io.jenetics.Genotype<G>> data,
1183             final Writer<? super C> chromosomeWriter
1184         )
1185             throws XMLStreamException
1186         {
1187             requireNonNull(data);
1188             requireNonNull(chromosomeWriter);
1189             requireNonNull(out);
1190 
1191             try (var xml = XML.writer(out)) {
1192                 Genotypes.<A, G, C>writer(chromosomeWriter).write(xml, data);
1193             }
1194         }
1195 
1196     }
1197 
1198 
1199     /**
1200      * Write the given {@link io.jenetics.Genotype} to the given output
1201      * stream.
1202      *
1203      @see Genotypes#write(OutputStream, Collection, Writer)
1204      *
1205      @param <A> the allele type
1206      @param <G> the gene type
1207      @param <C> the chromosome type
1208      @param out the target output stream
1209      @param data the genotypes to write
1210      @param chromosomeWriter the chromosome writer used to write the
1211      *        genotypes
1212      @throws XMLStreamException if an error occurs while writing the
1213      *         chromosome
1214      @throws NullPointerException if the one of the arguments is
1215      *         {@code null}
1216      */
1217     public static <
1218         A,
1219         extends Gene<A, G>,
1220         extends Chromosome<G>
1221     >
1222     void write(
1223         final OutputStream out,
1224         final Collection<io.jenetics.Genotype<G>> data,
1225         final Writer<? super C> chromosomeWriter
1226     )
1227         throws XMLStreamException
1228     {
1229         Genotypes.write(out, data, chromosomeWriter);
1230     }
1231 
1232 }