Home Books Software Projects Forums

Transforming XMI to HTML
Part Two




In Part One of this project we introduced the process of transforming XMI to HTML. In this continuation we will see how the transformation of the class details is performed and conclude with an overview of the tools used for the project.

Specification

Next we will take a look at how the specifications for a class are generated. A specification identifies an interface which a class implements. A class may implement one or more interfaces. We also want a hyperlink to be created for the name of an interface so that we may navigate to the interface's definition.


<!-- Specifications (interface or class) -->
<xsl:template name="specifications">

    <!-- Abstractions identify specifications -->
    <xsl:variable name="specifications" 
         select="Foundation.Core.ModelElement.clientDependency/
                 Foundation.Core.Abstraction"/>

    <xsl:if test="count($specifications) > 0">
        <tr>
            <td width="20%" class="info-title">Specifications:</td>  
            <td colspan="2" class="info">
            <xsl:for-each select="$specifications">
            
                <!-- get the supplier in the abstraction -->
                <xsl:variable name="abstraction"
                     select="key('abstraction', ./@xmi.idref)" />
                <xsl:variable name="target"
                     select="$abstraction/
                            Foundation.Core.Dependency.supplier/
                            */@xmi.idref" /> 
                <xsl:call-template name="classify">
                    <xsl:with-param name="target" select="$target"/>
                </xsl:call-template>
                
                <xsl:if test="position() != last()">
                    <xsl:text>,  </xsl:text>
                </xsl:if>
            </xsl:for-each>
            </td>
        </tr>
    </xsl:if>  
        
</xsl:template>


In XMI, a specification is defined as part of an abstraction dependency. In an abstraction, an element which is abstract is called the supplier while an element which is concrete is the client. An interface is abstract, while a class is the concrete realization of an interface's specification.

Enough theory. We had to get that out of the way to see how the XMI and XSLT work together. If a class contains a clientDependency element, it will reference an Abstraction element for the details of the dependency. We assign the node-list of all these dependencies to the "specifications" variable and use it to test whether any nodes exist. If the list is not empty, we start the HTML row which lists the specifications.

Next we iterate over the list of specifications and locate the definition for each abstraction dependency. XSLT control flow allows us to do the iteration over the node-list. XSLT keys are used to get the node for the abstraction, thereby speeding up this operation. Once we find the abstraction, we get the reference to the supplier (interface) into a variable named "target". The formating of the name and creation of a hyperlink is finally handled by the "classify" template.

Note that we separate the name of each interface by a comma if there is more than one. This is a requirement because a class may have more than one interface as an abstraction specification.

The XSLT for identifying realizations of an interface is almost identical to that for specifications except that we are looking for the client of an abstraction dependency instead of the supplier.

Generalization

To find the supertypes for a class we have to visit the Generalization elements referred to within the class definition. In particular we are interested in those tagged as <Foundation.Core. GeneralizableElement.generalization> since these identify supertypes. In contrast, those tagged as <Foundation.Core.GeneralizableElement.specialization> identify subtypes. Keep in mind that an interface is also a generalizable element and as such, may also have another interface as a supertype.


<!-- Supertypes (inheritance) -->
<xsl:template name="supertypes">
    
    <!-- Generalizations identify supertypes -->
    <xsl:variable name="generalizations" 
        select="Foundation.Core.GeneralizableElement.generalization/   
                Foundation.Core.Generalization"/>

    <xsl:if test="count($generalizations) > 0">
        <tr>
            <td width="20%" class="info-title">Supertypes:</td>  
            <td colspan="2" class="info">
            <xsl:for-each select="$generalizations">
            
                <!-- get the parent in the generalization -->
                <xsl:variable name="generalization"
                     select="key('generalization', ./@xmi.idref)" />
                <xsl:variable name="target"
                     select="$generalization/
                            Foundation.Core.Generalization.parent/
                            */@xmi.idref" /> 
                <xsl:call-template name="classify">
                    <xsl:with-param name="target" select="$target"/>
                </xsl:call-template>
                
                <xsl:if test="position() != last()">
                    <xsl:text>,  </xsl:text>
                </xsl:if>
            </xsl:for-each>
            </td>
        </tr>
    </xsl:if>  
</xsl:template>


First we assign the node-list for the Generalizations to a variable, "generalizations", and use it to test whether any nodes exist. If the list is not empty, we start the HTML row which lists the supertypes.

