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