Recipe13.3.Generating XTM Topic Maps from UML Modelsvia XMI


Recipe 13.3. Generating XTM Topic Maps from UML Modelsvia XMI

Problem

You want to use your favorite XMI-enabled UML modeling tool to author XTM Topic Maps.

Solution

Readers not already familiar with Topic Maps might want to read the "Discussion" section before reviewing this solution.

Since UML was not explicitly designed to represent topic maps, this example works only if certain conventions are adopted. Most conventions revolve around the use of specific UML stereotypes. A stereotype is a UML extension mechanism that lets you associate a symbol with any UML classifier that designates that classifier as having user-defined semantics. To implement Topic Maps in UML, refer to the stereotypes listed in Table 13-2.

Table 13-2. Conventions for topic mapping in UML

Stereotype

UML context

Meaning

Topic

Class

Represents a topic. It is the default role of a class in our conventions, so this stereotype is optional.

Subject

Class

Designates that the class models a subject indicator. Designate the class as a subject when it is the target of a subjectIndentityRef. Specifically, this stereotype disambiguates the subjectIndicator that references a published subject indicator rather than a topic or a resource. See http://www.topicmaps.org/xtm/index.html#elt-subjectIdentity.

Resource

Class

Designates that the class represents a resource used as the target of a topic-occurrence association.

Base Name

Class

Designates that the class models an alternate topic base name and therefore implies a scope. The scope elements are associated with the baseName class via associations with stereotype scope.

Occurrence

Association

Designates that the association points to a resource that is an occurrence of the topic.

Scope

Association

Indicates the association that specifies the scope of the topic map characteristic. The nature of the scope depends on the stereotype of the target class (topicRef, resourceRef, or subjectIndicatorRef).

Variant

Generalization Association and Class

Connects to the topic of which it is a variant via a generalization association with stereotype variant. The parameters controlling the variant's applicability are encoded into the class's attributes. The class itself is also given the stereotype, variant to distinguish it from a topic.


In addition to these stereotypes, the following UML-to-Topic Map mappings are used:


UML class

Models Topic Map topic, unless a non-topic stereotype is present. The class name is used as the topic base name. If the class has an attribute called ID, its default value is used as the topic ID. If no such ID is present, then the class name is also used as the topic ID.


UML association

Models a Topic Map association unless a stereotype specifies otherwise. The UML association name is the Topic Map association name. The UML role specifiers are the Topic Map role specifiers.


UML instantiates association

Models the Topic Map instanceOf relationship.


Unadorned UML generalization (inheritance) association

Used as a shortcut to specify the canonical superclass-subclass association where the ends are assigned the roles superclass and subclass automatically. This same relationship links classes with the baseName stereotype to the topic classes for which they represent an alternate scoped name. Generalization with the stereotype variant also specifies topic variants.

If you use Rational Rose and it cannot save models as XMI, you should download the XMI or RoseXMI add-in at http://www.rational.com/support/downloadcenter/addins/rose/index.jsp. I only used Rose to test this example, but other UML tools such as TogetherSoft's Together Control Center also support XMI, and this example will probably work with these tools as well.


