Copying Topic Map Objects

The org.tm4j.topicmap.TopicMapFactory interface provides convenience functions which allows topic map objects to be copied either within the same topic map or between two different topic maps, regardless of whether or not the topic maps come from the same TopicMapProvider. These methods are all named copy() and are distinguished by the parameters they accept. In all cases, the copies are always made in the TopicMap from which the TopicMapFactory doing the copy was retrieved. As well as passing in the source object, some of the copy() methods also accept a boolean parameter controlling the depth of the copy. A deep copy copies all of the objects considered to be "contained" within the source object (as deep copies) and creates shallow copies of referenced Topic objects. A shallow copy copies the source object only.

As an example of the difference between a shallow and a deep copy, consider copying a topic with a base name and an occurrence that plays a role in an association and has a single typing topic. Performing a deep copy of the topic will result in a deep copy of the source Topic object, the BaseName and Occurrence objects; a shallow copy of the typing Topic and no copy at all of the Member object connected to the Topic. A shallow copy of a topic results in a Topic object only being created. The created Topic object in a shallow copy has only sufficient information to cause the copy to merge with the source topic - this may be a copy of the subject, one of the subject indicators or one of the BaseNames of the source topic. In the case that none of these are defined for the source topic, the copied topic is created with a single subjectIndicator which uses the resourceLocator of the source topic as its address. All of this is shown diagramatically in Figure 4.1, “Results of Deep and Shallow Copies”

Figure 4.1. Results of Deep and Shallow Copies

Results of Deep and Shallow Copies

As has already been stated, the TopicMapFactory copy() methods result in the copied objects being copied into the TopicMap from which the TopicMapFactory was retrieved. So how do you copy an entire TopicMap ?

The answer is to create the destination TopicMap first and then call the method TopicMapFactory.copy(TopicMap tm). This copy is always a deep copy and will deep-copy all Topics and Association from the source topic map specified by the tm parameter to the destination topic map from which the TopicMapFactory was retrieved.

Example 4.2. Shallow, Deep and TopicMap Copying

The following program shows how to invoke the different forms of topic map object copying

package examples.basic;

import examples.ExampleBase;
import org.tm4j.topicmap.*;
import org.tm4j.net.Locator;

import java.util.Iterator;

public class CopyingExample extends ExampleBase
{
    public static void main(String[] args)
    {
	try
	{
	    CopyingExample theApp = new CopyingExample();
	    theApp.run();
	}
	catch(Exception ex)
	{
	    System.out.println("Error running test program: " + ex.toString());
	    ex.printStackTrace();
	}
    }

    public void run()
	throws Exception
    {
	TopicMap srcTopicMap = createTopicMap(
                   "http://www.tm4j.org/examples/copying/src.xtm");
	System.out.println("Initialising source topic map...");
	initialiseSrc(srcTopicMap);
	printMapInfo(srcTopicMap);

	Topic copyMe = (Topic)srcTopicMap.getObjectByID("copy-me");

	// Shallow copy example
	TopicMap destTopicMap = createTopicMap(
                   "http://www.tm4j.org/examples/copying/shallow.xtm");
	System.out.println("\nPerforming shallow copy of copy-me...");
	destTopicMap.getFactory().copy(copyMe, false);
	printMapInfo(destTopicMap);

	// Deep copy example
	destTopicMap = createTopicMap(
                   "http://www.tm4j.org/examples/copying/deep.xtm");
	System.out.println("\nPerforming deep copy of copy-me...");
	destTopicMap.getFactory().copy(copyMe, true);
	printMapInfo(destTopicMap);

	// Whole topic map copy example
	destTopicMap = createTopicMap(
                   "http://www.tm4j.org/examples/copying/all.xtm");
	System.out.println("\nPerforming complete copy of source topic map...");
	destTopicMap.getFactory().copy(srcTopicMap);
	printMapInfo(destTopicMap);
    }

