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}