[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: Merging

Subject: Re: Merging
From: Tom Mullen <Tom.Mullen@xxxxxxxxxxx>
Date: Wed, 16 Aug 2000 09:01:02 +0000 (GMT)
xslt merge append
Oliver,

Firstly, apologies for the length and use of bandwidth to those who are not
interested in this.

Just to recap:

>From your definition: Nodes are defined as equivalent if:

1) they have the same element name
2) they have the same attributes and attribute values (and by inference the
same number of attributes)
3) the parents of the two nodes are equivalent

The method below is based on Jenni's key() approach.

I've included a parameter mrgdiftype that can take the following string
values (default 'diff') (NB a node is considered removed when it appears in
the old doc and not in the new, a node is considered added if it appears in
the new and not in the old, a node is considered modified if its text nodes
are different in the two docs) :

a) append - removed and added nodes are copied, modified nodes cause an
error
b) change - removed nodes kept, added nodes cause error and new version
only of modified node copied
c) merge - removed and added nodes are copied, new version only of modified
node copied
d) replace - added nodes are copied, new version only of modified node
copied
e) diff - html report produced detailing additions, removals and
modifications.

The following templates are required for each element that requires a key
or special processing (e.g.. those with attributes)  The key definition is
also required.

mode=determine-comparison: finds the node equivalent to the context node in
the node list parameter "new" and applies the compall mode template for
these two nodes.

mode=determine-addition: determines if the context node exists in the node
list parameter "less".  If not it must call the additional-item template
for the context node.  The "head" parameter has the value "Added:" or
"Removed:" and must be passed on to addition-item template.

mode=diff-describe : describes the addition or removal of a node for a html
document

mode=difference-report : describes the difference in the text node values
for equivalent nodes for a html document

The idea is that these templates and the key definitions are created using
an xslt pre-compiler to produce an xslt file that can be included/imported
into the diff/merge utility.  Alternatively, for complex elements it may be
possible for a user to hand-create these templates in a xslt file that
could (with a clever use of import/include) override the pre-compiled
versions.

The code below is tailored to my own specific needs and is not generic. It
still requires some work (e.g. it always produces the html envelope) and
hasn't been thoroughly tested.  It also looks as though it's been hacked
rather than written, because that's how it was created (I'm still very low
on the learning curve).  It is also not as complete as your approach - e.g.
comment nodes are totally ignored and not copied.  However, I hope it
illustrates how I see key() could be used.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:param name="oldfile" />
<xsl:param name="mrgdiftype" select="'diff'"/>
<xsl:variable name="old" select="document($oldfile)" />
<xsl:variable name="new" select="/" />

<xsl:template match="/RELEASEDOC">
<html>
<head><title>Release Comparison</title></head>
<body>
<h1>Release Comparison</h1>

<!--
<xsl:comment>new:<xsl:value-of select="$new/"/></xsl:comment>
<xsl:comment>old:<xsl:value-of select="$old/"/></xsl:comment>
<xsl:comment>oldfile:<xsl:value-of select="$oldfile"/></xsl:comment>
-->

<p>

  Old release: <xsl:apply-templates
                 select="$old/RELEASEDOC/RELEASESELECTION" />

<!--
<xsl:comment><xsl:value-of select="$old/"/></xsl:comment>
<xsl:comment><xsl:value-of select="$oldfile"/></xsl:comment>
-->
</p>
<p>

  New release: <xsl:apply-templates
                 select="$new/RELEASEDOC/RELEASESELECTION" />

</p>
<ul>
<xsl:apply-templates mode="compall" select="$old/RELEASEDOC">
    <xsl:with-param name="new" select="$new/RELEASEDOC" />
</xsl:apply-templates>
</ul>
</body>
</html>
</xsl:template>

<xsl:template mode="compall" match="*">
  <xsl:param name="new" />

  <xsl:variable name="old" select="."/>

<!--
  <xsl:variable name="oldname" select="name($old)"/>
  <xsl:comment>Old name=<xsl:value-of select="$oldname"/></xsl:comment>
  <xsl:variable name="newname" select="name($new)"/>
  <xsl:comment>New name=<xsl:value-of select="$newname"/></xsl:comment>
-->


  <xsl:variable name="oldval" select="$old/text()"/>
  <xsl:variable name="newval" select="$new/text()"/>

<!--
  <xsl:comment>Old val=<xsl:value-of select="$oldval"/></xsl:comment>
  <xsl:comment>New val=<xsl:value-of select="$newval"/></xsl:comment>
-->

 <xsl:choose>
 <xsl:when test="$mrgdiftype='diff'">
<!--
  <xsl:comment>New val=<xsl:value-of select="$newval"/>Old
val=<xsl:value-of select="$oldval"/></xsl:comment>
-->
  <xsl:if test="not((string-length($oldval)=string-length($newval)) and
