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