static xmlNodeSetPtr exsltMathLowest (xmlNodeSetPtr ns) { xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL); double min, cur; int i; if ((ns == NULL) || (ns->nodeNr == 0)) return(ret); min = xmlXPathCastNodeToNumber(ns->nodeTab[0]); if (xmlXPathIsNaN(min)) return(ret); else xmlXPathNodeSetAddUnique(ret, ns->nodeTab[0]); for (i = 1; i < ns->nodeNr; i++) { cur = xmlXPathCastNodeToNumber(ns->nodeTab[i]); if (xmlXPathIsNaN(cur)) { xmlXPathEmptyNodeSet(ret); return(ret); } if (cur > min) continue; if (cur < min) { min = cur; xmlXPathEmptyNodeSet(ret); xmlXPathNodeSetAddUnique(ret, ns->nodeTab[i]); continue; } xmlXPathNodeSetAddUnique(ret, ns->nodeTab[i]); } return(ret); }
/** * exsltMathPower: * @base: a double * @power: a double * * Implements the EXSLT - Math power() function: * number math:power (number, number) * * Returns the power base and power arguments, or xmlXPathNAN * if either @base or @power is Nan. */ static double exsltMathPower (double base, double power) { double ret; if ((xmlXPathIsNaN(base) || xmlXPathIsNaN(power))) return(xmlXPathNAN); ret = pow(base, power); return(ret); }
/** * exsltMathAtan2: * @y: a double * @x: a double * * Implements the EXSLT - Math atan2() function: * number math:atan2 (number, number) * * Returns the arc tangent function of the y/x arguments, or xmlXPathNAN * if either @y or @x is Nan. */ static double exsltMathAtan2 (double y, double x) { double ret; if ((xmlXPathIsNaN(y) || xmlXPathIsNaN(x))) return(xmlXPathNAN); ret = atan2(y, x); return(ret); }
Datum xpath_number(PG_FUNCTION_ARGS) { text *document = PG_GETARG_TEXT_P(0); text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ xmlChar *xpath; float4 fRes; xmlXPathObjectPtr res; xpath_workspace workspace; xpath = pgxml_texttoxmlchar(xpathsupp); res = pgxml_xpath(document, xpath, &workspace); pfree(xpath); if (res == NULL) PG_RETURN_NULL(); fRes = xmlXPathCastToNumber(res); cleanup_workspace(&workspace); if (xmlXPathIsNaN(fRes)) PG_RETURN_NULL(); PG_RETURN_FLOAT4(fRes); }
Datum xpath_number(PG_FUNCTION_ARGS) { xmlChar *xpath; int32 pathsize; text *xpathsupp; float4 fRes; xmlXPathObjectPtr res; /* PG_GETARG_TEXT_P(0) is document buffer */ xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ pathsize = VARSIZE(xpathsupp) - VARHDRSZ; xpath = pgxml_texttoxmlchar(xpathsupp); res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath); pfree(xpath); if (res == NULL) PG_RETURN_NULL(); fRes = xmlXPathCastToNumber(res); if (xmlXPathIsNaN(fRes)) PG_RETURN_NULL(); PG_RETURN_FLOAT4(fRes); }
SEXP convertXPathObjectToR(xmlXPathObjectPtr obj, SEXP fun, int encoding, SEXP manageMemory) { SEXP ans = NULL_USER_OBJECT; switch(obj->type) { case XPATH_NODESET: ans = convertNodeSetToR(obj->nodesetval, fun, encoding, manageMemory); break; case XPATH_BOOLEAN: ans = ScalarLogical(obj->boolval); break; case XPATH_NUMBER: ans = ScalarReal(obj->floatval); if(xmlXPathIsInf(obj->floatval)) REAL(ans)[0] = xmlXPathIsInf(obj->floatval) < 0 ? R_NegInf : R_PosInf; else if(xmlXPathIsNaN(obj->floatval)) REAL(ans)[0] = NA_REAL; break; case XPATH_STRING: ans = mkString(XMLCHAR_TO_CHAR(obj->stringval)); //XXX encoding break; case XPATH_POINT: case XPATH_RANGE: case XPATH_LOCATIONSET: case XPATH_USERS: PROBLEM "currently unsupported xmlXPathObject type %d in convertXPathObjectToR. Please send mail to maintainer.", obj->type WARN default: ans = R_NilValue; } return(ans); }
/** * exsltMathLog: * @num: a double * * Implements the EXSLT - Math log() function: * number math:log (number) * * Returns the natural log of the argument, or xmlXPathNAN if @num is Nan. */ static double exsltMathLog (double num) { double ret; if (xmlXPathIsNaN(num)) return(xmlXPathNAN); ret = log(num); return(ret); }
/** * exsltMathCos: * @num: a double * * Implements the EXSLT - Math cos() function: * number math:cos (number) * * Returns the cosine of the argument, or xmlXPathNAN if @num is Nan. */ static double exsltMathCos (double num) { double ret; if (xmlXPathIsNaN(num)) return(xmlXPathNAN); ret = cos(num); return(ret); }
/** * exsltMathAtan: * @num: a double * * Implements the EXSLT - Math atan() function: * number math:atan (number) * * Returns the arc tangent of the argument, or xmlXPathNAN if @num is Nan. */ static double exsltMathAtan (double num) { double ret; if (xmlXPathIsNaN(num)) return(xmlXPathNAN); ret = atan(num); return(ret); }
/** * exsltMathSqrt: * @num: a double * * Implements the EXSLT - Math sqrt() function: * number math:sqrt (number) * * Returns the square root of the argument, or xmlXPathNAN if @num is Nan. */ static double exsltMathSqrt (double num) { double ret; if (xmlXPathIsNaN(num)) return(xmlXPathNAN); ret = sqrt(num); return(ret); }
/** * exsltMathExp: * @num: a double * * Implements the EXSLT - Math exp() function: * number math:exp (number) * * Returns the exponential function of the argument, or xmlXPathNAN if * @num is Nan. */ static double exsltMathExp (double num) { double ret; if (xmlXPathIsNaN(num)) return(xmlXPathNAN); ret = exp(num); return(ret); }
/** * exsltMathMin: * @ns: a node-set * * Implements the EXSLT - Math min() function: * number math:min (node-set) * * Returns the minimum value of the nodes passed as the argument, or * xmlXPathNAN if @ns is NULL or empty or if one of the nodes * turns into NaN. */ static double exsltMathMin (xmlNodeSetPtr ns) { double ret, cur; int i; if ((ns == NULL) || (ns->nodeNr == 0)) return(xmlXPathNAN); ret = xmlXPathCastNodeToNumber(ns->nodeTab[0]); if (xmlXPathIsNaN(ret)) return(xmlXPathNAN); for (i = 1; i < ns->nodeNr; i++) { cur = xmlXPathCastNodeToNumber(ns->nodeTab[i]); if (xmlXPathIsNaN(cur)) return(xmlXPathNAN); if (cur < ret) ret = cur; } return(ret); }
/** * xmlSchematronRunTest: * @ctxt: the schema validation context * @test: the current test * @instance: the document instace tree * @cur: the current node in the instance * * Validate a rule against a tree instance at a given position * * Returns 1 in case of success, 0 if error and -1 in case of internal error */ static int xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt, xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern) { xmlXPathObjectPtr ret; int failed; failed = 0; ctxt->xctxt->doc = instance; ctxt->xctxt->node = cur; ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt); if (ret == NULL) { failed = 1; } else { switch (ret->type) { case XPATH_XSLT_TREE: case XPATH_NODESET: if ((ret->nodesetval == NULL) || (ret->nodesetval->nodeNr == 0)) failed = 1; break; case XPATH_BOOLEAN: failed = !ret->boolval; break; case XPATH_NUMBER: if ((xmlXPathIsNaN(ret->floatval)) || (ret->floatval == 0.0)) failed = 1; break; case XPATH_STRING: if ((ret->stringval == NULL) || (ret->stringval[0] == 0)) failed = 1; break; case XPATH_UNDEFINED: case XPATH_POINT: case XPATH_RANGE: case XPATH_LOCATIONSET: case XPATH_USERS: failed = 1; break; } xmlXPathFreeObject(ret); } if ((failed) && (test->type == XML_SCHEMATRON_ASSERT)) ctxt->nberrors++; else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT)) ctxt->nberrors++; xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed); return(!failed); }
static void xsltNumberFormatInsertNumbers(xsltNumberDataPtr data, double *numbers, int numbers_max, xsltFormatPtr tokens, xmlBufferPtr buffer) { int i = 0; double number; xsltFormatTokenPtr token; /* * Handle initial non-alphanumeric token */ if (tokens->start != NULL) xmlBufferCat(buffer, tokens->start); for (i = 0; i < numbers_max; i++) { /* Insert number */ number = numbers[(numbers_max - 1) - i]; /* Round to nearest like XSLT 2.0 */ number = floor(number + 0.5); /* * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT * 2.0 says: * * It is a non-recoverable dynamic error if any undiscarded item * in the atomized sequence supplied as the value of the value * attribute of xsl:number cannot be converted to an integer, or * if the resulting integer is less than 0 (zero). */ if (number < 0.0) { xsltTransformError(NULL, NULL, NULL, "xsl-number : negative value\n"); /* Recover by treating negative values as zero. */ number = 0.0; } if (i < tokens->nTokens) { /* * The "n"th format token will be used to format the "n"th * number in the list */ token = &(tokens->tokens[i]); } else if (tokens->nTokens > 0) { /* * If there are more numbers than format tokens, then the * last format token will be used to format the remaining * numbers. */ token = &(tokens->tokens[tokens->nTokens - 1]); } else { /* * If there are no format tokens, then a format token of * 1 is used to format all numbers. */ token = &default_token; } /* Print separator, except for the first number */ if (i > 0) { if (token->separator != NULL) xmlBufferCat(buffer, token->separator); else xmlBufferCCat(buffer, DEFAULT_SEPARATOR); } switch (xmlXPathIsInf(number)) { case -1: xmlBufferCCat(buffer, "-Infinity"); break; case 1: xmlBufferCCat(buffer, "Infinity"); break; default: if (xmlXPathIsNaN(number)) { xmlBufferCCat(buffer, "NaN"); } else { switch (token->token) { case 'A': xsltNumberFormatAlpha(data, buffer, number, TRUE); break; case 'a': xsltNumberFormatAlpha(data, buffer, number, FALSE); break; case 'I': xsltNumberFormatRoman(data, buffer, number, TRUE); break; case 'i': xsltNumberFormatRoman(data, buffer, number, FALSE); break; default: if (IS_DIGIT_ZERO(token->token)) { xsltNumberFormatDecimal(buffer, number, token->token, token->width, data->digitsPerGroup, data->groupingCharacter, data->groupingCharacterLen); } break; } } } } /* * Handle final non-alphanumeric token */ if (tokens->end != NULL) xmlBufferCat(buffer, tokens->end); }
static void xsltNumberFormatInsertNumbers(xsltNumberDataPtr data, double *numbers, int numbers_max, xsltFormatPtr tokens, xmlBufferPtr buffer) { int i = 0; double number; xsltFormatTokenPtr token; /* * Handle initial non-alphanumeric token */ if (tokens->start != NULL) xmlBufferCat(buffer, tokens->start); for (i = 0; i < numbers_max; i++) { /* Insert number */ number = numbers[(numbers_max - 1) - i]; if (i < tokens->nTokens) { /* The "n"th format token will be used to format the "n"th * number in the list */ token = &(tokens->tokens[i]); } else if (tokens->nTokens > 0) { /* If there are more numbers than format tokens, then the * last format token will be used to format the remaining * numbers. */ token = &(tokens->tokens[tokens->nTokens - 1]); } else { /* If there are no format tokens, then a format token of * 1 is used to format all numbers. */ token = &default_token; } /* Print separator, except for the first number */ if (i > 0) { if (token->separator != NULL) xmlBufferCat(buffer, token->separator); else xmlBufferCCat(buffer, DEFAULT_SEPARATOR); } switch (xmlXPathIsInf(number)) { case -1: xmlBufferCCat(buffer, "-Infinity"); break; case 1: xmlBufferCCat(buffer, "Infinity"); break; default: if (xmlXPathIsNaN(number)) { xmlBufferCCat(buffer, "NaN"); } else { switch (token->token) { case 'A': xsltNumberFormatAlpha(buffer, number, TRUE); break; case 'a': xsltNumberFormatAlpha(buffer, number, FALSE); break; case 'I': xsltNumberFormatRoman(buffer, number, TRUE); break; case 'i': xsltNumberFormatRoman(buffer, number, FALSE); break; default: if (IS_DIGIT_ZERO(token->token)) { xsltNumberFormatDecimal(buffer, number, token->token, token->width, data->digitsPerGroup, data->groupingCharacter); } break; } } } } /* * Handle final non-alphanumeric token */ if (tokens->end != NULL) xmlBufferCat(buffer, tokens->end); }
/** * xsltFormatNumberConversion: * @self: the decimal format * @format: the format requested * @number: the value to format * @result: the place to ouput the result * * format-number() uses the JDK 1.1 DecimalFormat class: * * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html * * Structure: * * pattern := subpattern{;subpattern} * subpattern := {prefix}integer{.fraction}{suffix} * prefix := '\\u0000'..'\\uFFFD' - specialCharacters * suffix := '\\u0000'..'\\uFFFD' - specialCharacters * integer := '#'* '0'* '0' * fraction := '0'* '#'* * * Notation: * X* 0 or more instances of X * (X | Y) either X or Y. * X..Y any character from X up to Y, inclusive. * S - T characters in S, except those in T * * Special Characters: * * Symbol Meaning * 0 a digit * # a digit, zero shows as absent * . placeholder for decimal separator * , placeholder for grouping separator. * ; separates formats. * - default negative prefix. * % multiply by 100 and show as percentage * ? multiply by 1000 and show as per mille * X any other characters can be used in the prefix or suffix * ' used to quote special characters in a prefix or suffix. */ xmlXPathError xsltFormatNumberConversion(xsltDecimalFormatPtr self, xmlChar *format, double number, xmlChar **result) { xmlXPathError status = XPATH_EXPRESSION_OK; xmlBufferPtr buffer; xmlChar *the_format, *prefix = NULL, *suffix = NULL; xmlChar *nprefix, *nsuffix = NULL; xmlChar pchar; int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length; double scale; int j; xsltFormatNumberInfo format_info; /* delayed_multiplier allows a 'trailing' percent or permille to be treated as suffix */ int delayed_multiplier = 0; /* flag to show no -ve format present for -ve number */ char default_sign = 0; /* flag to show error found, should use default format */ char found_error = 0; *result = NULL; switch (xmlXPathIsInf(number)) { case -1: if (self->minusSign == NULL) *result = xmlStrdup(BAD_CAST "-"); else *result = xmlStrdup(self->minusSign); /* no-break on purpose */ case 1: if ((self == NULL) || (self->infinity == NULL)) *result = xmlStrcat(*result, BAD_CAST "Infinity"); else *result = xmlStrcat(*result, self->infinity); return(status); default: if (xmlXPathIsNaN(number)) { if ((self == NULL) || (self->noNumber == NULL)) *result = xmlStrdup(BAD_CAST "NaN"); else *result = xmlStrdup(self->noNumber); return(status); } } buffer = xmlBufferCreate(); if (buffer == NULL) { return XPATH_MEMORY_ERROR; } format_info.integer_digits = 0; format_info.frac_digits = 0; format_info.frac_hash = 0; format_info.group = -1; format_info.multiplier = 1; format_info.is_multiplier_set = FALSE; format_info.is_negative_pattern = FALSE; the_format = format; /* First we process the +ve pattern to get percent / permille, as well as main format */ prefix = the_format; prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if (prefix_length < 0) { found_error = 1; goto OUTPUT_NUMBER; } /* Here we process the "number" part of the format. It gets a little messy because of */ /* the percent/per-mille - if that appears at the end, it may be part of the suffix */ /* instead of part of the number, so the variable delayed_multiplier is used to handle it */ while ((*the_format != 0) && (*the_format != self->decimalPoint[0]) && (*the_format != self->patternSeparator[0])) { if (delayed_multiplier != 0) { format_info.multiplier = delayed_multiplier; format_info.is_multiplier_set = TRUE; delayed_multiplier = 0; } if (*the_format == self->digit[0]) { if (format_info.integer_digits > 0) { found_error = 1; goto OUTPUT_NUMBER; } if (format_info.group >= 0) format_info.group++; } else if (*the_format == self->zeroDigit[0]) { format_info.integer_digits++; if (format_info.group >= 0) format_info.group++; } else if (*the_format == self->grouping[0]) { /* Reset group count */ format_info.group = 0; } else if (*the_format == self->percent[0]) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 100; } else if (*the_format == self->permille[0]) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 1000; } else break; /* while */ the_format++; } /* We have finished the integer part, now work on fraction */ if (*the_format == self->decimalPoint[0]) the_format++; /* Skip over the decimal */ while (*the_format != 0) { if (*the_format == self->zeroDigit[0]) { if (format_info.frac_hash != 0) { found_error = 1; goto OUTPUT_NUMBER; } format_info.frac_digits++; } else if (*the_format == self->digit[0]) { format_info.frac_hash++; } else if (*the_format == self->percent[0]) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 100; the_format++; continue; /* while */ } else if (*the_format == self->permille[0]) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 1000; the_format++; continue; /* while */ } else if (*the_format != self->grouping[0]) { break; /* while */ } the_format++; if (delayed_multiplier != 0) { format_info.multiplier = delayed_multiplier; delayed_multiplier = 0; format_info.is_multiplier_set = TRUE; } } /* If delayed_multiplier is set after processing the "number" part, should be in suffix */ if (delayed_multiplier != 0) { the_format--; delayed_multiplier = 0; } suffix = the_format; suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if ( (suffix_length < 0) || ((*the_format != 0) && (*the_format != self->patternSeparator[0])) ) { found_error = 1; goto OUTPUT_NUMBER; } /* We have processed the +ve prefix, number part and +ve suffix. */ /* If the number is -ve, we must substitute the -ve prefix / suffix */ if (number < 0) { the_format = (xmlChar *)xmlStrchr(format, self->patternSeparator[0]); if (the_format == NULL) { /* No -ve pattern present, so use default signing */ default_sign = 1; } else { /* Flag changes interpretation of percent/permille in -ve pattern */ the_format++; /* Skip over pattern separator */ format_info.is_negative_pattern = TRUE; format_info.is_multiplier_set = FALSE; /* First do the -ve prefix */ nprefix = the_format; nprefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if (nprefix_length<0) { found_error = 1; goto OUTPUT_NUMBER; } /* Next skip over the -ve number info */ the_format += prefix_length; while (*the_format != 0) { if ( (*the_format == (self)->percent[0]) || (*the_format == (self)->permille[0]) ) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } format_info.is_multiplier_set = TRUE; delayed_multiplier = 1; } else if (IS_SPECIAL(self, *the_format)) delayed_multiplier = 0; else break; /* while */ the_format++; } if (delayed_multiplier != 0) { format_info.is_multiplier_set = FALSE; the_format--; } /* Finally do the -ve suffix */ if (*the_format != 0) { nsuffix = the_format; nsuffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if (nsuffix_length < 0) { found_error = 1; goto OUTPUT_NUMBER; } } else nsuffix_length = 0; if (*the_format != 0) { found_error = 1; goto OUTPUT_NUMBER; } /* Here's another Java peculiarity: * if -ve prefix/suffix == +ve ones, discard & use default */ if ((nprefix_length != prefix_length) || (nsuffix_length != suffix_length) || ((nprefix_length > 0) && (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) || ((nsuffix_length > 0) && (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) { prefix = nprefix; prefix_length = nprefix_length; suffix = nsuffix; suffix_length = nsuffix_length; } else { default_sign = 1; } } } OUTPUT_NUMBER: if (found_error != 0) { xsltPrintErrorContext(NULL, NULL, NULL); xsltGenericError(xsltGenericErrorContext, "xsltFormatNumberConversion : error in format string, using default\n"); default_sign = (number < 0.0) ? 1 : 0; prefix_length = suffix_length = 0; format_info.integer_digits = 1; format_info.frac_digits = 1; format_info.frac_hash = 4; format_info.group = -1; format_info.multiplier = 1; } /* Ready to output our number. First see if "default sign" is required */ if (default_sign != 0) xmlBufferAdd(buffer, self->minusSign, 1); /* Put the prefix into the buffer */ for (j = 0; j < prefix_length; j++) { if ((pchar = *prefix++) == SYMBOL_QUOTE) { pchar = *prefix++; prefix++; } xmlBufferAdd(buffer, &pchar, 1); } /* Next do the integer part of the number */ number = fabs(number) * (double)format_info.multiplier; scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash)); number = floor((scale * number + 0.5)) / scale; if ((self->grouping != NULL) && (self->grouping[0] != 0)) xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], format_info.integer_digits, format_info.group, (xmlChar) self->grouping[0]); else xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], format_info.integer_digits, format_info.group, (xmlChar) ','); /* Next the fractional part, if required */ if (format_info.frac_digits + format_info.frac_hash > 0) { number -= floor(number); if ((number != 0) || (format_info.frac_digits != 0)) { xmlBufferAdd(buffer, self->decimalPoint, 1); number = floor(scale * number + 0.5); for (j = format_info.frac_hash; j > 0; j--) { if (fmod(number, 10.0) >= 1.0) break; /* for */ number /= 10.0; } xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], format_info.frac_digits + j, 0, (xmlChar)0); } } /* Put the suffix into the buffer */ for (j = 0; j < suffix_length; j++) { if ((pchar = *suffix++) == SYMBOL_QUOTE) { pchar = *suffix++; suffix++; } xmlBufferAdd(buffer, &pchar, 1); } *result = xmlStrdup(xmlBufferContent(buffer)); xmlBufferFree(buffer); return status; }
// Based on default implementation from libxslt 1.1.22 and xsltICUSort.c example. void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, int nbsorts) { #ifdef XSLT_REFACTORED xsltStyleItemSortPtr comp; #else xsltStylePreCompPtr comp; #endif xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; xmlXPathObjectPtr *results = NULL, *res; xmlNodeSetPtr list = NULL; int descending, number, desc, numb; int len = 0; int i, j, incr; int tst; int depth; xmlNodePtr node; xmlXPathObjectPtr tmp; int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT]; if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || (nbsorts >= XSLT_MAX_SORT)) return; if (sorts[0] == NULL) return; comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi); if (comp == NULL) return; list = ctxt->nodeList; if ((list == NULL) || (list->nodeNr <= 1)) return; /* nothing to do */ for (j = 0; j < nbsorts; j++) { comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi); tempstype[j] = 0; if ((comp->stype == NULL) && (comp->has_stype != 0)) { comp->stype = xsltEvalAttrValueTemplate(ctxt, sorts[j], (const xmlChar *) "data-type", XSLT_NAMESPACE); if (comp->stype != NULL) { tempstype[j] = 1; if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) comp->number = 0; else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) comp->number = 1; else { xsltTransformError(ctxt, NULL, sorts[j], "xsltDoSortFunction: no support for data-type = %s\n", comp->stype); comp->number = 0; /* use default */ } } } temporder[j] = 0; if ((comp->order == NULL) && (comp->has_order != 0)) { comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], (const xmlChar *) "order", XSLT_NAMESPACE); if (comp->order != NULL) { temporder[j] = 1; if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) comp->descending = 0; else if (xmlStrEqual(comp->order, (const xmlChar *) "descending")) comp->descending = 1; else { xsltTransformError(ctxt, NULL, sorts[j], "xsltDoSortFunction: invalid value %s for order\n", comp->order); comp->descending = 0; /* use default */ } } } } len = list->nodeNr; resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); for (i = 1;i < XSLT_MAX_SORT;i++) resultsTab[i] = NULL; results = resultsTab[0]; comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi); descending = comp->descending; number = comp->number; if (results == NULL) return; // We are passing a language identifier to a function that expects a locale identifier. // The implementation of Collator should be lenient, and accept both "en-US" and "en_US", for example. // This lets an author specify sorting rules, e.g. "de_DE@collation=phonebook", which isn't // possible with language alone. Collator collator(comp->has_lang ? reinterpret_cast<const char*>(comp->lang) : "en", comp->lower_first); /* Shell's sort of node-set */ for (incr = len / 2; incr > 0; incr /= 2) { for (i = incr; i < len; i++) { j = i - incr; if (results[i] == NULL) continue; while (j >= 0) { if (results[j] == NULL) tst = 1; else { if (number) { /* We make NaN smaller than number in accordance with XSLT spec */ if (xmlXPathIsNaN(results[j]->floatval)) { if (xmlXPathIsNaN(results[j + incr]->floatval)) tst = 0; else tst = -1; } else if (xmlXPathIsNaN(results[j + incr]->floatval)) tst = 1; else if (results[j]->floatval == results[j + incr]->floatval) tst = 0; else if (results[j]->floatval > results[j + incr]->floatval) tst = 1; else tst = -1; } else tst = collator.collateUTF8(reinterpret_cast<const char*>(results[j]->stringval), reinterpret_cast<const char*>(results[j + incr]->stringval)); if (descending) tst = -tst; } if (tst == 0) { /* * Okay we need to use multi level sorts */ depth = 1; while (depth < nbsorts) { if (sorts[depth] == NULL) break; comp = static_cast<xsltStylePreComp*>(sorts[depth]->psvi); if (comp == NULL) break; desc = comp->descending; numb = comp->number; /* * Compute the result of the next level for the * full set, this might be optimized ... or not */ if (resultsTab[depth] == NULL) resultsTab[depth] = xsltComputeSortResult(ctxt, sorts[depth]); res = resultsTab[depth]; if (res == NULL) break; if (res[j] == NULL) { if (res[j+incr] != NULL) tst = 1; } else { if (numb) { /* We make NaN smaller than number in accordance with XSLT spec */ if (xmlXPathIsNaN(res[j]->floatval)) { if (xmlXPathIsNaN(res[j + incr]->floatval)) tst = 0; else tst = -1; } else if (xmlXPathIsNaN(res[j + incr]-> floatval)) tst = 1; else if (res[j]->floatval == res[j + incr]-> floatval) tst = 0; else if (res[j]->floatval > res[j + incr]->floatval) tst = 1; else tst = -1; } else tst = collator.collateUTF8(reinterpret_cast<const char*>(res[j]->stringval), reinterpret_cast<const char*>(res[j + incr]->stringval)); if (desc) tst = -tst; } /* * if we still can't differenciate at this level * try one level deeper. */ if (tst != 0) break; depth++; } } if (tst == 0) { tst = results[j]->index > results[j + incr]->index; } if (tst > 0) { tmp = results[j]; results[j] = results[j + incr]; results[j + incr] = tmp; node = list->nodeTab[j]; list->nodeTab[j] = list->nodeTab[j + incr]; list->nodeTab[j + incr] = node; depth = 1; while (depth < nbsorts) { if (sorts[depth] == NULL) break; if (resultsTab[depth] == NULL) break; res = resultsTab[depth]; tmp = res[j]; res[j] = res[j + incr]; res[j + incr] = tmp; depth++; } j -= incr; } else break; } } } for (j = 0; j < nbsorts; j++) { comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi); if (tempstype[j] == 1) { /* The data-type needs to be recomputed each time */ xmlFree((void *)(comp->stype)); comp->stype = NULL; } if (temporder[j] == 1) { /* The order needs to be recomputed each time */ xmlFree((void *)(comp->order)); comp->order = NULL; } if (resultsTab[j] != NULL) { for (i = 0;i < len;i++) xmlXPathFreeObject(resultsTab[j][i]); xmlFree(resultsTab[j]); } } }
/** * xsltFormatNumberConversion: * @self: the decimal format * @format: the format requested * @number: the value to format * @result: the place to ouput the result * * format-number() uses the JDK 1.1 DecimalFormat class: * * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html * * Structure: * * pattern := subpattern{;subpattern} * subpattern := {prefix}integer{.fraction}{suffix} * prefix := '\\u0000'..'\\uFFFD' - specialCharacters * suffix := '\\u0000'..'\\uFFFD' - specialCharacters * integer := '#'* '0'* '0' * fraction := '0'* '#'* * * Notation: * X* 0 or more instances of X * (X | Y) either X or Y. * X..Y any character from X up to Y, inclusive. * S - T characters in S, except those in T * * Special Characters: * * Symbol Meaning * 0 a digit * # a digit, zero shows as absent * . placeholder for decimal separator * , placeholder for grouping separator. * ; separates formats. * - default negative prefix. * % multiply by 100 and show as percentage * ? multiply by 1000 and show as per mille * X any other characters can be used in the prefix or suffix * ' used to quote special characters in a prefix or suffix. * * Returns a possible XPath error */ xmlXPathError xsltFormatNumberConversion(xsltDecimalFormatPtr self, xmlChar *format, double number, xmlChar **result) { xmlXPathError status = XPATH_EXPRESSION_OK; xmlBufferPtr buffer; xmlChar *the_format, *prefix = NULL, *suffix = NULL; xmlChar *nprefix, *nsuffix = NULL; xmlChar pchar; int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length; double scale; int j, len; int self_grouping_len; xsltFormatNumberInfo format_info; /* * delayed_multiplier allows a 'trailing' percent or * permille to be treated as suffix */ int delayed_multiplier = 0; /* flag to show no -ve format present for -ve number */ char default_sign = 0; /* flag to show error found, should use default format */ char found_error = 0; if (xmlStrlen(format) <= 0) { xsltTransformError(NULL, NULL, NULL, "xsltFormatNumberConversion : " "Invalid format (0-length)\n"); } *result = NULL; switch (xmlXPathIsInf(number)) { case -1: if (self->minusSign == NULL) *result = xmlStrdup(BAD_CAST "-"); else *result = xmlStrdup(self->minusSign); /* no-break on purpose */ case 1: if ((self == NULL) || (self->infinity == NULL)) *result = xmlStrcat(*result, BAD_CAST "Infinity"); else *result = xmlStrcat(*result, self->infinity); return(status); default: if (xmlXPathIsNaN(number)) { if ((self == NULL) || (self->noNumber == NULL)) *result = xmlStrdup(BAD_CAST "NaN"); else *result = xmlStrdup(self->noNumber); return(status); } } buffer = xmlBufferCreate(); if (buffer == NULL) { return XPATH_MEMORY_ERROR; } format_info.integer_hash = 0; format_info.integer_digits = 0; format_info.frac_digits = 0; format_info.frac_hash = 0; format_info.group = -1; format_info.multiplier = 1; format_info.add_decimal = FALSE; format_info.is_multiplier_set = FALSE; format_info.is_negative_pattern = FALSE; the_format = format; /* * First we process the +ve pattern to get percent / permille, * as well as main format */ prefix = the_format; prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if (prefix_length < 0) { found_error = 1; goto OUTPUT_NUMBER; } /* * Here we process the "number" part of the format. It gets * a little messy because of the percent/per-mille - if that * appears at the end, it may be part of the suffix instead * of part of the number, so the variable delayed_multiplier * is used to handle it */ self_grouping_len = xmlStrlen(self->grouping); while ((*the_format != 0) && (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) && (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) { if (delayed_multiplier != 0) { format_info.multiplier = delayed_multiplier; format_info.is_multiplier_set = TRUE; delayed_multiplier = 0; } if (xsltUTF8Charcmp(the_format, self->digit) == 0) { if (format_info.integer_digits > 0) { found_error = 1; goto OUTPUT_NUMBER; } format_info.integer_hash++; if (format_info.group >= 0) format_info.group++; } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) { format_info.integer_digits++; if (format_info.group >= 0) format_info.group++; } else if ((self_grouping_len > 0) && (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) { /* Reset group count */ format_info.group = 0; the_format += self_grouping_len; continue; } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 100; } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 1000; } else break; /* while */ if ((len=xmlUTF8Strsize(the_format, 1)) < 1) { found_error = 1; goto OUTPUT_NUMBER; } the_format += len; } /* We have finished the integer part, now work on fraction */ if ( (*the_format != 0) && (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) ) { format_info.add_decimal = TRUE; if ((len = xmlUTF8Strsize(the_format, 1)) < 1) { found_error = 1; goto OUTPUT_NUMBER; } the_format += len; /* Skip over the decimal */ } while (*the_format != 0) { if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) { if (format_info.frac_hash != 0) { found_error = 1; goto OUTPUT_NUMBER; } format_info.frac_digits++; } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) { format_info.frac_hash++; } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 100; if ((len = xmlUTF8Strsize(the_format, 1)) < 1) { found_error = 1; goto OUTPUT_NUMBER; } the_format += len; continue; /* while */ } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } delayed_multiplier = 1000; if ((len = xmlUTF8Strsize(the_format, 1)) < 1) { found_error = 1; goto OUTPUT_NUMBER; } the_format += len; continue; /* while */ } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) { break; /* while */ } if ((len = xmlUTF8Strsize(the_format, 1)) < 1) { found_error = 1; goto OUTPUT_NUMBER; } the_format += len; if (delayed_multiplier != 0) { format_info.multiplier = delayed_multiplier; delayed_multiplier = 0; format_info.is_multiplier_set = TRUE; } } /* * If delayed_multiplier is set after processing the * "number" part, should be in suffix */ if (delayed_multiplier != 0) { the_format -= len; delayed_multiplier = 0; } suffix = the_format; suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if ( (suffix_length < 0) || ((*the_format != 0) && (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) { found_error = 1; goto OUTPUT_NUMBER; } /* * We have processed the +ve prefix, number part and +ve suffix. * If the number is -ve, we must substitute the -ve prefix / suffix */ if (number < 0) { /* * Note that j is the number of UTF8 chars before the separator, * not the number of bytes! (bug 151975) */ j = xmlUTF8Strloc(format, self->patternSeparator); if (j < 0) { /* No -ve pattern present, so use default signing */ default_sign = 1; } else { /* Skip over pattern separator (accounting for UTF8) */ the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1); /* * Flag changes interpretation of percent/permille * in -ve pattern */ format_info.is_negative_pattern = TRUE; format_info.is_multiplier_set = FALSE; /* First do the -ve prefix */ nprefix = the_format; nprefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if (nprefix_length<0) { found_error = 1; goto OUTPUT_NUMBER; } while (*the_format != 0) { if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) || (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) { if (format_info.is_multiplier_set) { found_error = 1; goto OUTPUT_NUMBER; } format_info.is_multiplier_set = TRUE; delayed_multiplier = 1; } else if (IS_SPECIAL(self, the_format)) delayed_multiplier = 0; else break; /* while */ if ((len = xmlUTF8Strsize(the_format, 1)) < 1) { found_error = 1; goto OUTPUT_NUMBER; } the_format += len; } if (delayed_multiplier != 0) { format_info.is_multiplier_set = FALSE; the_format -= len; } /* Finally do the -ve suffix */ if (*the_format != 0) { nsuffix = the_format; nsuffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); if (nsuffix_length < 0) { found_error = 1; goto OUTPUT_NUMBER; } } else nsuffix_length = 0; if (*the_format != 0) { found_error = 1; goto OUTPUT_NUMBER; } /* * Here's another Java peculiarity: * if -ve prefix/suffix == +ve ones, discard & use default */ if ((nprefix_length != prefix_length) || (nsuffix_length != suffix_length) || ((nprefix_length > 0) && (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) || ((nsuffix_length > 0) && (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) { prefix = nprefix; prefix_length = nprefix_length; suffix = nsuffix; suffix_length = nsuffix_length; } /* else { default_sign = 1; } */ } } OUTPUT_NUMBER: if (found_error != 0) { xsltTransformError(NULL, NULL, NULL, "xsltFormatNumberConversion : " "error in format string '%s', using default\n", format); default_sign = (number < 0.0) ? 1 : 0; prefix_length = suffix_length = 0; format_info.integer_hash = 0; format_info.integer_digits = 1; format_info.frac_digits = 1; format_info.frac_hash = 4; format_info.group = -1; format_info.multiplier = 1; format_info.add_decimal = TRUE; } /* Ready to output our number. First see if "default sign" is required */ if (default_sign != 0) xmlBufferAdd(buffer, self->minusSign, xmlUTF8Strsize(self->minusSign, 1)); /* Put the prefix into the buffer */ for (j = 0; j < prefix_length; j++) { if ((pchar = *prefix++) == SYMBOL_QUOTE) { len = xmlUTF8Strsize(prefix, 1); xmlBufferAdd(buffer, prefix, len); prefix += len; j += len - 1; /* length of symbol less length of quote */ } else xmlBufferAdd(buffer, &pchar, 1); } /* Next do the integer part of the number */ number = fabs(number) * (double)format_info.multiplier; scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash)); number = floor((scale * number + 0.5)) / scale; if ((self->grouping != NULL) && (self->grouping[0] != 0)) { len = xmlStrlen(self->grouping); pchar = xsltGetUTF8Char(self->grouping, &len); xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], format_info.integer_digits, format_info.group, pchar, len); } else xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], format_info.integer_digits, format_info.group, ',', 1); /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */ if ((format_info.integer_digits + format_info.integer_hash + format_info.frac_digits == 0) && (format_info.frac_hash > 0)) { ++format_info.frac_digits; --format_info.frac_hash; } /* Add leading zero, if required */ if ((floor(number) == 0) && (format_info.integer_digits + format_info.frac_digits == 0)) { xmlBufferAdd(buffer, self->zeroDigit, xmlUTF8Strsize(self->zeroDigit, 1)); } /* Next the fractional part, if required */ if (format_info.frac_digits + format_info.frac_hash == 0) { if (format_info.add_decimal) xmlBufferAdd(buffer, self->decimalPoint, xmlUTF8Strsize(self->decimalPoint, 1)); } else { number -= floor(number); if ((number != 0) || (format_info.frac_digits != 0)) { xmlBufferAdd(buffer, self->decimalPoint, xmlUTF8Strsize(self->decimalPoint, 1)); number = floor(scale * number + 0.5); for (j = format_info.frac_hash; j > 0; j--) { if (fmod(number, 10.0) >= 1.0) break; /* for */ number /= 10.0; } xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], format_info.frac_digits + j, 0, 0, 0); } } /* Put the suffix into the buffer */ for (j = 0; j < suffix_length; j++) { if ((pchar = *suffix++) == SYMBOL_QUOTE) { len = xmlUTF8Strsize(suffix, 1); xmlBufferAdd(buffer, suffix, len); suffix += len; j += len - 1; /* length of symbol less length of escape */ } else xmlBufferAdd(buffer, &pchar, 1); } *result = xmlStrdup(xmlBufferContent(buffer)); xmlBufferFree(buffer); return status; }
/** * exsltMathConstant * @name: string * @precision: number * * Implements the EXSLT - Math constant function: * number math:constant(string, number) * * Returns a number value of the given constant with the given precision or * xmlXPathNAN if name is unknown. * The constants are PI, E, SQRRT2, LN2, LN10, LOG2E, and SQRT1_2 */ static double exsltMathConstant (xmlChar *name, double precision) { xmlChar *str; if ((name == NULL) || (xmlXPathIsNaN(precision)) || (precision < 1.0)) { return xmlXPathNAN; } if (xmlStrEqual(name, BAD_CAST "PI")) { int len = xmlStrlen(EXSLT_PI); if (precision <= len) len = (int)precision; str = xmlStrsub(EXSLT_PI, 0, len); if (str == NULL) return xmlXPathNAN; return xmlXPathCastStringToNumber(str); } else if (xmlStrEqual(name, BAD_CAST "E")) { int len = xmlStrlen(EXSLT_E); if (precision <= len) len = (int)precision; str = xmlStrsub(EXSLT_E, 0, len); if (str == NULL) return xmlXPathNAN; return xmlXPathCastStringToNumber(str); } else if (xmlStrEqual(name, BAD_CAST "SQRRT2")) { int len = xmlStrlen(EXSLT_SQRRT2); if (precision <= len) len = (int)precision; str = xmlStrsub(EXSLT_SQRRT2, 0, len); if (str == NULL) return xmlXPathNAN; return xmlXPathCastStringToNumber(str); } else if (xmlStrEqual(name, BAD_CAST "LN2")) { int len = xmlStrlen(EXSLT_LN2); if (precision <= len) len = (int)precision; str = xmlStrsub(EXSLT_LN2, 0, len); if (str == NULL) return xmlXPathNAN; return xmlXPathCastStringToNumber(str); } else if (xmlStrEqual(name, BAD_CAST "LN10")) { int len = xmlStrlen(EXSLT_LN10); if (precision <= len) len = (int)precision; str = xmlStrsub(EXSLT_LN10, 0, len); if (str == NULL) return xmlXPathNAN; return xmlXPathCastStringToNumber(str); } else if (xmlStrEqual(name, BAD_CAST "LOG2E")) { int len = xmlStrlen(EXSLT_LOG2E); if (precision <= len) len = (int)precision; str = xmlStrsub(EXSLT_LOG2E, 0, len); if (str == NULL) return xmlXPathNAN; return xmlXPathCastStringToNumber(str); } else if (xmlStrEqual(name, BAD_CAST "SQRT1_2")) { int len = xmlStrlen(EXSLT_SQRT1_2); if (precision <= len) len = (int)precision; str = xmlStrsub(EXSLT_SQRT1_2, 0, len); if (str == NULL) return xmlXPathNAN; return xmlXPathCastStringToNumber(str); } else { return xmlXPathNAN; } }