tags:

views:

497

answers:

1

I want to apply a XSL style sheet that counts the previous unique "ROLE" nodes and spits out the following output format of ROLE/@name, PERM/@perfrom and the number of unique ROLE nodes prior to the current node.
This is a continuation from this question but with a bit more complexity. I believe the Muenchian method is the best way to implement this because the length of this file will be large.

I have the following XML (sorry about the length)

<?xml version="1.0" encoding="utf-8" ?>
<ROLEACTIONINFO>
  <ROLE name="TESTER">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT0" field="ALL" permfrom="PERM1565"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT1">
    <ACTIONINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT1">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT8" field="ALL" permfrom="PERM1"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT1">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT7" field="ALL" permfrom="PERM2"/>
        <PERM type="PT7" field="ALL" permfrom="PERM54"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT2">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT6" field="ALL" permfrom="PERM1"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT2">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT5" field="ALL" permfrom="PERM2"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
  <ROLE name="PARENT3">
    <ACTIONINFO>
      <PERMINFO>
        <PERM type="PT2" field="ALL" permfrom="PERM44"/>
      </PERMINFO>
    </ACTIONINFO>
  </ROLE>
 </ROLEACTIONINFO>

Here is a version XSL sheet I have been playing with:

   <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output method="text"/>
  <xsl:template match="text()"/>

  <xsl:key name="kRole" match="ROLE[ACTIONINFO/PERMINFO/PERM]" use="@name" />

  <xsl:template match="PERM">
    <xsl:variable name="roles-so-far" select="ancestor::ROLE | ancestor::ROLE/preceding-sibling::ROLE[ACTIONINFO/PERMINFO/PERM]"/>
    <!-- Only select the first instance of each ROLE name -->
    <xsl:variable name="roles-so-far-unique"              
                   select="$roles-so-far[generate-id(ancestor::ROLE) = generate-id(key('kRole',ancestor::ROLE/@name)[1])]"/>
    <xsl:apply-templates select="ancestor::ROLE/@name"/>
    <xsl:text>&#x9;</xsl:text>
    <xsl:apply-templates select="@permfrom"/>
    <xsl:text>&#x9;</xsl:text>
    <xsl:value-of select="count($roles-so-far-unique)"/>
    <!-- linefeed -->
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

Here is the desired output:

TESTER  PERM1565 1
PARENT1 PERM1 2
PARENT1 PERM2 2
PARENT1 PERM54 2
PARENT2 PERM1 3
PARENT2 PERM2 3
PARENT3 PERM44 4

Here is the actual (incorrect) output:

TESTER  PERM1565 1
PARENT1 PERM1 2
PARENT1 PERM2 3
PARENT1 PERM54 3
PARENT2 PERM1 4
PARENT2 PERM2 5
PARENT3 PERM44 6

Thanks in advance.

+1  A: 

This goes from every <PERM>, taking the according <ROLE> and counting it's unique predecessors:

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

  <xsl:output method="text" />

  <xsl:variable name="TAB" select="'&#x09;'" />
  <xsl:variable name="LF"  select="'&#x0A;'" />

  <xsl:key name="kRole" match="ROLE" use="@name" />

  <xsl:template match="/">
    <xsl:apply-templates select="//PERM" />
  </xsl:template>

  <xsl:template match="PERM">
    <xsl:variable name="vThisRole" select="ancestor::ROLE[1]" />
    <xsl:variable name="vPrecedingRoles" select="
      ($vThisRole | $vThisRole/preceding-sibling::ROLE)
    " />
    <xsl:variable name="vUniquePrecedingRoles" select="
      $vPrecedingRoles[count(. | key('kRole', ./@name)[1]) = 1]
    " />
    <xsl:value-of select="
      concat(
        $vThisRole/@name, $TAB,
        @permfrom, $TAB,
        count($vUniquePrecedingRoles), $LF
      )
    " />
  </xsl:template>

</xsl:stylesheet>

Output:

TESTER  PERM1565        1
PARENT1 PERM1   2
PARENT1 PERM2   2
PARENT1 PERM54  2
PARENT2 PERM1   3
PARENT2 PERM2   3
PARENT3 PERM44  4

The XML you are dealing with is in a pretty bad shape, if you ask me. At least the fact that anything security relevant is expressed by "the count of preceding unique names" instead of a proper data structure makes me scratch my head. ;-) Any chance you can change the input?

Tomalak
This is a variant of my answer posted to the predecessor question: http://stackoverflow.com/questions/941662/xsl-counting-previous-unique-siblings/944544#944544. More extensive explanation is over there.
Tomalak
Unfornately I cannot change the input or output. (Yes it is ugly) The input is coming from some legacy system and the output is being sent to an off the shelf system. The XML is actually a step up compared to the TAB delimited output :) At least I can validate the datalengths/character formats and such with the XML data using an XSD before I dump it out to a TAB file.I'm not sure that working form the ROLE node down is going to do the trick but I'll let you know.Thanks again
Jay
So to restate my question... Do you actually want the two balanced or is there some other logic behind the ROLE - PERM distribution?
Tomalak
Each ROLE can have multiple ACTIONINFO and each ACTIONINFO can have one PERMINFO with 0-X PERM elements.Basically this is a security structure for an application and this is collecting the information on the roles in the app. This is why I was thinking that I needed to work from a PERM node template and go upwards in the tree to the the ROLE counts.Not sure if this answers your last question
Jay
Tomalak,I finally resolved the original issue using the method provided in this and the previous question. I ended up iterating within the ROLE template through the PERMs, but I first set the counter variable for the unique-role-count to a variable before the iteration and then used that variable in the iteration.The iteration will affect performance probably but that is not much of a concern right now. Thanks again for all the help!
Jay
So it seems you want to "go up" from the PERMs basically and attach to each PERM the ROLE it belongs to. I'll change my code accordingly.
Tomalak