Thursday, March 11, 2010

Novell IDM XSL - Change Attribute to Proper Case

Today I was working on a Novell IDM project and I needed to use some XSL to call an external Java to format some text.  So, when I created my policy, I created an XSLT policy instead of a standard DirXML policy.  My input values looked something like the following:

        <modify-attr attr-name="Given Name">
             <remove-all-values/>
        </modify-attr>
        <modify-attr attr-name="Given Name">
            <value>
                ROBERT
            </value>
        </modify-attr>
        <modify-attr attr-name="Surname">
             <remove-all-values/>
        </modify-attr>
        <modify-attr attr-name="Surname">
            <value>
                IVEY
            </value>
        </modify-attr>
        <modify-attr attr-name="Full Name">
             <remove-all-values/>
        </modify-attr>
        <modify-attr attr-name="Full Name">
            <value>
                ROBERT IVEY
            </value>
        </modify-attr>


 So, here is what the meat of my XSL policy looked like to transform it.  First, we find a match on the tags we want and store the attribute name and its value in some local variables:

    <xsl:template match="add-attr[@attr-name='Given Name']     |add-attr[@attr-name='Surname']">
        <xsl:variable name="attrName" select="./@attr-name"/>
        <xsl:variable name="newVal" select="./value/text()"/>

The next thing we do is ensure that there is a value.  This is done because on modify events there are two tags, the <remove-all-values/> one and the one with the new value to add.  We don't want to send a blank value and output two different tags.

         <xsl:choose>
        <xsl:when test="$newVal">

Now that we have matched the tag and ensured there is a value, lets go ahead and write the new version of the xml element and call the template to replace the value.  We can usee and otherwise statement to copy everything that isn't being replace (IE the <remove-all-values/> tags that we matched but didn't rewrite) and close up all of our xsl tags.

        <add-attr attr-name="{$attrName}">
            <value>
                <xsl:call-template name="convertCase">
                    <xsl:with-param name="UCData" select="$newVal"/>
                </xsl:call-template>
            </value>
        </add-attr>
        </xsl:when>
        <xsl:otherwise>
            <xsl:copy-of select="."/>
        </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

Lets have a look at the xsl template "convertCase" thats being called.  Its very simple and calls some java methods that are included using a jar file that we added to our IDM server.

    <xsl:template name="convertCase">
        <xsl:param name="UCData"/>
        <xsl:variable name="LCData" select="util:lowerString($UCData)"/>
        <xsl:variable name="newData" select="util:capitalizeWords($LCData)"/>
        <xsl:value-of select="$newData"/>
    </xsl:template>

The parameter UCData is passed to the method util:lowerString and stored in LCData.  Then, LCData is passed over to util:capitalizeWords and the new value is stored in newData.  Notice how newData is the selected value that we use in the replacement up in the xsl template for the modify.

This template is added to copy through everything we didn't match:

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

And our output should be exactly the same, except our snippet was modified to look like this:


        <modify-attr attr-name="Given Name">
             <remove-all-values/>
        </modify-attr>
        <modify-attr attr-name="Given Name">
            <value>
                Robert
            </value>
        </modify-attr>
        <modify-attr attr-name="Surname">
             <remove-all-values/>
        </modify-attr>
        <modify-attr attr-name="Surname">
            <value>
                Ivey
            </value>
        </modify-attr>
        <modify-attr attr-name="Full Name">
             <remove-all-values/>
        </modify-attr>
        <modify-attr attr-name="Full Name">
            <value>
                Robert Ivey
            </value>
        </modify-attr>

Please note, the java classes are custom code delivered by a consultant.  I do not have the source code, nor can I distribute this code without their permission.  A little time with a string tokenizer should help recreate this functionality, but I am by no means a java programmer.  Here is what all of our code looks like when we slap it together:

    <xsl:template match="add-attr[@attr-name='Given Name']     |add-attr[@attr-name='Surname']">
        <xsl:variable name="attrName" select="./@attr-name"/>
        <xsl:variable name="newVal" select="./value/text()"/>
         <xsl:choose>
        <xsl:when test="$newVal">
        <add-attr attr-name="{$attrName}">
            <value>
                <xsl:call-template name="convertCase">
                    <xsl:with-param name="UCData" select="$newVal"/>
                </xsl:call-template>
            </value>
        </add-attr>
        </xsl:when>
        <xsl:otherwise>
            <xsl:copy-of select="."/>
        </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="convertCase">        <xsl:param name="UCData"/>
        <xsl:variable name="LCData" select="util:lowerString($UCData)"/>
        <xsl:variable name="newData" select="util:capitalizeWords($LCData)"/>
        <xsl:value-of select="$newData"/>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

No comments:

Post a Comment