1/*
2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
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.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "XSLTUnicodeSort.h"
31
32#if ENABLE(XSLT)
33
34#include <libxslt/templates.h>
35#include <libxslt/xsltutils.h>
36#include <wtf/Vector.h>
37#include <wtf/unicode/Collator.h>
38
39#if OS(DARWIN) && !PLATFORM(GTK)
40#include "SoftLinkLibxslt.h"
41
42static void xsltTransformErrorTrampoline(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char* message, ...) WTF_ATTRIBUTE_PRINTF(4, 5);
43
44void xsltTransformErrorTrampoline(xsltTransformContextPtr context, xsltStylesheetPtr style, xmlNodePtr node, const char* message, ...)
45{
46 va_list args;
47 va_start(args, message);
48
49 va_list preflightArgs;
50 va_copy(preflightArgs, args);
51 size_t stringLength = vsnprintf(nullptr, 0, message, preflightArgs);
52 va_end(preflightArgs);
53
54 Vector<char, 1024> buffer(stringLength + 1);
55 vsnprintf(buffer.data(), stringLength + 1, message, args);
56 va_end(args);
57
58 static void (*xsltTransformErrorPointer)(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char*, ...) WTF_ATTRIBUTE_PRINTF(4, 5)
59 = reinterpret_cast<void (*)(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char*, ...)>(dlsym(WebCore::libxsltLibrary(), "xsltTransformError"));
60 xsltTransformErrorPointer(context, style, node, "%s", buffer.data());
61}
62
63#define xsltTransformError xsltTransformErrorTrampoline
64
65#endif
66
67namespace WebCore {
68
69// Based on default implementation from libxslt 1.1.22 and xsltICUSort.c example.
70void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, int nbsorts)
71{
72#ifdef XSLT_REFACTORED
73 xsltStyleItemSortPtr comp;
74#else
75 xsltStylePreCompPtr comp;
76#endif
77 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
78 xmlXPathObjectPtr *results = NULL, *res;
79 xmlNodeSetPtr list = NULL;
80 int descending, number, desc, numb;
81 int len = 0;
82 int i, j, incr;
83 int tst;
84 int depth;
85 xmlNodePtr node;
86 xmlXPathObjectPtr tmp;
87 int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT];
88
89 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
90 (nbsorts >= XSLT_MAX_SORT))
91 return;
92 if (sorts[0] == NULL)
93 return;
94 comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi);
95 if (comp == NULL)
96 return;
97
98 list = ctxt->nodeList;
99 if ((list == NULL) || (list->nodeNr <= 1))
100 return; /* nothing to do */
101
102 for (j = 0; j < nbsorts; j++) {
103 comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi);
104 tempstype[j] = 0;
105 if ((comp->stype == NULL) && (comp->has_stype != 0)) {
106 comp->stype =
107 xsltEvalAttrValueTemplate(ctxt, sorts[j], (const xmlChar *) "data-type", XSLT_NAMESPACE);
108 if (comp->stype != NULL) {
109 tempstype[j] = 1;
110 if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
111 comp->number = 0;
112 else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
113 comp->number = 1;
114 else {
115 xsltTransformError(ctxt, NULL, sorts[j],
116 "xsltDoSortFunction: no support for data-type = %s\n",
117 comp->stype);
118 comp->number = 0; /* use default */
119 }
120 }
121 }
122 temporder[j] = 0;
123 if ((comp->order == NULL) && (comp->has_order != 0)) {
124 comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], (const xmlChar *) "order", XSLT_NAMESPACE);
125 if (comp->order != NULL) {
126 temporder[j] = 1;
127 if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
128 comp->descending = 0;
129 else if (xmlStrEqual(comp->order,
130 (const xmlChar *) "descending"))
131 comp->descending = 1;
132 else {
133 xsltTransformError(ctxt, NULL, sorts[j],
134 "xsltDoSortFunction: invalid value %s for order\n",
135 comp->order);
136 comp->descending = 0; /* use default */
137 }
138 }
139 }
140 }
141
142 len = list->nodeNr;
143
144 resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
145 for (i = 1;i < XSLT_MAX_SORT;i++)
146 resultsTab[i] = NULL;
147
148 results = resultsTab[0];
149
150 comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi);
151 descending = comp->descending;
152 number = comp->number;
153 if (results == NULL)
154 return;
155
156 // We are passing a language identifier to a function that expects a locale identifier.
157 // The implementation of Collator should be lenient, and accept both "en-US" and "en_US", for example.
158 // This lets an author specify sorting rules, e.g. "de_DE@collation=phonebook", which isn't
159 // possible with language alone.
160 Collator collator(comp->has_lang ? reinterpret_cast<const char*>(comp->lang) : "en", comp->lower_first);
161
162 /* Shell's sort of node-set */
163 for (incr = len / 2; incr > 0; incr /= 2) {
164 for (i = incr; i < len; i++) {
165 j = i - incr;
166 if (results[i] == NULL)
167 continue;
168
169 while (j >= 0) {
170 if (results[j] == NULL)
171 tst = 1;
172 else {
173 if (number) {
174 /* We make NaN smaller than number in accordance
175 with XSLT spec */
176 if (xmlXPathIsNaN(results[j]->floatval)) {
177 if (xmlXPathIsNaN(results[j + incr]->floatval))
178 tst = 0;
179 else
180 tst = -1;
181 } else if (xmlXPathIsNaN(results[j + incr]->floatval))
182 tst = 1;
183 else if (results[j]->floatval ==
184 results[j + incr]->floatval)
185 tst = 0;
186 else if (results[j]->floatval >
187 results[j + incr]->floatval)
188 tst = 1;
189 else tst = -1;
190 } else
191 tst = collator.collateUTF8(reinterpret_cast<const char*>(results[j]->stringval), reinterpret_cast<const char*>(results[j + incr]->stringval));
192 if (descending)
193 tst = -tst;
194 }
195 if (tst == 0) {
196 /*
197 * Okay we need to use multi level sorts
198 */
199 depth = 1;
200 while (depth < nbsorts) {
201 if (sorts[depth] == NULL)
202 break;
203 comp = static_cast<xsltStylePreComp*>(sorts[depth]->psvi);
204 if (comp == NULL)
205 break;
206 desc = comp->descending;
207 numb = comp->number;
208
209 /*
210 * Compute the result of the next level for the
211 * full set, this might be optimized ... or not
212 */
213 if (resultsTab[depth] == NULL)
214 resultsTab[depth] = xsltComputeSortResult(ctxt,
215 sorts[depth]);
216 res = resultsTab[depth];
217 if (res == NULL)
218 break;
219 if (res[j] == NULL) {
220 if (res[j+incr] != NULL)
221 tst = 1;
222 } else {
223 if (numb) {
224 /* We make NaN smaller than number in
225 accordance with XSLT spec */
226 if (xmlXPathIsNaN(res[j]->floatval)) {
227 if (xmlXPathIsNaN(res[j +
228 incr]->floatval))
229 tst = 0;
230 else
231 tst = -1;
232 } else if (xmlXPathIsNaN(res[j + incr]->
233 floatval))
234 tst = 1;
235 else if (res[j]->floatval == res[j + incr]->
236 floatval)
237 tst = 0;
238 else if (res[j]->floatval >
239 res[j + incr]->floatval)
240 tst = 1;
241 else tst = -1;
242 } else
243 tst = collator.collateUTF8(reinterpret_cast<const char*>(res[j]->stringval), reinterpret_cast<const char*>(res[j + incr]->stringval));
244 if (desc)
245 tst = -tst;
246 }
247
248 /*
249 * if we still can't differenciate at this level
250 * try one level deeper.
251 */
252 if (tst != 0)
253 break;
254 depth++;
255 }
256 }
257 if (tst == 0) {
258 tst = results[j]->index > results[j + incr]->index;
259 }
260 if (tst > 0) {
261 tmp = results[j];
262 results[j] = results[j + incr];
263 results[j + incr] = tmp;
264 node = list->nodeTab[j];
265 list->nodeTab[j] = list->nodeTab[j + incr];
266 list->nodeTab[j + incr] = node;
267 depth = 1;
268 while (depth < nbsorts) {
269 if (sorts[depth] == NULL)
270 break;
271 if (resultsTab[depth] == NULL)
272 break;
273 res = resultsTab[depth];
274 tmp = res[j];
275 res[j] = res[j + incr];
276 res[j + incr] = tmp;
277 depth++;
278 }
279 j -= incr;
280 } else
281 break;
282 }
283 }
284 }
285
286 for (j = 0; j < nbsorts; j++) {
287 comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi);
288 if (tempstype[j] == 1) {
289 /* The data-type needs to be recomputed each time */
290 xmlFree((void *)(comp->stype));
291 comp->stype = NULL;
292 }
293 if (temporder[j] == 1) {
294 /* The order needs to be recomputed each time */
295 xmlFree((void *)(comp->order));
296 comp->order = NULL;
297 }
298 if (resultsTab[j] != NULL) {
299 for (i = 0;i < len;i++)
300 xmlXPathFreeObject(resultsTab[j][i]);
301 xmlFree(resultsTab[j]);
302 }
303 }
304}
305
306}
307
308#endif
309