contains($oldval,$newval))">
<!--
    <xsl:comment>different vals</xsl:comment>
-->
    <xsl:call-template name="different-value">
      <xsl:with-param name="new" select="$new" />
      <xsl:with-param name="old" select="$old" />
    </xsl:call-template>
  </xsl:if>

  <xsl:call-template name="list-additional">
    <xsl:with-param name="head">Added:</xsl:with-param>
    <xsl:with-param name="more" select="$new" />
    <xsl:with-param name="less" select="$old" />
  </xsl:call-template>

  <xsl:call-template name="list-additional">
    <xsl:with-param name="head">Removed:</xsl:with-param>
    <xsl:with-param name="more" select="$old" />
    <xsl:with-param name="less" select="$new" />
  </xsl:call-template>

  <xsl:call-template name="list-compare">
    <xsl:with-param name="old" select="$old" />
    <xsl:with-param name="new" select="$new" />
  </xsl:call-template>
 </xsl:when>
 <xsl:otherwise>
 <xsl:copy>
  <xsl:for-each select="@*">
    <xsl:copy/>
  </xsl:for-each>

<!--
  <xsl:comment>New val=<xsl:value-of select="$newval"/>Old
val=<xsl:value-of select="$oldval"/></xsl:comment>
-->
  <xsl:choose>

  <xsl:when test="not((string-length($oldval)=string-length($newval)) and
contains($oldval,$newval))">
<!--
    <xsl:comment>different vals</xsl:comment>
-->
    <xsl:call-template name="different-value">
      <xsl:with-param name="new" select="$new" />
      <xsl:with-param name="old" select="$old" />
    </xsl:call-template>
  </xsl:when>
  <xsl:otherwise>
    <xsl:for-each select="$new/text()">
      <xsl:copy/>
    </xsl:for-each>
  </xsl:otherwise>
  </xsl:choose>

  <xsl:call-template name="list-additional">
    <xsl:with-param name="head">Added:</xsl:with-param>
    <xsl:with-param name="more" select="$new" />
    <xsl:with-param name="less" select="$old" />
  </xsl:call-template>

  <xsl:call-template name="list-additional">
    <xsl:with-param name="head">Removed:</xsl:with-param>
    <xsl:with-param name="more" select="$old" />
    <xsl:with-param name="less" select="$new" />
  </xsl:call-template>

  <xsl:call-template name="list-compare">
    <xsl:with-param name="old" select="$old" />
    <xsl:with-param name="new" select="$new" />
  </xsl:call-template>

 </xsl:copy>
 </xsl:otherwise>
 </xsl:choose>
</xsl:template>



<xsl:template match="RELEASESELECTION">
  Version <xsl:value-of select="VERSION" />
</xsl:template>

<xsl:key name="items" match="CONFIGUREDITEM" use="@name" />
<xsl:key name="logs" match="LOGICALDEF" use="@name" />
<xsl:key name="claslog" match="CLASSDEF" use="@log" />

<xsl:template name="list-additional">
  <xsl:param name="head" />
  <xsl:param name="more" />
  <xsl:param name="less" />
  <!-- cycle through each item in the 'more' document -->
  <xsl:apply-templates select="$more/*" mode="determine-addition">
    <xsl:with-param name="head" select="$head" />
    <xsl:with-param name="less" select="$less" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template name="additional-item">
  <xsl:param name="item" />
  <xsl:param name="head" />
    <xsl:choose>
      <xsl:when test="$mrgdiftype='append'">
          <xsl:choose>
            <!-- items added and removed should be kept for append -->
            <xsl:when test="$head='Added:'">
              <xsl:copy-of select="$item"/>
            </xsl:when>
            <xsl:when test="$head='Removed:'">
              <xsl:copy-of select="$item"/>
            </xsl:when>
            <xsl:otherwise/>
          </xsl:choose>
      </xsl:when>
      <xsl:when test="$mrgdiftype='change'">
          <xsl:choose>
            <!-- items added should be ignored for change -->
            <xsl:when test="$head='Added:'">
              <xsl:comment>Error new file contains node not in old file