The stylesheet that transforms XMI to XTM is shown here. Use several entities to make the long element names of XMI manageable:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xslt [   <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <!-- XMI's high-level organization constructs    -->   <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <!ENTITY FC "Foundation.Core">   <!ENTITY FX "Foundation.Extension_Mechanisms">   <!ENTITY FD "Foundation.Data_Types">   <!ENTITY MM "Model_Management.Model">   <!ENTITY ME "&FC;.ModelElement">       <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <!-- Abbreviations for the basic elements of a XMI -->   <!-- file that are of most interest to this        -->   <!-- stylesheet.                                   -->   <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <!--Some generic kind of UML element -->   <!ENTITY ELEM "&FC;.Namespace.ownedElement">   <!--The association as a whole -->   <!ENTITY ASSOC "&FC;.Association">   <!--The connection part of the association-->   <!ENTITY CONN "&ASSOC;.connection">   <!--The ends of an association. -->   <!ENTITY END "&ASSOC;End">   <!ENTITY CONNEND "&CONN;/&END;">   <!ENTITY ENDTYPE "&END;.type">   <!-- A UML class -->   <!ENTITY CLASS "&FC;.Class">   <!--The name of some UML entity -->   <!ENTITY NAME "&FC;.ModelElement.name">   <!--A UML sterotype -->   <!ENTITY STEREOTYPE "&ME;.stereotype/&FX;.Stereotype">   <!--The place where UML documentation is stored in XMI. -->   <!-- We use for resource data -->   <!ENTITY TAGGEDVALUE       "&ME;.taggedValue/&FX;.TaggedValue/&FX;.TaggedValue.value">   <!-- A Supertype relation (inheritance) -->   <!ENTITY SUPERTYPE "&FC;.Generalization.supertype">   <!ENTITY SUBTYPE "&FC;.Generalization.subtype">   <!ENTITY SUPPLIER "&FC;.Dependency.supplier">   <!ENTITY CLIENT "&FC;.Dependency.client">   <!ENTITY DEPENDENCY        "/XMI/XMI.content/&MM;/&ELEM;/&FC;.Dependency">   <!ENTITY EXPRBODY         "&FC;.Attribute.initialValue/&FD;.Expression/&FD;.Expression.body">   <!ENTITY ATTR "&CLASS;ifier.feature/&FC;.Attribute">   <!--Used for pointing at standard XTM PSIs -->   <!ENTITY TM.ORG "http://www.topicmaps.org/xtm/index.html"> ]>     <xsl:stylesheet version="1.0"                   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                  xmlns:xtm="http://www.topicmaps.org/xtm/1.0"                  xmlns:xlink="http://www.w3.org/1999/xlink">      <xsl:param name="termOnErr" select="true( )"/>     <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

Use keys to simplify the identification of stereotypes and traverse UML associations, which use cross references between xmi.id and xmi.idref attributes:

  <!--Index classes by their name -->   <xsl:key name="classKey" match="&CLASS;" use="@xmi.id"/>   <!-- Index stereotypes by both name and xmi.id -->   <xsl:key name="stereotypeKey"                   match="&FX;.Stereotype" use="@xmi.id"/>   <xsl:key name="stereotypeKey"                  match="&FX;.Stereotype" use="&NAME;"/>      <!-- The xmi ids of stereotypes used to encode topic maps in UML  -->   <!-- We use these as an efficient means for checking if a sterotype-->   <!--  is attached to an element.                                   -->       <xsl:variable name="OCCURRENCE_ID"                  select="key('stereotypeKey','occurrence')/@xmi.id"/>   <xsl:variable name="RESOURCE_ID"                 select="key('stereotypeKey','resource')/@xmi.id"/>   <xsl:variable name="TOPIC_ID"                 select="key('stereotypeKey','topic')/@xmi.id"/>   <xsl:variable name="SUBJECT_ID"                 select="key('stereotypeKey','subject')/@xmi.id"/>   <xsl:variable name="BASENAME_ID"                 select="key('stereotypeKey','baseName')/@xmi.id"/>   <xsl:variable name="SCOPE_ID"                 select="key('stereotypeKey','scope')/@xmi.id"/>   <xsl:variable name="VARIANT_ID"                 select="key('stereotypeKey','variant')/@xmi.id"/>

You can convert a XMI UML model to a topic map in two passes. First, import topics from classes, and then import XTM associations from UML associations:

  <xsl:template match="/">     <xtm:topicMap>       <xsl:apply-templates mode="topics"/>       <xsl:apply-templates mode="associations"/>     </xtm:topicMap>   </xsl:template>

The only classes that should be translated into topic are the ones without a stereotype or with a topic stereotype. The other classes in the model are representations to which a topic can refer, such as subject indicators and resources:

  <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <!-- UML Classes to TOPICS Translation  -->   <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->      <xsl:template match="&ELEM;/&CLASS;" mode="topics">     <!-- Topics are modeled as classes whose    -->     <!-- stereotype is either empty or 'topic'  -->     <xsl:if test="not(&STEREOTYPE;/@xmi.idref) or                          &STEREOTYPE;/@xmi.idref = $TOPIC_ID">       <xsl:variable name="topicId">         <xsl:call-template name="getTopicId">           <xsl:with-param name="class" select="."/>           <xsl:with-param name="prefix" select="''"/>         </xsl:call-template>       </xsl:variable>       <xtm:topic >         <!--This for-each is solely to change context to the optional -->         <!-- Core.Attribute attribute named 'subjectIdentityid' -->         <xsl:for-each select="&ATTR;[&NAME; = 'subjectIdentity']">           <xtm:subjectIdentity>             <xtm:subjectIndicatorRef xlink:href="{&EXPRBODY;}"/>           </xtm:subjectIdentity>         </xsl:for-each>         <xtm:baseName>           <xtm:baseNameString>             <xsl:value-of select="&NAME;"/>           </xtm:baseNameString>         </xtm:baseName>         <xsl:apply-templates select="." mode="getAlternateBaseNames"/>         <xsl:apply-templates select="." mode="getVariants"/>         <xsl:apply-templates select="." mode="getInstanceOf">           <xsl:with-param name="classId" select="@xmi.id"/>         </xsl:apply-templates>         <xsl:apply-templates select="." mode="getOccurrences"/>       </xtm:topic>     </xsl:if>   </xsl:template>      <!-- Return the topic id of a topic class which is its id -->   <!-- attribute value or its name -->   <xsl:template name="getTopicId">     <xsl:param name="class"/>     <xsl:param name="prefix" select="'#'"/>     <xsl:for-each select="$class">       <xsl:choose>         <xsl:when test="&ATTR;/&NAME; = 'id' ">           <xsl:value-of select="&ATTR;[&NAME; = 'id']/&EXPRBODY;"/>         </xsl:when>         <xsl:otherwise>           <xsl:value-of select="concat($prefix,&NAME;)"/>         </xsl:otherwise>       </xsl:choose>     </xsl:for-each>   </xsl:template>      <!-- Return the subject identity of a subject class which -->   <!-- is its subjectIdentity attribute value or its name -->   <xsl:template name="getSubjectIdentity">     <xsl:param name="class"/>     <xsl:for-each select="$class">       <xsl:choose>         <xsl:when test="&ATTR;/&NAME; = 'subjectIdentity' ">           <xsl:value-of select="&ATTR;[&NAME; =                                 'subjectIdentity']&EXPRBODY;"/>         </xsl:when>         <xsl:otherwise>           <xsl:value-of select="concat('#',&NAME;)"/>         </xsl:otherwise>       </xsl:choose>     </xsl:for-each>   </xsl:template>       <!-- Return the resource identity of a resource class which -->   <!-- is either its resourceName attribute or its name -->   <xsl:template name="getResourceIdentity">     <xsl:param name="class"/>     <xsl:for-each select="$class">       <xsl:choose>         <xsl:when test="&ATTR;/&NAME; = 'resourceName' ">           <xsl:value-of select="&ATTR;[&NAME; =                                  'resourceName']/&EXPRBODY;"/>         </xsl:when>         <xsl:otherwise>           <xsl:value-of select="concat('#',&NAME;)"/>         </xsl:otherwise>       </xsl:choose>     </xsl:for-each>   </xsl:template>

You can model alternate base names and variants as specializations of the base topic class through the UML generalization association. Depending on your point of view, this may seem natural or an abuse of the concept. Nevertheless, it is effective and allows a visual cue in the UML diagram, rather than relying solely on stereotype tags:

<!-- Alternate base names are found by traversing UML -->   <!-- generalization relationships and looking for baseName -->   <!-- stereotypes -->      <xsl:template match="&ELEM;/&CLASS;" mode="getAlternateBaseNames">     <xsl:variable name="xmiId" select="@xmi.id"/>     <xsl:for-each select="../&FC;.Generalization                           [&SUPERTYPE;/&CLASS;/@xmi.idref = $xmiId]">       <xsl:variable name="subtypeXmiId"                     select="&FC;.Generalization.subtype/&CLASS;/@xmi.idref"/>       <xsl:variable name="class" select="key('classKey',$subtypeXmiId)"/>       <xsl:if test="$class/&STEREOTYPE;/@xmi.idref = $BASENAME_ID">         <xsl:variable name="name" select="$class/&NAME;"/>         <xtm:baseName>           <xsl:call-template name="getScope">             <xsl:with-param name="class" select="$class"/>           </xsl:call-template>           <xtm:baseNameString>             <xsl:value-of select="substring-after($name,'::')"/>           </xtm:baseNameString>         </xtm:baseName>       </xsl:if>     </xsl:for-each>   </xsl:template>       <!-- Variants are found by traversing UML -->   <!-- generalization relationships and looking for baseName -->   <!-- stereotypes -->       <xsl:template match="&ELEM;/&CLASS;" mode="getVariants">     <xsl:variable name="xmiId" select="@xmi.id"/>     <xsl:for-each select="../&FC;.Generalization                            [&SUPERTYPE;/&CLASS;/@xmi.idref = $xmiId]">       <xsl:variable name="subtypeXmiId"              select="&FC;.Generalization.subtype/&CLASS;/@xmi.idref"/>       <xsl:variable name="variantClass"                     select="key('classKey',$subtypeXmiId)"/>       <xsl:if test="$variantClass/&STEREOTYPE;/@xmi.idref = $VARIANT_ID">         <xsl:variable name="name" select="$variantClass/&NAME;"/>         <xtm:variant>           <xtm:variantName>             <xsl:call-template name="resourceRep">               <xsl:with-param name="class" select="$variantClass"/>             </xsl:call-template>           </xtm:variantName>           <xtm:parameters>             <xsl:call-template name="getVariantParams">               <xsl:with-param name="class" select="$variantClass"/>             </xsl:call-template>           </xtm:parameters>           <!-- Change context to this variant to get nested variants, -->           <!-- if any. -->           <xsl:apply-templates select="$variantClass" mode="getVariants"/>         </xtm:variant>       </xsl:if>     </xsl:for-each>   </xsl:template>       <!-- Gets a variant's parameters from   -->   <!-- the attributes of the variant class -->   <xsl:template name="getVariantParams">     <xsl:param name="class"/>     <xsl:if test="not($class/&ATTR;)">       <xsl:message terminate="{$termOnErr}">       A variant must have at least one parameter.       </xsl:message>     </xsl:if>     <xsl:for-each select="$class/&ATTR;">       <!-- A parameter is either modeled as a subject indicator  -->        <!-- or topic ref                                         -->       <xsl:choose>         <xsl:when test="&STEREOTYPE;/@xmi.idref = $SUBJECT_ID">             <xtm:subjectIndicatorRef xlink:href="{&EXPRBODY;}"/>         </xsl:when>         <xsl:otherwise>             <xtm:topicRef  xlink:href="{&EXPRBODY;}"/>         </xsl:otherwise>       </xsl:choose>     </xsl:for-each>   </xsl:template>

Topic Map occurrences are modeled as associations to classes containing resource references or data. Since inline resource data can be too large to fit nicely as an attribute value, this example allows the attribute description to be used as an alternate container of resource data:

<!-- Topic map occurrences are modeled as associations to -->   <!-- classes containing resource references or data -->   <xsl:template match="&ELEM;/&CLASS;" mode="getOccurrences">     <xsl:variable name="xmiId" select="@xmi.id"/>     <!--Search over the associations this class participates-->     <xsl:for-each              select="../&ASSOC;                         [&CONN;/*/&ENDTYPE;/&CLASS;/@xmi.idref = $xmiId]">        <!-- Test for the presence of the occurrence stereotype -->                            <xsl:if test="&STEREOTYPE;/@xmi.idref = $OCCURRENCE_ID">         <!--Get the id of the resource by looking at the other end -->         <!-- of the occurrence association -->         <xsl:variable name="resourceId"                        select="&CONN;/*/&ENDTYPE;/&CLASS;                                 [@xmi.idref != $xmiId]/@xmi.idref"/>         <!-- Get the class representing the resource -->         <xsl:variable name="resourceClass"                        select="key('classKey',$resourceId)"/>         <xtm:occurrence>           <xsl:apply-templates select="." mode="getInstanceOf">             <xsl:with-param name="classId" select="$resourceId"/>           </xsl:apply-templates>           <!--TODO: Can't model this yet!             <xsl:call-template name="getScope">               <xsl:with-param name="class"/>             </xsl:call-template>           -->           <!-- We either have a resource ref or resource data. -->           <!-- If the class has a resourceData attribute it   -->           <!-- is the later.                                   -->           <xsl:call-template name="resourceRep">             <xsl:with-param name="class" select="$resourceClass"/>           </xsl:call-template>         </xtm:occurrence>       </xsl:if>     </xsl:for-each>   </xsl:template>       <!-- This template determines how the resource is represented -->   <xsl:template name="resourceRep">       <xsl:param name="class" />       <xsl:variable name="resourceData">         <!--for-each to change context -->         <xsl:for-each select="$class/&ATTR;[&NAME; = 'resourceData']">           <xsl:choose>             <!--The resource data was encoded in the UML attr -->             <!--documentation                                 -->              <xsl:when test="&TAGGEDVALUE;">               <xsl:value-of select="&TAGGEDVALUE;"/>             </xsl:when>             <!--The resource data was encoded in the UML attr value -->             <xsl:otherwise>               <xsl:value-of select="&EXPRBODY;"/>             </xsl:otherwise>           </xsl:choose>         </xsl:for-each>       </xsl:variable>       <!-- If we found some resource data then use it. -->       <!-- Otherwise assume the user meant this to be a reference -->       <xsl:choose>         <xsl:when test="string($resourceData)">           <xtm:resourceData>             <xsl:value-of select="$resourceData"/>           </xtm:resourceData>         </xsl:when>         <xsl:otherwise>           <xsl:variable name="resource">             <xsl:call-template name="getResourceIdentity">               <xsl:with-param name="class" select="$class"/>             </xsl:call-template>           </xsl:variable>           <xtm:resourceRef xlink:href="{$resource}"/>         </xsl:otherwise>       </xsl:choose>   </xsl:template>

XTM instanceOf relationships are modeled as UML dependency associations, also called instantiates. This representation of instanceOf is quite natural:

  <!-- This template finds if a topic class has any instanceOf -->   <!-- associations. -->   <xsl:template match="&ELEM;/&CLASS;" mode="getInstanceOf">     <xsl:param name="classId"/>     <!-- We loop of dependency relations and determine  -->     <!-- how the instance is represented-->     <xsl:for-each              select="&DEPENDENCY;[&CLIENT;/&CLASS;/@xmi.idref = $classId]">       <xtm:instanceOf>         <xsl:variable name="instanceClass"              select="key('classKey',&SUPPLIER;/&CLASS;/@xmi.idref)"/>         <!-- Figure out if instance is modeled as a subject or a topic -->         <xsl:variable name="sterotypeId"                       select="$instanceClass/&STEREOTYPE;/@xmi.idref"/>         <xsl:choose>           <!-- This is the case of a subject indicator -->           <xsl:when test="$sterotypeId = $SUBJECT_ID">             <xsl:variable name="subjectIdentity">               <xsl:call-template name="getSubjectIdentity">                 <xsl:with-param name="class" select="$instanceClass"/>               </xsl:call-template>             </xsl:variable>             <xsl:if test="not(normalize-space($subjectIdentity))">               <xsl:message terminate="{$termOnErr}">               Subject with no identity!               </xsl:message>             </xsl:if>             <xtm:subjectIndicatorRef xlink:href="{$subjectIdentity}"/>           </xsl:when>           <!-- Otherwise the instance is represented by a topic -->           <xsl:when test="not($sterotypeId) or $sterotypeId = $TOPIC_ID">             <xsl:variable name="topicId">               <xsl:call-template name="getTopicId">                 <xsl:with-param name="class" select="$instanceClass"/>               </xsl:call-template>             </xsl:variable>             <xsl:if test="not(normalize-space($topicId))">               <xsl:message terminate="{$termOnErr}">               Topic with no id!               </xsl:message>             </xsl:if>             <topicRef xlink:href="{$topicId}"/>           </xsl:when>           <xsl:otherwise>             <xsl:message terminate="{$termOnErr}">               <xsl:text>instanceOf must point to a topic or a subject. </xsl:text>               <xsl:value-of select="$instanceClass/&NAME;"/>               <xsl:text> is a </xsl:text>               <xsl:value-of                    select="key('stereotypeKey',$sterotypeId)/&NAME;"/>               <xsl:text>.&#xa;</xsl:text>             </xsl:message>           </xsl:otherwise>         </xsl:choose>       </xtm:instanceOf>     </xsl:for-each>   </xsl:template>      <xsl:template name="getScope">     <xsl:param name="class"/>     <xsl:variable name="classesAssociations"                   select="/*/XMI.content/*/&ELEM;                           /&ASSOC;                           [&CONN;/*/                           &FC;.AssociationEnd.type/                           &CLASS;/@xmi.idref = $class/@xmi.id]"/>     <xsl:variable name="scopeAssociations"                    select="$classesAssociations[                           &FC;.ModelElement.stereotype/                           &FX;.Stereotype/                           @xmi.idref = $SCOPE_ID]"/>     <xsl:if test="$scopeAssociations">       <xtm:scope>         <xsl:for-each select="$scopeAssociations">           <xsl:variable name="targetClassId"                select="&CONN;/*/&ENDTYPE;/&CLASS;                               [@xmi.idref != $class/@xmi.id]/@xmi.idref"/>           <xsl:variable name="targetClass"                          select="key('classKey',$targetClassId)"/>           <xsl:call-template name="getScopeRef">             <xsl:with-param name="class" select="$targetClass"/>           </xsl:call-template>         </xsl:for-each>       </xtm:scope>     </xsl:if>   </xsl:template>      <xsl:template name="getScopeRef">     <xsl:param name="class"/>     <xsl:variable name="stereotypeId"                    select="$class/&FC;.ModelElement.stereotype/                           &FX;.Stereotype/                           @xmi.idref"/>     <xsl:choose>       <xsl:when test="not($stereotypeId) or $stereotypeId = $TOPIC_ID">         <xsl:variable name="topidId">           <xsl:call-template name="getTopicId">             <xsl:with-param name="class" select="$class"/>           </xsl:call-template>         </xsl:variable>         <xtm:topicRef xlink:href="{$topidId}"/>       </xsl:when>       <xsl:when test="$stereotypeId = $SUBJECT_ID">         <xsl:variable name="subjectId">           <xsl:call-template name="getSubjectIdentity">             <xsl:with-param name="class" select="$class"/>           </xsl:call-template>         </xsl:variable>         <xtm:subjectIndicatorRef xlink:href="{$subjectId}"/>       </xsl:when>       <xsl:when test="$stereotypeId = $RESOURCE_ID">         <xsl:variable name="resourceId">           <xsl:call-template name="getResourceIdentity">             <xsl:with-param name="class" select="$class"/>           </xsl:call-template>         </xsl:variable>         <xtm:resourceRef xlink:href="{$resourceId}"/>       </xsl:when>       <xsl:otherwise>         <xsl:message terminate="{$termOnErr}">         A Scope must be either a topicRef, subjectRef, or resourceRef!         </xsl:message>       </xsl:otherwise>     </xsl:choose>   </xsl:template>      <xsl:template match="text( )" mode="topics"/>      <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <!-- UML ASSOCIATION TO TOPIC ASSOCIATIONS -->   <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <xsl:template match="&ASSOC;" mode="associations">     <!-- Only named UML associations are topic map associations -->     <xsl:if test="normalize-space(&NAME;)">       <xtm:association >         <xtm:instanceOf>           <topicRef             xlink:href="{key('stereotypeKey',                              &STEREOTYPE;/@xmi.idref)/&NAME;}"/>         </xtm:instanceOf>         <xsl:for-each select="&CONNEND;">           <xtm:member>             <xtm:roleSpec>               <xtm:topicRef xlink:href="{&NAME;}"/>             </xtm:roleSpec>             <xsl:variable name="topicId">               <xsl:call-template name="getTopicId">                 <xsl:with-param name="class"                         select="key('classKey',                                     &ENDTYPE;/&CLASS;/@xmi.idref)"/>               </xsl:call-template>             </xsl:variable>             <xtm:topicRef xlink:href="{$topicId}"/>           </xtm:member>         </xsl:for-each>       </xtm:association>     </xsl:if>   </xsl:template>       <xsl:template match="&ELEM;/&FC;.Generalization"                 mode="associations">         <xsl:variable name="subClassId"                   select="&SUBTYPE;/&CLASS;/@xmi.idref"/>     <xsl:variable name="subClass"                   select="key('classKey',$subClassId)"/>     <xsl:variable name="superClassId"                   select="&SUPERTYPE;/&CLASS;/@xmi.idref"/>     <xsl:variable name="superClass"                   select="key('classKey',$superClassId)"/>          <!-- If a generalization relation exists from a topic to a -->     <!-- topic, we use this as an indication of a canonical     -->         <!-- superclass-subclass relation, Ideally we would use an -->      <!-- absence of a stereotype on the generalization, but the -->     <!-- version of XMI I am using is not storing stereotype   -->     <!-- info for generalizations                              -->     <xsl:if test="(not($subClass/&STEREOTYPE;/@xmi.idref) or                          $subClass/&STEREOTYPE;/@xmi.idref = $TOPIC_ID) and                         (not($superClass/&STEREOTYPE;/@xmi.idref) or                          $superClass/&STEREOTYPE;/@xmi.idref = $TOPIC_ID)">                                <xtm:association>         <xsl:variable name="id">           <xsl:choose>             <xsl:when test="normalize-space(&NAME;)">               <xsl:value-of select="&NAME;"/>             </xsl:when>             <xsl:otherwise>               <xsl:value-of select="@xmi.id"/>             </xsl:otherwise>           </xsl:choose>         </xsl:variable>                  <xsl:attribute name="id">           <xsl:value-of select="$id"/>         </xsl:attribute>                  <xtm:instanceOf>           <subjectIndicatorRef               xlink:href="&TM.ORG;#psi-superclass-subclass"/>         </xtm:instanceOf>                  <xtm:member>                    <xtm:roleSpec>             <xtm:subjectIndicatorRef                                  xlink:href="&TM.ORG;#psi-superclass"/>           </xtm:roleSpec>                      <xsl:variable name="superClassTopicId">             <xsl:call-template name="getTopicId">               <xsl:with-param name="class" select="$superClass"/>             </xsl:call-template>           </xsl:variable>           <xtm:topicRef xlink:href="{$superClassTopicId}"/>                    </xtm:member>                  <xtm:member>           <xtm:roleSpec>             <xtm:subjectIndicatorRef xlink:href="&TM.ORG;#psi-subclass"/>           </xtm:roleSpec>                      <xsl:variable name="subClassTopicId">             <xsl:call-template name="getTopicId">               <xsl:with-param name="class" select="$subClass"/>             </xsl:call-template>           </xsl:variable>               <xtm:topicRef xlink:href="{$subClassTopicId}"/>         </xtm:member>                </xtm:association>     </xsl:if>   </xsl:template>      <xsl:template match="text( )" mode="associations"/>    </xsl:stylesheet>

These templates are part of the second pass in which UML associations not already handled due to special stereotypes are converted into topic map associations. Here, the stereotype of an association is the topicRef that determines what kind of association is modeled. The stereotype entry was abused in this way largely because UML provided no other natural home for this information. Scoped associations are a problem I chose to ignore. In all other respects, a UML association matches the topic map concept well:

  <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <!-- UML ASSOCIATION TO TOPIC ASSOCIATIONS -->   <!--=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =-->   <xsl:template match="&ASSOC;" mode="associations">     <!-- Only named UML associations are topic map associations -->     <xsl:if test="normalize-space(&NAME;)">       <xtm:association >         <xtm:instanceOf>           <topicRef             xlink:href="{key('stereotypeKey',                              &STEREOTYPE;/@xmi.idref)/&NAME;}"/>         </xtm:instanceOf>         <xsl:for-each select="&CONNEND;">           <xtm:member>             <xtm:roleSpec>               <xtm:topicRef xlink:href="{&NAME;}"/>             </xtm:roleSpec>             <xsl:variable name="topicId">               <xsl:call-template name="getTopicId">                 <xsl:with-param name="class"                         select="key('classKey',                                     &ENDTYPE;/&CLASS;/@xmi.idref)"/>               </xsl:call-template>             </xsl:variable>             <xtm:topicRef xlink:href="{$topicId}"/>           </xtm:member>         </xsl:for-each>       </xtm:association>     </xsl:if>   </xsl:template>       <xsl:template match="&ELEM;/&FC;.Generalization"                 mode="associations">         <xsl:variable name="subClassId"                   select="&SUBTYPE;/&CLASS;/@xmi.idref"/>     <xsl:variable name="subClass"                   select="key('classKey',$subClassId)"/>     <xsl:variable name="superClassId"                   select="&SUPERTYPE;/&CLASS;/@xmi.idref"/>     <xsl:variable name="superClass"                   select="key('classKey',$superClassId)"/>

If a generalization relation exists from topic to topic, use it as an indication of a canonical superclass-subclass relation. The XTM specification provides explicit support for this important relationship via published subject indicators (PSI). Ideally, you would use an absence of a stereotype on the generalization, but the version of XMI I use does not store stereotype information for generalizations:

    <xsl:if test="(not($subClass/&STEREOTYPE;/@xmi.idref) or                     $subClass/&STEREOTYPE;/@xmi.idref = $TOPIC_ID) and                         (not($superClass/&STEREOTYPE;/@xmi.idref) or                          $superClass/&STEREOTYPE;/@xmi.idref = $TOPIC_ID)">                                <xtm:association>         <xsl:variable name="id">           <xsl:choose>             <xsl:when test="normalize-space(&NAME;)">               <xsl:value-of select="&NAME;"/>             </xsl:when>             <xsl:otherwise>               <xsl:value-of select="@xmi.id"/>             </xsl:otherwise>           </xsl:choose>         </xsl:variable>                  <xsl:attribute name="id">           <xsl:value-of select="$id"/>         </xsl:attribute>                  <xtm:instanceOf>           <subjectIndicatorRef               xlink:href="&TM.ORG;#psi-superclass-subclass"/>         </xtm:instanceOf>                  <xtm:member>                    <xtm:roleSpec>             <xtm:subjectIndicatorRef xlink:href="&TM.ORG;#psi-superclass"/>           </xtm:roleSpec>                      <xsl:variable name="superClassTopicId">             <xsl:call-template name="getTopicId">               <xsl:with-param name="class" select="$superClass"/>             </xsl:call-template>           </xsl:variable>           <xtm:topicRef xlink:href="{$superClassTopicId}"/>                    </xtm:member>                  <xtm:member>           <xtm:roleSpec>             <xtm:subjectIndicatorRef xlink:href="&TM.ORG;#psi-subclass"/>           </xtm:roleSpec>                      <xsl:variable name="subClassTopicId">             <xsl:call-template name="getTopicId">               <xsl:with-param name="class" select="$subClass"/>             </xsl:call-template>           </xsl:variable>               <xtm:topicRef xlink:href="{$subClassTopicId}"/>         </xtm:member>                </xtm:association>     </xsl:if>   </xsl:template>      <xsl:template match="text( )" mode="associations"/>    </xsl:stylesheet>

Discussion

Topic Maps represent knowledge about real-world subjects. These techniques enable computers and people to find relevant information faster and with greater precision. Topic Maps were first discussed in 1993, when the ideas were first expressed as a Davenport Group working document.[2] The paradigm was extended in the context of the GCA Research Institute (now called the IDEAlliance) in relation to applications of HyTime (http://www.hytime.org/papers/htguide.html). The XTM specification was an offshoot of this work, which was organized under the control of an independent organization called TopicMaps.org.

[2] The Davenport Group was founded by a Unix System vendor and others, including O'Reilly & Associates.

A topic is an electronic proxy to a real-world subject. Ozzy Osborne is a real-world subject; however, since you can't store the real Ozzy in a computer, you create an Ozzy topic as a surrogate. Topics have names called base names. A topic can have one universal name (properly called unconstrained ) and several other names that are specific to a scope. A scope is a context in which a topic map characteristic is valid. In the case of a topic name, a scope can indicate that the topic Ozzy Osborne is also John Michael Osbourne in the legal scope.[3]

[3] Unless John Michael legally changed his name to Ozzy, in which case his mom might be the only one who cares about this scope.

A topic can point to an occurrence, which is a resource that supplies information relevant to a topic. A resource might refer to an addressable content (resourceRef) or the content itself (resourceData).

A topic can participate in a special association called instanceOf that declares this topic to be a specific instance of a more general class of objects. The class can be designated by a reference to another topic or to a subject indicator. Subject indicators are an interesting topic map feature. They facilitate a method that specifies the nature of a subject by associating it with a standard published address, such as one maintained by a government standards body.

Topics can be related by associations. An association is a named relationship between two or more topics in which each topic plays a specified role in the association.

Several other interesting topic map facilities model knowledge about subjects. Readers interested in topic maps are encouraged to read the specification of XTM at http://www.TopicMaps.org. Compared to other specifications, this one is especially friendly to the uninitiated. Almost all of XTM's functionality is mapped on to some construct in UML (as explained in the "Solution" section). Dealing with the Topic Map notion of scope is this mapping's main difficulty. In the topic map paradigm, a scope is a method specifying that a topic characteristic is only valid in a particular setting. Scope applies to base names, associations, and occurrences. Although these conventions can deal with scope for base names, they cannot currently handle scopes for associations and occurrences. This is because these conventions are modeled as UML associations, and in UML an association is not normally context sensitive. You can model this feature via UML constraints. Alas, the version of XMI that is available for Rational Rose does not capture information on constraints. In practice, scope is an advanced topic map function that many users will not need, so this problem might not be a major liability, especially for novice topic mappers.

Figure 13-2 is an example of a topic map represented in UML. This topic models information about a tomato in the context of a meal. Although the example is somewhat whimsical, I used it because Sam Hunting used the same example in XML Topic Maps: Creating and Using Topic Maps for the Web (Addison Wesley, 2002). In this example, he exercised many of the topic map facilities, which allows you to check the resulting XMI's accuracy.

Figure 13-2. UML diagram of a tomato topic map


A portion of the resulting XTM file follows:

<xtm:topicMap xmlns:xtm="http://www.topicmaps.org/xtm/1.0" xmlns:xlink="http://www. w3.org/1999/xlink">    <xtm:topic >       <xtm:subjectIdentity>          <xtm:subjectIndicatorRef               xlink:href="http://www.topicmaps.org/xtm/1.0/language.xtm#en"/>       </xtm:subjectIdentity>       <xtm:baseName>          <xtm:baseNameString>EN</xtm:baseNameString>       </xtm:baseName>    </xtm:topic>    <xtm:topic >       <xtm:subjectIdentity>          <xtm:subjectIndicatorRef               xlink:href="http://www.topicmaps.org/xtm/1.0/language.xtm#fr"/>       </xtm:subjectIdentity>       <xtm:baseName>          <xtm:baseNameString>FR</xtm:baseNameString>       </xtm:baseName>    </xtm:topic>    <xtm:topic >       <xtm:subjectIdentity>          <xtm:subjectIndicatorRef               xlink:href="http://www.fed.gov/usda/doc/tomato.htm#gradeA"/>       </xtm:subjectIdentity>       <xtm:baseName>          <xtm:baseNameString>tomato</xtm:baseNameString>       </xtm:baseName>       <xtm:baseName>          <xtm:scope>             <xtm:topicRef xlink:href="#EN"/>          </xtm:scope>          <xtm:baseNameString>tomato</xtm:baseNameString>       </xtm:baseName>       <xtm:baseName>          <xtm:scope>             <xtm:topicRef xlink:href="#FR"/>          </xtm:scope>          <xtm:baseNameString>tomato</xtm:baseNameString>       </xtm:baseName>       <xtm:variant>          <xtm:variantName>             <xtm:resourceData>TMT</xtm:resourceData>          </xtm:variantName>          <xtm:parameters>             <xtm:topicRef xlink:href="cell_phone"/>             <xtm:topicRef xlink:href="TMT"/>          </xtm:parameters>       </xtm:variant>       <xtm:occurrence>          <xtm:resourceRef xlink:href="#tomato.gif"/>       </xtm:occurrence>    </xtm:topic>    <xtm:topic >       <xtm:baseName>          <xtm:baseNameString>tomato confite farcie aux douze saveurs          </xtm:baseNameString>       </xtm:baseName>       <xtm:instanceOf>          <topicRef xlink:href="#desert"/>       </xtm:instanceOf>    </xtm:topic>     <!-- Elided -->        <xtm:association >       <xtm:instanceOf>          <topicRef xlink:href="ingredient_of"/>       </xtm:instanceOf>       <xtm:member>          <xtm:roleSpec>             <xtm:topicRef xlink:href="anIngredient"/>          </xtm:roleSpec>          <xtm:topicRef xlink:href="myTomato"/>       </xtm:member>       <xtm:member>          <xtm:roleSpec>             <xtm:topicRef xlink:href="aDish"/>          </xtm:roleSpec>          <xtm:topicRef xlink:href="myConfite"/>       </xtm:member>    </xtm:association>    <xtm:association >       <xtm:instanceOf>          <topicRef xlink:href="ingredient_of"/>       </xtm:instanceOf>       <xtm:member>          <xtm:roleSpec>             <xtm:topicRef xlink:href="anIngredient"/>          </xtm:roleSpec>          <xtm:topicRef xlink:href="myCarmel"/>       </xtm:member>       <xtm:member>          <xtm:roleSpec>             <xtm:topicRef xlink:href="aDish"/>          </xtm:roleSpec>          <xtm:topicRef xlink:href="myConfite"/>       </xtm:member>    </xtm:association> <!-- Elided --> </xtm:topicMap>

See Also

UML and XMI are standards of the Object Management Group (OMG). More information is available at http://www.omg.org/uml/ and http://www.omg.org/technology/xml/index.htm.

TopicMaps.org (http://www.topicmaps.org) is the official site for Topic Map- and XTM-related information.

Recipe 13.4 shows how topic maps generate web sites.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net