    private void initialiseSrc(TopicMap tm)
	throws Exception
    {
	// Create the topic which will be copied
	Topic copyMe = tm.createTopic("copy-me");
	BaseName bn = copyMe.createName(null);
	bn.setData("Topic To Copy");

	// A typing topic for copy-me
	Topic type1 = tm.createTopic("type1");
	copyMe.addType(type1);

	// A topic which will be in an association with copy-me
	Topic other = tm.createTopic("other");
	// A typing topic for other
	Topic type2 = tm.createTopic("type2");
	other.addType(type2);
	bn = other.createName(null);
	bn.setData("Other Topic");

	// The association between copy-me and other
	Association assoc = tm.createAssociation(null);
	Member m1 = assoc.createMember(null);
	m1.addPlayer(copyMe);
	Member m2 = assoc.createMember(null);
	m2.addPlayer(other);

	// A subject indicator for copy-me
	Locator copyMeSI = 
            srcTopicMap.getLocatorFactory().createLocator(
              "URI", "http://www.tm4j.org/psi/example/copy-target");
	copyMe.addSubjectIndicator(copyMeSI);
    }

    private void printMapInfo(TopicMap tm)
    {
	System.out.println(
           "TopicMap: " + tm.getBaseLocator().getAddress());
	System.out.println(
           "Topic Count: " + tm.getTopics().size());
	System.out.println(
           "Association Count: " + tm.getAssociations().size());

	Topic copyMe = (Topic)tm.getObjectByID("copy-me");
	System.out.println("Topic 'copy-me':");
	System.out.println("  Type Count: " + copyMe.getTypes().size());
	System.out.println(
           "  Roles Played Count: " + copyMe.getRolesPlayed().size());
	System.out.println("  Names Count: " + copyMe.getNames().size());
	Iterator it = copyMe.getSubjectIndicators().iterator();
	while (it.hasNext())
	{
	    Locator si = (Locator)it.next();
	    System.out.println("  Subject Indicator: " + si.getAddress());
	}
    }
}

Note

Not shown in the example is the base class examples.ExampleBase, which implements the createTopicMap() method.

The output of the application is as follows:

 
Initialising source topic map...
TopicMap: http://www.tm4j.org/examples/copying/src.xtm
Topic Count: 7
Association Count: 3
Topic 'copy-me':
  Type Count: 1
  Roles Played Count: 2
  Names Count: 1
  Subject Indicator: http://www.tm4j.org/psi/example/copy-target

Performing shallow copy of copy-me...
TopicMap: http://www.tm4j.org/examples/copying/shallow.xtm
Topic Count: 1
Association Count: 0
Topic 'copy-me':
  Type Count: 0
  Roles Played Count: 0
  Names Count: 1
  Subject Indicator: http://www.tm4j.org/psi/example/copy-target

Performing deep copy of copy-me...
TopicMap: http://www.tm4j.org/examples/copying/deep.xtm
Topic Count: 5
Association Count: 1
Topic 'copy-me':
  Type Count: 1
  Roles Played Count: 1
  Names Count: 1
  Subject Indicator: http://www.tm4j.org/psi/example/copy-target

Performing complete copy of source topic map...
TopicMap: http://www.tm4j.org/examples/copying/all.xtm
Topic Count: 7
Association Count: 3
Topic 'copy-me':
  Type Count: 2
  Roles Played Count: 2
  Names Count: 1
  Subject Indicator: http://www.tm4j.org/psi/example/copy-target

      

Understanding why this output is produced will help you understand the different forms of copying.

In the first instance, the source topic map is created with two topics, both of which are typed (each typed by a one topic) and an association (untyped) between the two topics. In the source topic map, this results in a total of 7 topics (the two topics, the two typing topics plus 3 topics created automatically to type the XTM class-instance association), and 3 associations (the association we explicitly created plus two XTM class-instance associations between the typed topics and the types).

When one topic is shallow-copied, the resulting topic map contains just the copied topic. The topic's name and subject indicator is copied with it, but its type is not copied. So the copied topic loses some information. However, because the subject indicator and base name have been copied, under the rules of XTM, if the source topic map and this copy were merged, the copied topic would be merged into the source topic.

When the same topic is deep-copied, the resulting topic map contains the copied topic and a shallow copy of the typing topic. This results in a topic count of 5 (the copied topic, the shallow-copied typing topic and the 3 topics used to define the XTM class-instance association) and an association count of 1 (the XTM class-instance association).

Finally, when the entire topic map is copied, the resulting topic map contains (as you would expect) exactly the same number of topics and associations as the source topic map.