<xsl:value-of select="$item"/></xsl:comment>
            </xsl:when>
            <xsl:when test="$head='Removed:'">
              <xsl:copy-of select="$item"/>
            </xsl:when>
            <xsl:otherwise/>
          </xsl:choose>
      </xsl:when>
      <xsl:when test="$mrgdiftype='merge'">
          <xsl:choose>
            <!-- items added and removed should be kept for merge -->
            <xsl:when test="$head='Added:'">
              <xsl:copy-of select="$item"/>
            </xsl:when>
            <xsl:when test="$head='Removed:'">
              <xsl:copy-of select="$item"/>
            </xsl:when>
            <xsl:otherwise/>
          </xsl:choose>
      </xsl:when>
      <xsl:when test="$mrgdiftype='diff'">
          <xsl:choose>
            <!-- items added and removed should be reported for diff -->
            <xsl:when test="$head='Added:'">
              <xsl:apply-templates select="$item" mode="diff-describe">
                <xsl:with-param name="head" select="$head" />
              </xsl:apply-templates>
            </xsl:when>
            <xsl:when test="$head='Removed:'">
              <xsl:apply-templates select="$item" mode="diff-describe">
                <xsl:with-param name="head" select="$head" />
              </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise/>
          </xsl:choose>
      </xsl:when>
      <xsl:when test="$mrgdiftype='replace'">
          <xsl:choose>
            <!-- items removed should not be kept for replace -->
            <xsl:when test="$head='Added:'">
              <xsl:copy-of select="$item"/>
            </xsl:when>
            <xsl:when test="$head='Removed:'"/>
            <xsl:otherwise/>
          </xsl:choose>
      </xsl:when>
      <xsl:otherwise><xsl:comment>Wrong mrgdiftype -<xsl:value-of
select="$mrgdiftype"/></xsl:comment></xsl:otherwise>
    </xsl:choose>
</xsl:template>


<xsl:template mode="determine-addition" match="CONFIGUREDITEM">
  <xsl:param name="head" />
  <xsl:param name="less" />
    <xsl:variable name="item" select="." />
    <!-- select the 'less' document so that the key indexes into that
         document -->
    <xsl:for-each select="$less">
      <xsl:if test="not(key('items', $item/@name))">
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
        <xsl:call-template name="additional-item">
         <xsl:with-param name="item" select="$item" />
         <xsl:with-param name="head" select="$head" />
        </xsl:call-template>
      </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template mode="diff-describe" match="CONFIGUREDITEM">
  <xsl:param name="head" />
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
   <li><xsl:value-of select="$head"/><xsl:value-of select="@name" /></li>
</xsl:template>



<xsl:template mode="determine-addition" match="LOGICALDEF">
  <xsl:param name="head" />
  <xsl:param name="less" />
    <xsl:variable name="item" select="." />
    <!-- select the 'less' document so that the key indexes into that
         document -->
    <xsl:for-each select="$less">
      <xsl:if test="not(key('logs', $item/@name))">
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
        <xsl:call-template name="additional-item">
         <xsl:with-param name="item" select="$item" />
         <xsl:with-param name="head" select="$head" />
        </xsl:call-template>
      </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template mode="diff-describe" match="LOGICALDEF">
  <xsl:param name="head" />
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
   <li><xsl:value-of select="$head"/>Logical-<xsl:value-of select="@name"
/></li>
</xsl:template>



<xsl:template mode="determine-addition" match="CLASSDEF">
  <xsl:param name="head" />
  <xsl:param name="less" />
    <xsl:variable name="item" select="." />
    <!-- select the 'less' document so that the key indexes into that
         document -->
    <xsl:for-each select="$less">
      <xsl:if test="not(key('claslog', $item/@log))">
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
        <xsl:call-template name="additional-item">
         <xsl:with-param name="item" select="$item" />
         <xsl:with-param name="head" select="$head" />
        </xsl:call-template>
      </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template mode="diff-describe" match="CLASSDEF">
  <xsl:param name="head" />
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
   <li><xsl:value-of select="$head"/>Class-<xsl:value-of select="@log"
/></li>
</xsl:template>

<xsl:template mode="determine-addition" match="*">
  <xsl:param name="head" />
  <xsl:param name="less" />
    <xsl:variable name="item" select="." />
      <xsl:if test="not($less/*[name()=name($item)])">
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
        <xsl:call-template name="additional-item">
         <xsl:with-param name="item" select="$item" />
         <xsl:with-param name="head" select="$head" />
        </xsl:call-template>
      </xsl:if>
</xsl:template>

<xsl:template mode="diff-describe" match="*">
  <xsl:param name="head" />
  <xsl:variable name="item" select="." />
<!--
        <xsl:comment><xsl:value-of select="$item"/></xsl:comment>
-->
   <li><xsl:value-of select="$head"/><xsl:value-of select="name(.)"
/>=<xsl:value-of select="text()" /></li>
</xsl:template>

