Author: Alexander Zangerl <az@debian.org>
Subject: support for diff()ing frozen maps across the 1.2x->1.3x ABI/API changes

--- a/lib/TM.pm
+++ b/lib/TM.pm
@@ -796,10 +796,16 @@ For associations, the assertions are att
 =item I<identities>
 
 This hash consists of the non-trivial toplet identities that were found. If neither Subject- nor
-Indicator-based merging is active, then this hash is empty. Otherwise, the keys are toplet
-identifiers in the old map, with the corresponding topic identifier in the new map as value. This 
-includes standalone topics as well as assertions and associations that were renamed due to 
-changed player or role identities.
+Indicator-based merging is active and if neither map object was created with a TM version before 1.31, 
+then this hash is empty. Otherwise, the keys are toplet identifiers in the old map, with the 
+corresponding topic identifier in the new map as value. This includes standalone topics as well 
+as assertions and associations that were renamed due to changed player or role identities.
+
+For diff operations between maps where one map was created with a TM version before 1.31 (which can happen 
+with frozen/thawed or MLDBM-based maps) extra identifying steps are performed (because the identifier 
+format for assertions and infrastructure toplets and the stored format of toplets have changed). This situation 
+is detected automatically, and if so the identities hash will also include all map elements that were identical but 
+have different names due to the version incompatibility.
 
 =item I<modified>
 
@@ -846,10 +852,23 @@ sub diff {
 	my $key   = ($map eq $oldmap ? "old":"new");
 	my $value = ($map eq $oldmap ? 1:2);
 
-	for my $m (map { $_->[TM->LID] } ($map->toplets(\ '+all'))) {
+	# if either is older than tm 1.31, then we need to deal with base-prefixed toplets (called midlets then)
+	# which are no longer prefixed (for all infrastructure topics)
+	$xlatneeded||=1
+	    if (exists($map->{usual_suspects})); # this key is no longer present in newer maps
+
+	for my $m (keys %{$map->{mid2iid}})
+	{
 	    # get the topic-aspects (tid, locators and identifiers)
 	    # for finding unchanged/new/old topics
 	    my $midlet=$map->toplet($m);
+
+	    # fudging time is here :-(
+	    # if this is an old map with address and indicators but no lid, we frob one in.
+	    if (@{$midlet}==2)
+	    {
+		$midlet=[$m,@{$midlet}];
+	    }
 	    $locators{$key}->{$midlet->[TM->ADDRESS]}=$m
 		if ($midlet->[TM->ADDRESS]);
 	    map { $indicators{$key}->{$_}=$m } (@{$midlet->[TM->INDICATORS]});
@@ -864,6 +883,27 @@ sub diff {
     # first identity: same topic ids 
     my %old2new = map { ($_,$_) } grep { $seen{$_} == 3 } keys %seen;
     my $foundxlat;
+    # almost-first identity: infrastructure topics which were base-prefixed but are no longer
+    if (exists($oldmap->{usual_suspects}) ^ exists($newmap->{usual_suspects}))
+    {
+	for my $short (grep { $seen{$_} != 3 && $_!~/^$base/ } keys %seen)
+	{
+	    my $long=$base.$short;
+	    if ($infrastructure->{mid2iid}->{$short} && $seen{$long} == 3-$seen{$short})
+	    {
+		if ($seen{$long} == 2) # saw long in new map
+		{
+		    $old2new{$short}=$long;
+		}
+		else
+		{
+		    $old2new{$long}=$short;
+		}
+		$foundxlat||=1;
+	    }
+	}
+    }		
+
     if (grep($_==TM->Subject_based_Merging,@{$options->{consistency}}))
     {
 	# second: same locators
@@ -914,10 +954,9 @@ sub diff {
     # weed out the topics/midlets that are unchanged
     # and all the identical assertions
     my @checkassertion;
-    for my $t (keys %checkmidlet) {
-
-	if ($t =~ /^[A-F0-9]{32}$/i) {
-	    my $oa=$oldmap->retrieve($t);
+    for my $t (keys %checkmidlet) 
+    {
+	if (my $oa=$oldmap->retrieve($t)) { 
 	    my $on=$newmap->retrieve($old2new{$t});
 	    
 	    if ($oa && $on && $oa->[TM->LID] ne $on->[TM->LID]) {
@@ -938,6 +977,9 @@ sub diff {
 	    {
 		my ($a,$b)=@_;
 		
+		$a=["",@{$a}] if (@{$a}==2); # fudge in blank LID for old maps
+		$b=["",@{$b}] if (@{$b}==2); # fudge in blank LID for old maps
+		
 		my ($A, $B) = ($a->[TM->ADDRESS] ||'', $b->[TM->ADDRESS] ||'');       # just convert undef into ''
 		return 0 unless $A eq $B;                                             # different subject address?
 		my %SIDS;
@@ -945,11 +987,9 @@ sub diff {
 		return 0 if grep { $_ != 2 } values %SIDS;                            # if it is not exactly 2 (one from a, one from b), then not equal
 		return 1; # we're happy: different LIDs don't interest us here
 	    }
-	    
 	}
     }
 
-#warn "modified ".Dumper \%modified;
 
     my %old2newid;    
     my %identities; 
@@ -959,6 +999,10 @@ sub diff {
 	# into new namespace and compute the id
 	# don't waste time: do this only on the assertions that may be required
 	# minusass (or plusass) must be checked to find assertions with renamed-but-identical players
+	
+	# once more fudging time: if the "new" map object uses the long lid-format (ie. made with older api),
+	# we need to append the base ourselves because mklabel does not do that anymore.
+	my $maybebase=(($newmap->asserts)[0]->[TM->LID]=~/^$base/ )?$base:"";
 	for my $t (@checkassertion,keys %minusass)
 	{
 	    my $m=$oldmap->retrieve($t);
@@ -977,7 +1021,7 @@ sub diff {
 				 type=>$type,
 				 roles=>\@newroles,players=>\@newplayers);
 	    $newmap->canonicalize($n);
-	    my $newid=TM::mklabel($n);
+	    my $newid=$maybebase.TM::mklabel($n); 
 	    $old2newid{$t}=$newid;
 
 	    if ($plusass{$newid}) # we found a matching assertion, wohee!
@@ -1006,6 +1050,9 @@ sub diff {
 	{
 	    $unmodified=\%minus; $map=$oldmap; $candidates=\%minusass;
 	}
+
+	# working with potentially old maps we may need to base-prefix or not...
+	my $maybebase=(($map->asserts)[0]->[TM->LID]=~/^$base/ )?$base:"";
 	
 	for my $t (keys %{$candidates})
 	{
@@ -1014,13 +1061,13 @@ sub diff {
 	    if ($m->[TM->KIND] ne TM->ASSOC)
 	    {
 		# bn or oc: attach to referenced topic
-		$who=($map->get_x_players($m,"thing"))[0];
+		$who=($map->get_x_players($m,$maybebase."thing"))[0];
 		$what=$t;
 	    }
-	    elsif ($m->[TM->TYPE] eq "isa")
+	    elsif ($m->[TM->TYPE] eq $maybebase."isa")
 	    {
 		# isa associations get attached to the instance topic
-		$who=($map->get_x_players($m,"instance"))[0];
+		$who=($map->get_x_players($m,$maybebase."instance"))[0];
 		$what=$t;
 	    }
 	    else 
