001/*
002 * Java Genetic Algorithm Library (jenetics-7.2.0).
003 * Copyright (c) 2007-2023 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.stream;
021
022import static java.lang.String.format;
023import static java.util.Objects.requireNonNull;
024import static javax.xml.stream.XMLStreamConstants.CDATA;
025import static javax.xml.stream.XMLStreamConstants.CHARACTERS;
026import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
027import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
028
029import java.util.ArrayList;
030import java.util.Collections;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034import java.util.Objects;
035import java.util.function.Function;
036import java.util.stream.IntStream;
037import java.util.stream.Stream;
038
039import javax.xml.stream.XMLStreamException;
040import javax.xml.stream.XMLStreamReader;
041
042import io.jenetics.xml.stream.Reader.Type;
043
044/**
045 * XML reader class, used for reading objects in XML format.
046 *
047 * <b>XML</b>
048 * <pre> {@code
049 * <int-chromosome length="3">
050 *     <min>-2147483648</min>
051 *     <max>2147483647</max>
052 *     <alleles>
053 *         <allele>-1878762439</allele>
054 *         <allele>-957346595</allele>
055 *         <allele>-88668137</allele>
056 *     </alleles>
057 * </int-chromosome>
058 * }</pre>
059 *
060 * <b>Reader definition</b>
061 * <pre>{@code
062 * final Reader<IntegerChromosome> reader =
063 *     elem(
064 *         (Object[] v) -> {
065 *             final int length = (int)v[0];
066 *             final int min = (int)v[1];
067 *             final int max = (int)v[2];
068 *             final List<Integer> alleles = (List<Integer>)v[3];
069 *             assert alleles.size() == length;
070 *
071 *             return IntegerChromosome.of(
072 *                 alleles.stream()
073 *                     .map(value -> IntegerGene.of(value, min, max)
074 *                     .toArray(IntegerGene[]::new)
075 *             );
076 *         },
077 *         "int-chromosome",
078 *         attr("length").map(Integer::parseInt),
079 *         elem("min", text().map(Integer::parseInt)),
080 *         elem("max", text().map(Integer::parseInt)),
081 *         elem("alleles",
082 *             elems(elem("allele", text().map(Integer::parseInt)))
083 *         )
084 *     );
085 * }</pre>
086 *
087 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
088 * @version 3.9
089 * @since 3.9
090 */
091public abstract class Reader<T> {
092
093        /**
094         * Represents the XML element type.
095         */
096        enum Type {
097
098                /**
099                 * Denotes a element reader.
100                 */
101                ELEM,
102
103                /**
104                 * Denotes a element attribute reader.
105                 */
106                ATTR,
107
108                /**
109                 * Denotes a reader of elements of the same type.
110                 */
111                LIST,
112
113                /**
114                 * Denotes a reader of the text of a element.
115                 */
116                TEXT
117
118        }
119
120        private final String _name;
121        private final Type _type;
122
123        /**
124         * Create a new XML reader with the given name and type.
125         *
126         * @param name the element name of the reader
127         * @param type the element type of the reader
128         * @throws NullPointerException if one of the give arguments is {@code null}
129         */
130        Reader(final String name, final Type type) {
131                _name = requireNonNull(name);
132                _type = requireNonNull(type);
133        }
134
135        /**
136         * Read the given type from the underlying XML stream {@code reader}.
137         *
138         * <pre>{@code
139         * try (AutoCloseableXMLStreamReader xml = XML.reader(in)) {
140         *     // Move XML stream to first element.
141         *     xml.next();
142         *     return reader.read(xml);
143         * }
144         * }</pre>
145         *
146         * @param xml the underlying XML stream {@code reader}
147         * @return the data read from the XML stream, maybe {@code null}
148         * @throws XMLStreamException if an error occurs while reading the value
149         * @throws NullPointerException if the given {@code xml} stream reader is
150         *         {@code null}
151         */
152        public abstract T read(final XMLStreamReader xml) throws XMLStreamException;
153
154        /**
155         * Create a new reader for the new mapped type {@code B}.
156         *
157         * @param mapper the mapper function
158         * @param <B> the target type of the new reader
159         * @return a new reader
160         * @throws NullPointerException if the given {@code mapper} function is
161         *         {@code null}
162         */
163        public <B> Reader<B> map(final Function<? super T, ? extends B> mapper) {
164                requireNonNull(mapper);
165
166                return new Reader<>(_name, _type) {
167                        @Override
168                        public B read(final XMLStreamReader xml)
169                                throws XMLStreamException {
170                                try {
171                                        return mapper.apply(Reader.this.read(xml));
172                                } catch (RuntimeException e) {
173                                        throw new XMLStreamException(e);
174                                }
175                        }
176                };
177        }
178
179        /**
180         * Return the name of the element processed by this reader.
181         *
182         * @return the element name the reader is processing
183         */
184        String name() {
185                return _name;
186        }
187
188        /**
189         * Return the element type of the reader.
190         *
191         * @return the element type of the reader
192         */
193        Type type() {
194                return _type;
195        }
196
197        @Override
198        public String toString() {
199                return format("Reader[%s, %s]", name(), type());
200        }
201
202
203        /* *************************************************************************
204         * Static reader factory methods.
205         * ************************************************************************/
206
207        /**
208         * Return a {@code Reader} for reading an attribute of an element.
209         * <p>
210         * <b>XML</b>
211         * <pre> {@code <element length="3"/>}</pre>
212         *
213         * <b>Reader definition</b>
214         * <pre>{@code
215         * final Reader<Integer> reader =
216         *     elem(
217         *         v -> (Integer)v[0],
218         *         "element",
219         *         attr("length").map(Integer::parseInt)
220         *     );
221         * }</pre>
222         *
223         * @param name the attribute name
224         * @return an attribute reader
225         * @throws NullPointerException if the given {@code name} is {@code null}
226         */
227        public static Reader<String> attr(final String name) {
228                return new AttrReader(name);
229        }
230
231        /**
232         * Return a {@code Reader} for reading the text of an element.
233         * <p>
234         * <b>XML</b>
235         * <pre> {@code <element>1234<element>}</pre>
236         *
237         * <b>Reader definition</b>
238         * <pre>{@code
239         * final Reader<Integer> reader =
240         *     elem(
241         *         v -> (Integer)v[0],
242         *         "element",
243         *         text().map(Integer::parseInt)
244         *     );
245         * }</pre>
246         *
247         * @return an element text reader
248         */
249        public static Reader<String> text() {
250                return new TextReader();
251        }
252
253        /**
254         * Return a {@code Reader} for reading an object of type {@code T} from the
255         * XML element with the given {@code name}.
256         *
257         * <p>
258         * <b>XML</b>
259         * <pre> {@code <property name="size">1234<property>}</pre>
260         *
261         * <b>Reader definition</b>
262         * <pre>{@code
263         * final Reader<Property> reader =
264         *     elem(
265         *         v -> {
266         *             final String name = (String)v[0];
267         *             final Integer value = (Integer)v[1];
268         *             return Property.of(name, value);
269         *         },
270         *         "property",
271         *         attr("name"),
272         *         text().map(Integer::parseInt)
273         *     );
274         * }</pre>
275         *
276         * @param generator the generator function, which build the result object
277         *        from the given parameter array
278         * @param name the name of the root (subtree) element
279         * @param children the child element reader, which creates the values
280         *        forwarded to the {@code generator} function
281         * @param <T> the reader result type
282         * @return a node reader
283         * @throws NullPointerException if one of the given arguments is {@code null}
284         * @throws IllegalArgumentException if the given child readers contains more
285         *         than one <em>text</em> reader
286         */
287        public static <T> Reader<T> elem(
288                final Function<Object[], T> generator,
289                final String name,
290                final Reader<?>... children
291        ) {
292                requireNonNull(name);
293                requireNonNull(generator);
294                Stream.of(requireNonNull(children)).forEach(Objects::requireNonNull);
295
296                return new ElemReader<>(name, generator, List.of(children), Type.ELEM);
297        }
298
299        /**
300         * Return a {@code Reader} which reads the value from the child elements of
301         * the given parent element {@code name}.
302         * <p>
303         * <b>XML</b>
304         * <pre> {@code <min><property name="size">1234<property></min>}</pre>
305         *
306         * <b>Reader definition</b>
307         * <pre>{@code
308         * final Reader<Property> reader =
309         *     elem("min",
310         *         elem(
311         *             v -> {
312         *                 final String name = (String)v[0];
313         *                 final Integer value = (Integer)v[1];
314         *                 return Property.of(name, value);
315         *             },
316         *             "property",
317         *             attr("name"),
318         *             text().map(Integer::parseInt)
319         *         )
320         *     );
321         * }</pre>
322         *
323         * @param name the parent element name
324         * @param reader the child elements reader
325         * @param <T> the result type
326         * @return a node reader
327         * @throws NullPointerException if one of the given arguments is {@code null}
328         */
329        public static <T> Reader<T> elem(
330                final String name,
331                final Reader<? extends T> reader
332        ) {
333                requireNonNull(name);
334                requireNonNull(reader);
335
336                return elem(
337                        v -> {
338                                @SuppressWarnings("unchecked")
339                                T value = v.length > 0 ? (T)v[0] : null;
340                                return value;
341                        },
342                        name,
343                        reader
344                );
345        }
346
347        /**
348         * Return a {@code Reader} which collects the elements, read by the given
349         * child {@code reader}, and returns it as a list of these elements.
350         * <p>
351         * <b>XML</b>
352         * <pre> {@code
353         * <properties length="3">
354         *     <property>-1878762439</property>
355         *     <property>-957346595</property>
356         *     <property>-88668137</property>
357         * </properties>
358         * }</pre>
359         *
360         * <b>Reader definition</b>
361         * <pre>{@code
362         * Reader<List<Integer>> reader =
363         *     elem(
364         *         v -> (List<Integer>)v[0],
365         *         "properties",
366         *         elems(elem("property", text().map(Integer::parseInt)))
367         *     );
368         * }</pre>
369         *
370         * @param reader the child element reader
371         * @param <T> the element type
372         * @return a list reader
373         */
374        public static <T> Reader<List<T>> elems(final Reader<? extends T> reader) {
375                return new ListReader<>(reader);
376        }
377}
378
379
380/* *****************************************************************************
381 * XML reader implementations.
382 * ****************************************************************************/
383
384/**
385 * Reader implementation for reading the attribute of the current node.
386 *
387 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
388 * @version 3.9
389 * @since 3.9
390 */
391final class AttrReader extends Reader<String> {
392
393        AttrReader(final String name) {
394                super(name, Type.ATTR);
395        }
396
397        @Override
398        public String read(final XMLStreamReader xml) throws XMLStreamException {
399                xml.require(START_ELEMENT, null, null);
400                return xml.getAttributeValue(null, name());
401        }
402
403}
404
405/**
406 * Reader implementation for reading the text of the current node.
407 *
408 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
409 * @version 3.9
410 * @since 3.9
411 */
412final class TextReader extends Reader<String> {
413
414        TextReader() {
415                super("", Type.TEXT);
416        }
417
418        @Override
419        public String read(final XMLStreamReader xml) throws XMLStreamException {
420                final StringBuilder out = new StringBuilder();
421
422                int type = xml.getEventType();
423                do {
424                        out.append(xml.getText());
425                } while (xml.hasNext() && (type = xml.next()) == CHARACTERS || type == CDATA);
426
427
428                return out.toString();
429        }
430}
431
432/**
433 * Reader implementation for reading list of elements.
434 *
435 * @param <T> the element type
436 *
437 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
438 * @version 3.9
439 * @since 3.9
440 */
441final class ListReader<T> extends Reader<List<T>> {
442
443        private final Reader<? extends T> _adoptee;
444
445        ListReader(final Reader<? extends T> adoptee) {
446                super(adoptee.name(), Type.LIST);
447                _adoptee = adoptee;
448        }
449
450        @Override
451        public List<T> read(final XMLStreamReader xml) throws XMLStreamException {
452                xml.require(START_ELEMENT, null, name());
453                return Collections.singletonList(_adoptee.read(xml));
454        }
455}
456
457/**
458 * The main XML element reader implementation.
459 *
460 * @param <T> the reader data type
461 *
462 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
463 * @version 3.9
464 * @since 3.9
465 */
466final class ElemReader<T> extends Reader<T> {
467
468        // Given parameters.
469        private final Function<Object[], T> _creator;
470        private final List<Reader<?>> _children;
471
472        // Derived parameters.
473        private final Map<String, Integer> _readerIndexMapping = new HashMap<>();
474        private final int[] _attrReaderIndexes;
475        private final int[] _textReaderIndex;
476
477        ElemReader(
478                final String name,
479                final Function<Object[], T> creator,
480                final List<Reader<?>> children,
481                final Type type
482        ) {
483                super(name, type);
484
485                _creator = requireNonNull(creator);
486                _children = requireNonNull(children);
487
488                for (int i = 0; i < _children.size(); ++i) {
489                        _readerIndexMapping.put(_children.get(i).name(), i);
490                }
491                _attrReaderIndexes = IntStream.range(0, _children.size())
492                        .filter(i -> _children.get(i).type() == Type.ATTR)
493                        .toArray();
494                _textReaderIndex = IntStream.range(0, _children.size())
495                        .filter(i -> _children.get(i).type() == Type.TEXT)
496                        .toArray();
497
498                if (_textReaderIndex.length > 1) {
499                        throw new IllegalArgumentException(
500                                "Found more than one TEXT reader."
501                        );
502                }
503        }
504
505        @Override
506        public T read(final XMLStreamReader xml)
507                throws XMLStreamException
508        {
509                xml.require(START_ELEMENT, null, name());
510
511                final List<ReaderResult> results = _children.stream()
512                        .map(ReaderResult::of)
513                        .toList();
514
515                final ReaderResult text = _textReaderIndex.length == 1
516                        ? results.get(_textReaderIndex[0])
517                        : null;
518
519                for (int i : _attrReaderIndexes) {
520                        final ReaderResult result = results.get(i);
521                        result.put(result.reader().read(xml));
522                }
523
524                if (xml.hasNext()) {
525                        xml.next();
526
527                        boolean hasNext = false;
528                        do {
529                switch (xml.getEventType()) {
530                    case START_ELEMENT -> {
531                        final ReaderResult result = results.get(
532                                                        _readerIndexMapping.get(xml.getLocalName())
533                        );
534                        if (result != null) {
535                            result.put(result.reader().read(xml));
536                            if (xml.hasNext()) {
537                                hasNext = true;
538                                xml.next();
539                            } else {
540                                hasNext = false;
541                            }
542                        }
543                    }
544                    case CHARACTERS, CDATA -> {
545                        if (text != null) {
546                            text.put(text.reader().read(xml));
547                        } else {
548                            xml.next();
549                        }
550                        hasNext = true;
551                    }
552                    case END_ELEMENT -> {
553                        if (name().equals(xml.getLocalName())) {
554                            try {
555                                return _creator.apply(results.stream()
556                                        .map(ReaderResult::value)
557                                        .toArray());
558                            } catch (RuntimeException e) {
559                                throw new XMLStreamException(e);
560                            }
561                        }
562                    }
563                }
564
565                        } while (hasNext);
566                }
567
568                throw new XMLStreamException(format(
569                        "Premature end of file while reading '%s'.", name()
570                ));
571        }
572
573}
574
575/**
576 * Helper interface for storing the XML reader (intermediate) results.
577 */
578interface ReaderResult {
579
580        /**
581         * Return the underlying XML reader, which reads the result.
582         *
583         * @return return the underlying XML reader
584         */
585        Reader<?> reader();
586
587        /**
588         * Put the given {@code value} to the reader result.
589         *
590         * @param value the reader result
591         */
592        void put(final Object value);
593
594        /**
595         * Return the current reader result value.
596         *
597         * @return the current reader result value
598         */
599        Object value();
600
601        /**
602         * Create a reader result for the given XML reader
603         *
604         * @param reader the XML reader
605         * @return a reader result for the given reader
606         */
607        static ReaderResult of(final Reader<?> reader) {
608                return reader.type() == Type.LIST
609                        ? new ListResult(reader)
610                        : new ValueResult(reader);
611        }
612
613}
614
615/**
616 * Result object for values read from XML elements.
617 */
618final class ValueResult implements ReaderResult {
619
620        private final Reader<?> _reader;
621        private Object _value;
622
623        ValueResult(final Reader<?> reader) {
624                _reader = reader;
625        }
626
627        @Override
628        public void put(final Object value) {
629                _value = value;
630        }
631
632        @Override
633        public Reader<?> reader() {
634                return _reader;
635        }
636
637
638        @Override
639        public Object value() {
640                return _value;
641        }
642
643}
644
645/**
646 * Result object for list values read from XML elements.
647 */
648final class ListResult implements ReaderResult {
649
650        private final Reader<?> _reader;
651        private final List<Object> _value = new ArrayList<>();
652
653        ListResult(final Reader<?> reader) {
654                _reader = reader;
655        }
656
657        @Override
658        public void put(final Object value) {
659                if (value instanceof List) {
660                        _value.addAll((List<?>)value);
661                } else {
662                        _value.add(value);
663                }
664        }
665
666        @Override
667        public Reader<?> reader() {
668                return _reader;
669        }
670
671        @Override
672        public List<Object> value() {
673                return _value;
674        }
675
676}