1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Sablotron XSLT Processor.
13 *
14 * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15 * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16 * Ginger Alliance Ltd. All Rights Reserved.
17 *
18 * Contributor(s):
19 *
20 * Alternatively, the contents of this file may be used under the
21 * terms of the GNU General Public License Version 2 or later (the
22 * "GPL"), in which case the provisions of the GPL are applicable
23 * instead of those above. If you wish to allow use of your
24 * version of this file only under the terms of the GPL and not to
25 * allow others to use your version of this file under the MPL,
26 * indicate your decision by deleting the provisions above and
27 * replace them with the notice and other provisions required by
28 * the GPL. If you do not delete the provisions above, a recipient
29 * may use your version of this file under either the MPL or the
30 * GPL.
31 */
32
33 #include "numbering.h"
34 #include "verts.h"
35 #include "expr.h"
36 #include "context.h"
37 #include "domprovider.h"
38
cmpNames(Sit S,NodeHandle v,NodeHandle w)39 Bool cmpNames(Sit S, NodeHandle v, NodeHandle w)
40 {
41 int ret = 0;
42 const char *aux1, *aux2;
43 aux1 = S.dom().getNodeNameLocal(v);
44 aux2 = S.dom().getNodeNameLocal(w);
45 ret = strcmp(aux1, aux2);
46 S.dom().freeName(v, (char*)aux1);
47 S.dom().freeName(w, (char*)aux2);
48 if (!ret)
49 {
50 aux1 = S.dom().getNodeNameURI(v);
51 aux2 = S.dom().getNodeNameURI(w);
52 ret = strcmp(aux1, aux2);
53 S.dom().freeName(v, (char*)aux1);
54 S.dom().freeName(w, (char*)aux2);
55 }
56 return ret;
57 }
58
similarVerts(Sit S,NodeHandle v,NodeHandle w)59 Bool similarVerts(Sit S, NodeHandle v, NodeHandle w)
60 {
61 //some optimalization for native node may be done (compare QNames)
62 sabassert(!nhNull(v) && !nhNull(w));
63 SXP_NodeType typeV = S.dom().getNodeType(v);
64 // vertices of different types don't match
65 if (typeV != S.dom().getNodeType(w) )
66 return FALSE;
67 //test similarity
68 switch(typeV)
69 {
70 case DOCUMENT_NODE:
71 case TEXT_NODE:
72 case COMMENT_NODE:
73 // no expanded name, they match
74 return TRUE;
75 case ELEMENT_NODE:
76 //return toE(v) -> getName() == toE(w) -> getName();
77 case NAMESPACE_NODE:
78 //return toNS(v) -> getName() == toNS(w) -> getName();
79 case ATTRIBUTE_NODE:
80 //return toA(v) -> getName() == toA(w) -> getName();
81 case PROCESSING_INSTRUCTION_NODE:
82 //return toPI(v) -> getName() == toPI(w) -> getName();
83 return !cmpNames(S, v, w);
84 default:
85 // do this to keep compiler happy
86 return FALSE;
87 }
88 }
89
gotoPreceding(Sit S,NodeHandle v,Bool siblingOnly)90 NodeHandle gotoPreceding(Sit S, NodeHandle v, Bool siblingOnly)
91 {
92 sabassert(v);
93 switch(S.dom().getNodeType(v))
94 {
95 case DOCUMENT_NODE:
96 case ATTRIBUTE_NODE:
97 case NAMESPACE_NODE:
98 // no preceding nodes according to XPath spec
99 return NULL;
100 default:
101 {
102 NodeHandle par = S.dom().getParent(v);
103
104 if (siblingOnly)
105 {
106 return S.dom().getPreviousSibling(v);
107 //if (v -> ordinal)
108 // return toD(par) -> contents[v -> ordinal - 1];
109 //else
110 // return NULL;
111 }
112 else //preceding and ancestor-or-self union
113 {
114 //if we have preceding sibling we switch to it and dril
115 NodeHandle w = S.dom().getPreviousSibling(v);
116 if (!nhNull(w))
117 {
118 for (v = w;
119 !nhNull(v) && S.dom().getChildCount(v);
120 v = S.dom().getChildNo(v, S.dom().getChildCount(v) - 1));
121 return v;
122 }
123 else //go to parent
124 {
125 return S.dom().getNodeType(par) == DOCUMENT_NODE ? NULL : par;
126 }
127 }
128 }
129 }
130 }
131
countMatchingSiblings(Sit S,int & num,NodeHandle v,Expression * count)132 eFlag countMatchingSiblings(Sit S, int& num, NodeHandle v, Expression *count)
133 {
134 num = 0;
135 NodeHandle w;
136 Bool doesMatch;
137 Context c(NULL); //_cn_ we're matching a pattern
138 for (w = v; !nhNull(w); w = gotoPreceding(S, w, /* siblingOnly = */ TRUE))
139 {
140 if (count)
141 {
142 c.deppendall();
143 c.set(w);
144 E( count -> matchesPattern(S, &c, doesMatch) );
145 }
146 else
147 doesMatch = similarVerts(S, v, w);
148 if (doesMatch)
149 num++;
150 }
151 return OK;
152 }
153
xslNumberCount(Sit S,NumberingLevel level,Expression * count,Expression * from,NodeHandle curr,ListInt & result)154 eFlag xslNumberCount(
155 Sit S, NumberingLevel level,
156 Expression* count, Expression* from,
157 NodeHandle curr, ListInt& result)
158 {
159 result.deppendall();
160 int num;
161 NodeHandle w = NULL;
162 List<NodeHandle> matchingList;
163 Bool doesMatch;
164 Context c(NULL); //_cn_ we're matching a pattern
165 // construct the list of matching ancestors/preceding nodes
166 for (w = curr; !nhNull(w); )
167 {
168 c.deppendall();
169 c.set(w);
170 if (from)
171 {
172 E( from -> matchesPattern(S, &c, doesMatch) );
173 if (doesMatch) break; // leave the for loop
174 }
175 if (count)
176 E( count -> matchesPattern(S, &c, doesMatch) )
177 else
178 doesMatch = similarVerts(S, curr, w);
179 if (doesMatch)
180 {
181 matchingList.append(w);
182 if (level == NUM_SINGLE) break; // leave the for loop after finding a match
183 }
184 if (level == NUM_ANY)
185 w = gotoPreceding(S, w, /* siblingOnly = */ FALSE);
186 else
187 w = S.dom().getParent(w);
188 }
189 // construct the integer list out of matchingList
190 if (level == NUM_ANY)
191 result.append(matchingList.number());
192 else
193 for (int i = matchingList.number() - 1; i >= 0; i--)
194 {
195 E( countMatchingSiblings(S, num, matchingList[i], count) );
196 result.append(num);
197 }
198 return OK;
199 }
200
isAlnumFToken(const Str & s)201 Bool isAlnumFToken(const Str& s)
202 {
203 unsigned long c = utf8CharCode((const char*)s);
204 return utf8IsDigit(c) || utf8IsLetter(c);
205 }
206
getFToken(const char * & p,Str & fmt)207 Bool getFToken(const char *&p, Str& fmt)
208 {
209 if (!*p)
210 return FALSE;
211 const char* pOrig = p;
212 Bool alnum = isAlnumFToken(p);
213 do
214 p += utf8SingleCharLength(p);
215 while(*p && isAlnumFToken(p) == alnum);
216 fmt.nset(pOrig, (int)(p - pOrig));
217 return TRUE;
218 }
219
getFTokenParams(const Str & fmt,char & type,int & width)220 void getFTokenParams(const Str& fmt, char& type, int& width)
221 {
222 // defaults:
223 type = '1';
224 width = 1;
225 int len = utf8StrLength(fmt);
226 sabassert(len);
227 if (len > 1 && fmt[0] != '0') return; // with default values
228 switch(fmt[0])
229 {
230 case 'A':
231 case 'a':
232 case 'I':
233 case 'i':
234 {
235 type = fmt[0];
236 return;
237 }
238 case '0':
239 break;
240 default:
241 return;
242 }
243 // it remains to take care of the '0':
244 for (int i = 1; i < len - 1; i++)
245 if (fmt[i] != '0') return;
246 if (fmt[len - 1] != '1') return;
247 width = len;
248 }
249
appendABC(int num,Bool uppercase,DStr & result)250 void appendABC(int num, Bool uppercase, DStr& result)
251 {
252 DStr reversed;
253 do
254 {
255 num--;
256 reversed += (char)((uppercase ? 'A' : 'a') + num % 26);
257 num /= 26;
258 }
259 while (num > 0);
260 for (int i = reversed.length() - 1; i >= 0; i--)
261 result += reversed[i];
262 }
263
264 struct RomanDef
265 {
266 int value;
267 char symbol[3];
268 };
269
270 RomanDef romans[] =
271 {
272 { 1000, "mM"},
273 { 500, "dD"},
274 { 100, "cC"},
275 { 50, "lL"},
276 { 10, "xX"},
277 { 5, "vV"},
278 { 1, "iI"},
279 { 0, "oO"}
280 };
281
appendRoman(int num,Bool uppercase,DStr & result)282 void appendRoman(int num, Bool uppercase, DStr& result)
283 {
284 int step = 0,
285 prefix,
286 val;
287 if (uppercase != 0)
288 uppercase = 1;
289 while (num > 0)
290 {
291 if (num >= (val = romans[step].value))
292 {
293 result += romans[step].symbol[uppercase];
294 num -= val;
295 }
296 else
297 {
298 prefix = step + 2 - step % 2;
299 if (val > 1 && num >= (val - romans[prefix].value))
300 {
301 result += romans[prefix].symbol[uppercase];
302 result += romans[step].symbol[uppercase];
303 num -= (val - romans[prefix].value);
304 }
305 else
306 step++;
307 }
308 }
309 }
310
appendArabic(int num,int width,const Str & groupingSep,int groupingSize,DStr & tempResult)311 void appendArabic(
312 int num, int width,
313 const Str& groupingSep, int groupingSize, DStr& tempResult)
314 {
315 // just put separators and leading zeroes in the number
316 DStr sprFmt = DStr("%0") + width + "d";
317 char buff[32],
318 *p = buff;
319 int len = snprintf(buff, 32, (char*)sprFmt, num);
320 if (!groupingSize)
321 tempResult += buff;
322 else
323 {
324 int first = len % groupingSize;
325 if (first)
326 {
327 tempResult.nadd(p, first);
328 p += first;
329 len -= first;
330 if (len)
331 tempResult += groupingSep;
332 }
333 for (; len > 0; len -= groupingSize, p += groupingSize)
334 {
335 tempResult.nadd(p, groupingSize);
336 if (len > groupingSize)
337 tempResult += groupingSep;
338 }
339 }
340 }
341
formatSingleNumber(Sit S,int num,const Str & fmt,const Str & lang,NumberingLetterValue letterValue,const Str & groupingSep,int groupingSize,DStr & tempResult)342 void formatSingleNumber(
343 Sit S, int num, const Str& fmt,
344 const Str& lang, NumberingLetterValue letterValue,
345 const Str& groupingSep, int groupingSize, DStr& tempResult)
346 {
347 // we only add to tempResult, do not initialize it
348 char type;
349 int width;
350 // check value of num
351 if (num <= 0)
352 {
353 S.message(MT_WARN, W_NUMBER_NOT_POSITIVE, (char*)NULL, (char*)NULL);
354 num = num ? abs(num) : 1;
355 }
356 getFTokenParams(fmt, type, width);
357 switch(type)
358 {
359 case 'A':
360 case 'a':
361 appendABC(num, /* uppercase = */ type == 'A', tempResult);
362 break;
363 case 'I':
364 case 'i':
365 appendRoman(num, /* uppercase = */ type == 'I', tempResult);
366 break;
367 default:
368 appendArabic(num, width, groupingSep, groupingSize, tempResult);
369 }
370 }
371
xslNumberFormat(Sit S,ListInt & nums,const Str & format,const Str & lang,NumberingLetterValue letterValue,const Str & groupingSep,int groupingSize,Str & result)372 eFlag xslNumberFormat(
373 Sit S, ListInt& nums, const Str& format,
374 const Str& lang, NumberingLetterValue letterValue,
375 const Str& groupingSep, int groupingSize, Str& result)
376 {
377 DStr tempResult;
378 Str sep = ".",
379 sepRightmost,
380 alpha = "1",
381 firstToken;
382
383 const char *p = (const char*) format;
384 int ndx = 0;
385
386 if (getFToken(p, firstToken))
387 {
388 if (isAlnumFToken(firstToken) && nums.number())
389 {
390 alpha = firstToken;
391 formatSingleNumber(
392 S, nums[0], alpha, lang, letterValue,
393 groupingSep, groupingSize, tempResult);
394 ndx = 1;
395 }
396 else
397 {
398 // reset p to the beginning
399 p = (const char*) format;
400 if (!nums.number())
401 tempResult += sepRightmost = firstToken;;
402 }
403 }
404 // p points at the first separator (if any)
405 Bool readAllFormat = *p ? FALSE : TRUE;
406 for (; ndx < nums.number(); ndx++)
407 {
408 if (!readAllFormat)
409 {
410 // always update the rightmost separator
411 if (getFToken(p, sepRightmost))
412 {
413 if (getFToken(p, alpha))
414 {
415 // alpha token found
416 // use the current separator and reset the rightmost one
417 sep = sepRightmost;
418 sepRightmost.empty();
419 }
420 else
421 {
422 // no alpha token, use the same separator as last time
423 // keep current separator in sepRightmost
424 readAllFormat = TRUE;
425 if (!ndx)
426 sep = sepRightmost;
427 }
428
429 }
430 else
431 // both empty, use the same separator and alpha as last time
432 readAllFormat = TRUE;
433 } // if (!readAllFormat)
434 // sep and alpha are the last valid tokens of each kind
435 tempResult += sep;
436 formatSingleNumber(
437 S, nums[ndx], alpha, lang, letterValue,
438 groupingSep, groupingSize, tempResult);
439 } // for loop
440 // get the real rightmost separator
441 if (!readAllFormat)
442 {
443 while(getFToken(p, sepRightmost));
444 if (isAlnumFToken(sepRightmost))
445 sepRightmost.empty();
446 }
447 tempResult += sepRightmost;
448 result = tempResult;
449 return OK;
450 }
451