Index: /pom.xml
===================================================================
--- /pom.xml	(revision 8)
+++ /pom.xml	(revision 9)
@@ -6,5 +6,5 @@
 	<artifactId>runserver</artifactId>
 	<packaging>war</packaging>
-	<version>1.1.0</version>
+	<version>1.2.0</version>
 	<name>runserver Maven Webapp</name>
 	<url>http://maven.apache.org</url>
@@ -53,5 +53,5 @@
 			<groupId>geniusweb</groupId>
 			<artifactId>profile</artifactId>
-			<version>1.1.0</version>
+			<version>1.2.0</version>
 		</dependency>
 
@@ -59,5 +59,5 @@
 			<groupId>geniusweb</groupId>
 			<artifactId>party</artifactId>
-			<version>1.1.0</version>
+			<version>1.2.0</version>
 		</dependency>
 
@@ -65,5 +65,5 @@
 			<groupId>geniusweb</groupId>
 			<artifactId>references</artifactId>
-			<version>1.1.0</version>
+			<version>1.2.0</version>
 		</dependency>
 
@@ -71,5 +71,5 @@
 			<groupId>geniusweb</groupId>
 			<artifactId>protocol</artifactId>
-			<version>1.1.0</version>
+			<version>1.2.0</version>
 		</dependency>
 
Index: /src/main/webapp/newsession.xhtml
===================================================================
--- /src/main/webapp/newsession.xhtml	(revision 8)
+++ /src/main/webapp/newsession.xhtml	(revision 9)
@@ -29,5 +29,5 @@
 	<br /> Domain/Profile Server:
 	<input type="url" name="url" id="profilesserverurl"
-		value="localhost:8080/profilesserver-1.1.0"
+		value="localhost:8080/profilesserver-1.2.0"
 		pattern=".*:[0-9]+/profilesserver" size="30"
 		onchange="connectDomain()"> </input>
@@ -43,5 +43,5 @@
 		<br /> <b>Participants</b> <br /> Parties Server: <input type="url"
 			name="url" id="partiesserverurl"
-			value="localhost:8080/partiesserver-1.1.0"
+			value="localhost:8080/partiesserver-1.2.0"
 			pattern=".*:[0-9]+/partiesserver" size="30"
 			onchange="connectParties()"> </input> <br /> <br /> <b>Party
@@ -273,8 +273,8 @@
 	 refresh table: copy all parties elements in there.
 	 Typically parties is something like
