XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

An Introduction to TMAPI

February 02, 2005

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 TopicMaps and TopicNames (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 TopicNames, 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.