2 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "core/xml/XPathNodeSet.h"
29 #include "core/dom/Attr.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/Element.h"
32 #include "core/dom/NodeTraversal.h"
37 // When a node set is large, sorting it by traversing the whole document is
38 // better (we can assume that we aren't dealing with documents that we cannot
39 // even traverse in reasonable time).
40 const unsigned traversalSortCutoff
= 10000;
42 typedef WillBeHeapVector
<RawPtrWillBeMember
<Node
>> NodeSetVector
;
44 NodeSet
* NodeSet::create(const NodeSet
& other
)
46 NodeSet
* nodeSet
= NodeSet::create();
47 nodeSet
->m_isSorted
= other
.m_isSorted
;
48 nodeSet
->m_subtreesAreDisjoint
= other
.m_subtreesAreDisjoint
;
49 nodeSet
->m_nodes
.appendVector(other
.m_nodes
);
53 static inline Node
* parentWithDepth(unsigned depth
, const NodeSetVector
& parents
)
55 ASSERT(parents
.size() >= depth
+ 1);
56 return parents
[parents
.size() - 1 - depth
];
59 static void sortBlock(unsigned from
, unsigned to
, WillBeHeapVector
<NodeSetVector
>& parentMatrix
, bool mayContainAttributeNodes
)
61 // Should not call this function with less that two nodes to sort.
62 ASSERT(from
+ 1 < to
);
63 unsigned minDepth
= UINT_MAX
;
64 for (unsigned i
= from
; i
< to
; ++i
) {
65 unsigned depth
= parentMatrix
[i
].size() - 1;
70 // Find the common ancestor.
71 unsigned commonAncestorDepth
= minDepth
;
74 commonAncestor
= parentWithDepth(commonAncestorDepth
, parentMatrix
[from
]);
75 if (commonAncestorDepth
== 0)
79 for (unsigned i
= from
+ 1; i
< to
; ++i
) {
80 if (commonAncestor
!= parentWithDepth(commonAncestorDepth
, parentMatrix
[i
])) {
88 --commonAncestorDepth
;
91 if (commonAncestorDepth
== minDepth
) {
92 // One of the nodes is the common ancestor => it is the first in
93 // document order. Find it and move it to the beginning.
94 for (unsigned i
= from
; i
< to
; ++i
) {
95 if (commonAncestor
== parentMatrix
[i
][0]) {
96 parentMatrix
[i
].swap(parentMatrix
[from
]);
98 sortBlock(from
+ 1, to
, parentMatrix
, mayContainAttributeNodes
);
104 if (mayContainAttributeNodes
&& commonAncestor
->isElementNode()) {
105 // The attribute nodes and namespace nodes of an element occur before
106 // the children of the element. The namespace nodes are defined to occur
107 // before the attribute nodes. The relative order of namespace nodes is
108 // implementation-dependent. The relative order of attribute nodes is
109 // implementation-dependent.
110 unsigned sortedEnd
= from
;
111 // FIXME: namespace nodes are not implemented.
112 for (unsigned i
= sortedEnd
; i
< to
; ++i
) {
113 Node
* n
= parentMatrix
[i
][0];
114 if (n
->isAttributeNode() && toAttr(n
)->ownerElement() == commonAncestor
)
115 parentMatrix
[i
].swap(parentMatrix
[sortedEnd
++]);
117 if (sortedEnd
!= from
) {
118 if (to
- sortedEnd
> 1)
119 sortBlock(sortedEnd
, to
, parentMatrix
, mayContainAttributeNodes
);
124 // Children nodes of the common ancestor induce a subdivision of our
125 // node-set. Sort it according to this subdivision, and recursively sort
127 WillBeHeapHashSet
<RawPtrWillBeMember
<Node
>> parentNodes
;
128 for (unsigned i
= from
; i
< to
; ++i
)
129 parentNodes
.add(parentWithDepth(commonAncestorDepth
+ 1, parentMatrix
[i
]));
131 unsigned previousGroupEnd
= from
;
132 unsigned groupEnd
= from
;
133 for (Node
* n
= commonAncestor
->firstChild(); n
; n
= n
->nextSibling()) {
134 // If parentNodes contains the node, perform a linear search to move its
135 // children in the node-set to the beginning.
136 if (parentNodes
.contains(n
)) {
137 for (unsigned i
= groupEnd
; i
< to
; ++i
) {
138 if (parentWithDepth(commonAncestorDepth
+ 1, parentMatrix
[i
]) == n
)
139 parentMatrix
[i
].swap(parentMatrix
[groupEnd
++]);
142 if (groupEnd
- previousGroupEnd
> 1)
143 sortBlock(previousGroupEnd
, groupEnd
, parentMatrix
, mayContainAttributeNodes
);
145 ASSERT(previousGroupEnd
!= groupEnd
);
146 previousGroupEnd
= groupEnd
;
148 parentNodes
.remove(n
);
153 ASSERT(parentNodes
.isEmpty());
156 void NodeSet::sort() const
161 unsigned nodeCount
= m_nodes
.size();
163 const_cast<bool&>(m_isSorted
) = true;
167 if (nodeCount
> traversalSortCutoff
) {
172 bool containsAttributeNodes
= false;
174 WillBeHeapVector
<NodeSetVector
> parentMatrix(nodeCount
);
175 for (unsigned i
= 0; i
< nodeCount
; ++i
) {
176 NodeSetVector
& parentsVector
= parentMatrix
[i
];
177 Node
* n
= m_nodes
[i
].get();
178 parentsVector
.append(n
);
179 if (n
->isAttributeNode()) {
180 n
= toAttr(n
)->ownerElement();
181 parentsVector
.append(n
);
182 containsAttributeNodes
= true;
184 for (n
= n
->parentNode(); n
; n
= n
->parentNode())
185 parentsVector
.append(n
);
187 sortBlock(0, nodeCount
, parentMatrix
, containsAttributeNodes
);
189 // It is not possible to just assign the result to m_nodes, because some
190 // nodes may get dereferenced and destroyed.
191 WillBeHeapVector
<RefPtrWillBeMember
<Node
>> sortedNodes
;
192 sortedNodes
.reserveInitialCapacity(nodeCount
);
193 for (unsigned i
= 0; i
< nodeCount
; ++i
)
194 sortedNodes
.append(parentMatrix
[i
][0]);
196 const_cast<WillBeHeapVector
<RefPtrWillBeMember
<Node
>>&>(m_nodes
).swap(sortedNodes
);
199 static Node
* findRootNode(Node
* node
)
201 if (node
->isAttributeNode())
202 node
= toAttr(node
)->ownerElement();
203 if (node
->inDocument()) {
204 node
= &node
->document();
206 while (Node
* parent
= node
->parentNode())
212 void NodeSet::traversalSort() const
214 WillBeHeapHashSet
<RawPtrWillBeMember
<Node
>> nodes
;
215 bool containsAttributeNodes
= false;
217 unsigned nodeCount
= m_nodes
.size();
218 ASSERT(nodeCount
> 1);
219 for (unsigned i
= 0; i
< nodeCount
; ++i
) {
220 Node
* node
= m_nodes
[i
].get();
222 if (node
->isAttributeNode())
223 containsAttributeNodes
= true;
226 WillBeHeapVector
<RefPtrWillBeMember
<Node
>> sortedNodes
;
227 sortedNodes
.reserveInitialCapacity(nodeCount
);
229 for (Node
& n
: NodeTraversal::startsAt(findRootNode(m_nodes
.first().get()))) {
230 if (nodes
.contains(&n
))
231 sortedNodes
.append(&n
);
233 if (!containsAttributeNodes
|| !n
.isElementNode())
236 Element
* element
= toElement(&n
);
237 AttributeCollection attributes
= element
->attributes();
238 for (auto& attribute
: attributes
) {
239 RefPtrWillBeRawPtr
<Attr
> attr
= element
->attrIfExists(attribute
.name());
240 if (attr
&& nodes
.contains(attr
.get()))
241 sortedNodes
.append(attr
);
245 ASSERT(sortedNodes
.size() == nodeCount
);
246 const_cast<WillBeHeapVector
<RefPtrWillBeMember
<Node
>>&>(m_nodes
).swap(sortedNodes
);
249 void NodeSet::reverse()
251 if (m_nodes
.isEmpty())
255 unsigned to
= m_nodes
.size() - 1;
257 m_nodes
[from
].swap(m_nodes
[to
]);
263 Node
* NodeSet::firstNode() const
268 // FIXME: fully sorting the node-set just to find its first node is
271 return m_nodes
.at(0).get();
274 Node
* NodeSet::anyNode() const
279 return m_nodes
.at(0).get();