An Introduction to TMAPI
February 2, 2005
Robert Barta and Oliver Leimig
Introduction
There are several software packages for Java developers when they need to develop applications using XML Topic Maps. There are some proprietary software vendors and also open source packages like TM4j, tinyTIM, and a few others.
In the Java tradition to standardize interfaces, the TMAPI project has proposed a set of Java interfaces which particular Topic Map implementations may choose to adhere to. The obvious advantage for the application developer is to use only that single set of interfaces and to choose a particular implementation on other merits.
Those of you who every now and then read underlying standards may notice that there is a strong coherence between TMAPI and the class model behind TMDM, the TM data model. This, of course, is no coincidence: both have been developed in lock-step. The result is that Java developers are programming close to the (high-level) TM model.
In the rest of this article, we give a fast-lane introduction to creating and restoring information using TMAPI. This tutorial is a spin-off of our efforts to create a TMAPI-compliant AsTMa parser; we have no affiliation with the TMAPI project itself.
We assume that the reader has some familiarity with the basic Topic Map concepts:
- Topics represent subjects in the world.
- They can have names, URI references, and text data (and this attachment might have a type or a limiting scope).
- Topics are connected via associations, whereby some topics are the players and others the roles which are played.
- Associations also have a type and, optionally, a scope.
We will then use a running example about airports.
You can download some sample code as one coherent Java
file. Except for the classes java.util.Iterator
and
java.util.ArrayList
, all other classes used there and in this tutorial reside
in the package org.tmapi.core
, which TMAPI-conformant implementations will have
to offer.
Depending on your environment, you might compile the code via javac
TMAPITutorial.java -classpath "lib\tmapi-1_0.jar"
and execute it via something like
(here we used tinyTIM): >java -classpath
".;lib\tmapi-1_0.jar;\where\is\tinyTIM-1_0.jar" TMAPITutorial
or, if you have some
inclination to brute force and are blessed with a decent shell, java -classpath `find
. -iname '*.jar' | awk 'ORS=":" {print $1}'` TMAPITutorial
Preliminaries
Java being Java, we first have to set up a bit of infrastructure to host our topic
map.
TMAPI asks us to instantiate first a
TopicMapSystemFactory
and a TopicMapSystem
(lines 1 and 2 in the
following listing). Line 3 finally creates a TopicMap
in the current
TopicMapSystem
:
TopicMapSystemFactory tSystemFactory = TopicMapSystemFactory.newInstance(); TopicMapSystem tSystem = tSystemFactory.newTopicMapSystem(); TopicMap tMap = tSystem.createTopicMap('http://topicmaps.bond.edu.au/example/' );
The given parameter in the last statement sets the baseLocator
of the Topic
Map to http://topicmaps.bond.edu.au/example/
. That base locator is the base URL
which can later be used to address particular topics and associations within that
map. The
base locator can be chosen freely as seems appropriate for your application.
From then on, we use this tMap
instance to store topic map objects, such as
topics and associations.
Adding Topics with Names
Given a map tMap
, creating a topic object in there is straightforward:
Topic tAirportSydney = tMap.createTopic();
This, by itself, does not mean a lot, though. The topic has no further information,
so we
add a new topic name to it: tAirportSydney.createTopicName( "Airport Sydney,
Australia", null );
We pass in null
for the scope to let the name be
unconstrained.
The next thing to do is to define the type of the topic. We have to make sure the topic type is also in the map, and, being good citizens, we also add some topic name for it:
Topic tAirport = tMap.createTopic(); tAirport.createTopicName( "Airport", null );
Then, we simply add that as type tAirportSydney.addType( tAirport );
.
In the case that we need a scope for a topic name, we first have to make sure that all topics which compound the scope exist in the map. In our case, we use the language to scope names (probably not the best case of scope, but that is a different story):
Topic tGerman = tMap.createTopic(); tGerman.createTopicName( "german language", null ); ArrayList alScope = new ArrayList(); alScope.add( tGerman ); tAirportSydney.createTopicName("Flughafen Sydney, Australien", alScope );
After all scoping topics are added to a list, that list is used when actually adding a new topic name.
Adding Occurrences
In order to attach a reference Occurrence
to a Topic
, you have
to create a Locator
first. That is then used when a new occurrence is added to
a topic:
Locator locSydOcc = tMap.createLocator ("http://www.sydney.com.au/airport-transfers.htm" ); Occurrence ocSydAirportTransfers = tAirportSydney.createOccurrence ( locSydOcc, null, null );
We passed in null
for the type and the scope of the occurrence as may fit
here. Should we, though, want to type an occurrence, then we need yet another topic:
Topic tHomepage = tMap.createTopic(); tHomepage.createTopicName( "Homepage", null ); Locator locSydHome = tMap.createLocator ( "http://www.sydneyairport.com.au/SACL/default.htm" ); Occurrence ocSydAirportHP = tAirportSydney.createOccurrence ( locSydHome, tHomepage, null );
In a rather similar way, we can also add occurrence values, those which allow us to affiliate text content with a topic. In our running example, we add the airport three-letter code to the 'Sydney Airport' topic:
Topic tAirportCode = tMap.createTopic(); tAirportCode.createTopicName( "Airport Code", null ); tAirportSydney.createOccurrence("SYD", tAirportCode, null );
Adding Associations
Let's assume that we would like to connect our Sydney Airport
topic with a
new Tokyo Airport
in an association of type "has flight from to
".
Sydney airport would play the role of the departure location and Tokyo airport that
of the
destination.
First, we create an association in our map and give it a proper type:
Association assoc = tMap.createAssociation(); Topic tAssType = tMap.createTopic(); tAssType.createTopicName("has flight from to", null ); assoc.setType ( tAssType );
Assuming that we have created appropriate topics tDestination
,
tOrigin
, tTime
, and tAirportToko
, we can add the
association roles (together with the playing topic) to the association:
assoc.createAssociationRole( tAirportSydney, tOrigin ); assoc.createAssociationRole( tAirportTokyo, tDestination ); assoc.createAssociationRole( tTue1015, tTime );
The topic tTue1015
should represent a weekday and time, Tuesday 10:15 and is
also assumed to have been created before.
Realistically, we would like to scope this association only to be valid in summer. For this purpose, we add a last topic and use that as the scoping topic:
Topic tSummerTimetable = tMap.createTopic(); tSummerTimetable.createTopicName( "summer timetable", null ); assoc.addScopingTopic( tSummerTimetable );
Topic Identification
Topic identification, connecting your topics to the real world, is a central TM paradigm. While the terminology is somewhat contrived (consult Topic Map tutorials), the principle actually is simple: either you have a subject which is a resource with a URL (so, an online document) or you have a subject with no appropriate URL (like my cat, although the page claims otherwise). In the latter case, we can use URLs to "indirectly indicate" what subject we are talking about.
To demonstrate a topic which represents a resource on the internet, we consider the Tokyo Airport (Narita) homepage. It is rather informative, a fact we would like to capture as an occurrence value. For this purpose, we create the topic and give it a name and everything else we know about the site:
Topic tNaritaHP = tMap.createTopic(); tNaritaHP.createTopicName( "Narita Homepage", null ); tNaritaHP.createOccurrence( "informative", null, null );
To link our topic to a web resource, we first create a Locator
using the URL
and then set this locator as the subject locator
:
Locator locNarita = tMap.createLocator ( "http://www.narita-airport.or.jp/airport_e/" ); tNaritaHP.addSubjectLocator( locNarita );
Note that this is very different from using occurrence references. These are only attached URLs (or URIs, in general), saying something like "for more about that topic, read this stuff." A subject locator strongly connects a topic to a resource in such a way that when maps are merged, two topics having the same subject locator will be merged into one.
The situation is slightly different if we want to connect our topic
tAirportTokyo
with the actual airport. As the actual airport is not an
internet resource but has a homepage that represents it, we use that as the subject
indicator and the URL as the subject identifier
:
Topic tAirportTokyo = tMap.createTopic(); tAirportTokyo.createTopicName( "Narita", null ); tAirportTokyo.addSubjectIdentifier( locNarita );
Any number of subject identifiers can be added; in fact, the more you add, the more robust merging will be.
Reification
Reification, in the TMAPI sense, is used to make a specific part of a map into a topic itself, so that we are able to make statements about it. This more advanced use of Topic Maps is often avoided by beginners (and probably rightly so), but it can be quite useful to qualify information.
In our example, we show how an occurrence--so the relationship between a topic and
a URI
reference--can be reified. For this purpose, we first create a source locator for
the
homepage of Sydney airport (ocSydAirportHP
from above) and attach it to the
existing ocurrence:
Locator srcLocWP = tMap.createLocator(tMap.getBaseLocator().getReference() + \ "#sydney-airport-homepage"); ocSydAirportHP.addSourceLocator ( srcLocWP );
After that we create another topic and make it identify this very occurrence:
Topic tocSydAirportHP = tMap.createTopic(); tocSydAirportHP.addSubjectIdentifier( srcLocWP );
Once the topic tocSydAirportHP
stands for the occurrence, we can use it as part of associations to make statements
about it. Examples for this could be, for instance, that we have found the link via
Google
using a particular search string or that this particular occurrence was added by a
particular person.
Reification not only works for occurrences but also for whole TopicMap
s and
TopicName
s (and the rather exotic Variant
which we choose not to
mention). It also works for associations, where reification is more useful.
To see this, let us reify the association assoc
between the Sydney and Tokyo
airports that we had before. This association actually is about a particular flight.
To add flight information, such as the flight number JL772
, we have to create a
topic first:
Topic tJL772 = tMap.createTopic(); tJL772.createTopicName( "Japan Airlines Flight 772", null ); tJL772.createOccurrence( "JL772", tFlightNumber, null );
Then we can connect that topic with the association by (a) adding a source locator to the association and (b) using that as the subject locator in the topic:
Locator locAssoc = tMap.createLocator( tMap.getBaseLocator().getReference() + \ "#sydney-tokyo-flight" ); assoc.addSourceLocator( locAssoc ); tJL772.addSubjectIdentifier ( locAssoc );
Browsing
At some stage, when dumping the topic content into a user interface, you will want
to work
through all topics, all their components, or through associations. For all of this,
TMAPI
offers iterators which are created outside a loop and use methods such as
hasNext()
and next()
to visit every element:
Iterator i1 = tMap.getTopics().iterator(); while ( i1.hasNext() ) { Topic t = (Topic) i1.next(); ...}
We have to typecast to Topic
. Once we have a topic t
in our
hands, we might be interested in all the names. Again, we use an iterator for this:
Iterator i2 = t.getTopicNames().iterator(); while (i2.hasNext()) { TopicName tn = (TopicName) i2.next(); System.out.print( tn.getValue() ); // not exactly pretty printing }
The references the iterator gives us here have to be casted as TopicName
s, so
that we can access the string value later.
In analogous ways, you can drill into associations, occurrences and other parts of the map.
TMAPI also offers more sophisticated ways to retrieve particular information, especially
when speed is an issue. So, for instance, to extract all associations of a particular
type
tMyType
from the map, one would use
Collection as = idx.getAssociationsByType( tMyType );
on an index object idx
which, obviously, has to be created first:
import org.tmapi.index.core.AssociationIndex; AssociationIndex idx = (AssociationIndex) tMap.getHelperObject ( AssociationIndex.class ); idx.open(); Collection as = idx.getAssociationsByType( tMyType ); idx.close();
Different fast access methods need different kinds of indices, all of which you can find documented in the guide.
Conclusion
Java developers will find the interface simple enough to be used right away, as was the case for our project. TMAPI covers the building of Topic Map structures in memory and offers also means to navigate through such a structure in a standardized way.
Other features like transaction management, application-controlled merging (when and how), or persistency issues (how and where a topic map is stored) are outside the TMAPI scope and have to be dealt with by particular implementations. Also not part of TMAPI (yet) is a high-level query language. This may change once TMQL is finished.