001/* 002 * Java GPX Library (jpx-3.1.0). 003 * Copyright (c) 2016-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.jpx; 021 022import static java.lang.String.format; 023import static java.util.Objects.hash; 024import static java.util.Objects.requireNonNull; 025import static io.jenetics.jpx.Format.toIntString; 026import static io.jenetics.jpx.Lists.copyOf; 027import static io.jenetics.jpx.Lists.copyTo; 028 029import java.io.DataInput; 030import java.io.DataOutput; 031import java.io.IOException; 032import java.io.InvalidObjectException; 033import java.io.ObjectInputStream; 034import java.io.Serial; 035import java.io.Serializable; 036import java.net.URI; 037import java.util.ArrayList; 038import java.util.Iterator; 039import java.util.List; 040import java.util.Objects; 041import java.util.Optional; 042import java.util.function.Consumer; 043import java.util.function.Function; 044import java.util.function.Predicate; 045import java.util.stream.Stream; 046 047import org.w3c.dom.Document; 048 049import io.jenetics.jpx.GPX.Version; 050 051/** 052 * Represents a route - an ordered list of way-points representing a series of 053 * turn points leading to a destination. 054 * <p> 055 * Create a new route via the builder: 056 * <pre>{@code 057 * final Route route = Route.builder() 058 * .name("Route 1") 059 * .description("Fancy mountain-bike tour.") 060 * .addPoint(p -> p.lat(48.2081743).lon(16.3738189).ele(160)) 061 * .addPoint(p -> p.lat(48.2081743).lon(16.3738189).ele(161)) 062 * .addPoint(p -> p.lat(48.2081743).lon(16.3738189).ele(162)))) 063 * .build(); 064 * }</pre> 065 * 066 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 067 * @version 1.5 068 * @since 1.0 069 */ 070public final class Route implements Iterable<WayPoint>, Serializable { 071 072 @Serial 073 private static final long serialVersionUID = 2L; 074 075 private final String _name; 076 private final String _comment; 077 private final String _description; 078 private final String _source; 079 private final List<Link> _links; 080 private final UInt _number; 081 private final String _type; 082 private final Document _extensions; 083 private final List<WayPoint> _points; 084 085 /** 086 * Create a new {@code Route} with the given parameters and way-points. 087 * 088 * @param name the GPS name of the route 089 * @param comment the GPS comment of the route 090 * @param description the Text description of route for user. Not sent to GPS. 091 * @param source the source of data. Included to give user some idea of 092 * reliability and accuracy of data. 093 * @param links the links to external information about the route 094 * @param number the GPS route number 095 * @param type the type (classification) of the route 096 * @param extensions the extensions document 097 * @param points the sequence of route points 098 */ 099 private Route( 100 final String name, 101 final String comment, 102 final String description, 103 final String source, 104 final List<Link> links, 105 final UInt number, 106 final String type, 107 final Document extensions, 108 final List<WayPoint> points 109 ) { 110 _name = name; 111 _comment = comment; 112 _description = description; 113 _source = source; 114 _links = copyOf(links); 115 _number = number; 116 _type = type; 117 _extensions = extensions; 118 _points = copyOf(points); 119 } 120 121 /** 122 * Return the route name. 123 * 124 * @return the route name 125 */ 126 public Optional<String> getName() { 127 return Optional.ofNullable(_name); 128 } 129 130 /** 131 * Return the GPS comment of the route. 132 * 133 * @return the GPS comment of the route 134 */ 135 public Optional<String> getComment() { 136 return Optional.ofNullable(_comment); 137 } 138 139 /** 140 * Return the Text description of route for user. Not sent to GPS. 141 * 142 * @return the Text description of route for user. Not sent to GPS 143 */ 144 public Optional<String> getDescription() { 145 return Optional.ofNullable(_description); 146 } 147 148 /** 149 * Return the source of data. Included to give user some idea of reliability 150 * and accuracy of data. 151 * 152 * @return the source of data 153 */ 154 public Optional<String> getSource() { 155 return Optional.ofNullable(_source); 156 } 157 158 /** 159 * Return the links to external information about the route. 160 * 161 * @return the links to external information about the route 162 */ 163 public List<Link> getLinks() { 164 return _links; 165 } 166 167 /** 168 * Return the GPS route number. 169 * 170 * @return the GPS route number 171 */ 172 public Optional<UInt> getNumber() { 173 return Optional.ofNullable(_number); 174 } 175 176 /** 177 * Return the type (classification) of the route. 178 * 179 * @return the type (classification) of the route 180 */ 181 public Optional<String> getType() { 182 return Optional.ofNullable(_type); 183 } 184 185 /** 186 * Return the sequence of route points. 187 * 188 * @return the sequence of route points 189 */ 190 public List<WayPoint> getPoints() { 191 return _points; 192 } 193 194 /** 195 * Return a stream of {@link WayPoint} objects this route contains. 196 * 197 * @return a stream of {@link WayPoint} objects this route contains 198 */ 199 public Stream<WayPoint> points() { 200 return _points.stream(); 201 } 202 203 /** 204 * Return the (cloned) extensions document. The root element of the returned 205 * document has the name {@code extensions}. 206 * <pre>{@code 207 * <extensions> 208 * ... 209 * </extensions> 210 * }</pre> 211 * 212 * @since 1.5 213 * 214 * @return the extensions document 215 * @throws org.w3c.dom.DOMException if the document could not be cloned, 216 * because of an erroneous XML configuration 217 */ 218 public Optional<Document> getExtensions() { 219 return Optional.ofNullable(_extensions).map(XML::clone); 220 } 221 222 @Override 223 public Iterator<WayPoint> iterator() { 224 return _points.iterator(); 225 } 226 227 /** 228 * Convert the <em>immutable</em> route object into a <em>mutable</em> 229 * builder initialized with the current route values. 230 * 231 * @since 1.1 232 * 233 * @return a new route builder initialized with the values of {@code this} 234 * route 235 */ 236 public Builder toBuilder() { 237 return builder() 238 .name(_name) 239 .cmt(_comment) 240 .desc(_description) 241 .src(_source) 242 .links(_links) 243 .number(_number) 244 .extensions(_extensions) 245 .points(_points); 246 } 247 248 /** 249 * Return {@code true} if all route properties are {@code null} or empty. 250 * 251 * @return {@code true} if all route properties are {@code null} or empty 252 */ 253 public boolean isEmpty() { 254 return _name == null && 255 _comment == null && 256 _description == null && 257 _source == null && 258 _links.isEmpty() && 259 _number == null && 260 _extensions == null && 261 _points.isEmpty(); 262 } 263 264 /** 265 * Return {@code true} if not all route properties are {@code null} or empty. 266 * 267 * @since 1.1 268 * 269 * @return {@code true} if not all route properties are {@code null} or empty 270 */ 271 public boolean nonEmpty() { 272 return !isEmpty(); 273 } 274 275 @Override 276 public int hashCode() { 277 return hash( 278 _name, 279 _comment, 280 _description, 281 _source, 282 _type, 283 Lists.hashCode(_links), 284 _number, 285 _points 286 ); 287 } 288 289 @Override 290 public boolean equals(final Object obj) { 291 return obj == this || 292 obj instanceof Route route && 293 Objects.equals(route._name, _name) && 294 Objects.equals(route._comment, _comment) && 295 Objects.equals(route._description, _description) && 296 Objects.equals(route._source, _source) && 297 Objects.equals(route._type, _type) && 298 Lists.equals(route._links, _links) && 299 Objects.equals(route._number, _number) && 300 Objects.equals(route._points, _points); 301 } 302 303 @Override 304 public String toString() { 305 return format("Route[name=%s, points=%s]", _name, _points.size()); 306 } 307 308 /* ************************************************************************* 309 * Static object creation methods 310 * ************************************************************************/ 311 312 /** 313 * Return a new {@code Route} builder object. 314 * 315 * @return a new {@code Route} builder object 316 */ 317 public static Builder builder() { 318 return new Builder(); 319 } 320 321 /** 322 * Builder class for building {@code Route} objects. 323 * <pre>{@code 324 * final Route route = Route.builder() 325 * .name("Route 1") 326 * .description("Fancy mountain-bike tour.") 327 * .addPoint(p -> p.lat(48.2081743).lon(16.3738189).ele(160)) 328 * .addPoint(p -> p.lat(48.2081743).lon(16.3738189).ele(161)) 329 * .addPoint(p -> p.lat(48.2081743).lon(16.3738189).ele(162)))) 330 * .build(); 331 * }</pre> 332 */ 333 public static final class Builder implements Filter<WayPoint, Route> { 334 335 private String _name; 336 private String _comment; 337 private String _description; 338 private String _source; 339 private final List<Link> _links = new ArrayList<>(); 340 private UInt _number; 341 private String _type; 342 private Document _extensions; 343 private final List<WayPoint> _points = new ArrayList<>(); 344 345 private Builder() { 346 } 347 348 /** 349 * Set the route name. 350 * 351 * @param name the route name. 352 * @return {@code this} {@code Builder} for method chaining 353 */ 354 public Builder name(final String name) { 355 _name = name; 356 return this; 357 } 358 359 /** 360 * Return the current name value. 361 * 362 * @since 1.1 363 * 364 * @return the current name value 365 */ 366 public Optional<String> name() { 367 return Optional.ofNullable(_name); 368 } 369 370 /** 371 * Set the route comment. 372 * 373 * @param comment the route comment 374 * @return {@code this} {@code Builder} for method chaining 375 */ 376 public Builder cmt(final String comment) { 377 _comment = comment; 378 return this; 379 } 380 381 /** 382 * Return the current comment value. 383 * 384 * @since 1.1 385 * 386 * @return the current comment value 387 */ 388 public Optional<String> cmt() { 389 return Optional.ofNullable(_comment); 390 } 391 392 /** 393 * Set the route description. 394 * 395 * @param description the route description 396 * @return {@code this} {@code Builder} for method chaining 397 */ 398 public Builder desc(final String description) { 399 _description = description; 400 return this; 401 } 402 403 /** 404 * Return the current description value. 405 * 406 * @since 1.1 407 * 408 * @return the current description value 409 */ 410 public Optional<String> desc() { 411 return Optional.ofNullable(_description); 412 } 413 414 /** 415 * Set the source of the data. Included to give user some idea of 416 * reliability and accuracy of data. 417 * 418 * @param source the source of the data 419 * @return {@code this} {@code Builder} for method chaining 420 */ 421 public Builder src(final String source) { 422 _source = source; 423 return this; 424 } 425 426 /** 427 * Return the current source value. 428 * 429 * @since 1.1 430 * 431 * @return the current source value 432 */ 433 public Optional<String> src() { 434 return Optional.ofNullable(_source); 435 } 436 437 /** 438 * Set the links to additional information about the route. The link 439 * list may be {@code null}. 440 * 441 * @param links the links to additional information about the route 442 * @return {@code this} {@code Builder} for method chaining 443 * @throws NullPointerException if one of the links in the list is 444 * {@code null} 445 */ 446 public Builder links(final List<Link> links) { 447 copyTo(links, _links); 448 return this; 449 } 450 451 /** 452 * Set the links to external information about the route. 453 * 454 * @param link the links to external information about the route. 455 * @return {@code this} {@code Builder} for method chaining 456 * @throws NullPointerException if the given {@code link} is {@code null} 457 */ 458 public Builder addLink(final Link link) { 459 _links.add(requireNonNull(link)); 460 return this; 461 } 462 463 /** 464 * Set the links to external information about the route. 465 * 466 * @param href the links to external information about the route. 467 * @return {@code this} {@code Builder} for method chaining 468 * @throws NullPointerException if the given {@code href} is 469 * {@code null} 470 * @throws IllegalArgumentException if the given {@code href} is not a 471 * valid URL 472 */ 473 public Builder addLink(final String href) { 474 _links.add(Link.of(href)); 475 return this; 476 } 477 478 /** 479 * Return the current links. The returned link list is mutable. 480 * 481 * @since 1.1 482 * 483 * @return the current links 484 */ 485 public List<Link> links() { 486 return new NonNullList<>(_links); 487 } 488 489 /** 490 * Set the GPS route number. 491 * 492 * @param number the GPS route number 493 * @return {@code this} {@code Builder} for method chaining 494 */ 495 public Builder number(final UInt number) { 496 _number = number; 497 return this; 498 } 499 500 /** 501 * Set the GPS route number. 502 * 503 * @param number the GPS route number 504 * @return {@code this} {@code Builder} for method chaining 505 */ 506 public Builder number(final int number) { 507 _number = UInt.of(number); 508 return this; 509 } 510 511 /** 512 * Return the current number value. 513 * 514 * @since 1.1 515 * 516 * @return the current number value 517 */ 518 public Optional<UInt> number() { 519 return Optional.ofNullable(_number); 520 } 521 522 /** 523 * Set the type (classification) of the route. 524 * 525 * @param type the type (classification) of the route. 526 * @return {@code this} {@code Builder} for method chaining 527 */ 528 public Builder type(final String type) { 529 _type = type; 530 return this; 531 } 532 533 /** 534 * Return the current type value. 535 * 536 * @since 1.1 537 * 538 * @return the current type value 539 */ 540 public Optional<String> type() { 541 return Optional.ofNullable(_type); 542 } 543 544 /** 545 * Sets the extensions object, which may be {@code null}. The root 546 * element of the extensions document must be {@code extensions}. 547 * <pre>{@code 548 * <extensions> 549 * ... 550 * </extensions> 551 * }</pre> 552 * 553 * @since 1.5 554 * 555 * @param extensions the document 556 * @return {@code this} {@code Builder} for method chaining 557 * @throws IllegalArgumentException if the root element is not the 558 * an {@code extensions} node 559 */ 560 public Builder extensions(final Document extensions) { 561 _extensions = XML.checkExtensions(extensions); 562 return this; 563 } 564 565 /** 566 * Return the current extensions 567 * 568 * @since 1.5 569 * 570 * @return the extensions document 571 */ 572 public Optional<Document> extensions() { 573 return Optional.ofNullable(_extensions); 574 } 575 576 /** 577 * Sets the way-points of the route. The way-point list may be 578 * {@code null}. 579 * 580 * @param points the way-points 581 * @return {@code this} {@code Builder} for method chaining 582 * @throws NullPointerException if one of the way-points is {@code null} 583 */ 584 public Builder points(final List<WayPoint> points) { 585 copyTo(points, _points); 586 return this; 587 } 588 589 /** 590 * Adds a way-point to the route. 591 * 592 * @param point the way-point which is added to the route 593 * @return {@code this} {@code Builder} for method chaining 594 * @throws NullPointerException if the {@code point} is {@code null} 595 */ 596 public Builder addPoint(final WayPoint point) { 597 _points.add(requireNonNull(point)); 598 return this; 599 } 600 601 /** 602 * Add a new way-point via the given {@code WayPoint.Builder} class. 603 * 604 * @param point the way-point builder 605 * @return {@code this} {@code Builder} for method chaining 606 */ 607 public Builder addPoint(final Consumer<? super WayPoint.Builder> point) { 608 final WayPoint.Builder builder = WayPoint.builder(); 609 point.accept(builder); 610 return addPoint(builder.build()); 611 } 612 613 /** 614 * Return the current way-points. The returned list is mutable. 615 * 616 * @since 1.1 617 * 618 * @return the current, mutable way-point list 619 */ 620 public List<WayPoint> points() { 621 return new NonNullList<>(_points); 622 } 623 624 @Override 625 public Builder filter(final Predicate<? super WayPoint> predicate) { 626 points(_points.stream().filter(predicate).toList()); 627 return this; 628 } 629 630 @Override 631 public Builder map( 632 final Function<? super WayPoint, ? extends WayPoint> mapper 633 ) { 634 points( 635 _points.stream() 636 .map(mapper) 637 .map(WayPoint.class::cast) 638 .toList() 639 ); 640 return this; 641 } 642 643 @Override 644 public Builder flatMap( 645 final Function< 646 ? super WayPoint, 647 ? extends List<WayPoint>> mapper 648 ) { 649 points( 650 _points.stream() 651 .flatMap(wp -> mapper.apply(wp).stream()) 652 .toList() 653 ); 654 return this; 655 } 656 657 @Override 658 public Builder listMap( 659 final Function< 660 ? super List<WayPoint>, 661 ? extends List<WayPoint>> mapper 662 ) { 663 points(mapper.apply(_points)); 664 return this; 665 } 666 667 /** 668 * Create a new {@code Route} object with the set values. 669 * 670 * @return a new {@code Route} object with the set values 671 */ 672 @Override 673 public Route build() { 674 return of( 675 _name, 676 _comment, 677 _description, 678 _source, 679 _links, 680 _number, 681 _type, 682 _extensions, 683 _points 684 ); 685 } 686 687 } 688 689 /* ************************************************************************* 690 * Static object creation methods 691 * ************************************************************************/ 692 693 /** 694 * Create a new {@code Route} with the given parameters and way-points. 695 * 696 * @since 1.5 697 * 698 * @param name the GPS name of the route 699 * @param comment the GPS comment of the route 700 * @param description the Text description of route for user. Not sent to GPS. 701 * @param source the source of data. Included to give user some idea of 702 * reliability and accuracy of data. 703 * @param links the links to external information about the route 704 * @param number the GPS route number 705 * @param type the type (classification) of the route 706 * @param extensions the extensions document 707 * @param points the sequence of route points 708 * @return a new route object with the given parameters 709 */ 710 public static Route of( 711 final String name, 712 final String comment, 713 final String description, 714 final String source, 715 final List<Link> links, 716 final UInt number, 717 final String type, 718 final Document extensions, 719 final List<WayPoint> points 720 ) { 721 return new Route( 722 name, 723 comment, 724 description, 725 source, 726 links, 727 number, 728 type, 729 XML.extensions(XML.clone(extensions)), 730 points 731 ); 732 } 733 734 /** 735 * Create a new {@code Route} with the given parameters and way-points. 736 * 737 * @param name the GPS name of the route 738 * @param comment the GPS comment of the route 739 * @param description the Text description of route for user. Not sent to GPS. 740 * @param source the source of data. Included to give user some idea of 741 * reliability and accuracy of data. 742 * @param links the links to external information about the route 743 * @param number the GPS route number 744 * @param type the type (classification) of the route 745 * @param points the sequence of route points 746 * @return a new route object with the given parameters 747 */ 748 public static Route of( 749 final String name, 750 final String comment, 751 final String description, 752 final String source, 753 final List<Link> links, 754 final UInt number, 755 final String type, 756 final List<WayPoint> points 757 ) { 758 return of( 759 name, 760 comment, 761 description, 762 source, 763 links, 764 number, 765 type, 766 null, 767 points 768 ); 769 } 770 771 /** 772 * Create a new {@code Route} with the given parameters and way-points. 773 * 774 * @param name the GPS name of the route 775 * @param points the sequence of route points 776 * @return a new route object with the given parameters 777 */ 778 public static Route of( 779 final String name, 780 final List<WayPoint> points 781 ) { 782 return of( 783 name, 784 null, 785 null, 786 null, 787 null, 788 null, 789 null, 790 null, 791 points 792 ); 793 } 794 795 /** 796 * Create a new {@code Route} with the given parameters and way-points. 797 * 798 * @param points the sequence of route points 799 * @return a new route object with the given parameters 800 */ 801 public static Route of(final List<WayPoint> points) { 802 return of( 803 null, 804 null, 805 null, 806 null, 807 null, 808 null, 809 null, 810 null, 811 points 812 ); 813 } 814 815 816 /* ************************************************************************* 817 * Java object serialization 818 * ************************************************************************/ 819 820 @Serial 821 private Object writeReplace() { 822 return new SerialProxy(SerialProxy.ROUTE, this); 823 } 824 825 @Serial 826 private void readObject(final ObjectInputStream stream) 827 throws InvalidObjectException 828 { 829 throw new InvalidObjectException("Serialization proxy required."); 830 } 831 832 void write(final DataOutput out) throws IOException { 833 IO.writeNullableString(_name, out); 834 IO.writeNullableString(_comment, out); 835 IO.writeNullableString(_description, out); 836 IO.writeNullableString(_source, out); 837 IO.writes(_links, Link::write, out); 838 IO.writeNullable(_number, UInt::write, out); 839 IO.writeNullableString(_type, out); 840 IO.writeNullable(_extensions, IO::write, out); 841 IO.writes(_points, WayPoint::write, out); 842 } 843 844 static Route read(final DataInput in) throws IOException { 845 return new Route( 846 IO.readNullableString(in), 847 IO.readNullableString(in), 848 IO.readNullableString(in), 849 IO.readNullableString(in), 850 IO.reads(Link::read, in), 851 IO.readNullable(UInt::read, in), 852 IO.readNullableString(in), 853 IO.readNullable(IO::readDoc, in), 854 IO.reads(WayPoint::read, in) 855 ); 856 } 857 858 /* ************************************************************************* 859 * XML stream object serialization 860 * ************************************************************************/ 861 862 private static String url(final Route route) { 863 return route.getLinks().isEmpty() 864 ? null 865 : route.getLinks().get(0).getHref().toString(); 866 } 867 868 private static String urlname(final Route route) { 869 return route.getLinks().isEmpty() 870 ? null 871 : route.getLinks().get(0).getText().orElse(null); 872 } 873 874 // Define the needed writers for the different versions. 875 private static XMLWriters<Route> 876 writers(final Function<? super Number, String> formatter) { 877 return new XMLWriters<Route>() 878 .v00(XMLWriter.elem("name").map(r -> r._name)) 879 .v00(XMLWriter.elem("cmt").map(r -> r._comment)) 880 .v00(XMLWriter.elem("desc").map(r -> r._description)) 881 .v00(XMLWriter.elem("src").map(r -> r._source)) 882 .v11(XMLWriter.elems(Link.WRITER).map(r -> r._links)) 883 .v10(XMLWriter.elem("url").map(Route::url)) 884 .v10(XMLWriter.elem("urlname").map(Route::urlname)) 885 .v00(XMLWriter.elem("number").map(r -> toIntString(r._number))) 886 .v00(XMLWriter.elem("type").map(r -> r._type)) 887 .v00(XMLWriter.doc("extensions").map(gpx -> gpx._extensions)) 888 .v10(XMLWriter.elems(WayPoint.xmlWriter(Version.V10, "rtept", formatter)).map(r -> r._points)) 889 .v11(XMLWriter.elems(WayPoint.xmlWriter(Version.V11, "rtept", formatter)).map(r -> r._points)); 890 } 891 892 893 // Define the needed readers for the different versions. 894 private static XMLReaders 895 readers(final Function<? super String, Length> lengthParser) { 896 return new XMLReaders() 897 .v00(XMLReader.elem("name")) 898 .v00(XMLReader.elem("cmt")) 899 .v00(XMLReader.elem("desc")) 900 .v00(XMLReader.elem("src")) 901 .v11(XMLReader.elems(Link.READER)) 902 .v10(XMLReader.elem("url").map(Format::parseURI)) 903 .v10(XMLReader.elem("urlname")) 904 .v00(XMLReader.elem("number").map(UInt::parse)) 905 .v00(XMLReader.elem("type")) 906 .v00(XMLReader.doc("extensions")) 907 .v10(XMLReader.elems(WayPoint.xmlReader(Version.V10, "rtept", lengthParser))) 908 .v11(XMLReader.elems(WayPoint.xmlReader(Version.V11, "rtept", lengthParser))); 909 } 910 911 static XMLWriter<Route> xmlWriter( 912 final Version version, 913 final Function<? super Number, String> formatter 914 ) { 915 return XMLWriter.elem("rte", writers(formatter).writers(version)); 916 } 917 918 static XMLReader<Route> xmlReader( 919 final Version version, 920 final Function<? super String, Length> lengthParser 921 ) { 922 return XMLReader.elem( 923 version == Version.V10 ? Route::toRouteV10 : Route::toRouteV11, 924 "rte", 925 readers(lengthParser).readers(version) 926 ); 927 } 928 929 @SuppressWarnings("unchecked") 930 private static Route toRouteV11(final Object[] v) { 931 return new Route( 932 (String)v[0], 933 (String)v[1], 934 (String)v[2], 935 (String)v[3], 936 (List<Link>)v[4], 937 (UInt)v[5], 938 (String)v[6], 939 XML.extensions((Document)v[7]), 940 (List<WayPoint>)v[8] 941 ); 942 } 943 944 @SuppressWarnings("unchecked") 945 private static Route toRouteV10(final Object[] v) { 946 return new Route( 947 (String)v[0], 948 (String)v[1], 949 (String)v[2], 950 (String)v[3], 951 v[4] != null 952 ? List.of(Link.of((URI)v[4], (String)v[5], null)) 953 : null, 954 (UInt)v[6], 955 (String)v[7], 956 XML.extensions((Document)v[8]), 957 (List<WayPoint>)v[9] 958 ); 959 } 960 961}