-	 [{"uri":"http:130.161.180.1:8080/partiesserver/run/randomparty-1.1.0",
+	 [{"uri":"http:130.161.180.1:8080/partiesserver/run/randomparty-1.2.0",
 		 "capabilities":{"protocols":["SAOP"]},
 		 "description":"places random bids until it can accept an offer with utility >0.6",
-		 "id":"randomparty-1.1.0",
+		 "id":"randomparty-1.2.0",
 		 "partyClass":"geniusweb.exampleparties.randomparty.RandomParty"},
 		 		 ...]
@@ -571,6 +571,6 @@
 	function init() {
 		selectProtocol();
-		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.1.0"
-		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.1.0"
+		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.2.0"
+		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.2.0"
 		connectDomain();
 		connectParties();
Index: /src/main/webapp/newtournament.xhtml
===================================================================
--- /src/main/webapp/newtournament.xhtml	(revision 8)
+++ /src/main/webapp/newtournament.xhtml	(revision 9)
@@ -47,5 +47,5 @@
 	<div id="box" class="box">
 		<br /> Domain/Profile Server: <input type="url" name="url"
-			id="profilesserverurl" value="localhost:8080/profilesserver-1.1.0"
+			id="profilesserverurl" value="localhost:8080/profilesserver-1.2.0"
 			pattern=".*:[0-9]+/profilesserver" size="30"
 			onchange="connectDomain()"> </input> <br /> Domain: <select
@@ -76,5 +76,5 @@
 	<div id="box" class="box">
 		<br /> Parties Server: <input type="url" name="url"
-			id="partiesserverurl" value="localhost:8080/partiesserver-1.1.0"
+			id="partiesserverurl" value="localhost:8080/partiesserver-1.2.0"
 			pattern=".*:[0-9]+/partiesserver" size="30"
 			onchange="connectParties()"> </input> <br /> <br />
@@ -106,5 +106,6 @@
 	<div id="started" style="visibility: hidden">
 		Your tournament started. Click <a href="" id="logref">here</a> to view
-		the log file.
+		the log file. <br />
+		<a href="" id="plotref">show results table.</a>.
 	</div>
 
@@ -385,4 +386,5 @@
 	        		document.getElementById("started").setAttribute("style","");
     	    		document.getElementById("logref").href="log/"+this.responseText+".json";
+	        		document.getElementById("plotref").href="utilstable.xhtml?"+this.responseText;
 	        	} else 
 	        		alert("request failed:"+this.statusText);
@@ -421,6 +423,6 @@
 	*/
 	function init() {
-		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.1.0";
-		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.1.0";
+		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.2.0";
+		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.2.0";
 
 
Index: /src/main/webapp/plotlog.xhtml
===================================================================
--- /src/main/webapp/plotlog.xhtml	(revision 8)
+++ /src/main/webapp/plotlog.xhtml	(revision 9)
@@ -35,4 +35,5 @@
 	
 	
+	
 
 	
@@ -76,9 +77,11 @@
         */
         function processLogFile(json) {
-        	if (json['SAOPState']==undefined) {
+        	if (json['SAOPState']!=undefined) {
+        		processSAOP(json['SAOPState']);
+        	} else if (json['SHAOPState']!=undefined) {
+        		processSHAOP(json['SHAOPState']);
+        	} else {
             	setStatus("Unknown log file contents "+Object.keys(json));
-            	return;
-        	}
-    		processSAOP(json['SAOPState']);
+            }
         }
         
@@ -95,4 +98,23 @@
         	getProfiles(json, partyprofiles);
         }
+        
+        /**
+        Handle SHAOP protocol result. Get only SHAOP profiles. Remove all 'partial=XX' from relevant profiles.
+        */
+        function processSHAOP(json) {
+        	var partyprofiles={}
+        	var teams=json['settings']['SHAOPSettings']['participants'];
+        	for (var partyid in json['partyNumbers']) {
+        		var nr= json['partyNumbers'][partyid];
+        		if ( (nr & 1 != 0)) continue; // skip COB parties
+        		var shaop=teams[nr/2]['shaop'];
+        		var profile = shaop['profile'].split('?',1)[0]; // hacky, remove complete query.
+        		partyprofiles[partyid]=profile;
+        	}
+			
+        	
+        	getProfiles(json, partyprofiles);
+        }
+
         
         /**
@@ -334,4 +356,5 @@
 
 
+
 </script>
 
@@ -339,4 +362,5 @@
 
 <script type="application/javascript">
+	
 	
 	
@@ -368,4 +392,5 @@
 
 
+
 </script>
 
Index: /src/main/webapp/utilstable.xhtml
===================================================================
--- /src/main/webapp/utilstable.xhtml	(revision 9)
+++ /src/main/webapp/utilstable.xhtml	(revision 9)
@@ -0,0 +1,420 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+<title>Show tournament results table</title>
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script src="Chart.min_2.8.0.js"></script>
+</head>
+<style>
+canvas {
+	-moz-user-select: none;
+	-webkit-user-select: none;
+	-ms-user-select: none;
+}
+</style>
+<body>
+	<div class="noscript">
+		<h2 style="color: #ff0000">Seems your browser doesn't support
+			Javascript! Websockets rely on Javascript being enabled. Please
+			enable Javascript and reload this page!</h2>
+	</div>
+	<h1 id="header">Tournament results</h1>
+
+	Progress:
+	<div id="progress">Waiting for log file name</div>
+	<br />
+
+	<table id="outcomes">
+		<thead>
+			<tr>
+				<th align="center">run nr</th>
+				<th align="center">accepted bid</th>
+				<th align="center">party utilities</th>
+			</tr>
+		</thead>
+		<tbody id="outcomeslist">
+			<!--  one row for each session run added by script -->
+		</tbody>
+
+	</table>
+
+
+</body>
+
+<!-- Script to get/update the  table using the query given in the URL. -->
+<script type="application/javascript">
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+
+	
+	
+	<![CDATA[
+        "use strict";
+
+
+        
+        var ws = null;
+        var SHAOP=false;
+        /* dict with key=profile URL and value = the downloaded (json) profile */
+        var profiles = {};
+
+        <!-- Most functions are reactive programming written out-->
+
+        /**
+        Called when the page opens.
+        */
+        function connect() {
+        	
+        	var id=document.location.search;
+            id = location.search.split('?')[1]
+            if (id==undefined) return;
+            
+            // load the log file with given id
+            document.getElementById('header').innerHTML="Tournament results table of "+id;
+
+            var url = "log/"+id+".json";
+        	setStatus("Downloading file "+url);
+            var request=new XMLHttpRequest();
+            request.responseType = 'text';
+            request.open('Get', url)
+            request.onload=function() {
+            	if (request.status!=200) {
+            		setStatus("Failed to fetch "+url+":" +request.status+" "+request.statusText)
+            		return;
+            	}
+				processLogFile(JSON.parse(request.response));
+			}
+            request.send();
+ 		}
+        
+        
+        /**
+        Figure out the protocol contents and parse accordingly.
+        */
+        function processLogFile(json) {
+        	if (json['AllPermutationsState']!=undefined) {
+        		processAPP(json['AllPermutationsState']);
+        	} else {
+            	setStatus("Unknown log file contents "+Object.keys(json));
+            }
+        }
+        
+        /**
+        Handle AllPermutationsState protocol result.
+        */
+        function processAPP(json) {
+        	setStatus("processing the log file");
+        	
+        	if (json['toursettings']['AllPermutationsSettings']['sessionsettings']['SHAOPSettings']!=undefined) {
+        		SHAOP=true;
+        	}
+        	
+        	getAllProfiles(json, json['toursettings']['AllPermutationsSettings']['profiles']);
+        }
+        
+        /**
+         Step 1, get all profiles. Repeats recursiuvely till profileurls is empty.
+         Note just calling the function again without recurse would be better.
+         Problem is that we call this after we got the profile to ensure we 
+         have all profiles at the end.
+         @param profileurls the list of profile urls that need to be fetched.
+         @param profiles a dictionary of profiles . Key is profileurl, value the profile. Should
+         be initially {}
+         returns only after all profiles have been fetched.
+        */
+        function getAllProfiles(json, profileurls) {
+        	 for (var n in profileurls) {
+        		 getProfile(profileurls[n], function() {fillRows(json)} );
+        	 }
+        }
+         
+        /**
+        This function returns immediately and will add the profile to the profiles dictionary some time later.
+        @param callwhendone function is called (without arguments) when profiles contains no pending anymore
+        */
+        function getProfile(profileurl, callwhendone) {
+        	profiles[profileurl]='pending';
+        	
+			var profileurl1 = profileurl; // idem but possibly without query
+    		if (SHAOP) {
+    			profileurl1 = profileurl1.split('?',1)[0]; // hacky, remove complete query.
+    		}
+
+			setStatus("fetching profile "+profileurl1);
+			var ws;
+            if ('WebSocket' in window) {
+                ws = new WebSocket(profileurl1);
+            } else if ('MozWebSocket' in window) {
+                ws = new MozWebSocket(profileurl1);
+            } else {
+                setStatus('Fatal: WebSocket is not supported by this browser. Please use a newer browser');
+                return;
+            }
+            ws.onmessage = function (event) {
+                ws.close();
+            	var profile = JSON.parse(event.data);
+            	if (profile['LinearAdditiveUtilitySpace']==undefined) {
+                    setStatus('Fatal: profile '+uri+" does not contain a LinearAdditiveUtilitySpace.");
+                    return;
+            	}
+
+            	profiles[profileurl]=profile['LinearAdditiveUtilitySpace'];
+            	
+            	checkAllProfiles(callwhendone);
+            };
+            ws.onerror=function(event) {
+                ws.close();
+                setStatus('Error fetching profile '+uri+':'+event);
+            } 
+        }
+        
+        /**
+        Check if there are still pending profiles. If not, callwhendone().
+        */
+        function checkAllProfiles(callwhendone) {
+        	for (var key in profiles) {
+        		if (profiles[key] == 'pending') return;
+        	}
+        	callwhendone();
+        }
+        
+        /**
+        Step 2, fill the table with util values
+        @param profiles a dictionary of profiles . Key is profileurl, value the profile. Should
+        be initially {}
+       */
+        function fillRows(json)
+        {
+    	   	setStatus("Computing utilities");
+	       	var table = document.getElementById("outcomeslist");
+	    	table.innerHTML="";
+	    	var results = json['results'];
+	    	
+	    	for (var nr in results) {
+	    		var result = results[nr];
+	        	var row = table.insertRow(-1); //-1 = end
+	        	fillRow(row, nr, results[nr]);
+	    	}
+    	   	setStatus("done");
+        }
+
+        	
+        
+
+        /**
+        @param row  the table row object in the document
+        @param nr the row number
+        @param result a single json result from the APP results section. 
+        Contains participants, agreement and error
+        */
+        function fillRow(row, nr, result) {
+        	row.insertCell(0).innerHTML = nr;
+        	
+        	if (result['error']!=null) {
+        		row.insertCell(-1).innerHTML="session failed with error";
+        		return;
+        	}
+
+        	var agreedbid = result['agreement'];
+        	row.insertCell(-1).innerHTML = (agreedbid==null?"none":JSON.stringify(agreedbid['issuevalues']));
+        	if (agreedbid==null) agreedbid = {};
+        	else agreedbid = agreedbid['issuevalues'];
+        	
+        	// fill in the columns. If SHAOP, only the even parties 
+        	var stepsize = SHAOP? 2:1;
+        	for (var n=0; n<result['participants'].length; n=n+stepsize) {
+        		var party =  result['participants'][n]
+        		var profile =party['profile'];
+        		// make short name for readability
+        		var partyname = party['party']['partyref'];
+        		partyname = partyname.split('/');
+        		partyname = partyname[partyname.length-1];
+        		addUtilityCell(row.insertCell(-1), agreedbid, partyname, profile);
+        	}
+        }
+
+        /**
+        @param cell the table cell to put the result in
+        @param agreedbid the bid that was agreed on 
+        @param partyname the short name of the party
+        @param profileurl the profile url to use for the evaluation of the bid
+        @param bid the accepted bid, not null.
+        */
+        function addUtilityCell(cell, agreedbid, partyname, profileurl) {
+        	if (Object.keys(agreedbid).lengt==0) {
+            	cell.innerHTML = "0 :"+partyname;
+            	return;
+        	}
+        	var util = utility(profiles[profileurl],agreedbid);
+        	cell.innerHTML = Math.round(util*1000000)/1000000 +" :"+partyname;
+
+        }
+        
+
+
+        
+        /**
+        @json the log file contents
+        @param partyprofiles a dict with key=party id and value a the (linear additive) profile
+        */
+        function computeUtils(json, partyprofiles) {
+        	
+        	setStatus("Computing utilities.")
+        	var utilities={};
+			for (var party in partyprofiles) {
+				utilities[party]=[];
+			}
+        	var actions = json['actions'];
+        	for (var action in actions) {
+        		var offer = actions[action];
+        		if (offer['offer']==undefined)
+        			continue;
+        		var issueutilities = offer['offer']['bid']['issuevalues']
+				for (var party in partyprofiles) {
+					utilities[party].push(utility(partyprofiles[party],issueutilities));
+				}
+        	}
+        	return utilities;
+        	
+        	
+        }
+
+
+        
+        /******************* Private support funcs****************/
+        
+        /**
+        Set the progress/status text
+        */
+        function setStatus(text) {
+            document.getElementById('progress').innerHTML=text;
+        }
+
+        /*
+        @param sessions a list of IDs (Strings).
+        */
+        function update(sessions) {
+			var table = document.getElementById("sessionsTable");
+        	table.innerHTML="";
+        	for(var  session of sessions) {
+        		var row = table.insertRow(-1); //-1 = end
+				row.insertCell(0).innerHTML = session;
+        	}
+        	
+        }
+        
+   
+    
+	 	
+		/**
+	 	Compute utilityes
+	 	@param linadditive the linear additive utility space
+	 	@param issueValues the bid containing dict with values for the issues
+	 	@return utility of issueValues
+	 	*/
+	 	function utility(profile, issueValues) {
+			var util=0;
+			var weights = profile['issueWeights'];
+			
+			for (var issue in issueValues) {
+				util = util + weights[issue] * evaluate(profile['issueUtilities'][issue], issueValues[issue] );
+			}
+			return util;
+		}
+	
+	 	/**
+	 	Bit hacky, javascript version of evaluator of utility space. 
+	 	Would be much nicer if we could use java here
+	 	*/
+	 	function evaluate (utilfunc, value) {
+	 		if (utilfunc == undefined) {
+	 			return 0;
+	 		}
+			if (utilfunc['numberutils']!=undefined) {
+				// it's numeric issue. Compute
+				var minval = utilfunc['numberutils']['lowValue'];
+				var minutil = utilfunc['numberutils']['lowUtility'];
+				var maxval = utilfunc['numberutils']['highValue'];
+				var maxutil = utilfunc['numberutils']['highUtility'];
+				
+				var alfa = (value-minval) / (maxval - minval) ;
+				alfa = Math.max(Math.min(alfa, 1),0);
+				return alfa * maxutil + (1-alfa) * minutil;   
+			}
+			if (utilfunc['discreteutils']!=undefined) {
+				// it's discrete issue. Compute
+				return utilfunc['discreteutils']['valueUtilities'][value];
+			}
+	    	setStatus("Unknown utility function type "+Object.keys(utilfunc));
+	
+	 	}
+ 	
+ 	
+    ]]>
+
+
+
+
+
+</script>
+
+
+
+
+
+<script type="application/javascript">
+	
+	
+	
+	<![CDATA[
+        "use strict";
+        /*
+        This is called when the window DOM is loaded. window.onload runs BEFORE the window is loaded
+        and therefore is too early to remove the noscript message.
+        */
+       document.addEventListener("DOMContentLoaded", function() {
+			// Remove elements with "noscript" class - <noscript> is not allowed in XHTML
+			var noscripts = document.getElementsByClassName("noscript");
+			for (var i = 0; i < noscripts.length; i++) {
+			    noscripts[i].parentNode.removeChild(noscripts[i]);
+			}
+       		connect();
+
+         }, false);
+    ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+</script>
+
+
+</html>