Next we iterate over the list of supertypes and locate the definition for each generalization relationship. XSLT keys are used to get the node for the generalization, thereby speeding up this operation. Once we find the generalization, we get the reference to the parent (supertype) into a variable named "target". The formating of the name and creation of a hyperlink is finally handled by the "classify" template.

Notice that when we assign the parent xmi.id to the "target" variable we use a wildcard character ( * ) in the XPath expression. This is done because inheritance may exist for interfaces as well as classes, either of which may appear here. Therefore, we are able to use this template during generation of the HTML for interfaces as well as classes.

Note also that we separate the name of each supertype by a comma if there is more than one. This is a requirement when modeling for languages which support multiple inheritance.

The XSLT for identifying subtypes of a class is almost identical to that for supertypes except that we are looking for the child of a generalization relationship instead of the parent.

Operations and Attributes

Since the template for generating HTML for attributes is similar to that for operations, let's look at operations first, which are more complex.


<!-- Operations -->
<xsl:template name="operations">
    <tr>
        <td colspan="3" class="info-title">Operations:</td>
    </tr>
    <tr>
        <td class="feature-heading" width="20%">visibility</td>
        <td class="feature-heading" width="25%">return</td>
        <td class="feature-heading" width="55%">name</td>
    </tr>

    <xsl:apply-templates select="Foundation.Core.Classifier.feature/
                                 Foundation.Core.Operation" />
</xsl:template>


<xsl:template match="Foundation.Core.Operation">

    <xsl:variable name="return"
         select="Foundation.Core.BehavioralFeature.parameter/
         Foundation.Core.Parameter
     [Foundation.Core.Parameter.kind/@xmi.value='return']" />
          
    <xsl:variable name="target"
         select="$return/Foundation.Core.Parameter.type/*/@xmi.idref" />

    <xsl:variable name="parameters"
         select="Foundation.Core.BehavioralFeature.parameter/
         Foundation.Core.Parameter
     [Foundation.Core.Parameter.kind/@xmi.value!='return']" />
         
    <tr>
        <!-- Visibility -->
        <td class="feature-detail">
            <xsl:value-of 
                 select="Foundation.Core.ModelElement.visibility/
                         @xmi.value" />
        </td>

        <td class="feature-detail">

        <!-- Return Type -->
        <xsl:choose>
            <xsl:when test="string-length($target) = 0">
                <span class="datatype"><xsl:text>void</xsl:text></span>
            </xsl:when>

            <xsl:otherwise>
                <xsl:call-template name="classify">
                    <xsl:with-param name="target" select="$target" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>

        </td>

        <!-- Name -->
        <td class="feature-detail">
            <xsl:value-of select="Foundation.Core.ModelElement.name"/>
        </td>
    </tr>



Since operations have a return value, we first set up two XSL variables which are used to get the name of the return type, return and target. The return parameter is found in the list of parameters for an operation, which may also include input parameters. Fortunately, XMI provides a parameter kind designator. We look for a kind equal to 'return' and then get the type of the return parameter.

The generation of the HTML for an operation starts by generating the code for the visibility, return type and name of the operation, and includes a hyperlink to the definition for the return type.

If an operation is specified with any input parameters, these are created next by the template.


    <xsl:variable name="parameter-count" select="count($parameters)" />

    <xsl:if test="not(normalize-space($parameter-count)='0')">

    <tr>
        <td class="feature-detail" >
            <xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
        </td>
        
        <td class="parameter-heading" valign="top">parameters:</td>
        <td bgcolor="#ffffff" align="right">
            <table width="85%" align="right" 
                   cellpadding="0" cellspacing="0" border="0">
                <xsl:apply-templates select="$parameters" />
            </table>
        </td>
    </tr>

    </xsl:if >

</xsl:template>



Since we don't want to ouput any HTML if the operation has no input parameters, we first want to count the number of input parameters. XSL provides the function count() for this purpose. Also for the first time you see the use of the XSL if directive, which allows us to implement some basic boolean logic. If the count is not zero, then output the HTML for parameters.

As you can see, the list of input parameters is actually contained in its own table in the output. This allows the input parameters to appear nicely formated in columns for the type and name of each parameter. To retrieve this list, another XSL template is applied. It creates a row in the table for each input parameter, specifying the type and the name of the parameter. The template may be found in the full source code for the project.

