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 11-2.
Table 11-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
|
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
|
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 ( |
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:
Is a 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.
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.
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 Rose add-in at http://www.rational.com/support/downloadcenter/addins/rose/index.jsp. I tested only this example with Rose, 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. As was the case when you considered transforming XMI in Recipe 10.8, 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 Stereoptypes 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 stereoptypes 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="OCCURANCE_ID" select="key('stereotypeKey','occurance')/@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 id="{$topicId}"> <!--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:subjectIdicatorRef 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="getOccurances"/> </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 --> <!-- sterotypes --> <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 --> <!-- sterotypes --> <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 attibutes 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 modeld as a subject indicator --> <!-- or topic ref --> <xsl:choose> <xsl:when test="&STEREOTYPE;/@xmi.idref = $SUBJECT_ID"> <xtm:subjectIdicatorRef 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 occurances are modeled as associations to --> <!-- classes containing resource references or data --> <xsl:template match="&ELEM;/&CLASS;" mode="getOccurances"> <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 occurance stereotype --> <xsl:if test="&STEREOTYPE;/@xmi.idref = $OCCURANCE_ID"> <!--Get the id of the resource by looking at the other end --> <!-- of the occurance 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:occurance> <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:occurance> </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:subjectIdicatorRef xlink:href="{$subjectIdentity}"/> </xsl:when> <!-- Otheriwse 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:asociation id="{&NAME;}"> <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:asociation> </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:asociation> <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:asociation> </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 a problem I chose to
ignore (see Section 11.3.3). 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:asociation id="{&NAME;}"> <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:asociation> </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:asociation> <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:asociation> </xsl:if> </xsl:template> <xsl:template match="text( )" mode="associations"/> </xsl:stylesheet>
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.[27] 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.[28]
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 11-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.
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 id="EN"> <xtm:subjectIdentity> <xtm:subjectIdicatorRef 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 id="FR"> <xtm:subjectIdentity> <xtm:subjectIdicatorRef 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 id="myTomato"> <xtm:subjectIdentity> <xtm:subjectIdicatorRef 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>tomate</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:occurance> <xtm:resourceRef xlink:href="#tomato.gif"/> </xtm:occurance> </xtm:topic> <xtm:topic id="myConfite"> <xtm:baseName> <xtm:baseNameString>tomate confite farcie aux douze saveurs </xtm:baseNameString> </xtm:baseName> <xtm:instanceOf> <topicRef xlink:href="#desert"/> </xtm:instanceOf> </xtm:topic> <!-- Elided --> <xtm:asociation id="tomato_confite_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:asociation> <xtm:asociation id="caramels_confite"> <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:asociation> <!-- Elided --> </xtm:topicMap>
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 11.4 shows how topic maps generate web sites.