The CTM topic map writer is an open source project available under an Apache 2.0 license. The project is hosted on http://code.google.com/p/ctm-writer/. If you find bugs or have feature requests please file a ticket.

1. Scope

The Compact Topic Maps ( CTM ) is a text-based representation of a topic map and describes a standardized interchange format of topic maps, similar to XTM. The syntax is designed in a human-readable way and represents every information items and any relationships between items modeled by the topic maps data model ( TMDM ).

A community project, called TMAPIx, wants to provide additional features on top of the core TMAPI functions, like realizing some import or export functionality supporting XTM or CTM. This efforts are designed in a similar way to TMAPI, as should be independent of a specific implementation and only operates on core interfaces of the TMAPI.

This implementation of a CTM Writer provides the core functionality of exporting every topic map to CTM.

2. Getting started

For using the CTM writer you have to have some dependent libraries within your search path. The following table list the essential libraries.

Table 1: dependencies of the CTM writer implementation
library description

tmapi-io

library defines the abstract interfaces of a topic map writer or a reader

tmapi

library defines all information items of the TMDM as interfaces

3. Functionality

This section wants to give a short overview about the core functionality of the CTM writer implementation on the one hand and explain their algorithm on the other hand.

3.1. Serialize an instance of tmapi.org.core.TopicMap

The core functionality of the writer is to serialize the topic map instance using the CTM syntax. The writer will be transform any information item of the given topic map to a CTM pattern, which will be written to a user-defined output stream.

The exported patterns will be valid in context of the current CTM standard.

At first step the CTM header will be created, which specify the current version of the used CTM standard and some encoding information.

%encoding "UTF-8"
%version 1.0

At the second step the internal processor exports all defined prefixes to the CTM file. Prefixes are used in the CTM document to replace absolute IRI by relative IRIs using QNames to reduce the overhead of repeating IRI parts.

# prefixes
%prefix ontopedia  <http://psi.ontopedia.net/>
%prefix xtm  <http://www.topicmaps.org/xtm/1.0/>
%prefix xsd  <http://www.w3.org/2001/XMLSchema#>

The next step will be exporting the template definitions. Templates can be used to represent some frequently used definitions of topics or associations based on ontology knowledge.

# template definitions
def template-association-has-voice ($voice-type,  $character)
        has-voice (
                 voice-type : $voice-type,
                 character : $character
        )
end

The last step will be create a specific topic block for each topic contained by the given topic map and an association block for each association item. The writer will be detect automatically matching templates an reduce the given topic block by using template-invocations.

# topic definitions
ballet
         isa artform;
         -  "Ballet";
         -  "Ballett" @norwegian;
         <ontopedia:Ballet>.

After all the created CTM stream will be written to the given output stream.

3.2. Defining prefixes

The writer provides the functionality to define prefixes which will be used to reduce the base part of IRIs used as subject-identifier, subject-locator or item-identifiers. The writer will detect automatically the matching IRIs and try to reduce them by replacing the prefix part with its QName.

By using the setProperty function of the CTM topic map writer instance, the user can add own prefixes.

3.3. Defining templates and detecting adaptive information items

The writer provides the possibility to define CTM templates. This templates can be used to reduce the overhead caused by ontology overhead of topic items. Templates can be defined by using the Template class and adding some entries representing the content of this template. As an alternitive it is possible to define template in CTM and import the CTM file.

During the process of serialization the internal processor tries to detect the set of adaptive templates for a topic item or an association item. The topic block will be reduced by using template-invocations instead of the normal topic-tail-definition. In the case of association items, the normal association block will be replaced by the template-invocation.

3.4. Automatic template detection

The template detection algorithm is a powerful tool to detect ontology intersections between topic items. The algorithm currently works only on topic items of the same type.

A ontology intersection is defined as a multiple use of topic tails in the context of different topic items and will be detect dependent from the tail-type.

A CTM snippet of the composer Puccini without template detection
puccini isa composer;
    - "Puccini, Giacomo";
    - "Giacomo Puccini" @normal;
    - "Puccini" @short-name;
    article: <http://en.wikipedia.org/wiki/Giacomo_Puccini>;
    article: <http://www.ontopia.net/topicmaps/examples/opera/occurs/snl/puccini.htm> @snl, web;
    bibref: """Budden, Julian: "Puccini: His Life and Works", Oxford University Press (Oxford, 2002)""";
    bibref: """Sadie, Stanley (ed): "Puccini and His Operas", Macmillan (London, 2000)""";
    date-of-birth: "1858-12-22";
    date-of-death: "1924-11-29";
    gallery: <http://localhost:8080/operamap/occurs/puccini-gallery.htm> @local;
    illustration: <http://localhost:8080/operamap/occurs/composer/puccini.gif> @local;
    sound-clip: <http://www.puccini.it/files/vocepucc.wav> @italian, puccini-study-centre, web;
    webpage: <http://www.operone.de/komponist/puccini.html> @web;
    webpage: <http://localhost:8080/operamap/occurs/hnh-puccini.htm> @local, naxos;
    webpage: <http://www.naxos.com/composer/btm.asp?fullname=Puccini, Giacomo> @naxos, web;
    webpage: <http://www.r-ds.com/opera/pucciniana/gallery.htm> @r-ds, web;
    website: <http://www.landofpuccini.com>;
    website: <http://www.puccini.it> @italian, puccini-study-centre, web;
    <ontopedia:Puccini>.
