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/
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.
12 * The Original Code is the Sablotron XSLT Processor.
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.
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
33 #include "numbering.h"
37 #include "domprovider.h"
39 Bool cmpNames(Sit S, NodeHandle v, NodeHandle w)
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);
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);
59 Bool similarVerts(Sit S, NodeHandle v, NodeHandle w)
61 //some optimalization for native node may be done (compare QNames)
62 assert(!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) )
73 // no expanded name, they match
76 //return toE(v) -> getName() == toE(w) -> getName();
78 //return toNS(v) -> getName() == toNS(w) -> getName();
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);
85 // do this to keep compiler happy
90 NodeHandle gotoPreceding(Sit S, NodeHandle v, Bool siblingOnly)
93 switch(S.dom().getNodeType(v))
98 // no preceding nodes according to XPath spec
102 NodeHandle par = S.dom().getParent(v);
106 return S.dom().getPreviousSibling(v);
108 // return toD(par) -> contents[v -> ordinal - 1];
112 else //preceding and ancestor-or-self union
114 //if we have preceding sibling we switch to it and dril
115 NodeHandle w = S.dom().getPreviousSibling(v);
119 !nhNull(v) && S.dom().getChildCount(v);
120 v = S.dom().getChildNo(v, S.dom().getChildCount(v) - 1));
125 return S.dom().getNodeType(par) == DOCUMENT_NODE ? NULL : par;
132 eFlag countMatchingSiblings(Sit S, int& num, NodeHandle v, Expression *count)
137 Context c(NULL); //_cn_ we're matching a pattern
138 for (w = v; !nhNull(w); w = gotoPreceding(S, w, /* siblingOnly = */ TRUE))
144 E( count -> matchesPattern(S, &c, doesMatch) );
147 doesMatch = similarVerts(S, v, w);
154 eFlag xslNumberCount(
155 Sit S, NumberingLevel level,
156 Expression* count, Expression* from,
157 NodeHandle curr, ListInt& result)
162 List<NodeHandle> matchingList;
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); )
172 E( from -> matchesPattern(S, &c, doesMatch) );
173 if (doesMatch) break; // leave the for loop
176 E( count -> matchesPattern(S, &c, doesMatch) )
178 doesMatch = similarVerts(S, curr, w);
181 matchingList.append(w);
182 if (level == NUM_SINGLE) break; // leave the for loop after finding a match
184 if (level == NUM_ANY)
185 w = gotoPreceding(S, w, /* siblingOnly = */ FALSE);
187 w = S.dom().getParent(w);
189 // construct the integer list out of matchingList
190 if (level == NUM_ANY)
191 result.append(matchingList.number());
193 for (int i = matchingList.number() - 1; i >= 0; i--)
195 E( countMatchingSiblings(S, num, matchingList[i], count) );
201 Bool isAlnumFToken(const Str& s)
203 unsigned long c = utf8CharCode((const char*)s);
204 return utf8IsDigit(c) || utf8IsLetter(c);
207 Bool getFToken(const char *&p, Str& fmt)
211 const char* pOrig = p;
212 Bool alnum = isAlnumFToken(p);
214 p += utf8SingleCharLength(p);
215 while(*p && isAlnumFToken(p) == alnum);
216 fmt.nset(pOrig, (int)(p - pOrig));
220 void getFTokenParams(const Str& fmt, char& type, int& width)
225 int len = utf8StrLength(fmt);
227 if (len > 1 && fmt[0] != '0') return; // with default values
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;
250 void appendABC(int num, Bool uppercase, DStr& result)
256 reversed += (char)((uppercase ? 'A' : 'a') + num % 26);
260 for (int i = reversed.length() - 1; i >= 0; i--)
261 result += reversed[i];
282 void appendRoman(int num, Bool uppercase, DStr& result)
291 if (num >= (val = romans[step].value))
293 result += romans[step].symbol[uppercase];
298 prefix = step + 2 - step % 2;
299 if (val > 1 && num >= (val - romans[prefix].value))
301 result += romans[prefix].symbol[uppercase];
302 result += romans[step].symbol[uppercase];
303 num -= (val - romans[prefix].value);
313 const Str& groupingSep, int groupingSize, DStr& tempResult)
315 // just put separators and leading zeroes in the number
316 DStr sprFmt = DStr("%0") + width + "d";
319 int len = snprintf(buff, 32, (char*)sprFmt, num);
324 int first = len % groupingSize;
327 tempResult.nadd(p, first);
331 tempResult += groupingSep;
333 for (; len > 0; len -= groupingSize, p += groupingSize)
335 tempResult.nadd(p, groupingSize);
336 if (len > groupingSize)
337 tempResult += groupingSep;
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)
347 // we only add to tempResult, do not initialize it
350 // check value of num
353 S.message(MT_WARN, W_NUMBER_NOT_POSITIVE, (char*)NULL, (char*)NULL);
354 num = num ? abs(num) : 1;
356 getFTokenParams(fmt, type, width);
361 appendABC(num, /* uppercase = */ type == 'A', tempResult);
365 appendRoman(num, /* uppercase = */ type == 'I', tempResult);
368 appendArabic(num, width, groupingSep, groupingSize, tempResult);
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)
383 const char *p = (const char*) format;
386 if (getFToken(p, firstToken))
388 if (isAlnumFToken(firstToken) && nums.number())
392 S, nums[0], alpha, lang, letterValue,
393 groupingSep, groupingSize, tempResult);
398 // reset p to the beginning
399 p = (const char*) format;
401 tempResult += sepRightmost = firstToken;;
404 // p points at the first separator (if any)
405 Bool readAllFormat = *p ? FALSE : TRUE;
406 for (; ndx < nums.number(); ndx++)
410 // always update the rightmost separator
411 if (getFToken(p, sepRightmost))
413 if (getFToken(p, alpha))
416 // use the current separator and reset the rightmost one
418 sepRightmost.empty();
422 // no alpha token, use the same separator as last time
423 // keep current separator in sepRightmost
424 readAllFormat = TRUE;
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
437 S, nums[ndx], alpha, lang, letterValue,
438 groupingSep, groupingSize, tempResult);
440 // get the real rightmost separator
443 while(getFToken(p, sepRightmost));
444 if (isAlnumFToken(sepRightmost))
445 sepRightmost.empty();
447 tempResult += sepRightmost;