Finally, completing the output for the definition of a class, here is the template for the attributes. The HTML generated for each attribute includes the visibility, type and name for the attribute, and includes a hyperlink to the definition for the attribute type.


<!-- Attributes -->
<xsl:template name="attributes">
    <tr>
        <td colspan="3" class="info-title">Attributes:</td>
    </tr>
    <tr>
        <td class="feature-heading" width="20%">visibility</td>
        <td class="feature-heading" width="25%">type</td>
        <td class="feature-heading" width="55%">name</td>
    </tr>
    <xsl:apply-templates select="Foundation.Core.Classifier.feature/
                                 Foundation.Core.Attribute" />
</xsl:template>


<xsl:template match="Foundation.Core.Attribute">
    <xsl:variable name="target"
         select='Foundation.Core.StructuralFeature.type/*/@xmi.idref'/>


    <tr>
        <td class="feature-detail">
        <xsl:value-of 
             select="Foundation.Core.ModelElement.visibility/
                     @xmi.value"/> 
        </td>

        <td class="feature-detail">
        <xsl:call-template name="classify">
            <xsl:with-param name="target" select="$target" />
        </xsl:call-template>
        </td>

        <td class="feature-detail">
        <xsl:value-of select="Foundation.Core.ModelElement.name"/>
        </td>
    </tr>
</xsl:template>



That concludes our walk-through of the XSL rules for the XMI to HTML transformation of classes. Other transformation rules included in the full source code include the rules for generating HTML for interfaces, which are substantially similar to the rules for classes, and in fact reuse a good portion of the same rules.

To conclude this project, let's take a quick look at the tools that were used. Keep in mind that this is experimental technology. Everything presented so far is still evolving.

XSLT

The key technology we have employed in this project is XSLT, which is the portion of the emerging XSL standard which provides a language for transforming XML documents into other types of documents. XSLT is currently a W3C Version 1.0 Recommendation (November 16, 1999) and may be found at the W3C site. The principal editor of this document is James Clark, a pioneer in the XML field.

Xalan, Apache XML Project

The tool which was used to run the XSLT style sheet is the excellent Xalan-Java XSLT transformation engine. This software, implemented by Scott Boag and other developers at Lotus and IBM, has been donated to the open-source Apache XML project, where it is now supported. We found the software to be highly reliable and relatively bug free. You may follow this project by subscribing to the xalan-dev@xml.apache.org mailing list or reading the archived messages.

ArgoUML

The UML diagram and XMI for this project was produced by ArgoUML, described as a 'Free Object-Oriented Design Tool with Cognitive Support'. Also open-source and written in Java, ArgoUML is the creation of University of California graduate student Jason Robbins and a group of open-sourcers who participate in the project. While still in pre-version 1.0 status, ArgoUML already provides a wide range of functionality. It has supported XMI from the beginning as the principal file representation for a UML model under development.

A unique set of functionality that this tool provides is what Jason Robbins calls 'cognitive support'. This is the capability of the tool to monitor an object-oriented design while it is under development in order to actively provide suggestions for improving the design.

Anybody interested in participating in this project can visit the ArgoUML site or the active discussion group for the project.

Novosoft UML Library

Much credit goes to Constantine Plotnikov from Novosoft who created and maintains the Novosoft UML Library (NSUML), a key UML infrastructure component that has been integrated with ArgoUML. NSUML is an open-source (LGPL) Java library which provides XMI persistence for UML models. It implements the full UML 1.3 metamodel. The integration of NSUML and ArgoUML was performed largely by Toby Baier at Hamburg University.

XSLT by Example

If a picture is worth a thousand words, then how much is a good code example worth?

We invite you to view our XSLT tutorial, XSLT by Example. The tutorial focuses on XSLT and is based on the latest version of the XMI to HTML stylesheet.

Source Code

The complete XSL source code for this project as well as the XMI file is provided as a download. Remember, this is experimental software, still under development.

The software follows the open-source model and is protected by the GNU General Public License (GPL). Please read the license agreement in full prior to downloading. By downloading you agree to the terms of the GPL.

Download the XMI to HTML project sources.

To Be Continued...

This concludes our project for now. We hope the information presented was informative and we welcome suggestions for improvements. You may submit suggestions to our recently created forum, XMI to HTML, or please send email. And please keep posted for new developments...

Links: