]> git.stg.codes - stg.git/blob - doc/xslt/slides/keynote/xsltsl/cmp.xsl
Merge branch 'master' into full-month-stats
[stg.git] / doc / xslt / slides / keynote / xsltsl / cmp.xsl
1 <?xml version="1.0"?>
2
3 <xsl:stylesheet
4   version="1.0"
5   extension-element-prefixes="doc"
6   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
7   xmlns:doc="http://xsltsl.org/xsl/documentation/1.0"
8   xmlns:str="http://xsltsl.org/string"
9   xmlns:cmp="http://xsltsl.org/cmp"
10   exclude-result-prefixes="cmp str doc"
11 >
12
13   <doc:reference xmlns="">
14     <referenceinfo>
15       <releaseinfo role="meta">
16         $Id: cmp.xsl 6297 2006-09-14 01:32:27Z xmldoc $
17       </releaseinfo>
18       <author>
19         <surname>Hummel</surname>
20         <firstname>Mark</firstname>
21       </author>
22       <copyright>
23         <year>2003</year>
24         <holder>Mark Hummel</holder>
25       </copyright>
26     </referenceinfo>
27
28     <title>XML Compare</title>
29
30     <partintro>
31       <section>
32         <title>Introduction</title>
33
34         <para>This module provides a template for comparing two xml documents. </para>
35
36       </section>
37     </partintro>
38
39   </doc:reference>
40
41
42   <doc:template name="cmp:diff">
43     <refpurpose>Find differences</refpurpose>
44
45     <refdescription>
46       <para>Compare two xml documents and display differences. Two xml documents are defined to be the same if: They have the matching elements and attributes, and that the data in the elements also match. The comparison is order sensitive. </para>
47
48       <para>The element names from the documents at the current depth are compared, followed by their values, then any attribute names and values are compared. The process is applied then to the subtrees of the documents.</para>
49
50       <para>Notes: If there are leaf nodes in one nodeset which don't exist in the other, the value of those 'extra' elements won't appear as a difference.
51       </para>
52     </refdescription>
53
54     <refparameter>
55       <variablelist>
56         <varlistentry>
57           <term>ns1</term>
58           <term>ns2</term>
59           <listitem>
60             <para>The two nodesets which are to be compared. </para>
61           </listitem>
62         </varlistentry>
63       </variablelist>
64     </refparameter>
65
66     <refreturn>
67       <para>Returns the difference between the documents. </para>
68
69       <para>The format of the output is an xml document. A node is added to the result tree for every difference. The node contains the type of difference (e.g element name difference, attribute value difference, etc), the value in the first nodeset and the value in the second nodeset, and the parent node. The indentation level is the depth at which the difference was found relative to the first document. </para>
70
71     </refreturn>
72   </doc:template>
73
74   <!-- pass in a nodeset and compare. Is order sensitive. Output attribute, element and textual differences. -->
75
76   <xsl:template name="cmp:diff">
77     <xsl:param name="ns1"/>
78     <xsl:param name="ns2"/>
79
80     <!-- attribute compare -->
81         <!-- Optimisation attempt 
82
83         Can probaby change this into one loop ie -
84         <xsl:for-each 
85           i = position
86          if node1[i] = node2[i]...
87
88           -->
89
90         <!-- Need to check if there are two sets of attributes -->
91         <xsl:choose>
92           <xsl:when test='count($ns1/attribute::*) = count($ns2/attribute::*)'>
93             <xsl:for-each select="$ns1/attribute::*">
94               <xsl:variable name="name1" select="name()"/>
95               <xsl:variable name="value1" select="."/>
96               <xsl:variable name="i" select="position()"/>
97               
98               <xsl:for-each select="$ns2/attribute::*">
99                 
100                 <xsl:variable name="j" select="position()"/>
101                 <xsl:variable name="name2" select="name()"/>
102                 <xsl:variable name="value2" select="."/>
103                 
104                 <xsl:if test="$i = $j">
105                   <xsl:if test="$name1 != $name2">
106                     <attributeNameDifference>
107                       <parentElement><xsl:value-of select="name(..)"/></parentElement>
108                       <before><xsl:value-of select="$name1"/></before>
109                       <after><xsl:value-of select="$name2"/></after>
110                     </attributeNameDifference>
111                   </xsl:if>
112                   
113                   <xsl:if test="$name1 = $name2 and $value1 != $value2">
114                     <attributeValueDifference>
115                       <parentElement><xsl:value-of select="name(..)"/></parentElement>
116                       <before><xsl:value-of select="$value1"/></before>
117                       <after><xsl:value-of select="$value2"/></after>
118                     </attributeValueDifference>
119                   </xsl:if>
120               
121                 </xsl:if>
122               </xsl:for-each>
123             </xsl:for-each>
124             </xsl:when>
125           <xsl:otherwise>
126             <attributeNameDifference>
127               <parentElement>
128                 <xsl:value-of select="name(..)"/>
129               </parentElement>
130               <before><xsl:value-of select='$ns1/attribute::*'/></before>
131               <after><xsl:value-of select='$ns2/attribute::*'/></after>
132             </attributeNameDifference>
133           </xsl:otherwise>
134         </xsl:choose>
135         
136
137   <!-- Find element differences by comparing the element names from the same position in both documents. Iterate over all the nodes in the nodeset with the largest number of elements, so the extra elements will appear as differences. -->
138
139     <xsl:choose>
140       <!-- Define loop direction based on which tree has more nodes
141            FIXME: Replacing this with one for-each and a test for the case 
142                   of the second tree having more nodes would be more elegant 
143
144            Solution: Add variable for direction and assign the 'larger' nodeset to that
145                      variable. Then do one for-each. 
146            
147            FIXME: The solution is a bit too iterative. Make it more functional
148
149       -->
150      <xsl:when test="count($ns1) &gt; count($ns2)">
151        <xsl:for-each select="$ns1">
152           <xsl:variable name="i" select="position()"/> 
153           
154           <xsl:message>node[<xsl:value-of select='$i'/>]:
155             <xsl:value-of select='$ns1[$i]'/>
156           </xsl:message>
157
158         <!-- Element name compare -->
159           <xsl:if test="name($ns1[$i]) != name($ns2[$i])">
160                 <elementNameDifference>
161                   <parentElement><xsl:value-of select="name(..)"/></parentElement>
162                   <before><xsl:value-of select="name($ns1[$i])"/></before>
163                   <after><xsl:value-of select="name($ns2[$i])"/></after>
164                 </elementNameDifference>
165           </xsl:if>
166         
167           <!-- Element Value compare -->
168         
169           <xsl:if test="count($ns1/*) = 0">
170             <xsl:if test="$ns1[$i] != $ns2[$i]">
171                  <elementValueDifference>
172                    <parentElement><xsl:value-of select="name(..)"/></parentElement>
173                    <before><xsl:value-of select="$ns1[$i]"/></before>
174                    <after><xsl:value-of select="$ns2[$i]"/></after>
175                  </elementValueDifference>
176             </xsl:if>
177          </xsl:if>
178         
179        </xsl:for-each>
180       </xsl:when>
181       <xsl:otherwise>
182         <xsl:for-each select="$ns2">
183           <xsl:variable name="i" select="position()"/> 
184
185           <!-- Element Name compare -->
186         
187           <xsl:if test="name($ns1[$i]) != name($ns2[$i])">
188                <elementNameDifference>
189                   <parentElement><xsl:value-of select="name(..)"/></parentElement>
190                   <before><xsl:value-of select="name($ns1[$i])"/></before>
191                   <after><xsl:value-of select="name($ns2[$i])"/></after>
192                </elementNameDifference>
193
194           </xsl:if>
195         
196           <!-- value compare -->
197         
198           <xsl:if test="count($ns2/*) = 0">
199              <xsl:if test="$ns2[$i] != $ns1[$i]">
200                  <elementValueDifference>
201                    <parentElement><xsl:value-of select="name(..)"/></parentElement>
202                    <after><xsl:value-of select="$ns2[$i]"/></after>
203                    <before><xsl:value-of select="$ns1[$i]"/></before>
204                  </elementValueDifference>
205              </xsl:if>
206           </xsl:if>
207         
208         </xsl:for-each>
209       </xsl:otherwise>
210     </xsl:choose>
211         
212   <!-- stop processing when leaf node is reached. -->
213
214     <xsl:if test="count($ns1/*) &gt; 0 and count($ns2/*) &gt; 0">
215       <xsl:call-template name="cmp:diff">
216             <xsl:with-param name="ns1" select="$ns1/*"/>
217             <xsl:with-param name="ns2" select="$ns2/*"/>
218         </xsl:call-template>
219     </xsl:if>
220
221   </xsl:template>
222
223   <!-- Return false if the two nodesets are not identical
224   -->
225
226   <doc:template name="cmp:cmp">
227     <refpurpose>Compare</refpurpose>
228
229     <refdescription>
230       <para>Recursively compare two xml nodesets, stop when a difference is found and return false. Otherwise return true if the document is identical. </para>
231
232       <para>The element names from the documents at the current depth are compared, followed by their values, then any attribute names and values are compared. The process is applied then to the subtrees of the documents.</para>
233
234       <para>Notes: If there are leaf nodes in one nodeset which don't exist in the other, the value of those 'extra' elements won't appear as a difference.
235       </para>
236     </refdescription>
237
238     <refparameter>
239       <variablelist>
240         <varlistentry>
241           <term>ns1</term>
242           <term>ns2</term>
243           <listitem>
244             <para>The two nodesets which are to be compared. </para>
245           </listitem>
246         </varlistentry>
247       </variablelist>
248     </refparameter>
249
250     <refreturn>
251       <para>False when the nodesets are not identical, empty otherwise. </para>
252
253     </refreturn>
254   </doc:template>
255
256   <xsl:template name="cmp:cmp">
257    <xsl:param name="ns1"/>      
258    <xsl:param name="ns2"/>      
259    <xsl:param name="depth"/>
260
261    <xsl:choose>
262      <xsl:when test='count($ns1) != count($ns2)'>
263        <xsl:value-of select='"countDiff"'/>
264      </xsl:when>
265      <xsl:when test='count($ns1/attribute::*) != count($ns2/attribute::*)'>
266        <xsl:value-of select='"countDiff"'/>
267      </xsl:when>
268      <xsl:when test='$ns1 and $ns2'>
269
270        <xsl:variable name='result'>
271         <xsl:call-template name='cmp:cmp'>
272           <xsl:with-param name='ns1' select='$ns1/*'/>
273           <xsl:with-param name='ns2' select='$ns2/*'/>
274           <xsl:with-param name='depth' select='$depth+1'/>
275         </xsl:call-template>
276         </xsl:variable> 
277
278        <xsl:choose>
279           <xsl:when test='$result = "countDiff"'>
280             <xsl:value-of select='$result'/>
281           </xsl:when>
282           <xsl:when test='$result = "textDiff"'>
283             <xsl:value-of select='$result'/>
284           </xsl:when>     
285           <xsl:when test='$result = ""'>
286
287             <xsl:variable name='keyText1' select='name($ns1)'/>
288             <xsl:variable name='keyText2' select='name($ns2)'/>
289                     
290             <xsl:choose>
291               <!-- Check if the text of the nodesets are the same and the attributes-->
292               <xsl:when test='$ns1 = $ns2 and $keyText1 = $keyText2'>
293
294                 <!-- Check the attribute names are the same -->
295                 <!-- Number of attributes being different is caught higher up -->
296                 <xsl:if test='count($ns1/attribute::*)'>
297                   <xsl:for-each select='$ns1/attribute::*'>
298                     <xsl:variable name='i' select='position()'/>
299                     <xsl:variable name='name1' select='name(.)'/>
300                     <xsl:variable name='value1' select='.'/>
301                     
302                     <xsl:for-each select='$ns2/attribute::*'>
303                       <xsl:variable name='j' select='position()'/>
304                       <xsl:variable name='name2' select='name(.)'/>
305                       <xsl:variable name='value2' select='.'/>
306
307                       <xsl:if test='$i = $j and ($name1 != $name2 or 
308                                     $value1 != $value2)'>
309                         <xsl:value-of select='"textDiff"'/>
310                       </xsl:if>
311                       
312                     </xsl:for-each>
313                   </xsl:for-each>
314                 </xsl:if>
315                 <!--
316                 <xsl:variable name='diffResult'>
317                   <xsl:call-template name='cmp:diff'>
318                     <xsl:with-param name='ns1' select='$ns1'/>
319                     <xsl:with-param name='ns2' select='$ns2'/>
320                   </xsl:call-template>
321                 </xsl:variable>
322                 
323                 <xsl:if test='not($diffResult = "")'>
324                   <xsl:value-of select='"textDiff"'/>
325                 </xsl:if>
326                 -->
327
328               </xsl:when>
329               <xsl:otherwise>
330                 <xsl:value-of select='"textDiff"'/>
331               </xsl:otherwise>
332             </xsl:choose>
333           </xsl:when>
334         </xsl:choose>
335           
336      </xsl:when>
337      <xsl:when test='$ns1 and not($ns2)'>
338        <xsl:value-of select='"structDiff"'/>
339      </xsl:when>
340      <xsl:when test='$ns2 and not($ns1)'>
341        <xsl:value-of select='"structDiff"'/>
342      </xsl:when>
343    </xsl:choose>
344
345   </xsl:template>
346
347 </xsl:stylesheet>
348