APIExample2 : Working With Indexes

Running The Example

APIExample2 is a simple application which shows the basics of using the programmatic indexes provided by TMCore. The program creates a topic map from a directory (specified on the command line) and all its subdirectories. For each directory and file in the hierarchy a topic is created and assigned either the type "File" or "Directory" (these are defined by separate topics created when the program first initialises). For each file and for each subdirectory of the starting directory an association is created to the containing directory. That association is typed as "Parent-Child" with the containing directory playing a role of type "Parent" and the contained file or directory topic playing the role "Child". In addition, every file with a file name extension (eg. MyFile.txt ) is also typed by a topic that describes the extension (e.g. ".TXT"). The first time that a file with a particular extension is encountered, the typing topic is created. For each subsequent file with the same extension, that same topic is used. To assign the right extension type topic to a file topic, the org.tmapi.index.core.TopicsIndex is used to look up the extension type topic.

This example can be found in the file examples/CS/APIExample2 for C# code and examples/VB/APIExample2 for VB code.

Example 3.8. The Main Program Flow

public void Run(string startDirectory) 
{
  // Initialise a new ITopicMapSystem and create the topic map
  m_tmSystem = TopicMapSystemFactory.GetTopicMapSystem();
  m_tm = SafeCreateTM(m_tmSystem, "http://www.example.com/topicmaps/MyFileSystem");

  // Add the basic topics required for creating our file-system topic map
  CreateOntology();

  ProcessDirectory(new DirectoryInfo(startDirectory), null);

  ListTopicsAndAssociations();
  ListTopicTypes();
  ListDLLs();
}

When the program is run, the name of the directory to process is retrieved from the command-line (not shown in the code above). This is passed into the main program loop as the parameter startDirectory. In the main program loop, the following is done:

  1. A new TopicMapSystem is created and a new TopicMap is created in that system. This code is exactly the same as already discussed for APIExample1.

  2. The CreateOntology() method is invoked to "bootstrap" the topic map with the topics that define the basic topic types of "File", "Directory" and the basic association type "Parent-Child" and role types "Parent" and "Child".

  3. The ProcessDirectory() method is invoked to recurse through the directory specified by the startDirectory parameter, creating File and Directory topics and linking them together using Parent-Child associations.

  4. All of the topics and associations created are listed to the console by the method ListTopicsAndAssociations(). This method makes use of code very similar to that already seen in APIExample1 and will not be covered in detail here.

  5. All of the topics used to type other topics are listed to the console by the method ListTopicTypes().

  6. All of the topics of a specific type (the type representing files with the .DLL extension) are listed to the console by the method ListDLLs();

Example 3.9. Creating the Topic Map Ontology

/// <summary>
/// Creates the topics for the basic types used by this topic map.
/// </summary>
private void CreateOntology() 
{
  // Topic to type all topics that represent files
  m_tFile = CreateTopic("File", "http://www.networkedplanet.com/psi/examples/#File")
  // Topic to type all topics that represent directories
  m_tDirectory = CreateTopic("Directory", "http://www.networkedplanet.com/psi/examples/#Directory");

  // Topics for creating the Parent/Child associations
  m_tParent = CreateTopic("Parent", "http://www.networkedplanet.com/psi/examples/#Parent");
  m_tChild = CreateTopic("Child", "http://www.networkedplanet.com/psi/examples/#Child");
  m_tParentChild = CreateTopic("Parent-Child", "http://www.networkedplanet.com/psi/examples/#ParentChild");
}

/// <summary>
/// Utility method to create a topic with a single name and a 
/// single subject identifier.
/// </summary>
/// <param name="name">The name to add to the new topic</param>
/// <param name="subjectIdentifier">The subject identifier URL for the new topic</param>
/// <returns>The newly created topic</returns>
private ITopic CreateTopic(string name, string subjectIdentifier) 
{
  ITopic ret = m_tm.CreateTopic();
  ret.CreateTopicName(name);
  if (subjectIdentifier != null) 
  {
    ret.AddSubjectIdentifier(m_tm.CreateLocator(subjectIdentifier));
  }
  ret.Save();
  return ret;
}

The above code shows one simple pattern for populating a new topic map with the basic typing topics for an application (often called an 'ontology'). The method CreateTopic() is used to create a single topic with a given name and subject identifier. This method is then invoked from the CreateOntology() method for each topic to be created. Of course, if your application will make use of the same topic map each time, you should check that a particular topic does not exist before creating a new one with the same subject identifier - we will see how to use indexes to achieve this in the next section. In this code, the topics are then stored as member variables of the main program class - in more complex applications you may want to create a specific "Ontology" class to manage these topics.

