Index: fedd/abac-src/atnvis/src/com/algomagic/atn/PropertyLogParser.java
===================================================================
--- fedd/abac-src/atnvis/src/com/algomagic/atn/PropertyLogParser.java	(revision 8780cbecc6db2bdc2b9ef31eec6e5209ad3bff15)
+++ fedd/abac-src/atnvis/src/com/algomagic/atn/PropertyLogParser.java	(revision 8780cbecc6db2bdc2b9ef31eec6e5209ad3bff15)
@@ -0,0 +1,376 @@
+package com.algomagic.atn;
+
+import java.io.*;
+import java.util.*;
+
+public class PropertyLogParser 
+    implements LogParser, VisualizationConstants 
+{
+
+    private Properties _log;
+    private List _updates;
+    private Visualization _vis;
+
+
+    public PropertyLogParser( ) {
+
+    }
+
+    public List parse( Visualization vis, String log1, String log2 ) {
+	_vis = vis;
+	List updates = null;
+	List updates1 = parse( log1, LEFT_SIDE );
+	List updates2 = null;
+
+	if( log2 != null ) {
+	    updates2 = parse( log2, RIGHT_SIDE );
+	    return interleave( updates1, updates2 );
+	} 
+
+	return updates1;
+    }
+
+
+    private List interleave( List updates1, List updates2 ) {
+	List updates = new ArrayList( );
+	Iterator first, second;
+
+
+	ClusterUpdate u = (ClusterUpdate) updates1.get( 0 );
+	if( u.getLocal( ) ) {
+	    first = updates1.iterator( );
+	    second = updates2.iterator( );
+	} else {
+	    first = updates2.iterator( );
+	    second = updates1.iterator( );
+	}
+
+
+	Object o;
+	ClusterUpdate c;
+
+	while( first.hasNext( ) ) {
+	    o = first.next( );
+
+	    updates.add( o );
+
+	    if( o instanceof ClusterUpdate ) {
+		c = (ClusterUpdate)o;
+		if( ! c.getStart( ) ) {
+		    break;
+		}
+
+	    }
+	}
+
+	Iterator tmp = first;
+	first = second;
+	second = tmp;
+
+
+	while( first.hasNext( ) && second.hasNext( ) ) {
+
+	    while( first.hasNext( ) ) {
+		o = first.next( );
+
+		updates.add( o );
+
+		if( o instanceof ClusterUpdate ) {
+		    c = (ClusterUpdate)o;
+		    if( ! c.getStart( ) && c.getLocal( ) ) {
+			break;
+		    }
+
+		}
+	    }
+
+	    while( second.hasNext( ) ) {
+		o = second.next( );
+
+		updates.add( o );
+
+		if( o instanceof ClusterUpdate ) {
+		    c = (ClusterUpdate)o;
+		    if( ! c.getStart( ) && c.getLocal( ) ) {
+			break;
+		    }
+
+		}
+	    }
+
+	}
+
+	if( first.hasNext( ) ) {
+	    while( first.hasNext( ) ) {
+		updates.add( first.next( ) );
+	    }
+	} else if( second.hasNext( ) ) {
+	    while( second.hasNext( ) ) {
+		updates.add( second.next( ) );
+	    }
+	}
+
+	for( int i = 0; i<(updates.size( )-1); i++ ) {
+	    Update u1 = (Update)updates.get( i );
+	    Update u2 = (Update)updates.get( i+1 );
+
+	    if( u1 instanceof ClusterUpdate && u2 instanceof ClusterUpdate ) {
+		ClusterUpdate c1 = (ClusterUpdate)u1;
+		ClusterUpdate c2 = (ClusterUpdate)u2;
+
+		if( (!c1.getStart( ) && c1.getLocal( )) && (c2.getStart( ) && !c2.getLocal( )) ) {
+		    updates.set( i, c2 );
+		    updates.set( i+1, c1 );
+		}
+	    }
+	}
+
+
+
+	return updates;
+    }
+
+
+    private List parse( String log, int side ) {
+	_log = new Properties( );
+	_updates = new ArrayList( );
+	
+	File f = new File( log );
+	if( f.exists( ) ) {
+	    try {
+		_log.load( new FileInputStream( f ) );
+	    } catch( Exception e ) { }
+	}
+	
+
+	
+	_vis.setName( _log.getProperty( "subject" ), side );
+
+	Collection c = new ArrayList( );
+	for( int i=1; _log.containsKey( "policy." + i ); i++ ) {
+	    c.add( _log.getProperty( "policy." + i ) );
+	}
+
+	_vis.setPolicies( c, side );
+
+
+	boolean init = false;
+
+
+	for( int i=1; _log.containsKey( getKey(i) ) || i==1; i++ ) {
+	    String location = _log.getProperty( getKey(i) );
+	    boolean local = true;
+	    int index = i;
+
+
+
+	    if( !_log.containsKey( getKey( i, 1 ) ) ) {
+		continue;
+	    }
+
+
+	    if( location.equals( "remote" ) ) {
+		local = false;
+		
+		if( !init ) {
+
+		    _updates.add( new ClusterUpdate( i,
+						     true,
+						     local,
+						     side ) );
+		}
+	    } else if( location.equals( "local" ) ) {
+		local = true;
+
+		if( !init ) {
+		    _updates.add( new ClusterUpdate( i,
+						     true,
+						     local,
+						     side ) );
+		}
+	    } else if( location.equals( "init" ) ) {
+		if( !_log.containsKey( getKey( 1, 1 ) ) ) {
+		    continue;
+		} else {
+		    init = true;
+		    local = true;
+		    index = 2;
+ 		    _updates.add( new ClusterUpdate( index,
+						     true,
+						     local,
+						     side ) );
+		}
+	    }
+
+
+	    for( int j=1; _log.containsKey( getKey(i,j) ); j++ ) {
+		String type = _log.getProperty( getKey(i,j) );
+		
+		if( type.equals( "edge" ) ) {
+		    parseEdge( i, j, local, side, index );
+		} else if( type.equals( "node" ) ) {
+		    parseNode( i, j, local, side, index );
+		}
+	    }
+
+
+	    if( index==i) {
+
+		if( location.equals( "remote" ) ) {
+		    _updates.add( new ClusterUpdate( i,
+						     false,
+						     local,
+						     side ) );
+		} else {
+		    _updates.add( new ClusterUpdate( i,
+						     false,
+						     local,
+						     side ) );
+		}
+		
+		
+	    }
+
+
+	    init = false;
+
+	}
+
+
+	return _updates;
+    }
+
+    private void parseNode( int i, int j, boolean local, int side, int c ) {
+	String prefix = getPrefix(i,j);
+	String name, type, cluster, satisfied, processed;
+	name = _log.getProperty( prefix + "name" );
+	if( name == null ) {
+	    throw new RuntimeException( prefix );
+	}
+	
+	type = _log.getProperty( prefix + "nodeType" );
+	cluster = getCluster(c);
+	satisfied = _log.getProperty( prefix + "satisfied" );
+	processed = _log.getProperty( prefix + "processing" );
+
+// 	if( processed == null ) {
+// 	    processed = NOT_PROC;
+// 	} else if( processed.equals( "verifier" ) ) {
+// 	    processed = VERIFIER_PROC;
+// 	} else if( processed.equals( "opponent" ) ) {
+// 	    processed = OPPONENT_PROC;
+// 	}
+
+// 	if( satisfied == null ) {
+//   	    satisfied = UNKNOWN;
+// 	}
+
+
+	if( processed == null ) {
+	    processed = NOT_PROC;
+	} else if( processed.toLowerCase( ).equals( "verifier" ) ) {
+	    processed = VERIFIER_PROC;
+	} else if( processed.toLowerCase( ).equals( "opponent" ) ) {
+	    processed = OPPONENT_PROC;
+	} else if ( processed.toLowerCase( ).equals( "fully" ) ) {
+	    processed = FULLY_PROC;
+	}
+
+	if( satisfied == null ) {
+	    satisfied = UNKNOWN;
+	} else if( satisfied.toLowerCase( ).equals( "satisfied" ) ) {
+	    satisfied = SATISFIED;
+	} else if( satisfied.toLowerCase( ).equals( "unsatisfied" ) ) {
+	    satisfied = NOT_SATISFIED;
+	} else if( satisfied.toLowerCase( ).equals( "unknown" ) ) {
+	    satisfied = UNKNOWN;
+	}
+
+	NodeUpdate nu = new NodeUpdate( name, type, cluster, satisfied, processed, side, local );
+	_updates.add( nu );
+    }
+
+
+    private void parseEdge( int i, int j, boolean local, int side, int c ) {
+	String prefix = getPrefix(i,j);
+	String type, parentName, childName, childType, processed, satisfied;
+
+	type = _log.getProperty( prefix + "edgeType" );
+	parentName = _log.getProperty( prefix + "parentName" );
+	childName = _log.getProperty( prefix + "childName" );
+	childType = _log.getProperty( prefix + "childType" );
+	processed = _log.getProperty( prefix + "childProcessing" );
+	satisfied = _log.getProperty( prefix + "childSatisfied" );
+
+	if( type == null || parentName == null || childName == null ) {
+	    throw new RuntimeException( prefix );
+	}
+
+
+	if( childType == null ) {
+	    childType = "StandardTarget";
+	}
+
+
+	if( processed == null ) {
+	    processed = NOT_PROC;
+	} else if( processed.toLowerCase( ).equals( "verifier" ) ) {
+	    processed = VERIFIER_PROC;
+	} else if( processed.toLowerCase( ).equals( "opponent" ) ) {
+	    processed = OPPONENT_PROC;
+	} else if ( processed.toLowerCase( ).equals( "fully" ) ) {
+	    processed = FULLY_PROC;
+	}
+
+	if( satisfied == null ) {
+	    satisfied = UNKNOWN;
+	} else if( satisfied.toLowerCase( ).equals( "satisfied" ) ) {
+	    satisfied = SATISFIED;
+	} else if( satisfied.toLowerCase( ).equals( "failed" ) ) {
+	    satisfied = NOT_SATISFIED;
+	} else if( satisfied.toLowerCase( ).equals( "unknown" ) ) {
+	    satisfied = UNKNOWN;
+	}
+
+
+	if( childType != null && childType.toLowerCase( ).equals( "trivialtarget" ) ) {
+	    satisfied = SATISFIED;
+	}
+	
+
+	NodeUpdate nu = new NodeUpdate( childName, childType, getCluster(c), satisfied, processed, side, local );
+	_updates.add( new EdgeUpdate( parentName, nu, type, local, collectEvidence(i,j), side ) );
+    }
+
+
+    private Collection collectEvidence( int i, int j ) {
+	Collection c = new ArrayList( );
+
+	String key;
+	for( int k=1; _log.containsKey( key = getPrefix(i,j) + "evidence." + Integer.toString(k) ); k++ ) {
+	    c.add( _log.getProperty( key ) );
+	}
+
+	return c;
+    }
+
+
+
+    private String getKey( int i ) {
+	return "update." + Integer.toString( i ) + ".location";
+    }
+
+    private String getKey( int i, int j ) {
+	return getPrefix( i, j ) + "type";
+    }
+
+    private String getPrefix( int i, int j ) {
+	return "update." + Integer.toString( i ) + "." + Integer.toString( j ) + ".";
+    }
+
+    private String getCluster( int i ) {
+	return "cluster" + Integer.toString( i );
+    }
+
+
+}
