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, results[j]->stringval, -1, &status);
|
---|
185 | target2len = ucnv_toUChars(conv, target2, target2len, results[j+incr]->stringval, -1, &status);
|
---|
186 | tst = ucol_strcoll(coll, target, u_strlen(target), target2, 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, targetlen, res[j]->stringval, -1, &status);
|
---|
235 | target2len = ucnv_toUChars(conv, target2, target2len, 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 |
|
---|