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 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 (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 > 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 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 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 */
412 final 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 */
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 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 */
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 }
|