[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] Re: Merging
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
|
PURCHASE STYLUS STUDIO ONLINE TODAY!Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced! Download The World's Best XML IDE!Accelerate XML development with our award-winning XML IDE - Download a free trial today! Subscribe in XML format
|