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 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 #if ENABLE(XSLT) 33 34 #include "PlatformString.h" 35 #include <libxslt/templates.h> 36 #include <libxslt/xsltutils.h> 37 #include <wtf/unicode/Collator.h> 38 39 #if PLATFORM(MAC) 40 #include "SoftLinking.h" 41 #endif 42 43 #if PLATFORM(MAC) 44 45 SOFT_LINK_LIBRARY(libxslt) 46 SOFT_LINK(libxslt, xsltComputeSortResult, xmlXPathObjectPtr*, (xsltTransformContextPtr ctxt, xmlNodePtr sort), (ctxt, sort)) 47 SOFT_LINK(libxslt, xsltEvalAttrValueTemplate, xmlChar*, (xsltTransformContextPtr ctxt, xmlNodePtr node, const xmlChar *name, const xmlChar *ns), (ctxt, node, name, ns)) 48 49 static void xsltTransformErrorTrampoline(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char* message, ...) WTF_ATTRIBUTE_PRINTF(4, 5); 50 51 void xsltTransformErrorTrampoline(xsltTransformContextPtr context, xsltStylesheetPtr style, xmlNodePtr node, const char* message, ...) 52 { 53 va_list args; 54 va_start(args, message); 55 char* messageWithArgs; 56 vasprintf(&messageWithArgs, message, args); 57 va_end(args); 58 59 static void (*xsltTransformErrorPointer)(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char*, ...) WTF_ATTRIBUTE_PRINTF(4, 5) 60 = reinterpret_cast<void (*)(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char*, ...)>(dlsym(libxsltLibrary(), "xsltTransformError")); 61 xsltTransformErrorPointer(context, style, node, "%s", messageWithArgs); 62 63 free(messageWithArgs); 64 } 65 66 #define xsltTransformError xsltTransformErrorTrampoline 67 68 #endif 69 70 namespace WebCore { 71 72 // Based on default implementation from libxslt 1.1.22 and xsltICUSort.c example. 73 void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, int nbsorts) 74 { 75 #ifdef XSLT_REFACTORED 76 xsltStyleItemSortPtr comp; 77 #else 78 xsltStylePreCompPtr comp; 79 #endif 80 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; 81 xmlXPathObjectPtr *results = NULL, *res; 82 xmlNodeSetPtr list = NULL; 83 int descending, number, desc, numb; 84 int len = 0; 85 int i, j, incr; 86 int tst; 87 int depth; 88 xmlNodePtr node; 89 xmlXPathObjectPtr tmp; 90 int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT]; 91 92 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || 93 (nbsorts >= XSLT_MAX_SORT)) 94 return; 95 if (sorts[0] == NULL) 96 return; 97 comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi); 98 if (comp == NULL) 99 return; 100 101 list = ctxt->nodeList; 102 if ((list == NULL) || (list->nodeNr <= 1)) 103 return; /* nothing to do */ 104 105 for (j = 0; j < nbsorts; j++) { 106 comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi); 107 tempstype[j] = 0; 108 if ((comp->stype == NULL) && (comp->has_stype != 0)) { 109 comp->stype = 110 xsltEvalAttrValueTemplate(ctxt, sorts[j], 111 (const xmlChar *) "data-type", 112 XSLT_NAMESPACE); 113 if (comp->stype != NULL) { 114 tempstype[j] = 1; 115 if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) 116 comp->number = 0; 117 else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) 118 comp->number = 1; 119 else { 120 xsltTransformError(ctxt, NULL, sorts[j], 121 "xsltDoSortFunction: no support for data-type = %s\n", 122 comp->stype); 123 comp->number = 0; /* use default */ 124 } 125 } 126 } 127 temporder[j] = 0; 128 if ((comp->order == NULL) && (comp->has_order != 0)) { 129 comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], 130 (const xmlChar *) "order", 131 XSLT_NAMESPACE); 132 if (comp->order != NULL) { 133 temporder[j] = 1; 134 if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) 135 comp->descending = 0; 136 else if (xmlStrEqual(comp->order, 137 (const xmlChar *) "descending")) 138 comp->descending = 1; 139 else { 140 xsltTransformError(ctxt, NULL, sorts[j], 141 "xsltDoSortFunction: invalid value %s for order\n", 142 comp->order); 143 comp->descending = 0; /* use default */ 144 } 145 } 146 } 147 } 148 149 len = list->nodeNr; 150 151 resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); 152 for (i = 1;i < XSLT_MAX_SORT;i++) 153 resultsTab[i] = NULL; 154 155 results = resultsTab[0]; 156 157 comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi); 158 descending = comp->descending; 159 number = comp->number; 160 if (results == NULL) 161 return; 162 163 // We are passing a language identifier to a function that expects a locale identifier. 164 // The implementation of Collator should be lenient, and accept both "en-US" and "en_US", for example. 165 // This lets an author to really specify sorting rules, e.g. "de_DE@collation=phonebook", which isn't 166 // possible with language alone. 167 Collator collator(comp->has_lang ? (const char*)comp->lang : "en"); 168 collator.setOrderLowerFirst(comp->lower_first); 169 170 /* Shell's sort of node-set */ 171 for (incr = len / 2; incr > 0; incr /= 2) { 172 for (i = incr; i < len; i++) { 173 j = i - incr; 174 if (results[i] == NULL) 175 continue; 176 177 while (j >= 0) { 178 if (results[j] == NULL) 179 tst = 1; 180 else { 181 if (number) { 182 /* We make NaN smaller than number in accordance 183 with XSLT spec */ 184 if (xmlXPathIsNaN(results[j]->floatval)) { 185 if (xmlXPathIsNaN(results[j + incr]->floatval)) 186 tst = 0; 187 else 188 tst = -1; 189 } else if (xmlXPathIsNaN(results[j + incr]->floatval)) 190 tst = 1; 191 else if (results[j]->floatval == 192 results[j + incr]->floatval) 193 tst = 0; 194 else if (results[j]->floatval > 195 results[j + incr]->floatval) 196 tst = 1; 197 else tst = -1; 198 } else { 199 String str1 = String::fromUTF8((const char*)results[j]->stringval); 200 String str2 = String::fromUTF8((const char*)results[j + incr]->stringval); 201 tst = collator.collate(str1.characters(), str1.length(), str2.characters(), str2.length()); 202 } 203 if (descending) 204 tst = -tst; 205 } 206 if (tst == 0) { 207 /* 208 * Okay we need to use multi level sorts 209 */ 210 depth = 1; 211 while (depth < nbsorts) { 212 if (sorts[depth] == NULL) 213 break; 214 comp = static_cast<xsltStylePreComp*>(sorts[depth]->psvi); 215 if (comp == NULL) 216 break; 217 desc = comp->descending; 218 numb = comp->number; 219 220 /* 221 * Compute the result of the next level for the 222 * full set, this might be optimized ... or not 223 */ 224 if (resultsTab[depth] == NULL) 225 resultsTab[depth] = xsltComputeSortResult(ctxt, 226 sorts[depth]); 227 res = resultsTab[depth]; 228 if (res == NULL) 229 break; 230 if (res[j] == NULL) { 231 if (res[j+incr] != NULL) 232 tst = 1; 233 } else { 234 if (numb) { 235 /* We make NaN smaller than number in 236 accordance with XSLT spec */ 237 if (xmlXPathIsNaN(res[j]->floatval)) { 238 if (xmlXPathIsNaN(res[j + 239 incr]->floatval)) 240 tst = 0; 241 else 242 tst = -1; 243 } else if (xmlXPathIsNaN(res[j + incr]-> 244 floatval)) 245 tst = 1; 246 else if (res[j]->floatval == res[j + incr]-> 247 floatval) 248 tst = 0; 249 else if (res[j]->floatval > 250 res[j + incr]->floatval) 251 tst = 1; 252 else tst = -1; 253 } else { 254 String str1 = String::fromUTF8((const char*)res[j]->stringval); 255 String str2 = String::fromUTF8((const char*)res[j + incr]->stringval); 256 tst = collator.collate(str1.characters(), str1.length(), str2.characters(), str2.length()); 257 } 258 if (desc) 259 tst = -tst; 260 } 261 262 /* 263 * if we still can't differenciate at this level 264 * try one level deeper. 265 */ 266 if (tst != 0) 267 break; 268 depth++; 269 } 270 } 271 if (tst == 0) { 272 tst = results[j]->index > results[j + incr]->index; 273 } 274 if (tst > 0) { 275 tmp = results[j]; 276 results[j] = results[j + incr]; 277 results[j + incr] = tmp; 278 node = list->nodeTab[j]; 279 list->nodeTab[j] = list->nodeTab[j + incr]; 280 list->nodeTab[j + incr] = node; 281 depth = 1; 282 while (depth < nbsorts) { 283 if (sorts[depth] == NULL) 284 break; 285 if (resultsTab[depth] == NULL) 286 break; 287 res = resultsTab[depth]; 288 tmp = res[j]; 289 res[j] = res[j + incr]; 290 res[j + incr] = tmp; 291 depth++; 292 } 293 j -= incr; 294 } else 295 break; 296 } 297 } 298 } 299 300 for (j = 0; j < nbsorts; j++) { 301 comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi); 302 if (tempstype[j] == 1) { 303 /* The data-type needs to be recomputed each time */ 304 xmlFree((void *)(comp->stype)); 305 comp->stype = NULL; 306 } 307 if (temporder[j] == 1) { 308 /* The order needs to be recomputed each time */ 309 xmlFree((void *)(comp->order)); 310 comp->order = NULL; 311 } 312 if (resultsTab[j] != NULL) { 313 for (i = 0;i < len;i++) 314 xmlXPathFreeObject(resultsTab[j][i]); 315 xmlFree(resultsTab[j]); 316 } 317 } 318 } 319 320 } 321 322 #endif 323