Reader.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.0.0).
003  * Copyright (c) 2007-2022 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  */
020 package io.jenetics.xml.stream;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 import static javax.xml.stream.XMLStreamConstants.CDATA;
025 import static javax.xml.stream.XMLStreamConstants.CHARACTERS;
026 import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
027 import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
028 
029 import java.util.ArrayList;
030 import java.util.Collections;
031 import java.util.HashMap;
032 import java.util.List;
033 import java.util.Map;
034 import java.util.Objects;
035 import java.util.function.Function;
036 import java.util.stream.IntStream;
037 import java.util.stream.Stream;
038 
039 import javax.xml.stream.XMLStreamException;
040 import javax.xml.stream.XMLStreamReader;
041 
042 import 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  */
091 public 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 xmlthrows 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 (sub-tree) 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 > (T)v[0null;
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 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  */
391 final 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 xmlthrows 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  */
412 final class TextReader extends Reader<String> {
413 
414     TextReader() {
415         super("", Type.TEXT);
416     }
417 
418     @Override
419     public String read(final XMLStreamReader xmlthrows 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  */
441 final 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 xmlthrows 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  */
466 final 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
532                             .get(_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                         break;
545                     case CHARACTERS:
546                     case CDATA:
547                         if (text != null) {
548                             text.put(text.reader().read(xml));
549                         else {
550                             xml.next();
551                         }
552                         hasNext = true;
553 
554                         break;
555                     case END_ELEMENT:
556                         if (name().equals(xml.getLocalName())) {
557                             try {
558                                 return _creator.apply(
559                                     results.stream()
560                                         .map(ReaderResult::value)
561                                         .toArray()
562                                 );
563                             catch (RuntimeException e) {
564                                 throw new XMLStreamException(e);
565                             }
566                         }
567                 }
568 
569             while (hasNext);
570         }
571 
572         throw new XMLStreamException(format(
573             "Premature end of file while reading '%s'.", name()
574         ));
575     }
576 
577 }
578 
579 /**
580  * Helper interface for storing the XML reader (intermediate) results.
581  */
582 interface ReaderResult {
583 
584     /**
585      * Return the underlying XML reader, which reads the result.
586      *
587      @return return the underlying XML reader
588      */
589     Reader<?> reader();
590 
591     /**
592      * Put the given {@code value} to the reader result.
593      *
594      @param value the reader result
595      */
596     void put(final Object value);
597 
598     /**
599      * Return the current reader result value.
600      *
601      @return the current reader result value
602      */
603     Object value();
604 
605     /**
606      * Create a reader result for the given XML reader
607      *
608      @param reader the XML reader
609      @return a reader result for the given reader
610      */
611     static ReaderResult of(final Reader<?> reader) {
612         return reader.type() == Type.LIST
613             new ListResult(reader)
614             new ValueResult(reader);
615     }
616 
617 }
618 
619 /**
620  * Result object for values read from XML elements.
621  */
622 final class ValueResult implements ReaderResult {
623 
624     private final Reader<?> _reader;
625     private Object _value;
626 
627     ValueResult(final Reader<?> reader) {
628         _reader = reader;
629     }
630 
631     @Override
632     public void put(final Object value) {
633         _value = value;
634     }
635 
636     @Override
637     public Reader<?> reader() {
638         return _reader;
639     }
640 
641 
642     @Override
643     public Object value() {
644         return _value;
645     }
646 
647 }
648 
649 /**
650  * Result object for list values read from XML elements.
651  */
652 final class ListResult implements ReaderResult {
653 
654     private final Reader<?> _reader;
655     private final List<Object> _value = new ArrayList<>();
656 
657     ListResult(final Reader<?> reader) {
658         _reader = reader;
659     }
660 
661     @Override
662     public void put(final Object value) {
663         if (value instanceof List) {
664             _value.addAll((List<?>)value);
665         else {
666             _value.add(value);
667         }
668     }
669 
670     @Override
671     public Reader<?> reader() {
672         return _reader;
673     }
674 
675     @Override
676     public List<Object> value() {
677         return _value;
678     }
679 
680 }