<xsl:template name="list-compare">
  <xsl:param name="old" />
  <xsl:param name="new" />
  <!-- cycle through each item in the 'more' document -->
  <xsl:apply-templates select="$old/*" mode="determine-comparison">
    <xsl:with-param name="new" select="$new" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template mode="determine-comparison" match="CONFIGUREDITEM">
  <xsl:param name="new" />
    <xsl:variable name="old2" select="." />
    <xsl:for-each select="$new">
      <xsl:if test="key('items', $old2/@name)">
	<xsl:variable name="new2" select="key('items', $old2/@name)"/>
          <xsl:apply-templates mode="compall" select="$old2">
            <xsl:with-param name="new" select="$new2" />
          </xsl:apply-templates>
      </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template mode="determine-comparison" match="LOGICALDEF">
  <xsl:param name="new" />
    <xsl:variable name="old2" select="." />
    <xsl:for-each select="$new">
      <xsl:if test="key('logs', $old2/@name)">
	<xsl:variable name="new2" select="key('logs', $old2/@name)"/>
          <xsl:apply-templates mode="compall" select="$old2">
            <xsl:with-param name="new" select="$new2" />
          </xsl:apply-templates>
      </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template mode="determine-comparison" match="CLASSDEF">
  <xsl:param name="new" />
    <xsl:variable name="old2" select="." />
    <xsl:for-each select="$new">
      <xsl:if test="key('claslog', $old2/@log)">
	<xsl:variable name="new2" select="key('claslog', $old2/@log)"/>
          <xsl:apply-templates mode="compall" select="$old2">
            <xsl:with-param name="new" select="$new2" />
          </xsl:apply-templates>
      </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template mode="determine-comparison" match="*">
  <xsl:param name="new" />
    <xsl:variable name="old2" select="." />
    <xsl:if test="$new/*[name()=name($old2)]">
 	  <xsl:variable name="new2" select="$new/*[name()=name($old2)]"/>
          <xsl:apply-templates mode="compall" select="$old2">
            <xsl:with-param name="new" select="$new2" />
          </xsl:apply-templates>
    </xsl:if>
</xsl:template>


<xsl:template name="different-value">
  <xsl:param name="old" />
  <xsl:param name="new" />
    <xsl:choose>
      <xsl:when test="$mrgdiftype='append'">
         <!--  for append values should not be different - report error-->
         <xsl:message terminate="yes">Append mode specified but nodes have
different values</xsl:message>
      </xsl:when>
      <xsl:when test="$mrgdiftype='change'">
         <!--  for change new value should be used-->
         <xsl:for-each select="$new/text()">
           <xsl:copy/>
         </xsl:for-each>
      </xsl:when>
      <xsl:when test="$mrgdiftype='merge'">
         <!--  for merge new value should be used-->
         <xsl:for-each select="$new/text()">
           <xsl:copy/>
         </xsl:for-each>
      </xsl:when>
      <xsl:when test="$mrgdiftype='diff'">
         <!--  for diff , difference should be reported-->
         <xsl:apply-templates select="$old" mode="difference-report">
           <xsl:with-param name="new" select="$new" />
         </xsl:apply-templates>
      </xsl:when>
      <xsl:when test="$mrgdiftype='replace'">
         <!--  for replace new value should be used-->
         <xsl:for-each select="$new/text()">
           <xsl:copy/>
         </xsl:for-each>
      </xsl:when>
      <xsl:otherwise><xsl:comment>Wrong mrgdiftype -<xsl:value-of
select="$mrgdiftype"/></xsl:comment></xsl:otherwise>
    </xsl:choose>

</xsl:template>

<xsl:template mode="difference-report" match="*">
  <xsl:param name="new" />
  <xsl:variable name="oldval" select="text()"/>
  <xsl:variable name="newval" select="$new/text()"/>
  <xsl:variable name="oldname" select="name(.)" />
  <xsl:variable name="newname" select="name($new)" />
  <p>NEW:<xsl:value-of select="$newname"/>=<xsl:value-of
select="$newval"/></p>
  <p>OLD:<xsl:value-of select="$oldname"/>=<xsl:value-of
select="$oldval"/></p>
</xsl:template>



<xsl:template match="*" mode="describe">
  <xsl:value-of select="text()" />
</xsl:template>

<xsl:template match="CONFIGUREDITEM" mode="describe">
  <xsl:value-of select="@name" />
</xsl:template>

<xsl:template match="LOGICALDEF" mode="describe">
   <xsl:text>Logical-</xsl:text><xsl:value-of select="@name"
/>=<xsl:value-of select="DIRECTORY" />
</xsl:template>

<xsl:template match="CLASSDEF" mode="describe">
  <xsl:value-of select="@log" />
</xsl:template>


</xsl:stylesheet>


-----------------------------------------------------------------
        Visit our Internet site at http://www.reuters.com

Any views expressed in this message are those of  the  individual
sender,  except  where  the sender specifically states them to be
the views of Reuters Ltd.


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread
  • Re: Merging
    • Oliver Becker - Tue, 15 Aug 2000 12:31:33 +0200 (MET DST)
      • Tom Mullen - Wed, 16 Aug 2000 09:01:02 +0000 (GMT) <=
        • Tom Mullen - Wed, 16 Aug 2000 11:36:18 +0000 (GMT)

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.