GCC 4.2 build fix.
[WebKit-https.git] / WebCore / xml / XSLTUnicodeSort.cpp
1 /*
2  * Copyright (C) 2007 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 Computer, 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 // FIXME: <rdar://5611712> Remove the PLATFORM(WIN) check once ICU on Windows has collation support.
33 #if ENABLE(XSLT) && USE(ICU_UNICODE) && !PLATFORM(WIN)
34
35 #include <libxslt/templates.h>
36 #include <libxslt/xsltutils.h>
37 #include <unicode/ucnv.h>
38 #include <unicode/ucol.h>
39 #include <unicode/ustring.h>
40
41 #if PLATFORM(MAC)
42 #include "SoftLinking.h"
43 #endif
44
45 #if PLATFORM(MAC)
46 SOFT_LINK_LIBRARY(libxslt)
47 SOFT_LINK(libxslt, xsltComputeSortResult, xmlXPathObjectPtr*, (xsltTransformContextPtr ctxt, xmlNodePtr sort), (ctxt, sort))
48 SOFT_LINK(libxslt, xsltEvalAttrValueTemplate, xmlChar*, (xsltTransformContextPtr ctxt, xmlNodePtr node, const xmlChar *name, const xmlChar *ns), (ctxt, node, name, ns))
49
50 static void init_xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char *, ...) WTF_ATTRIBUTE_PRINTF(4, 5);
51 static void (*softLink_xsltTransformError)(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char *, ...) WTF_ATTRIBUTE_PRINTF(4, 5) = init_xsltTransformError;
52
53 static void init_xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char* msg, ...)
54 {
55     softLink_xsltTransformError = (void (*) (xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char *, ...))dlsym(libxsltLibrary(), "xsltTransformError");
56     ASSERT(softLink_xsltTransformError);
57
58     va_list args;
59     va_start(args, msg);
60 #if PLATFORM(WIN_OS)
61     char str[1024];
62     vsnprintf(str, sizeof(str) - 1, msg, args);
63 #else
64     char* str;
65     vasprintf(&str, msg, args);
66 #endif
67     va_end(args);
68
69     softLink_xsltTransformError(ctxt, style, node, "%s", str);
70
71 #if !PLATFORM(WIN_OS)
72     free(str);
73 #endif
74 }
75
76 inline void xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char* msg, ...) WTF_ATTRIBUTE_PRINTF(4, 5);
77
78 inline void xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char* msg, ...)
79 {
80     va_list args;
81     va_start(args, msg);
82 #if PLATFORM(WIN_OS)
83     char str[1024];
84     vsnprintf(str, sizeof(str) - 1, msg, args);
85 #else
86     char* str;
87     vasprintf(&str, msg, args);
88 #endif
89     va_end(args);
90
91     softLink_xsltTransformError(ctxt, style, node, "%s", str);
92
93 #if !PLATFORM(WIN_OS)
94     free(str);
95 #endif
96 }
97
98 #endif
99
100 namespace WebCore {
101
102 // Based on default implementation from libxslt 1.1.22 and xsltICUSort.c example.
103 void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, int nbsorts)
104 {
105 #ifdef XSLT_REFACTORED
106     xsltStyleItemSortPtr comp;
107 #else
108     xsltStylePreCompPtr comp;
109 #endif
110     xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
111     xmlXPathObjectPtr *results = NULL, *res;
112     xmlNodeSetPtr list = NULL;
113     int descending, number, desc, numb;
114     int len = 0;
115     int i, j, incr;
116     int tst;
117     int depth;
118     xmlNodePtr node;
119     xmlXPathObjectPtr tmp;    
120     int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT];
121
122     /* Start ICU change */
123     UCollator *coll = 0;
124     UConverter *conv;
125     UErrorCode status;
126     UChar *target,*target2;
127     int targetlen, target2len;
128     /* End ICU change */
129
130     if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
131         (nbsorts >= XSLT_MAX_SORT))
132         return;
133     if (sorts[0] == NULL)
134         return;
135     comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi);
136     if (comp == NULL)
137         return;
138
139     list = ctxt->nodeList;
140     if ((list == NULL) || (list->nodeNr <= 1))
141         return; /* nothing to do */
142
143     for (j = 0; j < nbsorts; j++) {
144         comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi);
145         tempstype[j] = 0;
146         if ((comp->stype == NULL) && (comp->has_stype != 0)) {
147             comp->stype =
148                 xsltEvalAttrValueTemplate(ctxt, sorts[j],
149                                           (const xmlChar *) "data-type",
150                                           XSLT_NAMESPACE);
151             if (comp->stype != NULL) {
152                 tempstype[j] = 1;
153                 if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
154                     comp->number = 0;
155                 else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
156                     comp->number = 1;
157                 else {
158                     xsltTransformError(ctxt, NULL, sorts[j],
159                           "xsltDoSortFunction: no support for data-type = %s\n",
160                                      comp->stype);
161                     comp->number = 0; /* use default */
162                 }
163             }
164         }
165         temporder[j] = 0;
166         if ((comp->order == NULL) && (comp->has_order != 0)) {
167             comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
168                                                     (const xmlChar *) "order",
169                                                     XSLT_NAMESPACE);
170             if (comp->order != NULL) {
171                 temporder[j] = 1;
172                 if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
173                     comp->descending = 0;
174                 else if (xmlStrEqual(comp->order,
175                                      (const xmlChar *) "descending"))
176                     comp->descending = 1;
177                 else {
178                     xsltTransformError(ctxt, NULL, sorts[j],
179                              "xsltDoSortFunction: invalid value %s for order\n",
180                                      comp->order);
181                     comp->descending = 0; /* use default */
182                 }
183             }
184         }
185     }
186
187     len = list->nodeNr;
188
189     resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
190     for (i = 1;i < XSLT_MAX_SORT;i++)
191         resultsTab[i] = NULL;
192
193     results = resultsTab[0];
194
195     comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi);
196     descending = comp->descending;
197     number = comp->number;
198     if (results == NULL)
199         return;
200
201     /* Start ICU change */
202     status = U_ZERO_ERROR;
203     conv = ucnv_open("UTF8", &status);
204     if (U_FAILURE(status))
205         xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error opening converter\n");
206
207     if (comp->has_lang) 
208         coll = ucol_open((const char*)comp->lang, &status);
209     if (U_FAILURE(status) || !comp->has_lang) {
210         status = U_ZERO_ERROR;
211         coll = ucol_open("en", &status);
212     }
213     if (U_FAILURE(status))
214         xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error opening collator\n");
215
216     if (comp->lower_first) 
217         ucol_setAttribute(coll,UCOL_CASE_FIRST,UCOL_LOWER_FIRST,&status);
218     else 
219         ucol_setAttribute(coll,UCOL_CASE_FIRST,UCOL_UPPER_FIRST,&status);
220     if (U_FAILURE(status))
221         xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error setting collator attribute\n");
222     /* End ICU change */
223
224     /* Shell's sort of node-set */
225     for (incr = len / 2; incr > 0; incr /= 2) {
226         for (i = incr; i < len; i++) {
227             j = i - incr;
228             if (results[i] == NULL)
229                 continue;
230             
231             while (j >= 0) {
232                 if (results[j] == NULL)
233                     tst = 1;
234                 else {
235                     if (number) {
236                         /* We make NaN smaller than number in accordance
237                            with XSLT spec */
238                         if (xmlXPathIsNaN(results[j]->floatval)) {
239                             if (xmlXPathIsNaN(results[j + incr]->floatval))
240                                 tst = 0;
241                             else
242                                 tst = -1;
243                         } else if (xmlXPathIsNaN(results[j + incr]->floatval))
244                             tst = 1;
245                         else if (results[j]->floatval ==
246                                 results[j + incr]->floatval)
247                             tst = 0;
248                         else if (results[j]->floatval > 
249                                 results[j + incr]->floatval)
250                             tst = 1;
251                         else tst = -1;
252                     } else {
253                         /* Start ICU change */
254                         targetlen = xmlStrlen(results[j]->stringval) * 2;
255                         target2len = xmlStrlen(results[j + incr]->stringval) * 2;
256                         target = (UChar*)xmlMalloc(targetlen * sizeof(UChar));
257                         target2 = (UChar*)xmlMalloc(target2len * sizeof(UChar));
258                         targetlen = ucnv_toUChars(conv, target, targetlen, (const char*)results[j]->stringval, -1, &status);
259                         target2len = ucnv_toUChars(conv, target2, target2len, (const char*)results[j+incr]->stringval, -1, &status);
260                         tst = ucol_strcoll(coll, target, u_strlen(target), target2, u_strlen(target2));
261                         xmlFree(target);
262                         xmlFree(target2);
263                         /* End ICU change */
264                     }
265                     if (descending)
266                         tst = -tst;
267                 }
268                 if (tst == 0) {
269                     /*
270                      * Okay we need to use multi level sorts
271                      */
272                     depth = 1;
273                     while (depth < nbsorts) {
274                         if (sorts[depth] == NULL)
275                             break;
276                         comp = static_cast<xsltStylePreComp*>(sorts[depth]->psvi);
277                         if (comp == NULL)
278                             break;
279                         desc = comp->descending;
280                         numb = comp->number;
281
282                         /*
283                          * Compute the result of the next level for the
284                          * full set, this might be optimized ... or not
285                          */
286                         if (resultsTab[depth] == NULL) 
287                             resultsTab[depth] = xsltComputeSortResult(ctxt,
288                                                         sorts[depth]);
289                         res = resultsTab[depth];
290                         if (res == NULL) 
291                             break;
292                         if (res[j] == NULL) {
293                             if (res[j+incr] != NULL)
294                                 tst = 1;
295                         } else {
296                             if (numb) {
297                                 /* We make NaN smaller than number in
298                                    accordance with XSLT spec */
299                                 if (xmlXPathIsNaN(res[j]->floatval)) {
300                                     if (xmlXPathIsNaN(res[j +
301                                                     incr]->floatval))
302                                         tst = 0;
303                                     else
304                                         tst = -1;
305                                 } else if (xmlXPathIsNaN(res[j + incr]->
306                                                 floatval))
307                                     tst = 1;
308                                 else if (res[j]->floatval == res[j + incr]->
309                                                 floatval)
310                                     tst = 0;
311                                 else if (res[j]->floatval > 
312                                         res[j + incr]->floatval)
313                                     tst = 1;
314                                 else tst = -1;
315                             } else {
316                                 /* Start ICU change */
317                                 targetlen = xmlStrlen(res[j]->stringval) * 2;
318                                 target2len = xmlStrlen(res[j + incr]->stringval) * 2;
319                                 target = (UChar*)xmlMalloc(targetlen * sizeof(UChar));
320                                 target2 = (UChar*)xmlMalloc(target2len * sizeof(UChar));
321                                 targetlen = ucnv_toUChars(conv, target, targetlen, (const char*)res[j]->stringval, -1, &status);
322                                 target2len = ucnv_toUChars(conv, target2, target2len, (const char*)res[j+incr]->stringval, -1, &status);
323                                 tst = ucol_strcoll(coll, target, u_strlen(target), target2, u_strlen(target2));
324                                 xmlFree(target);
325                                 xmlFree(target2);
326                                 /* End ICU change */
327                             }
328                             if (desc)
329                                 tst = -tst;
330                         }
331
332                         /*
333                          * if we still can't differenciate at this level
334                          * try one level deeper.
335                          */
336                         if (tst != 0)
337                             break;
338                         depth++;
339                     }
340                 }
341                 if (tst == 0) {
342                     tst = results[j]->index > results[j + incr]->index;
343                 }
344                 if (tst > 0) {
345                     tmp = results[j];
346                     results[j] = results[j + incr];
347                     results[j + incr] = tmp;
348                     node = list->nodeTab[j];
349                     list->nodeTab[j] = list->nodeTab[j + incr];
350                     list->nodeTab[j + incr] = node;
351                     depth = 1;
352                     while (depth < nbsorts) {
353                         if (sorts[depth] == NULL)
354                             break;
355                         if (resultsTab[depth] == NULL)
356                             break;
357                         res = resultsTab[depth];
358                         tmp = res[j];
359                         res[j] = res[j + incr];
360                         res[j + incr] = tmp;
361                         depth++;
362                     }
363                     j -= incr;
364                 } else
365                     break;
366             }
367         }
368     }
369
370     /* Start ICU change */
371     ucol_close(coll);
372     ucnv_close(conv);
373     /* End ICU change */
374
375     for (j = 0; j < nbsorts; j++) {
376         comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi);
377         if (tempstype[j] == 1) {
378             /* The data-type needs to be recomputed each time */
379             xmlFree((void *)(comp->stype));
380             comp->stype = NULL;
381         }
382         if (temporder[j] == 1) {
383             /* The order needs to be recomputed each time */
384             xmlFree((void *)(comp->order));
385             comp->order = NULL;
386         }
387         if (resultsTab[j] != NULL) {
388             for (i = 0;i < len;i++)
389                 xmlXPathFreeObject(resultsTab[j][i]);
390             xmlFree(resultsTab[j]);
391         }
392     }
393 }
394
395 }
396
397 #endif