Commit 10ddfec
committed
Fix document order of nodes that tie on their ancestor indexes
REXML::XPathParser.sort keys each node on its index under each ancestor,
but two different nodes could end up with the same key, and ties are
then broken arbitrarily by an unstable sort.
The walk stopped at the root element, so the root and everything outside
it -- comments and PIs at document level -- all keyed on an empty array:
<!--c1--><?pi1?><root/><!--c2--><?pi2?>
//comment() | //processing-instruction()
was: c1, c2, pi1, pi2 want: c1, pi1, c2, pi2
Walk up to the document instead, so those nodes are ordered against each
other.
An attribute borrowed the key of the element carrying it, so the element
and its attributes tied:
<root><a x="1" y="2"><b/></a></root>
//a/@* | //a | //a/*
was: @x, @y, <a>, <b> want: <a>, @x, @y, <b>
Extend an attribute's key past its element's with ATTRIBUTE_POSITION. A
child index is never negative, so -1 lands the attributes after <a> and
ahead of <b>, which is where document order wants them.
The attributes of one element tied with each other too, which went
unnoticed because a small enough sort leaves its input alone:
<a z="1" m="2" b="3"/> //a/@* -> z, m, b
<a z="1" ... 26 attributes/> //a/@* -> scrambled
XPath 1.0 leaves their relative order implementation dependent, but
document order is a total ordering, so it has to be decided. Key them
on where they were written, which is the order the small case already
appeared to have. Finding that out means walking the attribute list, so
index the whole list at once and remember it for the rest of the sort;
asking per attribute would make sorting quadratic in the number of
attributes an element carries, which is something a document gets to
choose.
The ancestor walk becomes a method of its own, since both branches of
the key need it. It, attribute_position and ATTRIBUTE_POSITION are
private rather than public names marked :nodoc:, sort being the only
part callers outside the class use.1 parent fa0427c commit 10ddfec
2 files changed
Lines changed: 148 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
804 | 804 | | |
805 | 805 | | |
806 | 806 | | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
807 | 812 | | |
808 | 813 | | |
809 | 814 | | |
| |||
815 | 820 | | |
816 | 821 | | |
817 | 822 | | |
818 | | - | |
819 | | - | |
820 | | - | |
821 | | - | |
822 | | - | |
823 | | - | |
824 | | - | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
825 | 832 | | |
826 | | - | |
827 | | - | |
828 | | - | |
829 | | - | |
830 | 833 | | |
831 | | - | |
832 | | - | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
833 | 865 | | |
| 866 | + | |
834 | 867 | | |
| 868 | + | |
835 | 869 | | |
836 | 870 | | |
837 | 871 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| 8 | + | |
6 | 9 | | |
7 | 10 | | |
8 | 11 | | |
| |||
1564 | 1567 | | |
1565 | 1568 | | |
1566 | 1569 | | |
| 1570 | + | |
| 1571 | + | |
| 1572 | + | |
| 1573 | + | |
| 1574 | + | |
| 1575 | + | |
| 1576 | + | |
| 1577 | + | |
| 1578 | + | |
| 1579 | + | |
| 1580 | + | |
| 1581 | + | |
| 1582 | + | |
| 1583 | + | |
| 1584 | + | |
| 1585 | + | |
| 1586 | + | |
| 1587 | + | |
| 1588 | + | |
| 1589 | + | |
| 1590 | + | |
| 1591 | + | |
| 1592 | + | |
| 1593 | + | |
| 1594 | + | |
| 1595 | + | |
| 1596 | + | |
| 1597 | + | |
| 1598 | + | |
| 1599 | + | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
| 1603 | + | |
| 1604 | + | |
| 1605 | + | |
| 1606 | + | |
| 1607 | + | |
| 1608 | + | |
| 1609 | + | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
| 1619 | + | |
| 1620 | + | |
| 1621 | + | |
| 1622 | + | |
| 1623 | + | |
| 1624 | + | |
| 1625 | + | |
| 1626 | + | |
| 1627 | + | |
| 1628 | + | |
| 1629 | + | |
| 1630 | + | |
| 1631 | + | |
| 1632 | + | |
| 1633 | + | |
| 1634 | + | |
| 1635 | + | |
| 1636 | + | |
| 1637 | + | |
| 1638 | + | |
| 1639 | + | |
| 1640 | + | |
| 1641 | + | |
| 1642 | + | |
| 1643 | + | |
| 1644 | + | |
| 1645 | + | |
| 1646 | + | |
| 1647 | + | |
| 1648 | + | |
| 1649 | + | |
| 1650 | + | |
| 1651 | + | |
| 1652 | + | |
1567 | 1653 | | |
1568 | 1654 | | |
1569 | 1655 | | |
| |||
1607 | 1693 | | |
1608 | 1694 | | |
1609 | 1695 | | |
| 1696 | + | |
| 1697 | + | |
| 1698 | + | |
| 1699 | + | |
| 1700 | + | |
| 1701 | + | |
| 1702 | + | |
| 1703 | + | |
| 1704 | + | |
| 1705 | + | |
| 1706 | + | |
| 1707 | + | |
| 1708 | + | |
| 1709 | + | |
| 1710 | + | |
1610 | 1711 | | |
1611 | 1712 | | |
0 commit comments