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