OLD | NEW |
(Empty) | |
| 1 /** |
| 2 * xsltICUSort.c: module provided by Richard Jinks to provide a |
| 3 * sort function replacement using ICU, it is not |
| 4 * included in standard due to the size of the ICU |
| 5 * library |
| 6 * |
| 7 * See http://mail.gnome.org/archives/xslt/2002-November/msg00093.html |
| 8 * http://oss.software.ibm.com/icu/index.html |
| 9 * |
| 10 * Copyright Richard Jinks |
| 11 */ |
| 12 #define IN_LIBXSLT |
| 13 #include "libxslt.h" |
| 14 |
| 15 #include <libxml/parserInternals.h> |
| 16 |
| 17 #include "xslt.h" |
| 18 #include "xsltInternals.h" |
| 19 #include "xsltutils.h" |
| 20 #include "transform.h" |
| 21 #include "templates.h" |
| 22 |
| 23 #include <unicode/ucnv.h> |
| 24 #include <unicode/ustring.h> |
| 25 #include <unicode/utypes.h> |
| 26 #include <unicode/uloc.h> |
| 27 #include <unicode/ucol.h> |
| 28 |
| 29 /** |
| 30 * xsltICUSortFunction: |
| 31 * @ctxt: a XSLT process context |
| 32 * @sorts: array of sort nodes |
| 33 * @nbsorts: the number of sorts in the array |
| 34 * |
| 35 * reorder the current node list accordingly to the set of sorting |
| 36 * requirement provided by the arry of nodes. |
| 37 * uses the ICU library |
| 38 */ |
| 39 void |
| 40 xsltICUSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, |
| 41 int nbsorts) { |
| 42 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; |
| 43 xmlXPathObjectPtr *results = NULL, *res; |
| 44 xmlNodeSetPtr list = NULL; |
| 45 int descending, number, desc, numb; |
| 46 int len = 0; |
| 47 int i, j, incr; |
| 48 int tst; |
| 49 int depth; |
| 50 xmlNodePtr node; |
| 51 xmlXPathObjectPtr tmp; |
| 52 xsltStylePreCompPtr comp; |
| 53 int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT]; |
| 54 |
| 55 /* Start ICU change */ |
| 56 UCollator *coll = 0; |
| 57 UConverter *conv; |
| 58 UErrorCode status; |
| 59 UChar *target,*target2; |
| 60 int targetlen, target2len; |
| 61 /* End ICU change */ |
| 62 |
| 63 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || |
| 64 (nbsorts >= XSLT_MAX_SORT)) |
| 65 return; |
| 66 if (sorts[0] == NULL) |
| 67 return; |
| 68 comp = sorts[0]->_private; |
| 69 if (comp == NULL) |
| 70 return; |
| 71 |
| 72 list = ctxt->nodeList; |
| 73 if ((list == NULL) || (list->nodeNr <= 1)) |
| 74 return; /* nothing to do */ |
| 75 |
| 76 for (j = 0; j < nbsorts; j++) { |
| 77 comp = sorts[j]->_private; |
| 78 tempstype[j] = 0; |
| 79 if ((comp->stype == NULL) && (comp->has_stype != 0)) { |
| 80 comp->stype = |
| 81 xsltEvalAttrValueTemplate(ctxt, sorts[j], |
| 82 (const xmlChar *) "data-type", |
| 83 XSLT_NAMESPACE); |
| 84 if (comp->stype != NULL) { |
| 85 tempstype[j] = 1; |
| 86 if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) |
| 87 comp->number = 0; |
| 88 else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) |
| 89 comp->number = 1; |
| 90 else { |
| 91 xsltTransformError(ctxt, NULL, sorts[j], |
| 92 "xsltDoSortFunction: no support for data-type = %s\n", |
| 93 comp->stype); |
| 94 comp->number = 0; /* use default */ |
| 95 } |
| 96 } |
| 97 } |
| 98 temporder[j] = 0; |
| 99 if ((comp->order == NULL) && (comp->has_order != 0)) { |
| 100 comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], |
| 101 (const xmlChar *) "order", |
| 102 XSLT_NAMESPACE); |
| 103 if (comp->order != NULL) { |
| 104 temporder[j] = 1; |
| 105 if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) |
| 106 comp->descending = 0; |
| 107 else if (xmlStrEqual(comp->order, |
| 108 (const xmlChar *) "descending")) |
| 109 comp->descending = 1; |
| 110 else { |
| 111 xsltTransformError(ctxt, NULL, sorts[j], |
| 112 "xsltDoSortFunction: invalid value %s for order\n", |
| 113 comp->order); |
| 114 comp->descending = 0; /* use default */ |
| 115 } |
| 116 } |
| 117 } |
| 118 } |
| 119 |
| 120 len = list->nodeNr; |
| 121 |
| 122 resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); |
| 123 for (i = 1;i < XSLT_MAX_SORT;i++) |
| 124 resultsTab[i] = NULL; |
| 125 |
| 126 results = resultsTab[0]; |
| 127 |
| 128 comp = sorts[0]->_private; |
| 129 descending = comp->descending; |
| 130 number = comp->number; |
| 131 if (results == NULL) |
| 132 return; |
| 133 |
| 134 /* Start ICU change */ |
| 135 status = U_ZERO_ERROR; |
| 136 conv = ucnv_open("UTF8", &status); |
| 137 if(U_FAILURE(status)) { |
| 138 xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error opening
converter\n"); |
| 139 } |
| 140 if(comp->has_lang) |
| 141 coll = ucol_open(comp->lang, &status); |
| 142 if(U_FAILURE(status) || !comp->has_lang) { |
| 143 status = U_ZERO_ERROR; |
| 144 coll = ucol_open("en", &status); |
| 145 } |
| 146 if(U_FAILURE(status)) { |
| 147 xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error opening
collator\n"); |
| 148 } |
| 149 if(comp->lower_first) |
| 150 ucol_setAttribute(coll,UCOL_CASE_FIRST,UCOL_LOWER_FIRST,&status); |
| 151 else |
| 152 ucol_setAttribute(coll,UCOL_CASE_FIRST,UCOL_UPPER_FIRST,&status); |
| 153 if(U_FAILURE(status)) { |
| 154 xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error setting
collator attribute\n"); |
| 155 } |
| 156 /* End ICU change */ |
| 157 |
| 158 /* Shell's sort of node-set */ |
| 159 for (incr = len / 2; incr > 0; incr /= 2) { |
| 160 for (i = incr; i < len; i++) { |
| 161 j = i - incr; |
| 162 if (results[i] == NULL) |
| 163 continue; |
| 164 |
| 165 while (j >= 0) { |
| 166 if (results[j] == NULL) |
| 167 tst = 1; |
| 168 else { |
| 169 if (number) { |
| 170 if (results[j]->floatval == results[j + incr]->floatval) |
| 171 tst = 0; |
| 172 else if (results[j]->floatval > |
| 173 results[j + incr]->floatval) |
| 174 tst = 1; |
| 175 else tst = -1; |
| 176 } else { |
| 177 /* tst = xmlStrcmp(results[j]->stringval, |
| 178 results[j + incr]->stringval); */ |
| 179 /* Start ICU change */ |
| 180 targetlen = xmlStrlen(results[j]->stringval) * 2; |
| 181 target2len = xmlStrlen(results[j + incr]->stringval) * 2
; |
| 182 target = xmlMalloc(targetlen * sizeof(UChar)); |
| 183 target2 = xmlMalloc(target2len * sizeof(UChar)); |
| 184 targetlen = ucnv_toUChars(conv, target, targetlen, resul
ts[j]->stringval, -1, &status); |
| 185 target2len = ucnv_toUChars(conv, target2, target2len, re
sults[j+incr]->stringval, -1, &status); |
| 186 tst = ucol_strcoll(coll, target, u_strlen(target), targe
t2, u_strlen(target2)); |
| 187 /* End ICU change */ |
| 188 } |
| 189 if (descending) |
| 190 tst = -tst; |
| 191 } |
| 192 if (tst == 0) { |
| 193 /* |
| 194 * Okay we need to use multi level sorts |
| 195 */ |
| 196 depth = 1; |
| 197 while (depth < nbsorts) { |
| 198 if (sorts[depth] == NULL) |
| 199 break; |
| 200 comp = sorts[depth]->_private; |
| 201 if (comp == NULL) |
| 202 break; |
| 203 desc = comp->descending; |
| 204 numb = comp->number; |
| 205 |
| 206 /* |
| 207 * Compute the result of the next level for the |
| 208 * full set, this might be optimized ... or not |
| 209 */ |
| 210 if (resultsTab[depth] == NULL) |
| 211 resultsTab[depth] = xsltComputeSortResult(ctxt, |
| 212 sorts[depth]); |
| 213 res = resultsTab[depth]; |
| 214 if (res == NULL) |
| 215 break; |
| 216 if (res[j] == NULL) |
| 217 tst = 1; |
| 218 else { |
| 219 if (numb) { |
| 220 if (res[j]->floatval == res[j + incr]->floatval) |
| 221 tst = 0; |
| 222 else if (res[j]->floatval > |
| 223 res[j + incr]->floatval) |
| 224 tst = 1; |
| 225 else tst = -1; |
| 226 } else { |
| 227 /* tst = xmlStrcmp(res[j]->stringval, |
| 228 res[j + incr]->stringval); */ |
| 229 /* Start ICU change */ |
| 230 targetlen = xmlStrlen(res[j]->stringval) * 2; |
| 231 target2len = xmlStrlen(res[j + incr]->stringval)
* 2; |
| 232 target = xmlMalloc(targetlen * sizeof(UChar)); |
| 233 target2 = xmlMalloc(target2len * sizeof(UChar)); |
| 234 targetlen = ucnv_toUChars(conv, target, targetle
n, res[j]->stringval, -1, &status); |
| 235 target2len = ucnv_toUChars(conv, target2, target
2len, res[j+incr]->stringval, -1, &status); |
| 236 tst = ucol_strcoll(coll, target, u_strlen(target
), target2, u_strlen(target2)); |
| 237 /* End ICU change */ |
| 238 } |
| 239 if (desc) |
| 240 tst = -tst; |
| 241 } |
| 242 /* |
| 243 * if we still can't differenciate at this level |
| 244 * try one level deeper. |
| 245 */ |
| 246 if (tst != 0) |
| 247 break; |
| 248 depth++; |
| 249 } |
| 250 } |
| 251 if (tst == 0) { |
| 252 tst = results[j]->index > results[j + incr]->index; |
| 253 } |
| 254 if (tst > 0) { |
| 255 tmp = results[j]; |
| 256 results[j] = results[j + incr]; |
| 257 results[j + incr] = tmp; |
| 258 node = list->nodeTab[j]; |
| 259 list->nodeTab[j] = list->nodeTab[j + incr]; |
| 260 list->nodeTab[j + incr] = node; |
| 261 depth = 1; |
| 262 while (depth < nbsorts) { |
| 263 if (sorts[depth] == NULL) |
| 264 break; |
| 265 if (resultsTab[depth] == NULL) |
| 266 break; |
| 267 res = resultsTab[depth]; |
| 268 tmp = res[j]; |
| 269 res[j] = res[j + incr]; |
| 270 res[j + incr] = tmp; |
| 271 depth++; |
| 272 } |
| 273 j -= incr; |
| 274 } else |
| 275 break; |
| 276 } |
| 277 } |
| 278 } |
| 279 |
| 280 /* Start ICU change */ |
| 281 ucol_close(coll); |
| 282 ucnv_close(conv); |
| 283 /* End ICU change */ |
| 284 |
| 285 for (j = 0; j < nbsorts; j++) { |
| 286 comp = sorts[j]->_private; |
| 287 if (tempstype[j] == 1) { |
| 288 /* The data-type needs to be recomputed each time */ |
| 289 xmlFree(comp->stype); |
| 290 comp->stype = NULL; |
| 291 } |
| 292 if (temporder[j] == 1) { |
| 293 /* The order needs to be recomputed each time */ |
| 294 xmlFree(comp->order); |
| 295 comp->order = NULL; |
| 296 } |
| 297 if (resultsTab[j] != NULL) { |
| 298 for (i = 0;i < len;i++) |
| 299 xmlXPathFreeObject(resultsTab[j][i]); |
| 300 xmlFree(resultsTab[j]); |
| 301 } |
| 302 } |
| 303 } |
| 304 |
OLD | NEW |