Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygennumbers.c
Go to the documentation of this file.
00001 /* 00002 * numbers.c: Implementation of the XSLT number functions 00003 * 00004 * Reference: 00005 * http://www.w3.org/TR/1999/REC-xslt-19991116 00006 * 00007 * See Copyright for the status of this software. 00008 * 00009 * daniel@veillard.com 00010 * Bjorn Reese <breese@users.sourceforge.net> 00011 */ 00012 00013 #define IN_LIBXSLT 00014 #include "libxslt.h" 00015 00016 #include <math.h> 00017 #include <limits.h> 00018 #include <float.h> 00019 #include <string.h> 00020 00021 #include <libxml/xmlmemory.h> 00022 #include <libxml/parserInternals.h> 00023 #include <libxml/xpath.h> 00024 #include <libxml/xpathInternals.h> 00025 #include <libxml/encoding.h> 00026 #include "xsltutils.h" 00027 #include "pattern.h" 00028 #include "templates.h" 00029 #include "transform.h" 00030 #include "numbersInternals.h" 00031 00032 #ifndef FALSE 00033 # define FALSE (0 == 1) 00034 # define TRUE (1 == 1) 00035 #endif 00036 00037 #define SYMBOL_QUOTE ((xmlChar)'\'') 00038 00039 #define DEFAULT_TOKEN (xmlChar)'0' 00040 #define DEFAULT_SEPARATOR "." 00041 00042 #define MAX_TOKENS 1024 00043 00044 typedef struct _xsltFormatToken xsltFormatToken; 00045 typedef xsltFormatToken *xsltFormatTokenPtr; 00046 struct _xsltFormatToken { 00047 xmlChar *separator; 00048 xmlChar token; 00049 int width; 00050 }; 00051 00052 typedef struct _xsltFormat xsltFormat; 00053 typedef xsltFormat *xsltFormatPtr; 00054 struct _xsltFormat { 00055 xmlChar *start; 00056 xsltFormatToken tokens[MAX_TOKENS]; 00057 int nTokens; 00058 xmlChar *end; 00059 }; 00060 00061 static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 00062 static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz"; 00063 static xsltFormatToken default_token; 00064 00065 /* 00066 * **** Start temp insert **** 00067 * 00068 * The following two routines (xsltUTF8Size and xsltUTF8Charcmp) 00069 * will be replaced with calls to the corresponding libxml routines 00070 * at a later date (when other inter-library dependencies require it) 00071 */ 00072 00079 static int 00080 xsltUTF8Size(xmlChar *utf) { 00081 xmlChar mask; 00082 int len; 00083 00084 if (utf == NULL) 00085 return -1; 00086 if (*utf < 0x80) 00087 return 1; 00088 /* check valid UTF8 character */ 00089 if (!(*utf & 0x40)) 00090 return -1; 00091 /* determine number of bytes in char */ 00092 len = 2; 00093 for (mask=0x20; mask != 0; mask>>=1) { 00094 if (!(*utf & mask)) 00095 return len; 00096 len++; 00097 } 00098 return -1; 00099 } 00100 00109 static int 00110 xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) { 00111 00112 if (utf1 == NULL ) { 00113 if (utf2 == NULL) 00114 return 0; 00115 return -1; 00116 } 00117 return xmlStrncmp(utf1, utf2, xsltUTF8Size(utf1)); 00118 } 00119 00120 /***** Stop temp insert *****/ 00121 /************************************************************************ 00122 * * 00123 * Utility functions * 00124 * * 00125 ************************************************************************/ 00126 00127 #define IS_SPECIAL(self,letter) \ 00128 ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \ 00129 (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \ 00130 (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \ 00131 (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \ 00132 (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0)) 00133 00134 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x) 00135 #define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1) 00136 00137 static int 00138 xsltIsDigitZero(unsigned int ch) 00139 { 00140 /* 00141 * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt 00142 */ 00143 switch (ch) { 00144 case 0x0030: case 0x0660: case 0x06F0: case 0x0966: 00145 case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66: 00146 case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50: 00147 case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0: 00148 case 0x1810: case 0xFF10: 00149 return TRUE; 00150 default: 00151 return FALSE; 00152 } 00153 } 00154 00155 static void 00156 xsltNumberFormatDecimal(xmlBufferPtr buffer, 00157 double number, 00158 int digit_zero, 00159 int width, 00160 int digitsPerGroup, 00161 int groupingCharacter, 00162 int groupingCharacterLen) 00163 { 00164 /* 00165 * This used to be 00166 * xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4]; 00167 * which would be length 68 on x86 arch. It was changed to be a longer, 00168 * fixed length in order to try to cater for (reasonable) UTF8 00169 * separators and numeric characters. The max UTF8 char size will be 00170 * 6 or less, so the value used [500] should be *much* larger than needed 00171 */ 00172 xmlChar temp_string[500]; 00173 xmlChar *pointer; 00174 xmlChar temp_char[6]; 00175 int i; 00176 int val; 00177 int len; 00178 00179 /* Build buffer from back */ 00180 pointer = &temp_string[sizeof(temp_string)] - 1; /* last char */ 00181 *pointer = 0; 00182 i = 0; 00183 while (pointer > temp_string) { 00184 if ((i >= width) && (fabs(number) < 1.0)) 00185 break; /* for */ 00186 if ((i > 0) && (groupingCharacter != 0) && 00187 (digitsPerGroup > 0) && 00188 ((i % digitsPerGroup) == 0)) { 00189 if (pointer - groupingCharacterLen < temp_string) { 00190 i = -1; /* flag error */ 00191 break; 00192 } 00193 pointer -= groupingCharacterLen; 00194 xmlCopyCharMultiByte(pointer, groupingCharacter); 00195 } 00196 00197 val = digit_zero + (int)fmod(number, 10.0); 00198 if (val < 0x80) { /* shortcut if ASCII */ 00199 if (pointer <= temp_string) { /* Check enough room */ 00200 i = -1; 00201 break; 00202 } 00203 *(--pointer) = val; 00204 } 00205 else { 00206 /* 00207 * Here we have a multibyte character. It's a little messy, 00208 * because until we generate the char we don't know how long 00209 * it is. So, we generate it into the buffer temp_char, then 00210 * copy from there into temp_string. 00211 */ 00212 len = xmlCopyCharMultiByte(temp_char, val); 00213 if ( (pointer - len) < temp_string ) { 00214 i = -1; 00215 break; 00216 } 00217 pointer -= len; 00218 memcpy(pointer, temp_char, len); 00219 } 00220 number /= 10.0; 00221 ++i; 00222 } 00223 if (i < 0) 00224 xsltGenericError(xsltGenericErrorContext, 00225 "xsltNumberFormatDecimal: Internal buffer size exceeded"); 00226 xmlBufferCat(buffer, pointer); 00227 } 00228 00229 static void 00230 xsltNumberFormatAlpha(xmlBufferPtr buffer, 00231 double number, 00232 int is_upper) 00233 { 00234 char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1]; 00235 char *pointer; 00236 int i; 00237 char *alpha_list; 00238 double alpha_size = (double)(sizeof(alpha_upper_list) - 1); 00239 00240 /* Build buffer from back */ 00241 pointer = &temp_string[sizeof(temp_string)]; 00242 *(--pointer) = 0; 00243 alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list; 00244 00245 for (i = 1; i < (int)sizeof(temp_string); i++) { 00246 number--; 00247 *(--pointer) = alpha_list[((int)fmod(number, alpha_size))]; 00248 number /= alpha_size; 00249 if (fabs(number) < 1.0) 00250 break; /* for */ 00251 } 00252 xmlBufferCCat(buffer, pointer); 00253 } 00254 00255 static void 00256 xsltNumberFormatRoman(xmlBufferPtr buffer, 00257 double number, 00258 int is_upper) 00259 { 00260 /* 00261 * Based on an example by Jim Walsh 00262 */ 00263 while (number >= 1000.0) { 00264 xmlBufferCCat(buffer, (is_upper) ? "M" : "m"); 00265 number -= 1000.0; 00266 } 00267 if (number >= 900.0) { 00268 xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm"); 00269 number -= 900.0; 00270 } 00271 while (number >= 500.0) { 00272 xmlBufferCCat(buffer, (is_upper) ? "D" : "d"); 00273 number -= 500.0; 00274 } 00275 if (number >= 400.0) { 00276 xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd"); 00277 number -= 400.0; 00278 } 00279 while (number >= 100.0) { 00280 xmlBufferCCat(buffer, (is_upper) ? "C" : "c"); 00281 number -= 100.0; 00282 } 00283 if (number >= 90.0) { 00284 xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc"); 00285 number -= 90.0; 00286 } 00287 while (number >= 50.0) { 00288 xmlBufferCCat(buffer, (is_upper) ? "L" : "l"); 00289 number -= 50.0; 00290 } 00291 if (number >= 40.0) { 00292 xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl"); 00293 number -= 40.0; 00294 } 00295 while (number >= 10.0) { 00296 xmlBufferCCat(buffer, (is_upper) ? "X" : "x"); 00297 number -= 10.0; 00298 } 00299 if (number >= 9.0) { 00300 xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix"); 00301 number -= 9.0; 00302 } 00303 while (number >= 5.0) { 00304 xmlBufferCCat(buffer, (is_upper) ? "V" : "v"); 00305 number -= 5.0; 00306 } 00307 if (number >= 4.0) { 00308 xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv"); 00309 number -= 4.0; 00310 } 00311 while (number >= 1.0) { 00312 xmlBufferCCat(buffer, (is_upper) ? "I" : "i"); 00313 number--; 00314 } 00315 } 00316 00317 static void 00318 xsltNumberFormatTokenize(const xmlChar *format, 00319 xsltFormatPtr tokens) 00320 { 00321 int ix = 0; 00322 int j; 00323 int val; 00324 int len; 00325 00326 default_token.token = DEFAULT_TOKEN; 00327 default_token.width = 1; 00328 default_token.separator = BAD_CAST(DEFAULT_SEPARATOR); 00329 00330 00331 tokens->start = NULL; 00332 tokens->tokens[0].separator = NULL; 00333 tokens->end = NULL; 00334 00335 /* 00336 * Insert initial non-alphanumeric token. 00337 * There is always such a token in the list, even if NULL 00338 */ 00339 while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) || 00340 IS_DIGIT(val)) ) { 00341 if (format[ix] == 0) /* if end of format string */ 00342 break; /* while */ 00343 ix += len; 00344 } 00345 if (ix > 0) 00346 tokens->start = xmlStrndup(format, ix); 00347 00348 00349 for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS; 00350 tokens->nTokens++) { 00351 if (format[ix] == 0) 00352 break; /* for */ 00353 00354 /* 00355 * separator has already been parsed (except for the first 00356 * number) in tokens->end, recover it. 00357 */ 00358 if (tokens->nTokens > 0) { 00359 tokens->tokens[tokens->nTokens].separator = tokens->end; 00360 tokens->end = NULL; 00361 } 00362 00363 val = xmlStringCurrentChar(NULL, format+ix, &len); 00364 if (IS_DIGIT_ONE(val) || 00365 IS_DIGIT_ZERO(val)) { 00366 tokens->tokens[tokens->nTokens].width = 1; 00367 while (IS_DIGIT_ZERO(val)) { 00368 tokens->tokens[tokens->nTokens].width++; 00369 ix += len; 00370 val = xmlStringCurrentChar(NULL, format+ix, &len); 00371 } 00372 if (IS_DIGIT_ONE(val)) { 00373 tokens->tokens[tokens->nTokens].token = val - 1; 00374 ix += len; 00375 val = xmlStringCurrentChar(NULL, format+ix, &len); 00376 } 00377 } else if ( (val == (xmlChar)'A') || 00378 (val == (xmlChar)'a') || 00379 (val == (xmlChar)'I') || 00380 (val == (xmlChar)'i') ) { 00381 tokens->tokens[tokens->nTokens].token = val; 00382 ix += len; 00383 val = xmlStringCurrentChar(NULL, format+ix, &len); 00384 } else { 00385 /* XSLT section 7.7 00386 * "Any other format token indicates a numbering sequence 00387 * that starts with that token. If an implementation does 00388 * not support a numbering sequence that starts with that 00389 * token, it must use a format token of 1." 00390 */ 00391 tokens->tokens[tokens->nTokens].token = (xmlChar)'0'; 00392 tokens->tokens[tokens->nTokens].width = 1; 00393 } 00394 /* 00395 * Skip over remaining alphanumeric characters from the Nd 00396 * (Number, decimal digit), Nl (Number, letter), No (Number, 00397 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt 00398 * (Letters, titlecase), Lm (Letters, modifiers), and Lo 00399 * (Letters, other (uncased)) Unicode categories. This happens 00400 * to correspond to the Letter and Digit classes from XML (and 00401 * one wonders why XSLT doesn't refer to these instead). 00402 */ 00403 while (IS_LETTER(val) || IS_DIGIT(val)) { 00404 ix += len; 00405 val = xmlStringCurrentChar(NULL, format+ix, &len); 00406 } 00407 00408 /* 00409 * Insert temporary non-alphanumeric final tooken. 00410 */ 00411 j = ix; 00412 while (! (IS_LETTER(val) || IS_DIGIT(val))) { 00413 if (val == 0) 00414 break; /* while */ 00415 ix += len; 00416 val = xmlStringCurrentChar(NULL, format+ix, &len); 00417 } 00418 if (ix > j) 00419 tokens->end = xmlStrndup(&format[j], ix - j); 00420 } 00421 } 00422 00423 static void 00424 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data, 00425 double *numbers, 00426 int numbers_max, 00427 xsltFormatPtr tokens, 00428 xmlBufferPtr buffer) 00429 { 00430 int i = 0; 00431 double number; 00432 xsltFormatTokenPtr token; 00433 00434 /* 00435 * Handle initial non-alphanumeric token 00436 */ 00437 if (tokens->start != NULL) 00438 xmlBufferCat(buffer, tokens->start); 00439 00440 for (i = 0; i < numbers_max; i++) { 00441 /* Insert number */ 00442 number = numbers[(numbers_max - 1) - i]; 00443 if (i < tokens->nTokens) { 00444 /* 00445 * The "n"th format token will be used to format the "n"th 00446 * number in the list 00447 */ 00448 token = &(tokens->tokens[i]); 00449 } else if (tokens->nTokens > 0) { 00450 /* 00451 * If there are more numbers than format tokens, then the 00452 * last format token will be used to format the remaining 00453 * numbers. 00454 */ 00455 token = &(tokens->tokens[tokens->nTokens - 1]); 00456 } else { 00457 /* 00458 * If there are no format tokens, then a format token of 00459 * 1 is used to format all numbers. 00460 */ 00461 token = &default_token; 00462 } 00463 00464 /* Print separator, except for the first number */ 00465 if (i > 0) { 00466 if (token->separator != NULL) 00467 xmlBufferCat(buffer, token->separator); 00468 else 00469 xmlBufferCCat(buffer, DEFAULT_SEPARATOR); 00470 } 00471 00472 switch (xmlXPathIsInf(number)) { 00473 case -1: 00474 xmlBufferCCat(buffer, "-Infinity"); 00475 break; 00476 case 1: 00477 xmlBufferCCat(buffer, "Infinity"); 00478 break; 00479 default: 00480 if (xmlXPathIsNaN(number)) { 00481 xmlBufferCCat(buffer, "NaN"); 00482 } else { 00483 00484 switch (token->token) { 00485 case 'A': 00486 xsltNumberFormatAlpha(buffer, 00487 number, 00488 TRUE); 00489 00490 break; 00491 case 'a': 00492 xsltNumberFormatAlpha(buffer, 00493 number, 00494 FALSE); 00495 00496 break; 00497 case 'I': 00498 xsltNumberFormatRoman(buffer, 00499 number, 00500 TRUE); 00501 00502 break; 00503 case 'i': 00504 xsltNumberFormatRoman(buffer, 00505 number, 00506 FALSE); 00507 00508 break; 00509 default: 00510 if (IS_DIGIT_ZERO(token->token)) { 00511 xsltNumberFormatDecimal(buffer, 00512 number, 00513 token->token, 00514 token->width, 00515 data->digitsPerGroup, 00516 data->groupingCharacter, 00517 data->groupingCharacterLen); 00518 } 00519 break; 00520 } 00521 } 00522 00523 } 00524 } 00525 00526 /* 00527 * Handle final non-alphanumeric token 00528 */ 00529 if (tokens->end != NULL) 00530 xmlBufferCat(buffer, tokens->end); 00531 00532 } 00533 00534 static int 00535 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context, 00536 xmlNodePtr node, 00537 const xmlChar *count, 00538 const xmlChar *from, 00539 double *array, 00540 xmlDocPtr doc, 00541 xmlNodePtr elem) 00542 { 00543 int amount = 0; 00544 int cnt = 0; 00545 xmlNodePtr cur; 00546 xsltCompMatchPtr countPat = NULL; 00547 xsltCompMatchPtr fromPat = NULL; 00548 00549 if (count != NULL) 00550 countPat = xsltCompilePattern(count, doc, elem, NULL, context); 00551 if (from != NULL) 00552 fromPat = xsltCompilePattern(from, doc, elem, NULL, context); 00553 00554 /* select the starting node */ 00555 switch (node->type) { 00556 case XML_ELEMENT_NODE: 00557 cur = node; 00558 break; 00559 case XML_ATTRIBUTE_NODE: 00560 cur = ((xmlAttrPtr) node)->parent; 00561 break; 00562 case XML_TEXT_NODE: 00563 case XML_PI_NODE: 00564 case XML_COMMENT_NODE: 00565 cur = node->parent; 00566 break; 00567 default: 00568 cur = NULL; 00569 break; 00570 } 00571 00572 while (cur != NULL) { 00573 /* process current node */ 00574 if (count == NULL) { 00575 if ((node->type == cur->type) && 00576 /* FIXME: must use expanded-name instead of local name */ 00577 xmlStrEqual(node->name, cur->name)) { 00578 if ((node->ns == cur->ns) || 00579 ((node->ns != NULL) && 00580 (cur->ns != NULL) && 00581 (xmlStrEqual(node->ns->href, 00582 cur->ns->href) ))) 00583 cnt++; 00584 } 00585 } else { 00586 if (xsltTestCompMatchList(context, cur, countPat)) 00587 cnt++; 00588 } 00589 if ((from != NULL) && 00590 xsltTestCompMatchList(context, cur, fromPat)) { 00591 break; /* while */ 00592 } 00593 00594 /* Skip to next preceding or ancestor */ 00595 if ((cur->type == XML_DOCUMENT_NODE) || 00596 #ifdef LIBXML_DOCB_ENABLED 00597 (cur->type == XML_DOCB_DOCUMENT_NODE) || 00598 #endif 00599 (cur->type == XML_HTML_DOCUMENT_NODE)) 00600 break; /* while */ 00601 00602 while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) || 00603 (cur->prev->type == XML_XINCLUDE_START) || 00604 (cur->prev->type == XML_XINCLUDE_END))) 00605 cur = cur->prev; 00606 if (cur->prev != NULL) { 00607 for (cur = cur->prev; cur->last != NULL; cur = cur->last); 00608 } else { 00609 cur = cur->parent; 00610 } 00611 00612 } 00613 00614 array[amount++] = (double) cnt; 00615 00616 if (countPat != NULL) 00617 xsltFreeCompMatchList(countPat); 00618 if (fromPat != NULL) 00619 xsltFreeCompMatchList(fromPat); 00620 return(amount); 00621 } 00622 00623 static int 00624 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context, 00625 xmlNodePtr node, 00626 const xmlChar *count, 00627 const xmlChar *from, 00628 double *array, 00629 int max, 00630 xmlDocPtr doc, 00631 xmlNodePtr elem) 00632 { 00633 int amount = 0; 00634 int cnt; 00635 xmlNodePtr ancestor; 00636 xmlNodePtr preceding; 00637 xmlXPathParserContextPtr parser; 00638 xsltCompMatchPtr countPat; 00639 xsltCompMatchPtr fromPat; 00640 00641 if (count != NULL) 00642 countPat = xsltCompilePattern(count, doc, elem, NULL, context); 00643 else 00644 countPat = NULL; 00645 if (from != NULL) 00646 fromPat = xsltCompilePattern(from, doc, elem, NULL, context); 00647 else 00648 fromPat = NULL; 00649 context->xpathCtxt->node = node; 00650 parser = xmlXPathNewParserContext(NULL, context->xpathCtxt); 00651 if (parser) { 00652 /* ancestor-or-self::*[count] */ 00653 for (ancestor = node; 00654 (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE); 00655 ancestor = xmlXPathNextAncestor(parser, ancestor)) { 00656 00657 if ((from != NULL) && 00658 xsltTestCompMatchList(context, ancestor, fromPat)) 00659 break; /* for */ 00660 00661 if ((count == NULL && node->type == ancestor->type && 00662 xmlStrEqual(node->name, ancestor->name)) || 00663 xsltTestCompMatchList(context, ancestor, countPat)) { 00664 /* count(preceding-sibling::*) */ 00665 cnt = 0; 00666 for (preceding = ancestor; 00667 preceding != NULL; 00668 preceding = 00669 xmlXPathNextPrecedingSibling(parser, preceding)) { 00670 if (count == NULL) { 00671 if ((preceding->type == ancestor->type) && 00672 xmlStrEqual(preceding->name, ancestor->name)){ 00673 if ((preceding->ns == ancestor->ns) || 00674 ((preceding->ns != NULL) && 00675 (ancestor->ns != NULL) && 00676 (xmlStrEqual(preceding->ns->href, 00677 ancestor->ns->href) ))) 00678 cnt++; 00679 } 00680 } else { 00681 if (xsltTestCompMatchList(context, preceding, 00682 countPat)) 00683 cnt++; 00684 } 00685 } 00686 array[amount++] = (double)cnt; 00687 if (amount >= max) 00688 break; /* for */ 00689 } 00690 } 00691 xmlXPathFreeParserContext(parser); 00692 } 00693 xsltFreeCompMatchList(countPat); 00694 xsltFreeCompMatchList(fromPat); 00695 return amount; 00696 } 00697 00698 static int 00699 xsltNumberFormatGetValue(xmlXPathContextPtr context, 00700 xmlNodePtr node, 00701 const xmlChar *value, 00702 double *number) 00703 { 00704 int amount = 0; 00705 xmlBufferPtr pattern; 00706 xmlXPathObjectPtr obj; 00707 00708 pattern = xmlBufferCreate(); 00709 if (pattern != NULL) { 00710 xmlBufferCCat(pattern, "number("); 00711 xmlBufferCat(pattern, value); 00712 xmlBufferCCat(pattern, ")"); 00713 context->node = node; 00714 obj = xmlXPathEvalExpression(xmlBufferContent(pattern), 00715 context); 00716 if (obj != NULL) { 00717 *number = obj->floatval; 00718 amount++; 00719 xmlXPathFreeObject(obj); 00720 } 00721 xmlBufferFree(pattern); 00722 } 00723 return amount; 00724 } 00725 00734 void 00735 xsltNumberFormat(xsltTransformContextPtr ctxt, 00736 xsltNumberDataPtr data, 00737 xmlNodePtr node) 00738 { 00739 xmlBufferPtr output = NULL; 00740 int amount, i; 00741 double number; 00742 xsltFormat tokens; 00743 int tempformat = 0; 00744 00745 if ((data->format == NULL) && (data->has_format != 0)) { 00746 data->format = xsltEvalAttrValueTemplate(ctxt, data->node, 00747 (const xmlChar *) "format", 00748 XSLT_NAMESPACE); 00749 tempformat = 1; 00750 } 00751 if (data->format == NULL) { 00752 return; 00753 } 00754 00755 output = xmlBufferCreate(); 00756 if (output == NULL) 00757 goto XSLT_NUMBER_FORMAT_END; 00758 00759 xsltNumberFormatTokenize(data->format, &tokens); 00760 00761 /* 00762 * Evaluate the XPath expression to find the value(s) 00763 */ 00764 if (data->value) { 00765 amount = xsltNumberFormatGetValue(ctxt->xpathCtxt, 00766 node, 00767 data->value, 00768 &number); 00769 if (amount == 1) { 00770 xsltNumberFormatInsertNumbers(data, 00771 &number, 00772 1, 00773 &tokens, 00774 output); 00775 } 00776 00777 } else if (data->level) { 00778 00779 if (xmlStrEqual(data->level, (const xmlChar *) "single")) { 00780 amount = xsltNumberFormatGetMultipleLevel(ctxt, 00781 node, 00782 data->count, 00783 data->from, 00784 &number, 00785 1, 00786 data->doc, 00787 data->node); 00788 if (amount == 1) { 00789 xsltNumberFormatInsertNumbers(data, 00790 &number, 00791 1, 00792 &tokens, 00793 output); 00794 } 00795 } else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) { 00796 double numarray[1024]; 00797 int max = sizeof(numarray)/sizeof(numarray[0]); 00798 amount = xsltNumberFormatGetMultipleLevel(ctxt, 00799 node, 00800 data->count, 00801 data->from, 00802 numarray, 00803 max, 00804 data->doc, 00805 data->node); 00806 if (amount > 0) { 00807 xsltNumberFormatInsertNumbers(data, 00808 numarray, 00809 amount, 00810 &tokens, 00811 output); 00812 } 00813 } else if (xmlStrEqual(data->level, (const xmlChar *) "any")) { 00814 amount = xsltNumberFormatGetAnyLevel(ctxt, 00815 node, 00816 data->count, 00817 data->from, 00818 &number, 00819 data->doc, 00820 data->node); 00821 if (amount > 0) { 00822 xsltNumberFormatInsertNumbers(data, 00823 &number, 00824 1, 00825 &tokens, 00826 output); 00827 } 00828 } 00829 } 00830 /* Insert number as text node */ 00831 xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0); 00832 00833 if (tokens.start != NULL) 00834 xmlFree(tokens.start); 00835 if (tokens.end != NULL) 00836 xmlFree(tokens.end); 00837 for (i = 0;i < tokens.nTokens;i++) { 00838 if (tokens.tokens[i].separator != NULL) 00839 xmlFree(tokens.tokens[i].separator); 00840 } 00841 00842 XSLT_NUMBER_FORMAT_END: 00843 if (tempformat == 1) { 00844 /* The format need to be recomputed each time */ 00845 data->format = NULL; 00846 } 00847 if (output != NULL) 00848 xmlBufferFree(output); 00849 } 00850 00851 static int 00852 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info) 00853 { 00854 int count=0; /* will hold total length of prefix/suffix */ 00855 int len; 00856 00857 while (1) { 00858 /* 00859 * prefix / suffix ends at end of string or at 00860 * first 'special' character 00861 */ 00862 if (**format == 0) 00863 return count; 00864 /* if next character 'escaped' just count it */ 00865 if (**format == SYMBOL_QUOTE) { 00866 if (*++(*format) == 0) 00867 return -1; 00868 } 00869 else if (IS_SPECIAL(self, *format)) 00870 return count; 00871 /* 00872 * else treat percent/per-mille as special cases, 00873 * depending on whether +ve or -ve 00874 */ 00875 else { 00876 /* 00877 * for +ve prefix/suffix, allow only a 00878 * single occurence of either 00879 */ 00880 if (xsltUTF8Charcmp(*format, self->percent) == 0) { 00881 if (info->is_multiplier_set) 00882 return -1; 00883 info->multiplier = 100; 00884 info->is_multiplier_set = TRUE; 00885 } else if (xsltUTF8Charcmp(*format, self->permille) == 0) { 00886 if (info->is_multiplier_set) 00887 return -1; 00888 info->multiplier = 1000; 00889 info->is_multiplier_set = TRUE; 00890 } 00891 } 00892 00893 if ((len=xsltUTF8Size(*format)) < 1) 00894 return -1; 00895 count += len; 00896 *format += len; 00897 } 00898 } 00899 00942 xmlXPathError 00943 xsltFormatNumberConversion(xsltDecimalFormatPtr self, 00944 xmlChar *format, 00945 double number, 00946 xmlChar **result) 00947 { 00948 xmlXPathError status = XPATH_EXPRESSION_OK; 00949 xmlBufferPtr buffer; 00950 xmlChar *the_format, *prefix = NULL, *suffix = NULL; 00951 xmlChar *nprefix, *nsuffix = NULL; 00952 xmlChar pchar; 00953 int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length; 00954 double scale; 00955 int j, len; 00956 int self_grouping_len; 00957 xsltFormatNumberInfo format_info; 00958 /* 00959 * delayed_multiplier allows a 'trailing' percent or 00960 * permille to be treated as suffix 00961 */ 00962 int delayed_multiplier = 0; 00963 /* flag to show no -ve format present for -ve number */ 00964 char default_sign = 0; 00965 /* flag to show error found, should use default format */ 00966 char found_error = 0; 00967 00968 if (xmlStrlen(format) <= 0) { 00969 xsltTransformError(NULL, NULL, NULL, 00970 "xsltFormatNumberConversion : " 00971 "Invalid format (0-length)\n"); 00972 } 00973 *result = NULL; 00974 switch (xmlXPathIsInf(number)) { 00975 case -1: 00976 if (self->minusSign == NULL) 00977 *result = xmlStrdup(BAD_CAST "-"); 00978 else 00979 *result = xmlStrdup(self->minusSign); 00980 /* no-break on purpose */ 00981 case 1: 00982 if ((self == NULL) || (self->infinity == NULL)) 00983 *result = xmlStrcat(*result, BAD_CAST "Infinity"); 00984 else 00985 *result = xmlStrcat(*result, self->infinity); 00986 return(status); 00987 default: 00988 if (xmlXPathIsNaN(number)) { 00989 if ((self == NULL) || (self->noNumber == NULL)) 00990 *result = xmlStrdup(BAD_CAST "NaN"); 00991 else 00992 *result = xmlStrdup(self->noNumber); 00993 return(status); 00994 } 00995 } 00996 00997 buffer = xmlBufferCreate(); 00998 if (buffer == NULL) { 00999 return XPATH_MEMORY_ERROR; 01000 } 01001 01002 format_info.integer_hash = 0; 01003 format_info.integer_digits = 0; 01004 format_info.frac_digits = 0; 01005 format_info.frac_hash = 0; 01006 format_info.group = -1; 01007 format_info.multiplier = 1; 01008 format_info.add_decimal = FALSE; 01009 format_info.is_multiplier_set = FALSE; 01010 format_info.is_negative_pattern = FALSE; 01011 01012 the_format = format; 01013 01014 /* 01015 * First we process the +ve pattern to get percent / permille, 01016 * as well as main format 01017 */ 01018 prefix = the_format; 01019 prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); 01020 if (prefix_length < 0) { 01021 found_error = 1; 01022 goto OUTPUT_NUMBER; 01023 } 01024 01025 /* 01026 * Here we process the "number" part of the format. It gets 01027 * a little messy because of the percent/per-mille - if that 01028 * appears at the end, it may be part of the suffix instead 01029 * of part of the number, so the variable delayed_multiplier 01030 * is used to handle it 01031 */ 01032 self_grouping_len = xmlStrlen(self->grouping); 01033 while ((*the_format != 0) && 01034 (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) && 01035 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) { 01036 01037 if (delayed_multiplier != 0) { 01038 format_info.multiplier = delayed_multiplier; 01039 format_info.is_multiplier_set = TRUE; 01040 delayed_multiplier = 0; 01041 } 01042 if (xsltUTF8Charcmp(the_format, self->digit) == 0) { 01043 if (format_info.integer_digits > 0) { 01044 found_error = 1; 01045 goto OUTPUT_NUMBER; 01046 } 01047 format_info.integer_hash++; 01048 if (format_info.group >= 0) 01049 format_info.group++; 01050 } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) { 01051 format_info.integer_digits++; 01052 if (format_info.group >= 0) 01053 format_info.group++; 01054 } else if ((self_grouping_len > 0) && 01055 (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) { 01056 /* Reset group count */ 01057 format_info.group = 0; 01058 the_format += self_grouping_len; 01059 continue; 01060 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) { 01061 if (format_info.is_multiplier_set) { 01062 found_error = 1; 01063 goto OUTPUT_NUMBER; 01064 } 01065 delayed_multiplier = 100; 01066 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) { 01067 if (format_info.is_multiplier_set) { 01068 found_error = 1; 01069 goto OUTPUT_NUMBER; 01070 } 01071 delayed_multiplier = 1000; 01072 } else 01073 break; /* while */ 01074 01075 if ((len=xsltUTF8Size(the_format)) < 1) { 01076 found_error = 1; 01077 goto OUTPUT_NUMBER; 01078 } 01079 the_format += len; 01080 01081 } 01082 01083 /* We have finished the integer part, now work on fraction */ 01084 if (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) { 01085 format_info.add_decimal = TRUE; 01086 the_format += xsltUTF8Size(the_format); /* Skip over the decimal */ 01087 } 01088 01089 while (*the_format != 0) { 01090 01091 if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) { 01092 if (format_info.frac_hash != 0) { 01093 found_error = 1; 01094 goto OUTPUT_NUMBER; 01095 } 01096 format_info.frac_digits++; 01097 } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) { 01098 format_info.frac_hash++; 01099 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) { 01100 if (format_info.is_multiplier_set) { 01101 found_error = 1; 01102 goto OUTPUT_NUMBER; 01103 } 01104 delayed_multiplier = 100; 01105 if ((len = xsltUTF8Size(the_format)) < 1) { 01106 found_error = 1; 01107 goto OUTPUT_NUMBER; 01108 } 01109 the_format += len; 01110 continue; /* while */ 01111 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) { 01112 if (format_info.is_multiplier_set) { 01113 found_error = 1; 01114 goto OUTPUT_NUMBER; 01115 } 01116 delayed_multiplier = 1000; 01117 if ((len = xsltUTF8Size(the_format)) < 1) { 01118 found_error = 1; 01119 goto OUTPUT_NUMBER; 01120 } 01121 the_format += len; 01122 continue; /* while */ 01123 } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) { 01124 break; /* while */ 01125 } 01126 if ((len = xsltUTF8Size(the_format)) < 1) { 01127 found_error = 1; 01128 goto OUTPUT_NUMBER; 01129 } 01130 the_format += len; 01131 if (delayed_multiplier != 0) { 01132 format_info.multiplier = delayed_multiplier; 01133 delayed_multiplier = 0; 01134 format_info.is_multiplier_set = TRUE; 01135 } 01136 } 01137 01138 /* 01139 * If delayed_multiplier is set after processing the 01140 * "number" part, should be in suffix 01141 */ 01142 if (delayed_multiplier != 0) { 01143 the_format -= len; 01144 delayed_multiplier = 0; 01145 } 01146 01147 suffix = the_format; 01148 suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); 01149 if ( (suffix_length < 0) || 01150 ((*the_format != 0) && 01151 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) { 01152 found_error = 1; 01153 goto OUTPUT_NUMBER; 01154 } 01155 01156 /* 01157 * We have processed the +ve prefix, number part and +ve suffix. 01158 * If the number is -ve, we must substitute the -ve prefix / suffix 01159 */ 01160 if (number < 0) { 01161 /* 01162 * Note that j is the number of UTF8 chars before the separator, 01163 * not the number of bytes! (bug 151975) 01164 */ 01165 j = xmlUTF8Strloc(format, self->patternSeparator); 01166 if (j < 0) { 01167 /* No -ve pattern present, so use default signing */ 01168 default_sign = 1; 01169 } 01170 else { 01171 /* Skip over pattern separator (accounting for UTF8) */ 01172 the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1); 01173 /* 01174 * Flag changes interpretation of percent/permille 01175 * in -ve pattern 01176 */ 01177 format_info.is_negative_pattern = TRUE; 01178 format_info.is_multiplier_set = FALSE; 01179 01180 /* First do the -ve prefix */ 01181 nprefix = the_format; 01182 nprefix_length = xsltFormatNumberPreSuffix(self, 01183 &the_format, &format_info); 01184 if (nprefix_length<0) { 01185 found_error = 1; 01186 goto OUTPUT_NUMBER; 01187 } 01188 01189 while (*the_format != 0) { 01190 if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) || 01191 (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) { 01192 if (format_info.is_multiplier_set) { 01193 found_error = 1; 01194 goto OUTPUT_NUMBER; 01195 } 01196 format_info.is_multiplier_set = TRUE; 01197 delayed_multiplier = 1; 01198 } 01199 else if (IS_SPECIAL(self, the_format)) 01200 delayed_multiplier = 0; 01201 else 01202 break; /* while */ 01203 if ((len = xsltUTF8Size(the_format)) < 1) { 01204 found_error = 1; 01205 goto OUTPUT_NUMBER; 01206 } 01207 the_format += len; 01208 } 01209 if (delayed_multiplier != 0) { 01210 format_info.is_multiplier_set = FALSE; 01211 the_format -= len; 01212 } 01213 01214 /* Finally do the -ve suffix */ 01215 if (*the_format != 0) { 01216 nsuffix = the_format; 01217 nsuffix_length = xsltFormatNumberPreSuffix(self, 01218 &the_format, &format_info); 01219 if (nsuffix_length < 0) { 01220 found_error = 1; 01221 goto OUTPUT_NUMBER; 01222 } 01223 } 01224 else 01225 nsuffix_length = 0; 01226 if (*the_format != 0) { 01227 found_error = 1; 01228 goto OUTPUT_NUMBER; 01229 } 01230 /* 01231 * Here's another Java peculiarity: 01232 * if -ve prefix/suffix == +ve ones, discard & use default 01233 */ 01234 if ((nprefix_length != prefix_length) || 01235 (nsuffix_length != suffix_length) || 01236 ((nprefix_length > 0) && 01237 (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) || 01238 ((nsuffix_length > 0) && 01239 (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) { 01240 prefix = nprefix; 01241 prefix_length = nprefix_length; 01242 suffix = nsuffix; 01243 suffix_length = nsuffix_length; 01244 } /* else { 01245 default_sign = 1; 01246 } 01247 */ 01248 } 01249 } 01250 01251 OUTPUT_NUMBER: 01252 if (found_error != 0) { 01253 xsltTransformError(NULL, NULL, NULL, 01254 "xsltFormatNumberConversion : " 01255 "error in format string '%s', using default\n", format); 01256 default_sign = (number < 0.0) ? 1 : 0; 01257 prefix_length = suffix_length = 0; 01258 format_info.integer_hash = 0; 01259 format_info.integer_digits = 1; 01260 format_info.frac_digits = 1; 01261 format_info.frac_hash = 4; 01262 format_info.group = -1; 01263 format_info.multiplier = 1; 01264 format_info.add_decimal = TRUE; 01265 } 01266 01267 /* Ready to output our number. First see if "default sign" is required */ 01268 if (default_sign != 0) 01269 xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign)); 01270 01271 /* Put the prefix into the buffer */ 01272 for (j = 0; j < prefix_length; j++) { 01273 if ((pchar = *prefix++) == SYMBOL_QUOTE) { 01274 len = xsltUTF8Size(prefix); 01275 xmlBufferAdd(buffer, prefix, len); 01276 prefix += len; 01277 j += len - 1; /* length of symbol less length of quote */ 01278 } else 01279 xmlBufferAdd(buffer, &pchar, 1); 01280 } 01281 01282 /* Next do the integer part of the number */ 01283 number = fabs(number) * (double)format_info.multiplier; 01284 scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash)); 01285 number = floor((scale * number + 0.5)) / scale; 01286 if ((self->grouping != NULL) && 01287 (self->grouping[0] != 0)) { 01288 01289 len = xmlStrlen(self->grouping); 01290 pchar = xsltGetUTF8Char(self->grouping, &len); 01291 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], 01292 format_info.integer_digits, 01293 format_info.group, 01294 pchar, len); 01295 } else 01296 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], 01297 format_info.integer_digits, 01298 format_info.group, 01299 ',', 1); 01300 01301 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */ 01302 if ((format_info.integer_digits + format_info.integer_hash + 01303 format_info.frac_digits == 0) && (format_info.frac_hash > 0)) { 01304 ++format_info.frac_digits; 01305 --format_info.frac_hash; 01306 } 01307 01308 /* Add leading zero, if required */ 01309 if ((floor(number) == 0) && 01310 (format_info.integer_digits + format_info.frac_digits == 0)) { 01311 xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit)); 01312 } 01313 01314 /* Next the fractional part, if required */ 01315 if (format_info.frac_digits + format_info.frac_hash == 0) { 01316 if (format_info.add_decimal) 01317 xmlBufferAdd(buffer, self->decimalPoint, 01318 xsltUTF8Size(self->decimalPoint)); 01319 } 01320 else { 01321 number -= floor(number); 01322 if ((number != 0) || (format_info.frac_digits != 0)) { 01323 xmlBufferAdd(buffer, self->decimalPoint, 01324 xsltUTF8Size(self->decimalPoint)); 01325 number = floor(scale * number + 0.5); 01326 for (j = format_info.frac_hash; j > 0; j--) { 01327 if (fmod(number, 10.0) >= 1.0) 01328 break; /* for */ 01329 number /= 10.0; 01330 } 01331 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], 01332 format_info.frac_digits + j, 01333 0, 0, 0); 01334 } 01335 } 01336 /* Put the suffix into the buffer */ 01337 for (j = 0; j < suffix_length; j++) { 01338 if ((pchar = *suffix++) == SYMBOL_QUOTE) { 01339 len = xsltUTF8Size(suffix); 01340 xmlBufferAdd(buffer, suffix, len); 01341 suffix += len; 01342 j += len - 1; /* length of symbol less length of escape */ 01343 } else 01344 xmlBufferAdd(buffer, &pchar, 1); 01345 } 01346 01347 *result = xmlStrdup(xmlBufferContent(buffer)); 01348 xmlBufferFree(buffer); 01349 return status; 01350 } 01351 Generated on Sun May 27 2012 04:19:37 for ReactOS by
1.7.6.1
|