A CTM snippet of the composer Puccini with template detection
puccini
        template-topic-composer("1924-11-29","1858-12-22",<http://en.wikipedia.org/wiki/Giacomo_Puccini>,
                                                        <http://localhost:8080/operamap/occurs/composer/puccini.gif>,
                                                        <http://www.r-ds.com/opera/pucciniana/gallery.htm>,"Giacomo Puccini","Giacomo Puccini","Puccini");
         isa composer;
         -  "Puccini, Giacomo";
         sound-clip :  <http://www.puccini.it/files/vocepucc.wav> @puccini-study-centre,  italian,  web;
         article :  <http://www.ontopia.net/topicmaps/examples/opera/occurs/snl/puccini.htm> @snl,  web;
         webpage :  <http://www.naxos.com/composer/btm.asp?fullname=Puccini, Giacomo> @web,  naxos;
         website :  <http://www.puccini.it> @puccini-study-centre,  italian,  web;
         bibref :  "Sadie, Stanley (ed): 'Puccini and His Operas', Macmillan (London, 2000)";
         bibref :  "Budden, Julian: 'Puccini: His Life and Works', Oxford University Press (Oxford, 2002)";
         webpage :  <http://localhost:8080/operamap/occurs/hnh-puccini.htm> @local,  naxos;
         website :  <http://www.landofpuccini.com>;
         gallery :  <http://localhost:8080/operamap/occurs/puccini-gallery.htm> @local;
         webpage :  <http://www.operone.de/komponist/puccini.html> @web;
         <ontopedia:Puccini>.

3.4.1. ontology intersection in topic blocks

Two name definitions are overlapping only if their type, their scope and their variants are equal. The value has no relevance.

topicA <o:topic_a>
        - "Topic A" @theme.

topicB <o:topic_b>
        - "Topic B" @theme.

These name definitions are equal because the have the same type, in this case the default-name-type of the TMDM. They also have the same scope defined by the "theme" topic and they havn’t any variants, so the two name definitions are overlapping.

Two occurrence definitions are overlapping only if their type, data-type and their scope are equal. The value has no relevance.

topicA <o:topic_a>
        - age: "10"^^xsd:integer @theme.

topicB <o:topic_b>
        - age: "11"^^xsd:integer @theme.

These occurrence definitions are equal because the have the same type ( the age topic ) and the same data-type ( xsd:integer ). They also have the same scope defined by the "theme" topic, so the two occurrence definitions are overlapping.

Two super-type-relations are overlapping only if the use the same topic as type;

topicA <o:topic_a>
        ako topic.

topicB <o:topic_b>
        ako topic.

These super-type-relations are equal because the use the same type.

Two instance-relations are overlapping only if the use the same topic as type;

topicA <o:topic_a>
        isa topic.

topicB <o:topic_b>
        isa topic.

These instance-relations are equal because the use the same type.

Over all the processor creates a set of template candidates which are ordered by the number of contained definitions, so the candidate with the most contained definitions will be preferred. Each candidate will be checked against the relevance threshold defined by the property writer.features.templateDetection.relevanceThreshold. The relevance of a candidate will be calculate by the number of potential and adaptive topic items.

3.4.2. ontology intersection in association blocks

Two association definitions are equal only if they have the same type and contains the same role-types. The player have to relevance.

associationA ( roleA : playerA , roleB : playerB )

associationA ( roleA : playerC , roleB : playerD )

These association definitions have the same type, in this case "associationA", and also contains the two role type "roleA" and "roleB". Because of that the association definitions are overlapping and can be marked as template candidate.

3.5. Automatic template merging

The template merging algorithm is a powerful tool, which provides the functionality of detect overlapping templates. In this case the algorithm use the same ontology interpretation algorithm like the template detection algorithm.

At the first step the algorithm tries to detect the intersection between defined templates and mark them as potential merging candidates. After all the marked candidates will be checked against the minimum count of overlapping templates defined by the property writer.features.templateMerger.threshold. The detected candidates will be extract as a template definition and the dependent content of the template definitions will be replaced by a template-invocation.

A template definition of composer without template merging
def template-topic-composer ($topic,  $date-of-death,  $date-of-birth,  $article,  $illustration,  $webpage,  $name,  $name2,  $name3)
         $topic
                 isa composer;
                 date-of-death :  $date-of-death;
                 date-of-birth :  $date-of-birth;
                 article :  $article;
                 illustration :  $illustration @local;
                 webpage :  $webpage;
                 -  $name @normal;
                 -  $name2;
                 -  $name3 @short-name.
end
A template definition of composer with template merging
def template-topic-composer ($topic,  $date-of-death,  $date-of-birth,  $article,  $illustration,  $webpage,  $name,  $name2,  $name3)
         $topic
                 isa composer;
                 article :  $article;
                 illustration :  $illustration @local;
                 webpage :  $webpage;
                 template-topic-composer-invoc($topic,  $date-of-death,  $date-of-birth, $name,  $name2,  $name3).
end

3.6. Automatic prefix detection

The prefix detection algorithm is a powerful tool which extract frequently used prefixes of IRIs. The algorithm extract all IRIs used as item-identifiers, subject-identifiers or subject-locators. For each IRI the algorithm extracts every possible prefix and store them as candidates and count the matching IRIs. The candidates will be ordered by length, so the longer prefixes will be prefered. As last step the prefix detection algorithm will be generate a QName for detected prefixes, because of that it extract some part information of the prefix IRI, like the second level domain or the path information. If there is a name collision, a number will be attached. During the serialization process the stored and user-defined prefixes will be used to transform every matching IRI to relative IRIs.

A template snippet containing a topic block without prefix detection
antioch
         isa city;
         -  "Antioch";
         <http://psi.ontopedia.net/Antioch>.
A template snippet containing a topic block with prefix detection
%prefix ontopedia  <http://psi.ontopedia.net/>

antioch
         isa city;
         -  "Antioch";
         <ontopedia:Antioch>.

4. Feature String

The writer provides some additional features represent by special string patterns called features strings.

4.1. writer.identity.engineprefix

The property writer.identity.engineprefix defines the prefix of identifier generated by the topic map engine. This prefix will be used to detect generated identifiers used as item-identifiers or subject-identifiers to exclude them from the export process.

The value of this property should be a string representing the QName or baseURI of the used topic maps engine. In case of using the tinyTiM Engine or the Ontopia engine the value should be urn.

4.2. writer.features.export.itemidentifier

The boolean property writer.features.export.itemidentifier enables or disables the export of item-identifiers. This feature can be necessary if the used CTM reader does not support item-identifiers. The value of this property should be true or false, default is false.

4.3. writer.features.prefixDetection.enabled

The boolean property writer.features.prefixDetection.enabled enables or disables the automatic prefix detection algorithm. If this feature is enabled, the writer tries to detect prefixes automatically. The default value is true.

4.4. writer.features.templateDetection.enabled

The boolean property writer.features.templateDetection.enabled enables or disables the template detection algorithm. If this property is enabled the writer try to detect templates by extracting the ontology knowledge of given topic items and association items. The default value is false.

4.5. writer.features.templateDetection.topicTemplates

The boolean property writer.features.templateDetection.topicTemplates enables or disables the template detection algorithm. If this property is enabled the writer try to detect templates for topic items by extracting the ontology knowledge of given topic items. The default value is false.

4.6. writer.features.templateDetection.associationTemplates

The boolean property writer.features.templateDetection.associationTemplates enables or disables the template detection algorithm. If this property is enabled the writer try to detect templates for association items by extracting the ontology knowledge of given association items. The default values is false.

4.7. writer.features.templateDetection.relevanceThreshold

The property writer.features.templateDetection.relevanceThreshold represents the threshold used in the context of the template detection algorithm. This threshold defines the minimum percentage of topic items which have to match to the detected template candidate. The default value is 0.8 or 80%.

4.8. writer.features.templateMerger.enabled

The boolean property "writer.features.templateMerger.enabled* enables or disables the template merging algorithm. The algorithm tries to detect intersections between template and try to extract them by using template-invocations. The default value is false.

4.9. writer.features.templateMerger.threshold

The property writer.features.templateMerger.threshold define the minimum number of templates as a part of the intersection. The default value is false.

5. Tutorial

This section will provide an overview about the usage of the described functionality and the writer itselfs.

5.1. Creating a new CTM writer

At the first step we have to create a new instance of the writer. The constructor of the writer needs two parameters.

The first parameter specifies the output stream as a target for the serialized topic map. The writer supports every instance of output streams like a file output stream, a socket output stream or a string stream. In the following example we use a file to export the CTM string.

The second parameter is a string representing a base URI. The given base URI is used in the worst case, if a topic has no item-identifier, no subject-identifier and no subject-locator, in this case the engine will be generate a new identifier based on the given URI and the internal id of the topic map engine.

The third optional parameter is a string representing a feature line, containing a list of feature-strings and their values. For more information see chapter <<>>

1:      /*
2:       * create a new file
3:       */
4:      final File file = new File("./output.ctm");
5:      if ( file.exists()){
6:              file.createNewFile();
7:      }
8:      /*
9:       * create file output stream
10:      */
11:     final FileOutputStream outputStream = new FileOutputStream(file);
12:     /*
13:      * create ctm writer
14:      */
15:     final String baseURI = "http://tutorials.topicmapslab.de/ctm";
16:     final CTMTopicMapWriter writer = new CTMTopicMapWriter(outputStream, baseURI);

In the 4. line we create a new file, which should be used for the file output stream defined in line 11. If the file does not exists, it will be created in line 6. In the 15. line we define the base locator which will be used in the described case. After all we create a new instance of the CTM topic map writer in source line 16.

5.2. Use additional features

The writer provides some additional features which can be used for a specific serialization process. The additional features are controlled by used special properties used "features string".

There are three different possibilities to set or change feature strings, the property file, the property parameter and the property class.

5.2.1. the properties file

During the process of initialization the writer load the current properties from the property file. The property file will be searched at the current class path. If the property file can not be found, default values will be used.

the default property file
writer.identity.engineprefix = urn
writer.features.export.itemidentifier = false
writer.features.prefixDetection.enabled = true
writer.features.templateDetection.enabled = true
writer.features.templateDetection.topicTemplates = true
writer.features.templateDetection.associationTemplates = true
writer.features.templateDetection.relevanceThreshold = 0.8
writer.features.templateMerger.enabled = true
writer.features.templateMerger.threshold = 2

5.2.2. the property parameter

The second possibility to change properties is to use the third parameter of the constructor of the topic map CTM writer. This optional parameter represents a comma separated list of key-value-pairs, similar to the shown property file.

1:  final File file = new File("./output.ctm");
2:  if ( file.exists()){
3:              file.createNewFile();
4:  }
5:  final FileOutputStream outputStream = new FileOutputStream(file);
6:  final String baseURI = "http://tutorials.topicmapslab.de/ctm";
7:  final String line = "writer.features.export.itemidentifier = false, "
8:              + "writer.features.prefixDetection.enabled = true, "
9:              + "writer.features.templateDetection.enabled = true, "
10:             + "writer.features.templateDetection.topicTemplates = true , "
11:             + "writer.features.templateDetection.associationTemplates = true, "
12:             + "writer.features.templateMerger.enabled = false";
13: final CTMTopicMapWriter writer = new CTMTopicMapWriter(outputStream, baseURI, line);

This code snippet looks like the last one except form the third parameter given to the constructor in line 13. As you can see, the parameter only contains a key-value-list separated by commas.

5.2.3. the property class

The internal representation of feature strings are realized by a property class, which provides methods to manipulate the properties. The class provides a method for each supported property and one method to parse a key-value-list from a given string, similar to the property parameter.

1:  final String line = "writer.features.export.itemidentifier = false, "
2:              + "writer.features.prefixDetection.enabled = true, "
3:              + "writer.features.templateDetection.enabled = true, "
4:              + "writer.features.templateDetection.topicTemplates = true , "
5:              + "writer.features.templateDetection.associationTemplates = true, "
6:              + "writer.features.templateMerger.enabled = false";
7:
8:      CTMTopicMapWriter writer = new CTMTopicMapWriter( new FileOutputStream(file), "www.topicmapslab.de", line);
9:      writer.setProperty(CTMTopicMapWriterProperties.IDENTITY_ENGINEPREFIX,"urn:ontopia");

The line 1 until line 6 we define a property string, which contains a comma separated list of key-value-pairs. In the 9. line we try to set the property describing the engine prefix to "urn:ontopia".

5.3. define prefixes

Now we want to define our own prefixes and want to add them to the internal prefix store of the CTM writer. Of course we can also use the automatic prefix detection algorithm, but sometimes the generate QNames are not equal to our interest or there are a prefix which will not be detected.

We will need two information for each prefix to define it. The first parameter namespace represents the prefix and will be used for transformation of the absolute IRI to a relative IRI. The second information are the prefix itself, which has to be a valid absolute IRI in context of the RFC 3987.

The prefixes are stored in a internal Map and will be handled by a special class called PrefixHandler. An instance of the handler can not access directly because it will be encapsulated by the CTM topic map writer instance.

1:      final String namespace = "tml";
2:      final String iri = "http://www.topicmapslab.de/";
3:      CTMTopicMapWriter writer = new CTMTopicMapWriter( new FileOutputStream(file), "www.topicmapslab.de", line);
4:      writer.setPrefix(namespace, iri);

As we describe, we have to define our two parameters at first. In line 1 we define the QName value and set them to the value "tml". In the second line we define the real IRI of our prefix and set them to "http://www.topicmapslab.de/". The last slash can be removed, because it will be added automatically if no slash or hex is postponed. The last operation to do is to call the setPrefix method of the CTM topic map writer instance to add the prefix definition, as you can see in line 4.

5.4. define templates

The next step will be more complicated than the last one. We want to define our own template definitions, which should be used during the exporting process. There are two possibilities to define templates - define by entries and define by CTM.

5.4.1. define by entries

One possibility defining templates is to create a new Template instance and add some entries representing the content of the template, as we will see in the next chapters. At first we have to create a new instance of a template with a given name.

1:      /*
2:       * create template with the name "myTemplate"
3:       */
4:      final String templateName = "myTemplate";
5:      Template template = new Template(templateName);
6:      /*
7:       * add template to CTM writer
8:       */
9:      writer.addTemplate(template);

Because of the clear arrangement, we split the name definition and the template definition, but of course it will be possible to do in one step. In line 4 we create a new string and set them to the value "myTemplate". In the 5th line we use the string variable to create a new template instance and add them to the CTM writer in line 9. The given name will be used in the CTM file.

The code snippet looks very simple, because it only creates an empty template and add them to the CTM writer, but let us see the generated CTM content.

1:      def myTemplate ()
2:              # empty
3:      end

As you can see in the first line, the given name are used as a template identification and because of missing entries the template is empty.

The possible entries are distinguish from each other by their representing ontology and can contain variables or constants as a value.

define a type entry

Type entries representing the type-instance-association of the TMDM. The value of this template has to be an identifier of specific topic type or a variable.

1:      /*
2:       * read the topic type from the topic map
3:       */
4:      Locator locator = topicMap.createLocator("http://psi.ontopedia.net/Composer");
5:      Topic type = topicMap.getTopicBySubjectIdentifier(locator);
6:      /*
7:       * create type entry with the specific type
8:       */
9:      IsInstanceOfEntry entry = new IsInstanceOfEntry(type);
10:     /*
11:      * add entry to template
12:      */
13:     template.add(entry);

At first we will have to extract the topic type from our topic map as the essential parameter of the entry. In line 4 and 5 we use method of the TMAPI to extract a topic representing our type information. In line 9 we create a new instance of IsInstanceOfEntry which represent the type entry and. At last step we add the new entry to our template, as you can see in line 13.

After defining the new entry we want to check the CTM output.

1:      def myTemplate ($topic)
2:               $topic
3:                       isa composer.
4:      end

As you can see in line 1 and 2 the template processor create a new variable $topic representing the topic using our template by invocation. In line 3 we can see the output of our new entry. The keyword isa symbolize a type-instance-association and the identifier composer was generated by internal processor to represent the given type http://psi.ontopedia.net/Composer.

define a super-type entry

Super-type entries representing the supertype-subtype-association of the TMDM. The value of this template has to be an identifier of specific topic supertype or a variable.

1:      /*
2:       * read the topic type from the topic map
3:       */
4:      Locator locator = topicMap.createLocator("http://psi.ontopedia.net/Musician");
5:      Topic supertype = topicMap.getTopicBySubjectIdentifier(locator);
6:      /*
7:       * create type entry with the specific supertype
8:       */
9:      AKindOfEntry entry = new AKindOfEntry(supertype);
10:     /*
11:      * add entry to template
12:      */
13:     template.add(entry);

At first we will have to extract the topic type from our topic map as the essential parameter of the entry. We use the the TMAPI methods to find the desired topic type representing the supertype, as you can see in line 4 and 5. In line 9 we create a new instance of AKindOfEntry which represent the type entry and. At last step we add the new entry to our template.

After defining the new entry we want to check the CTM output.

1:      def myTemplate ($topic)
2:               $topic
3:                       ako musician.
4:      end

As you can see in line 1 and 2 the template processor create a new variable $topic representing the topic using our template by invocation. In line 3 we can see the output of our new entry. The keyword ako symbolize a supertype-subtype-association and the identifier musician was generated by internal processor to represent the given type http://psi.ontopedia.net/Musician.

define a name entry

A name entry only represent a name item of a topic item. Name entries are more complex than type or supertype entries, because the number of arguments are higher, but most of them are optional and can be let out. The only non-optional parameter is the identifier information given by a variable name or a string. Name entries also can specify the type of the name, which will be represented by a topic type, as default the default-name-type of the TMDM will be used. The last parameter can be used to define some scoping information by a number of given themes or variables.

1:      /*
2:       * read the topic type of scope entry from the topic map
3:       */
4:      Locator locator = topicMap.createLocator("http://psi.ontopedia.net/short_name");
5:      Topic shortName = topicMap.getTopicBySubjectIdentifier(locator);
6:      /*
7:       * create new scope entry
8:       */
9:      ScopeEntry scopeEntry = new ScopeEntry(shortName);
10:     /*
11:      * create new name entry
12:      */
13:     final String variable = "$name";
14:     NameEntry entry = new NameEntry(variable,scopeEntry);
15:     /*
16:      * add entry to template
17:      */
18:     template.add(entry);

The code snippet show a small and simple example, how to create a name entry. In the first lines ( 4 and 5 ) the TMAPI methods are used to extract a theme represented by a topic item form the topic map. The extracted theme are used in line 9 to create a new instance of ScopeEntry which represents the scope information of our name entry. The real name entry are instantiated in line 14 with the value information specified by the variable $name. Like all entries the last necessary step is adding the entry to a template.

After serialization let us take a look at the serialized CTM pattern.

1:      def myTemplate ($topic,  $name)
2:               $topic
3:                       -  $name @short-name.
4:      end

Line 3 contains the exported name entry symbolized by the hyphen at the beginning. The scope information are symbolized by @ and a list of themes identified by there topic identity.

define an occurrence entry

The TMDM differs between names and occurrences as characteristic informations of a topic, because of that CTM also support two different patterns to define this, so we take a look at occurrence entries. The definition of occurrence entries are similar to name entries. The supported parameters are nearly the same, like the identifier or value parameter, the type parameter and the scope parameter, but occurrence entries additionally supporting some datatype information representing the datatype of the given value. Most of the parameters are optional two except the type and the value or identifier parameter.

1:      /*
2:       * read the topic type of scope entry from the topic map
3:       */
4:      Locator locator = topicMap.createLocator("http://psi.ontopedia.net/Web");
5:      Topic web = topicMap.getTopicBySubjectIdentifier(locator);
6:      /*
7:       * create new scope entry
8:       */
9:      ScopeEntry scopeEntry = new ScopeEntry(web);
10:     /*
11:      * read the topic type of occurrence entry from the topic map
12:      */
13:     locator = topicMap.createLocator("http://psi.ontopedia.net/webpage");
14:     Topic type = topicMap.getTopicBySubjectIdentifier(locator);
15:     /*
16:      * create new occurrence entry
17:      */
18:     final String variable = "$webpage";
19:     OccurrenceEntry entry = new OccurrenceEntry(variable,type,scopeEntry);
20:     /*
21:      * add entry to template
22:      */
23:     template.add(entry);

In the code snippet we show a easy way to define our own template with exactly one occurrence entry. In line 4 and 5 we use TMAPI methods to get the topic type of our theme using in context of scoping information in line 9. In the lines 13 and 14 we use TMAPI against to get the topic type of our new occurrence. Now we can create the occurrence entry, as you can see in line 19 using the scope entry of line 9, the type of line 14 and the variable name of line 18. After all we have to add the entry to the template.

1:      def myTemplate ($topic,  $webpage)
2:               $topic
3:                       webpage :  $webpage @web.
4:      end

The 3. line containing our new occurrence entry. The pattern starts with the topic identity of the occurrence type followed by the colon. The scope information are symbolized by @ and a list of themes identified by there topic identity.

define an association entry

One of the core features and benefits of a topic map are associations to model relations between topic items. Templates also support the modeling of association constraints as association entries. The TMDM required that an Association is typed, because of that we have to define a topic type as the type of association item representing by the association entry. The second parameter an entry is needed are a set of role entries defining a set of parameterized role constraints. Each role constraint has to define a topic type as the type of the role and a player, which can be variable or a constant topic playing this role.

1:      /*
2:       * create role entry for role-type composer
3:       */
4:      Locator locatorRTA = topicMap.createLocator("http://psi.ontopedia.net/Composer");
5:      Topic typeRTA = topicMap.getTopicBySubjectIdentifier(locatorRTA);
6:      RoleEntry roleEntryA = new RoleEntry(typeRTA, "$composer");
7:      /*
8:       * create role entry for role-type work
9:       */
10:     Locator locatorRTB = topicMap.createLocator("http://psi.ontopedia.net/Work");
11:     Topic typeRTB = topicMap.getTopicBySubjectIdentifier(locatorRTB);
12:     RoleEntry roleEntryB = new RoleEntry(typeRTB, "$work");
13:     /*
14:      * create association entry by type and role entries
15:      */
16:     Locator locator = topicMap.createLocator("http://psi.ontopedia.net/composed_by");
17:     Topic type = topicMap.getTopicBySubjectIdentifier(locator);
18:     AssociationEntry entry = new AssociationEntry(type,roleEntryA, roleEntryB);
19:     /*
20:      * add to template
21:      */
22:     template.add(entry);

The code snippet first defines two role entries between line 4 and 12. At first we have to extract the role type by using TMAPI methods. The extracted type is used to create a new instance of RoleEntry in line 6 or 12. After the instantiation of all role entries, we can create our new association entry. As described we have to extract the topic type as type information of our association item, so we use TMAPI in line 16ff. After all we have to add the new entry to our template.

Note: If you use association entries and topic entries in the same template, that could be problematic.

1:      def myTemplate ($composer,  $work)
2:              composed-by (
3:                       work : $work,
4:                       composer : $composer
5:              )
6:      end

This output are generated by our template definition. In line 2 we see the association type represented by its topic identity. The round brackets symbolize the definition of an association and containing a comma separated list of role-player-constraints. Each role-player constraint are symbolized by one role entry and we can see the result of our two entries in line 3 and 4. Please note that in this case no default variable $topic will be created, because the template only containing association entries.

define a template-invocation entry

The last possible entry type are template entries. Template entries realize the modeling of template-invocation as a part of templates. Of course we have to define the template, which should be called by the invocation as a parameter of the new entry.

1:      /*
2:       * create internal template
3:       */
4:      Template invoc = new Template("template-invoc");
5:      /*
6:       * add entries to internal template
7:       */
8:      NameEntry entry = new NameEntry("$name");
9:      invoc.add(entry);
10:     /*
11:      * create template entry
12:      */
13:     TemplateEntry templateEntry = new TemplateEntry(template, entry.getValueOrVariable());
14:     /*
15:      * add to template
16:      */
17:     template.add(templateEntry);

In code snippet replacing the definition of a name entry by using a template-invocation. In line 4 we create a template instance representing the template we called by invocation. In line 8 and 9 we create a name entry in the same way we see in the example [name_entry]. The new template is used to create a new template entry in line 13. The second argument of the constructor is a list of all containing variables. After all we have to add the entry to the template.

1:      def myTemplate ($topic,  $name)
2:              template-invoc($topic, $name)
3:      end

Instead of containing a name entry the exported CTM pattern make use of invocations to swap out the name entry for reuse by other templates. In line 3 we see the template-invocation pattern containing the tempalte name template-invoc and a list of arguments represented by the template variables. As we can see the default variable $topic pass to the template invocation.

5.4.2. define by CTM

A more comfortable way to create a template by importing a CTM file containing the CTM template definition. The template definitions look like the exported templates of the CTM writer. The drawback of this alternative is the missing possibility to control the definition process.

1:      /*
2:       * the file containing template definitions
3:       */
4:      final File file = new File("template.ctm");
5:      /*
6:       * read all templates from file
7:       */
8:      Set<Template> templates = Template.fromCTM(file);

In line 4 we create a new file instance pointing to the file containing the CTM template definitions. The next step is to call the static method of the Template class to parse the CTM file and extract the containing template definitions. Because of the fact that each CTM file can contain more than one template, a set of templates will be returned.

5.5. serialize the topic map

After defining additional content like prefixes or templates, the last step to do is to call the write() method of the CTM writer to serialize the topic map instance to the given file. Please note that all user defined templates and prefixes have to add before calling the serializing method.

1:      /*
2:       * create a new file
3:       */
4:      final File file = new File("./output.ctm");
5:      if (file.exists()) {
6:              file.createNewFile();
7:      }
8:      /*
9:       * create file output stream
10:      */
11:     final FileOutputStream outputStream = new FileOutputStream(file);
12:     /*
13:      * create CTM writer
14:      */
15:     final String baseURI = "http://tutorials.topicmapslab.de/ctm";
16:     final String line = "writer.features.export.itemidentifier = false, "
17:                             + "writer.features.prefixDetection.enabled = true, "
18:                             + "writer.features.templateDetection.enabled = true, "
19:                             + "writer.features.templateDetection.topicTemplates = true , "
20:                             + "writer.features.templateDetection.associationTemplates = true, "
21:                             + "writer.features.templateMerger.enabled = false";
22:     final CTMTopicMapWriter writer = new CTMTopicMapWriter(outputStream, baseURI, line);
23:     /*
24:      * serialization of the topic map
25:      */
26:     writer.write(topicMap);

This example is similar to the first one in chapter [create-writer] except of the command line defining the additional features. But the interesting line is number 16, we call the write method with one argument - the topic map to serialize - and export the topic map to CTM.

topicmapslab.de