1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is TransforMiiX XSLT processor code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Jonas Sicking.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jonas Sicking <sicking@bigfoot.com>
24 : * Peter Van der Beken <peterv@propagandism.org>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "txNodeSorter.h"
41 : #include "txExecutionState.h"
42 : #include "txXPathResultComparator.h"
43 : #include "nsGkAtoms.h"
44 : #include "txNodeSetContext.h"
45 : #include "txExpr.h"
46 : #include "txStringUtils.h"
47 : #include "prmem.h"
48 : #include "nsQuickSort.h"
49 :
50 : /*
51 : * Sorts Nodes as specified by the W3C XSLT 1.0 Recommendation
52 : */
53 :
54 0 : txNodeSorter::txNodeSorter() : mNKeys(0)
55 : {
56 0 : }
57 :
58 0 : txNodeSorter::~txNodeSorter()
59 : {
60 0 : txListIterator iter(&mSortKeys);
61 0 : while (iter.hasNext()) {
62 0 : SortKey* key = (SortKey*)iter.next();
63 0 : delete key->mComparator;
64 : delete key;
65 : }
66 0 : }
67 :
68 : nsresult
69 0 : txNodeSorter::addSortElement(Expr* aSelectExpr, Expr* aLangExpr,
70 : Expr* aDataTypeExpr, Expr* aOrderExpr,
71 : Expr* aCaseOrderExpr, txIEvalContext* aContext)
72 : {
73 0 : nsAutoPtr<SortKey> key(new SortKey);
74 0 : NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
75 0 : nsresult rv = NS_OK;
76 :
77 : // Select
78 0 : key->mExpr = aSelectExpr;
79 :
80 : // Order
81 0 : bool ascending = true;
82 0 : if (aOrderExpr) {
83 0 : nsAutoString attrValue;
84 0 : rv = aOrderExpr->evaluateToString(aContext, attrValue);
85 0 : NS_ENSURE_SUCCESS(rv, rv);
86 :
87 0 : if (TX_StringEqualsAtom(attrValue, nsGkAtoms::descending)) {
88 0 : ascending = false;
89 : }
90 0 : else if (!TX_StringEqualsAtom(attrValue, nsGkAtoms::ascending)) {
91 : // XXX ErrorReport: unknown value for order attribute
92 0 : return NS_ERROR_XSLT_BAD_VALUE;
93 : }
94 : }
95 :
96 :
97 : // Create comparator depending on datatype
98 0 : nsAutoString dataType;
99 0 : if (aDataTypeExpr) {
100 0 : rv = aDataTypeExpr->evaluateToString(aContext, dataType);
101 0 : NS_ENSURE_SUCCESS(rv, rv);
102 : }
103 :
104 0 : if (!aDataTypeExpr || TX_StringEqualsAtom(dataType, nsGkAtoms::text)) {
105 : // Text comparator
106 :
107 : // Language
108 0 : nsAutoString lang;
109 0 : if (aLangExpr) {
110 0 : rv = aLangExpr->evaluateToString(aContext, lang);
111 0 : NS_ENSURE_SUCCESS(rv, rv);
112 : }
113 :
114 : // Case-order
115 0 : bool upperFirst = false;
116 0 : if (aCaseOrderExpr) {
117 0 : nsAutoString attrValue;
118 :
119 0 : rv = aCaseOrderExpr->evaluateToString(aContext, attrValue);
120 0 : NS_ENSURE_SUCCESS(rv, rv);
121 :
122 0 : if (TX_StringEqualsAtom(attrValue, nsGkAtoms::upperFirst)) {
123 0 : upperFirst = true;
124 : }
125 0 : else if (!TX_StringEqualsAtom(attrValue,
126 0 : nsGkAtoms::lowerFirst)) {
127 : // XXX ErrorReport: unknown value for case-order attribute
128 0 : return NS_ERROR_XSLT_BAD_VALUE;
129 : }
130 : }
131 :
132 0 : key->mComparator = new txResultStringComparator(ascending,
133 : upperFirst,
134 0 : lang);
135 0 : NS_ENSURE_TRUE(key->mComparator, NS_ERROR_OUT_OF_MEMORY);
136 : }
137 0 : else if (TX_StringEqualsAtom(dataType, nsGkAtoms::number)) {
138 : // Number comparator
139 0 : key->mComparator = new txResultNumberComparator(ascending);
140 0 : NS_ENSURE_TRUE(key->mComparator, NS_ERROR_OUT_OF_MEMORY);
141 : }
142 : else {
143 : // XXX ErrorReport: unknown data-type
144 0 : return NS_ERROR_XSLT_BAD_VALUE;
145 : }
146 :
147 : // mSortKeys owns key now.
148 0 : rv = mSortKeys.add(key);
149 0 : NS_ENSURE_SUCCESS(rv, rv);
150 :
151 0 : key.forget();
152 0 : mNKeys++;
153 :
154 0 : return NS_OK;
155 : }
156 :
157 : nsresult
158 0 : txNodeSorter::sortNodeSet(txNodeSet* aNodes, txExecutionState* aEs,
159 : txNodeSet** aResult)
160 : {
161 0 : if (mNKeys == 0 || aNodes->isEmpty()) {
162 0 : NS_ADDREF(*aResult = aNodes);
163 :
164 0 : return NS_OK;
165 : }
166 :
167 0 : *aResult = nsnull;
168 :
169 0 : nsRefPtr<txNodeSet> sortedNodes;
170 0 : nsresult rv = aEs->recycler()->getNodeSet(getter_AddRefs(sortedNodes));
171 0 : NS_ENSURE_SUCCESS(rv, rv);
172 :
173 0 : txNodeSetContext* evalContext = new txNodeSetContext(aNodes, aEs);
174 0 : NS_ENSURE_TRUE(evalContext, NS_ERROR_OUT_OF_MEMORY);
175 :
176 0 : rv = aEs->pushEvalContext(evalContext);
177 0 : NS_ENSURE_SUCCESS(rv, rv);
178 :
179 : // Create and set up memoryblock for sort-values and indexarray
180 0 : PRUint32 len = static_cast<PRUint32>(aNodes->size());
181 :
182 : // Limit resource use to something sane.
183 0 : PRUint32 itemSize = sizeof(PRUint32) + mNKeys * sizeof(txObject*);
184 0 : if (mNKeys > (PR_UINT32_MAX - sizeof(PRUint32)) / sizeof(txObject*) ||
185 : len >= PR_UINT32_MAX / itemSize) {
186 0 : return NS_ERROR_OUT_OF_MEMORY;
187 : }
188 :
189 0 : void* mem = PR_Malloc(len * itemSize);
190 0 : NS_ENSURE_TRUE(mem, NS_ERROR_OUT_OF_MEMORY);
191 :
192 0 : PRUint32* indexes = static_cast<PRUint32*>(mem);
193 0 : txObject** sortValues = reinterpret_cast<txObject**>(indexes + len);
194 :
195 : PRUint32 i;
196 0 : for (i = 0; i < len; ++i) {
197 0 : indexes[i] = i;
198 : }
199 0 : memset(sortValues, 0, len * mNKeys * sizeof(txObject*));
200 :
201 : // Sort the indexarray
202 : SortData sortData;
203 0 : sortData.mNodeSorter = this;
204 0 : sortData.mContext = evalContext;
205 0 : sortData.mSortValues = sortValues;
206 0 : sortData.mRv = NS_OK;
207 0 : NS_QuickSort(indexes, len, sizeof(PRUint32), compareNodes, &sortData);
208 :
209 : // Delete these here so we don't have to deal with them at every possible
210 : // failurepoint
211 0 : PRUint32 numSortValues = len * mNKeys;
212 0 : for (i = 0; i < numSortValues; ++i) {
213 0 : delete sortValues[i];
214 : }
215 :
216 0 : if (NS_FAILED(sortData.mRv)) {
217 0 : PR_Free(mem);
218 : // The txExecutionState owns the evalcontext so no need to handle it
219 0 : return sortData.mRv;
220 : }
221 :
222 : // Insert nodes in sorted order in new nodeset
223 0 : for (i = 0; i < len; ++i) {
224 0 : rv = sortedNodes->append(aNodes->get(indexes[i]));
225 0 : if (NS_FAILED(rv)) {
226 0 : PR_Free(mem);
227 : // The txExecutionState owns the evalcontext so no need to handle it
228 0 : return rv;
229 : }
230 : }
231 :
232 0 : PR_Free(mem);
233 0 : delete aEs->popEvalContext();
234 :
235 0 : NS_ADDREF(*aResult = sortedNodes);
236 :
237 0 : return NS_OK;
238 : }
239 :
240 : // static
241 : int
242 0 : txNodeSorter::compareNodes(const void* aIndexA, const void* aIndexB,
243 : void* aSortData)
244 : {
245 0 : SortData* sortData = static_cast<SortData*>(aSortData);
246 0 : NS_ENSURE_SUCCESS(sortData->mRv, -1);
247 :
248 0 : txListIterator iter(&sortData->mNodeSorter->mSortKeys);
249 0 : PRUint32 indexA = *static_cast<const PRUint32*>(aIndexA);
250 0 : PRUint32 indexB = *static_cast<const PRUint32*>(aIndexB);
251 : txObject** sortValuesA = sortData->mSortValues +
252 0 : indexA * sortData->mNodeSorter->mNKeys;
253 : txObject** sortValuesB = sortData->mSortValues +
254 0 : indexB * sortData->mNodeSorter->mNKeys;
255 :
256 : unsigned int i;
257 : // Step through each key until a difference is found
258 0 : for (i = 0; i < sortData->mNodeSorter->mNKeys; ++i) {
259 0 : SortKey* key = (SortKey*)iter.next();
260 : // Lazy create sort values
261 0 : if (!sortValuesA[i] &&
262 0 : !calcSortValue(sortValuesA[i], key, sortData, indexA)) {
263 0 : return -1;
264 : }
265 0 : if (!sortValuesB[i] &&
266 0 : !calcSortValue(sortValuesB[i], key, sortData, indexB)) {
267 0 : return -1;
268 : }
269 :
270 : // Compare node values
271 0 : int compRes = key->mComparator->compareValues(sortValuesA[i],
272 0 : sortValuesB[i]);
273 0 : if (compRes != 0)
274 0 : return compRes;
275 : }
276 : // All keys have the same value for these nodes
277 :
278 0 : return indexA - indexB;
279 : }
280 :
281 : //static
282 : bool
283 0 : txNodeSorter::calcSortValue(txObject*& aSortValue, SortKey* aKey,
284 : SortData* aSortData, PRUint32 aNodeIndex)
285 : {
286 0 : aSortData->mContext->setPosition(aNodeIndex + 1); // position is 1-based
287 :
288 : nsresult rv = aKey->mComparator->createSortableValue(aKey->mExpr,
289 : aSortData->mContext,
290 0 : aSortValue);
291 0 : if (NS_FAILED(rv)) {
292 0 : aSortData->mRv = rv;
293 0 : return false;
294 : }
295 :
296 0 : return true;
297 : }
|