Example 3.10. Using Indexes

      [1] /// <summary>
      [2] /// Adds a topic representing a file into the topic map.
      [3] /// </summary>
      [4] /// <param name="fileInfo">The file to be processed.</param>
      [5] /// <param name="parentDir">The topic representing the directory that contains this file.</param>
      [6] private void ProcessFile(FileInfo fileInfo, ITopic parentDir) 
      [7] {
      [8]   // Create a topic representing the file
      [9]   ITopic fileTopic = m_tm.CreateTopic();
      [10]   fileTopic.AddType(m_tFile);
      [11]   fileTopic.CreateTopicName(fileInfo.FullName);
      [12]   fileTopic.CreateTopicName(fileInfo.Name, new ITopic[] {parentDir});
      [14]   if (fileInfo.Extension != null) 
      [15]   {
      [16]     // Lookup the topic that represents this file's file extension
      [17]     string fileTypeIdentifier = 
      [18]       "http://www.networkedplanet.com/psi/examples/extensions/#" + 
      [19]       fileInfo.Extension.Substring(1).ToUpper();
      [21]     ITopic extensionType = m_tm.TopicsIndex.GetTopicBySubjectIdentifier(fileTypeIdentifier);
      [22]     if (extensionType == null) 
      [23]     {
      [24]       // Create a topic to represent this files's file extension
      [25]       extensionType = m_tm.CreateTopic();
      [26]       extensionType.CreateTopicName("File Type: " + fileInfo.Extension.ToUpper());
      [27]       extensionType.AddSubjectIdentifier(fileTypeIdentifier);
      [28]       extensionType.Save();
      [29]     }
      [30]     fileTopic.AddType(extensionType);
      [31]   }
      [33]   fileTopic.Save();
      [34]   ...
      [35] }

The code snippet above shows part of the process of creating a topic to represent a file. From line [14] to line [31] is code for assigning a type to the file topic based on the extension of the file name. So for example all files representing .DLL files will be typed by a topic "DLL", all .TXT files by a topic "TXT" and so on. We want to be sure that we only create the "DLL" or "TXT" topic on the first time we encounter a file with that extension and not on subsequent files with the same extension. The best way to identify topics is by assigning a subject identifier to them. A subject identifier is simply a unique URI identifier for the topic. In our example we create a subject identifier with the URI "http://www.networkedplanet.com/psi/examples/extensions/#{extension}". Note that we force the file name extension to upper case to ensure that "X.DLL" and "y.dll" both receive the same typing topic as subject identifiers are case-sensitive. Having created a locator string with the appropriate URL (lines [17]-[19]), we then look up the topic with that subject identifier using the TopicsIndex . Every ITopicMap provides access to a set of indexes of the different objects in the topic map. All of these indexes are accessed through read-only properties of the ITopicMap interface - they are easily identified as each property name ends with "Index". Each of the indexes provide a different set of look-up methods. The index we are using here, the TopicsIndex provides methods to look up a topic by its subject identifier, its subject locator, or its type(s). On line 21, the method ITopicsIndex.GetTopicBySubjectIdentifier() is invoked. Because a topic map can only ever have one topic with a given subject identifier, this method will either return a single ITopic instance or null. If the method returns null, a new topic is created on lines 24-28. Finally the topic (either the one returned by GetTopicBySubjectIdentifier() or the newly created topic) is added to the types for the file topic using the method ITopic.AddType(), and the file topic is saved to commit all the changes.

Example 3.11. Listing All Topic Types

      [1] /// <summary>
      [2] /// Displays the topics which are used to type other topics in the topic map.
      [3] /// </summary>
      [4] private void ListTopicTypes()
      [5] {
      [6]   System.Console.WriteLine("All Topic Types:");
      [7]   IList topicTypes = m_tm.TopicsIndex.GetTopicTypes();
      [8]   foreach(ITopic t in topicTypes) 
      [9]   {
      [10]     PrintTopicNames(t);
      [11]   }
      [12]   System.Console.WriteLine();
      [13] }

The example above shows another use of the indexes. In addition to allowing you to look up objects by certain properties, most of the indexes also allow you to do a reverse look up e.g. to quickly find all topics which are used to type other topics. These reverse look up methods can be extremely useful in constructing views and indexes for the user. In this example the reverse look up is to find all topics which are used to type other topics in the topic map and is achieved on line [7] with a call to ITopicsIndex.GetTopicTypes(), which returns an IList of ITopic instances.