Recipe 13.3. Generating XTM Topic Maps from UML Modelsvia XMIProblemYou want to use your favorite XMI-enabled UML modeling tool to author XTM Topic Maps. SolutionReaders 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.
In addition to these stereotypes, the following UML-to-Topic Map mappings are used:
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>.
</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> DiscussionTopic 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.
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]
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 mapA 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 AlsoUML 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. |