Commit | Line | Data |
---|---|---|
c74c7c13 ML |
1 | This makes generated IDs deterministic. |
2 | ||
3 | Written by Daniel Veillard. | |
4 | ||
5 | This should be fixed in next release (2.29). | |
6 | See https://bugzilla.gnome.org/show_bug.cgi?id=751621. | |
7 | ||
8 | diff --git a/libxslt/functions.c b/libxslt/functions.c | |
9 | index 6448bde..5b00a6d 100644 | |
10 | --- a/libxslt/functions.c | |
11 | +++ b/libxslt/functions.c | |
12 | @@ -651,6 +651,63 @@ xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) | |
13 | } | |
14 | ||
15 | /** | |
16 | + * xsltCleanupIds: | |
17 | + * @ctxt: the transformation context | |
18 | + * @root: the root of the resulting document | |
19 | + * | |
20 | + * This clean up ids which may have been saved in Element contents | |
21 | + * by xsltGenerateIdFunction() to provide stable IDs on elements. | |
22 | + * | |
23 | + * Returns the number of items cleaned or -1 in case of error | |
24 | + */ | |
25 | +int | |
26 | +xsltCleanupIds(xsltTransformContextPtr ctxt, xmlNodePtr root) { | |
27 | + xmlNodePtr cur; | |
28 | + int count = 0; | |
29 | + | |
30 | + if ((ctxt == NULL) || (root == NULL)) | |
31 | + return(-1); | |
32 | + if (root->type != XML_ELEMENT_NODE) | |
33 | + return(-1); | |
34 | + | |
35 | + cur = root; | |
36 | + while (cur != NULL) { | |
37 | + if (cur->type == XML_ELEMENT_NODE) { | |
38 | + if (cur->content != NULL) { | |
39 | + cur->content = NULL; | |
40 | + count++; | |
41 | + } | |
42 | + if (cur->children != NULL) { | |
43 | + cur = cur->children; | |
44 | + continue; | |
45 | + } | |
46 | + } | |
47 | + if (cur->next != NULL) { | |
48 | + cur = cur->next; | |
49 | + continue; | |
50 | + } | |
51 | + do { | |
52 | + cur = cur->parent; | |
53 | + if (cur == NULL) | |
54 | + break; | |
55 | + if (cur == (xmlNodePtr) root) { | |
56 | + cur = NULL; | |
57 | + break; | |
58 | + } | |
59 | + if (cur->next != NULL) { | |
60 | + cur = cur->next; | |
61 | + break; | |
62 | + } | |
63 | + } while (cur != NULL); | |
64 | + } | |
65 | + | |
66 | +fprintf(stderr, "Attributed %d IDs for element, cleaned up %d\n", | |
67 | + ctxt->nextid, count); | |
68 | + | |
69 | + return(count); | |
70 | +} | |
71 | + | |
72 | +/** | |
73 | * xsltGenerateIdFunction: | |
74 | * @ctxt: the XPath Parser context | |
75 | * @nargs: the number of arguments | |
76 | @@ -701,7 +758,39 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ | |
77 | if (obj) | |
78 | xmlXPathFreeObject(obj); | |
79 | ||
80 | - val = (long)((char *)cur - (char *)&base_address); | |
81 | + /* | |
82 | + * Try to provide stable ID for generated document: | |
83 | + * - usually ID are computed to be placed on elements via attributes | |
84 | + * so using the element as the node for the ID | |
85 | + * - the cur->content should be a correct placeholder for this, we use | |
86 | + * it to hold element node numbers in xmlXPathOrderDocElems to | |
87 | + * speed up XPath too | |
88 | + * - xsltCleanupIds() clean them up before handing the XSLT output | |
89 | + * to the API client. | |
90 | + * - other nodes types use the node address method but that should | |
91 | + * not end up in resulting document ID | |
92 | + * - we can enable this by default without risk of performance issues | |
93 | + * only the one pass xsltCleanupIds() is added | |
94 | + */ | |
95 | + if (cur->type == XML_ELEMENT_NODE) { | |
96 | + if (cur->content == NULL) { | |
97 | + xsltTransformContextPtr tctxt; | |
98 | + | |
99 | + tctxt = xsltXPathGetTransformContext(ctxt); | |
100 | + if (tctxt == NULL) { | |
101 | + val = (long)((char *)cur - (char *)&base_address); | |
102 | + } else { | |
103 | + tctxt->nextid++; | |
104 | + val = tctxt->nextid; | |
105 | + cur->content = (void *) (val); | |
106 | + } | |
107 | + } else { | |
108 | + val = (long) cur->content; | |
109 | + } | |
110 | + } else { | |
111 | + val = (long)((char *)cur - (char *)&base_address); | |
112 | + } | |
113 | + | |
114 | if (val >= 0) { | |
115 | sprintf((char *)str, "idp%ld", val); | |
116 | } else { | |
117 | diff --git a/libxslt/functions.h b/libxslt/functions.h | |
118 | index e0e0bf9..4a1e163 100644 | |
119 | --- a/libxslt/functions.h | |
120 | +++ b/libxslt/functions.h | |
121 | @@ -64,6 +64,13 @@ XSLTPUBFUN void XSLTCALL | |
122 | int nargs); | |
123 | ||
124 | /* | |
125 | + * Cleanup for ID generation | |
126 | + */ | |
127 | +XSLTPUBFUN int XSLTCALL | |
128 | + xsltCleanupIds (xsltTransformContextPtr ctxt, | |
129 | + xmlNodePtr root); | |
130 | + | |
131 | +/* | |
132 | * And the registration | |
133 | */ | |
134 | ||
135 | diff --git a/libxslt/transform.c b/libxslt/transform.c | |
136 | index 24f9eb2..2bdf6bf 100644 | |
137 | --- a/libxslt/transform.c | |
138 | +++ b/libxslt/transform.c | |
139 | @@ -700,6 +700,7 @@ xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) { | |
140 | cur->traceCode = (unsigned long*) &xsltDefaultTrace; | |
141 | cur->xinclude = xsltGetXIncludeDefault(); | |
142 | cur->keyInitLevel = 0; | |
143 | + cur->nextid = 0; | |
144 | ||
145 | return(cur); | |
146 | ||
147 | @@ -6092,6 +6093,13 @@ xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc, | |
148 | if (root != NULL) { | |
149 | const xmlChar *doctype = NULL; | |
150 | ||
151 | + /* | |
152 | + * cleanup ids which may have been saved in Elements content ptrs | |
153 | + */ | |
154 | + if (ctxt->nextid != 0) { | |
155 | + xsltCleanupIds(ctxt, root); | |
156 | + } | |
157 | + | |
158 | if ((root->ns != NULL) && (root->ns->prefix != NULL)) | |
159 | doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); | |
160 | if (doctype == NULL) | |
161 | diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h | |
162 | index 95e8fe6..8eedae4 100644 | |
163 | --- a/libxslt/xsltInternals.h | |
164 | +++ b/libxslt/xsltInternals.h | |
badcb119 | 165 | @@ -1782,6 +1782,8 @@ struct _xsltTransformContext { |
c74c7c13 | 166 | int maxTemplateVars; |
badcb119 MB |
167 | unsigned long opLimit; |
168 | unsigned long opCount; | |
c74c7c13 ML |
169 | + |
170 | + unsigned long nextid;/* for generating stable ids */ | |
171 | }; | |
172 | ||
173 | /** |