+Mon Jan 29 18:40:23 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
+
+ * FEATURES: updated
+ * tests/numbers/Makefile.am tests/numbers/format-number.*
+ tests/Makefile.am configure.in: added number formattting
+ test from Bjorn
+ * libxslt/attributes.[ch]: separated attribute support, started
+ add support for attribute-sets
+ * libxslt/functions.[ch]: update for number and formatting
+ from Bjorn
+ * libxslt/transform.c libxslt/xslt.c libxslt/xsltInternals.h:
+ cleanups updates, etc ...
+
Mon Jan 29 00:53:25 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
* FEATURES: updated
YES xsl:comment
-NO xsl:copy
+YES xsl:copy
NO use-attribute-sets = qnames
YES xsl:value-of
tests/Makefile
tests/REC1/Makefile
tests/REC2/Makefile
+tests/numbers/Makefile
doc/Makefile
xslt-config
libxslt.spec
variables.h \
functions.h \
namespaces.h \
+ attributes.h \
transform.h \
xsltInternals.h
variables.c \
functions.c \
namespaces.c \
+ attributes.c \
transform.c
--- /dev/null
+/*
+ * attributes.c: Implementation of the XSLT attributes handling
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * Daniel.Veillard@imag.fr
+ */
+
+#include "xsltconfig.h"
+
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_NAN_H
+#include <nan.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/uri.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "attributes.h"
+#include "namespaces.h"
+#include "templates.h"
+
+/*
+ * Useful macros
+ */
+
+#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
+ ((c) == 0x0D))
+
+#define IS_BLANK_NODE(n) \
+ (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
+
+
+/*
+ * The in-memory structure corresponding to an XSLT Attribute in
+ * an attribute set
+ */
+
+typedef struct _xsltAttrElem xsltAttrElem;
+typedef xsltAttrElem *xsltAttrElemPtr;
+struct _xsltAttrElem {
+ struct _xsltAttrElem *next;/* chained list */
+ xmlNodePtr attr; /* the xsl:attribute definition */
+};
+
+/************************************************************************
+ * *
+ * XSLT Attribute handling *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewAttrElem:
+ * @attr: the new xsl:attribute node
+ *
+ * Create a new XSLT AttrElem
+ *
+ * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
+ */
+xsltAttrElemPtr
+xsltNewAttrElem(xmlNodePtr attr) {
+ xsltAttrElemPtr cur;
+
+ cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
+ if (cur == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltNewAttrElem : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltAttrElem));
+ cur->attr = attr;
+ return(cur);
+}
+
+/**
+ * xsltFreeAttrElem:
+ * @attr: an XSLT AttrElem
+ *
+ * Free up the memory allocated by @attr
+ */
+void
+xsltFreeAttrElem(xsltAttrElemPtr attr) {
+ memset(attr, -1, sizeof(xsltAttrElem));
+ xmlFree(attr);
+}
+
+/**
+ * xsltFreeAttrElemList:
+ * @list: an XSLT AttrElem list
+ *
+ * Free up the memory allocated by @list
+ */
+void
+xsltFreeAttrElemList(xsltAttrElemPtr list) {
+ xsltAttrElemPtr next;
+
+ while (list != NULL) {
+ next = list->next;
+ xsltFreeAttrElem(list);
+ list = next;
+ }
+}
+
+/**
+ * xsltAddAttrElemList:
+ * @list: an XSLT AttrElem list
+ * @attr: the new xsl:attribute node
+ *
+ * Add the new attribute to the list.
+ *
+ * Returns the new list pointer
+ */
+xsltAttrElemPtr
+xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
+ xsltAttrElemPtr next;
+
+ if (attr == NULL)
+ return(list);
+ while (list != NULL) {
+ next = list->next;
+ if (list->attr == attr)
+ return(list);
+ if (next == NULL) {
+ list->next = xsltNewAttrElem(attr);
+ return(list);
+ }
+ list = next;
+ }
+ return(xsltNewAttrElem(attr));
+}
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltParseStylesheetAttributeSet:
+ * @style: the XSLT stylesheet
+ * @template: the "preserve-space" element
+ *
+ * parse an XSLT stylesheet preserve-space element and record
+ * elements needing preserving
+ */
+
+void
+xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
+ xmlChar *prop = NULL;
+ xmlChar *ncname = NULL;
+ xmlChar *prefix = NULL;
+ xmlChar *attributes;
+ xmlChar *attribute, *end;
+ xmlNodePtr list, delete;
+ xsltAttrElemPtr values;
+
+ if ((cur == NULL) || (style == NULL))
+ return;
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
+ if (prop == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xslt:attribute-set : name is missing\n");
+ goto error;
+ }
+
+ ncname = xmlSplitQName2(prop, &prefix);
+ if (ncname == NULL) {
+ ncname = prop;
+ prop = NULL;
+ prefix = NULL;
+ }
+
+ if (style->attributeSets == NULL)
+ style->attributeSets = xmlHashCreate(10);
+ if (style->attributeSets == NULL)
+ goto error;
+
+ values = xmlHashLookup2(style->attributeSets, ncname, prefix);
+
+ /*
+ * check the children list
+ */
+ list = cur->children;
+ delete = NULL;
+ while (list != NULL) {
+ if (IS_XSLT_ELEM(cur)) {
+ if (!IS_XSLT_NAME(cur, "attribute")) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xslt:attribute-set : unexpected child xsl:%s\n",
+ cur->name);
+ delete = cur;
+ } else {
+#ifdef DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "add attribute to list %s\n", ncname);
+#endif
+ values = xsltAddAttrElemList(values, cur);
+ }
+ } else {
+ xsltGenericError(xsltGenericErrorContext,
+ "xslt:attribute-set : unexpected child %s\n", cur->name);
+ delete = cur;
+ }
+ list = list->next;
+ }
+
+ /*
+ * Check a possible use-attribute-sets definition
+ */
+ /* TODO check recursion */
+
+ attributes = xmlGetNsProp(cur, (const xmlChar *)"use-attribute-sets",
+ XSLT_NAMESPACE);
+ if (attributes == NULL) {
+ goto done;
+ }
+
+ attribute = attributes;
+ while (*attribute != 0) {
+ while (IS_BLANK(*attribute)) attribute++;
+ if (*attribute == 0)
+ break;
+ end = attribute;
+ while ((*end != 0) && (!IS_BLANK(*end))) end++;
+ attribute = xmlStrndup(attribute, end - attribute);
+ if (attribute) {
+#ifdef DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xslt:attribute-set : %s adds use %s\n", ncname);
+#endif
+ TODO /* add use-attribute-sets support to atribute-set */
+ }
+ attribute = end;
+ }
+ xmlFree(attributes);
+
+done:
+ /*
+ * Update the value
+ */
+ xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, values, NULL);
+#ifdef DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "updated attribute list %s\n", ncname);
+#endif
+
+error:
+ if (prop != NULL)
+ xmlFree(prop);
+ if (ncname != NULL)
+ xmlFree(ncname);
+ if (prefix != NULL)
+ xmlFree(prefix);
+}
+
+/**
+ * xsltAttribute:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt attribute node
+ *
+ * Process the xslt attribute node on the source node
+ */
+void
+xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst) {
+ xmlChar *prop = NULL;
+ xmlChar *ncname = NULL;
+ xmlChar *prefix = NULL;
+ xmlChar *value = NULL;
+ xmlNsPtr ns = NULL;
+ xmlAttrPtr attr;
+
+
+ if (ctxt->insert == NULL)
+ return;
+ if (ctxt->insert->children != NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xslt:attribute : node has already children\n");
+ return;
+ }
+ prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
+ if (prop == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xslt:attribute : name is missing\n");
+ goto error;
+ }
+
+ ncname = xmlSplitQName2(prop, &prefix);
+ if (ncname == NULL) {
+ ncname = prop;
+ prop = NULL;
+ prefix = NULL;
+ }
+ if (xmlStrEqual(ncname, (const xmlChar *) "xmlns")) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xslt:attribute : xmlns forbidden\n");
+ goto error;
+ }
+ prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"namespace");
+ if (prop != NULL) {
+ TODO /* xsl:attribute namespace */
+ xmlFree(prop);
+ return;
+ } else {
+ if (prefix != NULL) {
+ ns = xmlSearchNs(inst->doc, inst, prefix);
+ if (ns == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "no namespace bound to prefix %s\n", prefix);
+ } else {
+ ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
+ }
+ }
+ }
+
+
+ value = xsltEvalTemplateString(ctxt, node, inst);
+ if (value == NULL) {
+ if (ns) {
+ attr = xmlSetNsProp(ctxt->insert, ns, ncname,
+ (const xmlChar *)"");
+ } else
+ attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)"");
+ } else {
+ if (ns) {
+ attr = xmlSetNsProp(ctxt->insert, ns, ncname, value);
+ } else
+ attr = xmlSetProp(ctxt->insert, ncname, value);
+
+ }
+
+error:
+ if (prop != NULL)
+ xmlFree(prop);
+ if (ncname != NULL)
+ xmlFree(ncname);
+ if (prefix != NULL)
+ xmlFree(prefix);
+ if (value != NULL)
+ xmlFree(value);
+}
+
+/**
+ * xsltApplyAttributeSet:
+ * @ctxt: the XSLT stylesheet
+ * @node: the node in the source tree.
+ * @inst: the xslt attribute node
+ * @attributes: the set list.
+ *
+ * Apply the xsl:use-attribute-sets
+ */
+
+void
+xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xmlChar *attributes) {
+ xmlChar *ncname = NULL;
+ xmlChar *prefix = NULL;
+ xmlChar *attribute, *end;
+ xsltAttrElemPtr values;
+
+ if (attributes == NULL) {
+ return;
+ }
+
+ attribute = attributes;
+ while (*attribute != 0) {
+ while (IS_BLANK(*attribute)) attribute++;
+ if (*attribute == 0)
+ break;
+ end = attribute;
+ while ((*end != 0) && (!IS_BLANK(*end))) end++;
+ attribute = xmlStrndup(attribute, end - attribute);
+ if (attribute) {
+#ifdef DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "apply attribute set %s\n", attribute);
+#endif
+ ncname = xmlSplitQName2(attribute, &prefix);
+ if (ncname == NULL) {
+ ncname = attribute;
+ attribute = NULL;
+ prefix = NULL;
+ }
+
+ /* TODO: apply cascade */
+ values = xmlHashLookup2(ctxt->style->attributeSets, ncname, prefix);
+ while (values != NULL) {
+ xsltAttribute(ctxt, node, values->attr);
+ values = values->next;
+ }
+ if (attribute != NULL)
+ xmlFree(attribute);
+ if (ncname != NULL)
+ xmlFree(ncname);
+ if (prefix != NULL)
+ xmlFree(prefix);
+ }
+ attribute = end;
+ }
+}
+
+/**
+ * xsltFreeAttributeSetsHashes:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by attribute sets
+ */
+void
+xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
+ if (style->attributeSets != NULL)
+ xmlHashFree((xmlHashTablePtr) style->attributeSets,
+ (xmlHashDeallocator) xsltFreeAttrElemList);
+ style->attributeSets = NULL;
+}
--- /dev/null
+/*
+ * attributes.h: interface for the XSLT attribute handling
+ *
+ * See Copyright for the status of this software.
+ *
+ * Daniel.Veillard@imag.fr
+ */
+
+#ifndef __XML_XSLT_ATTRIBUTES_H__
+#define __XML_XSLT_ATTRIBUTES_H__
+
+#include <libxml/tree.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void xsltParseStylesheetAttributeSet (xsltStylesheetPtr style,
+ xmlNodePtr cur);
+void xsltAttribute (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst);
+void xsltFreeAttributeSetsHashes (xsltStylesheetPtr style);
+void xsltApplyAttributeSet (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xmlChar *attributes);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_ATTRIBUTES_H__ */
+
* See Copyright for the status of this software.
*
* Daniel.Veillard@imag.fr
- * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting
+ * Bjorn Reese <breese@users.sourceforge.net> for number formatting
*/
#include "xsltconfig.h"
#define DIGIT_LIST "0123456789"
#define SYMBOL_QUOTE ((xmlChar)'\'')
-#define SYMBOL_PATTERN_SEPARATOR ((xmlChar)';')
-#define SYMBOL_ZERO_DIGIT ((xmlChar)'#')
-#define SYMBOL_DIGIT ((xmlChar)'0')
-#define SYMBOL_DECIMAL_POINT ((xmlChar)'.')
-#define SYMBOL_GROUPING ((xmlChar)',')
-#define SYMBOL_MINUS ((xmlChar)'-')
-#define SYMBOL_PERCENT ((xmlChar)'%')
-#define SYMBOL_PERMILLE ((xmlChar)'?')
#define ID_STRING "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-static struct _xsltDecimalFormat globalDecimalFormat;
/************************************************************************
* *
*/
/* TODO
*
- * The JDK description does not tell where percent and permille may
- * and may not appear within the format string.
- *
- * Error handling.
+ * The JDK description does not tell where they may and may not appear
+ * within the format string. Nor does it tell what happens to integer
+ * values that does not fit into the format string.
*
* Inf and NaN not tested.
*/
#define IS_SPECIAL(self,letter) \
- ((letter == self->zeroDigit) || \
- (letter == self->digit) || \
- (letter == self->decimalPoint) || \
- (letter == self->grouping) || \
- (letter == self->minusSign) || \
- (letter == self->percent) || \
- (letter == self->permille)) \
+ (((letter) == (self)->zeroDigit[0]) || \
+ ((letter) == (self)->digit[0]) || \
+ ((letter) == (self)->decimalPoint[0]) || \
+ ((letter) == (self)->grouping[0]) || \
+ ((letter) == (self)->minusSign[0]) || \
+ ((letter) == (self)->percent[0]) || \
+ ((letter) == (self)->permille[0]))
static xmlXPathError
-PrivateDecimalFormat(xsltDecimalFormatPtr self,
- xmlChar *format,
- double number,
- xmlChar **result)
+xsltFormatNumberConversion(xsltDecimalFormatPtr self,
+ xmlChar *format,
+ double number,
+ xmlChar **result)
{
xmlXPathError status = XPATH_EXPRESSION_OK;
xmlChar *the_format;
int decimal_point;
double divisor;
int digit;
+ int is_percent = FALSE;
+ int is_permille = FALSE;
buffer = xmlBufferCreate();
if (buffer == NULL) {
/* Find positive or negative template */
the_format = (xmlChar *)xmlStrchr(format,
- self->patternSeparator);
+ self->patternSeparator[0]);
if ((the_format != NULL) && (number < 0.0)) {
/* Use negative template */
the_format++;
group = 0;
for ( ; i < length; i++) {
- if (the_format[i] == self->digit) {
+ if (the_format[i] == self->digit[0]) {
if (decimal_point) {
if (fraction_zeroes > 0) {
status = XPATH_EXPR_ERROR;
group++;
}
- } else if (the_format[i] == self->zeroDigit) {
+ } else if (the_format[i] == self->zeroDigit[0]) {
if (decimal_point)
fraction_zeroes++;
else {
group++;
}
- } else if (the_format[i] == self->grouping) {
+ } else if (the_format[i] == self->grouping[0]) {
if (decimal_point) {
status = XPATH_EXPR_ERROR;
goto DECIMAL_FORMAT_END;
}
group = 0;
- } else if (the_format[i] == self->decimalPoint) {
+ } else if (the_format[i] == self->decimalPoint[0]) {
if (decimal_point) {
status = XPATH_EXPR_ERROR;
goto DECIMAL_FORMAT_END;
} else
break;
}
+ if (the_format[i] == self->percent[0]) {
+ is_percent = TRUE;
+ } else if (the_format[i] == self->permille[0]) {
+ is_permille = TRUE;
+ }
/* Format the number */
if (use_minus)
- xmlBufferAdd(buffer, &(self->minusSign), 1);
+ xmlBufferAdd(buffer, self->minusSign, 1);
number = fabs(number);
+ if (is_percent)
+ number /= 100.0;
+ else if (is_permille)
+ number /= 1000.0;
number = floor(0.5 + number * pow(10.0, (double)(fraction_digits + fraction_zeroes)));
/* Integer part */
}
if (decimal_point)
- xmlBufferAdd(buffer, &(self->decimalPoint), 1);
+ xmlBufferAdd(buffer, self->decimalPoint, 1);
/* Fraction part */
for (j = fraction_digits + fraction_zeroes; j > 0; j--) {
}
}
+ if (is_percent)
+ xmlBufferAdd(buffer, self->percent, 1);
+ else if (is_permille)
+ xmlBufferAdd(buffer, self->permille, 1);
+
/* Suffix */
for ( ; i < length; i++) {
if (the_format[i] == SYMBOL_QUOTE)
xmlXPathObjectPtr numberObj = NULL;
xmlXPathObjectPtr formatObj = NULL;
xmlXPathObjectPtr decimalObj = NULL;
+ xsltStylesheetPtr sheet;
+ xsltDecimalFormatPtr formatValues;
xmlChar *result;
+
+ sheet = ((xsltTransformContextPtr)ctxt->context->extra)->style;
+ formatValues = sheet->decimalFormat;
switch (nargs) {
case 3:
CAST_TO_STRING;
decimalObj = valuePop(ctxt);
- globalDecimalFormat.decimalPoint = decimalObj->stringval[0]; /* hack */
+ formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
/* Intentional fall-through */
case 2:
CAST_TO_STRING;
return;
}
- if (PrivateDecimalFormat(&globalDecimalFormat,
- formatObj->stringval,
- numberObj->floatval,
- &result) == XPATH_EXPRESSION_OK) {
+ if (xsltFormatNumberConversion(formatValues,
+ formatObj->stringval,
+ numberObj->floatval,
+ &result) == XPATH_EXPRESSION_OK) {
valuePush(ctxt, xmlXPathNewString(result));
}
xsltElementAvailableFunction);
xmlXPathRegisterFunc(ctxt, (const xmlChar *)"function-available",
xsltFunctionAvailableFunction);
-
- globalDecimalFormat.digit = SYMBOL_DIGIT;
- globalDecimalFormat.patternSeparator = SYMBOL_PATTERN_SEPARATOR;
-
- globalDecimalFormat.decimalPoint = SYMBOL_DECIMAL_POINT;
- globalDecimalFormat.grouping = SYMBOL_GROUPING;
- globalDecimalFormat.percent = SYMBOL_PERCENT;
- globalDecimalFormat.permille = SYMBOL_PERMILLE; /* #x2030 */
- globalDecimalFormat.zeroDigit = SYMBOL_ZERO_DIGIT;
-
- globalDecimalFormat.minusSign = '-';
- globalDecimalFormat.infinity = xmlStrdup(BAD_CAST("")); /* #x221E */
- globalDecimalFormat.noNumber = xmlStrdup(BAD_CAST("")); /* #xFFFD */
}
* See Copyright for the status of this software.
*
* Daniel.Veillard@imag.fr
- * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting
+ * Bjorn Reese <breese@users.sourceforge.net> for number formatting
*/
#ifndef __XML_XSLT_FUNCTIONS_H__
#endif
/*
- * Data structure of decimal-format
- */
-typedef struct _xsltDecimalFormat {
- /* Used for interpretation of pattern */
- xmlChar digit;
- xmlChar patternSeparator;
- /* May appear in result */
- xmlChar minusSign;
- xmlChar *infinity;
- xmlChar *noNumber;
- /* Used for interpretation of pattern and may appear in result */
- xmlChar decimalPoint;
- xmlChar grouping;
- xmlChar percent;
- xmlChar permille;
- xmlChar zeroDigit;
-} xsltDecimalFormat, *xsltDecimalFormatPtr;
-
-/*
* Interfaces for the functions implementations
*/
}
/**
+ * xsltCopy:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt copy node
+ *
+ * Process the xslt copy node on the source node
+ */
+void
+xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst) {
+ xmlChar *prop;
+ xmlNodePtr copy;
+
+ if ((node->type != XML_DOCUMENT_NODE) &&
+ (node->type != XML_HTML_DOCUMENT_NODE)) {
+ copy = xsltCopyNode(ctxt, node, ctxt->insert);
+ if (node->type == XML_ELEMENT_NODE) {
+ prop = xmlGetNsProp(inst, (const xmlChar *)"use-attribute-sets",
+ XSLT_NAMESPACE);
+ if (prop != NULL) {
+ TODO /* xsl:copy use-attribute-sets */
+ }
+ }
+ }
+
+ xsltApplyOneTemplate(ctxt, ctxt->node, inst->children);
+}
+
+/**
* xsltComment:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
}
/**
- * xsltAttribute:
- * @ctxt: a XSLT process context
- * @node: the node in the source tree.
- * @inst: the xslt attribute node
- *
- * Process the xslt attribute node on the source node
- */
-void
-xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
- xmlNodePtr inst) {
- xmlChar *prop = NULL;
- xmlChar *ncname = NULL;
- xmlChar *prefix = NULL;
- xmlChar *value = NULL;
- xmlNsPtr ns = NULL;
- xmlAttrPtr attr;
-
-
- if (ctxt->insert == NULL)
- return;
- if (ctxt->insert->children != NULL) {
- xsltGenericError(xsltGenericErrorContext,
- "xslt:attribute : node has already children\n");
- return;
- }
- prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
- if (prop == NULL) {
- xsltGenericError(xsltGenericErrorContext,
- "xslt:attribute : name is missing\n");
- goto error;
- }
-
- ncname = xmlSplitQName2(prop, &prefix);
- if (ncname == NULL) {
- ncname = prop;
- prop = NULL;
- prefix = NULL;
- }
- if (xmlStrEqual(ncname, (const xmlChar *) "xmlns")) {
- xsltGenericError(xsltGenericErrorContext,
- "xslt:attribute : xmlns forbidden\n");
- goto error;
- }
- prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"namespace");
- if (prop != NULL) {
- TODO /* xsl:attribute namespace */
- xmlFree(prop);
- return;
- } else {
- if (prefix != NULL) {
- ns = xmlSearchNs(inst->doc, inst, prefix);
- if (ns == NULL) {
- xsltGenericError(xsltGenericErrorContext,
- "no namespace bound to prefix %s\n", prefix);
- } else {
- ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
- }
- }
- }
-
-
- value = xsltEvalTemplateString(ctxt, node, inst);
- if (value == NULL) {
- if (ns) {
- attr = xmlSetNsProp(ctxt->insert, ns, ncname,
- (const xmlChar *)"");
- } else
- attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)"");
- } else {
- if (ns) {
- attr = xmlSetNsProp(ctxt->insert, ns, ncname, value);
- } else
- attr = xmlSetProp(ctxt->insert, ncname, value);
-
- }
-
-error:
- if (prop != NULL)
- xmlFree(prop);
- if (ncname != NULL)
- xmlFree(ncname);
- if (prefix != NULL)
- xmlFree(prefix);
- if (value != NULL)
- xmlFree(value);
-}
-
-/**
* xsltCopyOf:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
ctxt->insert = insert;
xsltValueOf(ctxt, node, cur);
ctxt->insert = oldInsert;
+ } else if (IS_XSLT_NAME(cur, "copy")) {
+ ctxt->insert = insert;
+ xsltCopy(ctxt, node, cur);
+ ctxt->insert = oldInsert;
} else if (IS_XSLT_NAME(cur, "copy-of")) {
ctxt->insert = insert;
xsltCopyOf(ctxt, node, cur);
}
xsltCallTemplate(ctxt, node, cur);
} else if (IS_XSLT_NAME(cur, "message")) {
+ xsltMessage(ctxt, node, cur);
} else {
#ifdef DEBUG_PROCESS
xsltGenericError(xsltGenericDebugContext,
#include "pattern.h"
#include "variables.h"
#include "namespaces.h"
+#include "attributes.h"
#include "xsltutils.h"
#define DEBUG_PARSING
* Routines to handle XSLT data structures *
* *
************************************************************************/
+static xsltDecimalFormatPtr
+xsltNewDecimalFormat(xmlChar *name)
+{
+ xsltDecimalFormatPtr self;
+
+ self = xmlMalloc(sizeof(xsltDecimalFormat));
+ if (self != NULL) {
+ self->next = NULL;
+ self->name = (name == NULL) ? name : xmlStrdup(name);
+
+ /* Default values */
+ self->digit = xmlStrdup(BAD_CAST("0"));
+ self->patternSeparator = xmlStrdup(BAD_CAST(";"));
+ self->decimalPoint = xmlStrdup(BAD_CAST("."));
+ self->grouping = xmlStrdup(BAD_CAST(","));
+ self->percent = xmlStrdup(BAD_CAST("%"));
+ self->permille = xmlStrdup(BAD_CAST("?"));
+ self->zeroDigit = xmlStrdup(BAD_CAST("#"));
+ self->minusSign = xmlStrdup(BAD_CAST("-"));
+ self->infinity = xmlStrdup(BAD_CAST("Infinity"));
+ self->noNumber = xmlStrdup(BAD_CAST("NaN"));
+ }
+ return self;
+}
+
+static void
+xsltFreeDecimalFormat(xsltDecimalFormatPtr self)
+{
+ if (self != NULL) {
+ if (self->digit)
+ xmlFree(self->digit);
+ if (self->patternSeparator)
+ xmlFree(self->patternSeparator);
+ if (self->decimalPoint)
+ xmlFree(self->decimalPoint);
+ if (self->grouping)
+ xmlFree(self->grouping);
+ if (self->percent)
+ xmlFree(self->percent);
+ if (self->permille)
+ xmlFree(self->permille);
+ if (self->zeroDigit)
+ xmlFree(self->zeroDigit);
+ if (self->minusSign)
+ xmlFree(self->minusSign);
+ if (self->infinity)
+ xmlFree(self->infinity);
+ if (self->noNumber)
+ xmlFree(self->noNumber);
+ if (self->name)
+ xmlFree(self->name);
+ xmlFree(self);
+ }
+}
+
+static void
+xsltFreeDecimalFormatList(xsltStylesheetPtr self)
+{
+ xsltDecimalFormatPtr iter;
+ xsltDecimalFormatPtr tmp;
+
+ if (self == NULL)
+ return;
+
+ iter = self->decimalFormat;
+ while (iter != NULL) {
+ tmp = iter->next;
+ xsltFreeDecimalFormat(iter);
+ iter = tmp;
+ }
+}
+
+/**
+ * xsltDecimalFormatGetByName:
+ * @sheet: the XSLT stylesheet
+ * @name: the decimal-format name to find
+ *
+ * Find decimal-format by name
+ */
+xsltDecimalFormatPtr
+xsltDecimalFormatGetByName(xsltStylesheetPtr sheet, xmlChar *name)
+{
+ xsltDecimalFormatPtr result;
+
+ if (name == NULL)
+ return sheet->decimalFormat;
+
+ for (result = sheet->decimalFormat->next;
+ result != NULL;
+ result = result->next) {
+ if (xmlStrEqual(name, result->name))
+ break; /* for */
+ }
+ return result;
+}
+
/**
* xsltNewTemplate:
memset(cur, 0, sizeof(xsltStylesheet));
cur->omitXmlDeclaration = -1;
cur->standalone = -1;
+ cur->decimalFormat = xsltNewDecimalFormat(NULL);
cur->indent = -1;
return(cur);
}
return;
xsltFreeTemplateHashes(sheet);
+ xsltFreeDecimalFormatList(sheet);
xsltFreeTemplateList(sheet->templates);
+ xsltFreeAttributeSetsHashes(sheet);
if (sheet->doc != NULL)
xmlFreeDoc(sheet->doc);
if (sheet->variables != NULL)
}
/**
+ * xsltParseStylesheetDecimalFormat:
+ * @style: the XSLT stylesheet
+ * @cur: the "decimal-format" element
+ *
+ * parse an XSLT stylesheet decimal-format element and
+ * and record the formatting characteristics
+ */
+void
+xsltParseStylesheetDecimalFormat(xsltStylesheetPtr sheet, xmlNodePtr cur)
+{
+ xmlChar *prop;
+ xsltDecimalFormatPtr format;
+ xsltDecimalFormatPtr iter;
+
+ if ((cur == NULL) || (sheet == NULL))
+ return;
+
+ format = sheet->decimalFormat;
+
+ prop = xmlGetNsProp(cur, BAD_CAST("name"), XSLT_NAMESPACE);
+ if (prop != NULL) {
+ format = xsltDecimalFormatGetByName(sheet, prop);
+ if (format != NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltParseStylesheetDecimalFormat: %s already exists\n", prop);
+ return;
+ }
+ format = xsltNewDecimalFormat(prop);
+ if (format == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltParseStylesheetDecimalFormat: failed creating new decimal-format\n");
+ return;
+ }
+ /* Append new decimal-format structure */
+ for (iter = sheet->decimalFormat; iter->next; iter = iter->next)
+ ;
+ if (iter)
+ iter->next = format;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->decimalPoint != NULL) xmlFree(format->decimalPoint);
+ format->decimalPoint = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->grouping != NULL) xmlFree(format->grouping);
+ format->grouping = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->infinity != NULL) xmlFree(format->infinity);
+ format->infinity = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->minusSign != NULL) xmlFree(format->minusSign);
+ format->minusSign = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->noNumber != NULL) xmlFree(format->noNumber);
+ format->noNumber = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"percent", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->percent != NULL) xmlFree(format->percent);
+ format->percent = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->permille != NULL) xmlFree(format->permille);
+ format->permille = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->zeroDigit != NULL) xmlFree(format->zeroDigit);
+ format->zeroDigit = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"digit", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->digit != NULL) xmlFree(format->digit);
+ format->digit = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (format->patternSeparator != NULL) xmlFree(format->patternSeparator);
+ format->patternSeparator = prop;
+ }
+}
+
+/**
* xsltParseStylesheetPreserveSpace:
* @style: the XSLT stylesheet
* @template: the "preserve-space" element
}
/**
+ * xsltParseRemoveBlanks:
+ * @style: the XSLT stylesheet
+ *
+ * Clean-up the stylesheet content from unwanted ignorable blank nodes
+ * and process xslt:text
+ */
+void
+xsltParseRemoveBlanks(xsltStylesheetPtr style) {
+ xmlNodePtr cur, delete;
+
+ /*
+ * This content comes from the stylesheet
+ * For stylesheets, the set of whitespace-preserving
+ * element names consists of just xsl:text.
+ */
+ cur = (xmlNodePtr) style->doc;
+ if (cur == NULL)
+ return;
+ cur = cur->children;
+ delete = NULL;
+ while (cur != NULL) {
+ if (delete != NULL) {
+#ifdef DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseRemoveBlanks: removing ignorable blank node\n");
+#endif
+ xmlUnlinkNode(delete);
+ xmlFreeNode(delete);
+ delete = NULL;
+ }
+ if (IS_XSLT_ELEM(cur)) {
+ if (IS_XSLT_NAME(cur, "text")) {
+ goto skip_children;
+ }
+ } else if (cur->type == XML_TEXT_NODE) {
+ if (IS_BLANK_NODE(cur)) {
+ if (xmlNodeGetSpacePreserve(cur) != 1) {
+ delete = cur;
+ }
+ }
+ } else if (cur->type != XML_ELEMENT_NODE) {
+ delete = cur;
+ }
+
+ /*
+ * Skip to next node
+ */
+ if (cur->children != NULL) {
+ if (cur->children->type != XML_ENTITY_DECL) {
+ cur = cur->children;
+ continue;
+ }
+ }
+skip_children:
+ if (cur->next != NULL) {
+ cur = cur->next;
+ continue;
+ }
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL)
+ break;
+ if (cur == (xmlNodePtr) style->doc) {
+ cur = NULL;
+ break;
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ break;
+ }
+ } while (cur != NULL);
+ }
+ if (delete != NULL) {
+#ifdef DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseRemoveBlanks: removing ignorable blank node\n");
+#endif
+ xmlUnlinkNode(delete);
+ xmlFreeNode(delete);
+ delete = NULL;
+ }
+}
+
+/**
* xsltParseTemplateContent:
* @style: the XSLT stylesheet
* @ret: the "template" structure
if (delete != NULL) {
#ifdef DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
- "xsltParseStylesheetTemplate: removing ignorable blank node\n");
+ "xsltParseStylesheetTemplate: removing text\n");
#endif
xmlUnlinkNode(delete);
xmlFreeNode(delete);
delete = cur;
goto skip_children;
}
- } else if (cur->type == XML_TEXT_NODE) {
- if (IS_BLANK_NODE(cur)) {
- if (xmlNodeGetSpacePreserve(cur) != 1) {
- delete = cur;
- }
- }
- } else if (cur->type != XML_ELEMENT_NODE) {
- delete = cur;
}
/*
if (delete != NULL) {
#ifdef DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
- "xsltParseStylesheetTemplate: removing ignorable blank node\n");
+ "xsltParseStylesheetTemplate: removing text\n");
#endif
xmlUnlinkNode(delete);
xmlFreeNode(delete);
*/
cur = template->children;
while (cur != NULL) {
- /*
- * Remove Blank nodes found at this level.
- */
- if (IS_BLANK_NODE(cur)) {
- xmlNodePtr blank = cur;
-
- cur = cur->next;
- xmlUnlinkNode(blank);
- xmlFreeNode(blank);
- continue;
- }
if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param"))))
break;
cur = cur->next;
* Browse the remaining of the template
*/
while (cur != NULL) {
- /*
- * Remove Blank nodes found at this level.
- */
- if (IS_BLANK_NODE(cur)) {
- xmlNodePtr blank = cur;
-
- cur = cur->next;
- xmlUnlinkNode(blank);
- xmlFreeNode(blank);
- continue;
- }
if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
xmlNodePtr param = cur;
cur = cur->next;
}
while (cur != NULL) {
- if (IS_BLANK_NODE(cur)) {
- cur = cur->next;
- continue;
- }
if (!(IS_XSLT_ELEM(cur))) {
#ifdef DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
} else if (IS_XSLT_NAME(cur, "key")) {
TODO /* Handle key */
} else if (IS_XSLT_NAME(cur, "decimal-format")) {
- TODO /* Handle decimal-format */
+ xsltParseStylesheetDecimalFormat(style, cur);
} else if (IS_XSLT_NAME(cur, "attribute-set")) {
- TODO /* Handle attribute-set */
+ xsltParseStylesheetAttributeSet(style, cur);
} else if (IS_XSLT_NAME(cur, "variable")) {
xsltParseGlobalVariable(style, cur);
} else if (IS_XSLT_NAME(cur, "param")) {
ret = xsltNewStylesheet();
if (ret == NULL)
return(NULL);
-
+
/*
- * First step, locate the xsl:stylesheet element and the
+ * First steps, remove blank nodes,
+ * locate the xsl:stylesheet element and the
* namespace declaration.
*/
cur = xmlDocGetRootElement(doc);
}
ret->doc = doc;
+ xsltParseRemoveBlanks(ret);
if ((IS_XSLT_ELEM(cur)) &&
((IS_XSLT_NAME(cur, "stylesheet")) ||
(IS_XSLT_NAME(cur, "transform")))) {
};
/*
+ * Data structure of decimal-format
+ */
+typedef struct _xsltDecimalFormat {
+ struct _xsltDecimalFormat *next; /* chained list */
+ xmlChar *name;
+ /* Used for interpretation of pattern */
+ xmlChar *digit;
+ xmlChar *patternSeparator;
+ /* May appear in result */
+ xmlChar *minusSign;
+ xmlChar *infinity;
+ xmlChar *noNumber; /* Not-a-number */
+ /* Used for interpretation of pattern and may appear in result */
+ xmlChar *decimalPoint;
+ xmlChar *grouping;
+ xmlChar *percent;
+ xmlChar *permille;
+ xmlChar *zeroDigit;
+} xsltDecimalFormat, *xsltDecimalFormatPtr;
+
+/*
* The in-memory structure corresponding to an XSLT Stylesheet
* NOTE: most of the content is simply linked from the doc tree
* structure, no specific allocation is made.
xmlHashTablePtr nsAliases; /* the namespace alias hash tables */
/*
+ * Attribute sets
+ */
+ xmlHashTablePtr attributeSets;/* the attribute sets hash tables */
+
+ /*
* Output related stuff.
*/
xmlChar *method; /* the output method */
xmlChar *version; /* version string */
xmlChar *encoding; /* encoding string */
int omitXmlDeclaration; /* omit-xml-declaration = "yes" | "no" */
+
+ /* Number formatting */
+ xsltDecimalFormatPtr decimalFormat;
int standalone; /* standalone = "yes" | "no" */
xmlChar *doctypePublic; /* doctype-public string */
xmlChar *doctypeSystem; /* doctype-system string */
/*
* Functions associated to the internal types
+xsltDecimalFormatPtr xsltDecimalFormatGetByName(xsltStylesheetPtr sheet,
+ xmlChar *name);
*/
xsltStylesheetPtr xsltParseStylesheetFile (const xmlChar* filename);
void xsltFreeStylesheet (xsltStylesheetPtr sheet);
## Process this file with automake to produce Makefile.in
-SUBDIRS=REC1 REC2
+SUBDIRS=REC1 REC2 numbers
test tests: all
--- /dev/null
+## Process this file with automake to produce Makefile.in
+
+$(top_builddir)/libxslt/xsltproc:
+ @(cd ../../libxslt ; make xsltproc)
+
+EXTRA_DIST = doc.xsl doc.xml result.xml
+
+all: test
+
+test tests: $(top_builddir)/libxslt/xsltproc
+ @(rm -f .memdump ; touch .memdump)
+ @($(top_builddir)/libxslt/xsltproc format-number.xsl format-number.xml > format-number.res ; \
+ diff format-number.out format-number.res ; \
+ grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0";\
+ rm -f format-number.res)
+
--- /dev/null
+<?xml version="1.0" encoding="iso-8859-1"?>
+<pi>
+ one prefix3.14suffix
+ two _-3.14_
+ three _-003.1415_
+ four _(3.1)_
+ five prefix3*14suffix</pi>
--- /dev/null
+<functions>
+ <pi>3.1415</pi>
+ <negpi>-3.1415</negpi>
+</functions>
--- /dev/null
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output
+ method="xml"
+ indent="yes"
+ encoding="iso-8859-1"
+/>
+
+<xsl:decimal-format
+ name = "special"
+ decimal-separator = "*"
+/>
+
+<xsl:template match="functions">
+ <pi>
+ one <xsl:value-of select="format-number(pi, 'prefix#,#,###.##suffix')"/>
+ two <xsl:value-of select="format-number(negpi, '_#,#,###.##_')"/>
+ three <xsl:value-of select="format-number(negpi, '_#,#,000.000##_')"/>
+ four <xsl:value-of select="format-number(negpi, '_#.#_;_(#.#)_')"/>
+ five <xsl:value-of select="format-number(pi, 'prefix#,#,###*##suffix','special')"/>
+ </pi>
+</xsl:template>
+
+</xsl:stylesheet>