Modernize some aspects of text codecs, eliminate WebKit use of strcasecmp
[WebKit-https.git] / Source / WebCore / xml / XSLTUnicodeSort.cpp
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
42 static void xsltTransformErrorTrampoline(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char* message, ...) WTF_ATTRIBUTE_PRINTF(4, 5);
43
44 void 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
67 namespace WebCore {
68
69 // Based on default implementation from libxslt 1.1.22 and xsltICUSort.c example.
70 void 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