001 package nl.cwi.sen1.visplugin.graphplugin; 002 003 import java.util.HashMap; 004 005 import nl.cwi.sen1.gui.plugin.prefusedot.DotAdapter; 006 import nl.cwi.sen1.relationstores.Factory; 007 import nl.cwi.sen1.relationstores.types.Location; 008 import nl.cwi.sen1.relationstores.types.RElem; 009 import nl.cwi.sen1.relationstores.types.RElemElements; 010 import nl.cwi.sen1.relationstores.types.RTuple; 011 import nl.cwi.sen1.relationstores.types.RType; 012 import prefuse.data.Graph; 013 import prefuse.data.Node; 014 015 public class GraphBuilder { 016 017 /** 018 * Cache/registry for created nodes. 019 */ 020 private HashMap<String, Node> m_nodeCache = new HashMap<String, Node>(); 021 022 /** 023 * Cache and register the Locations for created Nodes 024 * 025 */ 026 private HashMap<String, Location> m_locCache = new HashMap<String, Location>(); 027 028 /** 029 * Factory used to create types. 030 * 031 */ 032 private Factory m_factory; 033 034 /** 035 * Supported graph relation. 036 * 037 * @todo Needs better implementation needs to be resolved reflection, 038 * dynamic dispatch... in a the base or utility class for all plugins, 039 * remark by Anton G. 040 */ 041 private final String m_relationGraph = "relation([str,str])"; 042 043 private final String m_relationGraphTuple = "relation([tuple([str,loc]),tuple([str,loc])])"; 044 045 private final String m_attributedGraphTuple = "relation([tuple([str,relation([str,str])]),str])"; 046 047 /** 048 * @param factory 049 */ 050 public GraphBuilder(Factory factory) { 051 m_factory = factory; 052 } 053 054 /** 055 * Convert RTuple into a Graph Dataset. 056 * 057 * @param fact 058 * RTuple with the data 059 * @return PieChart dataset 060 */ 061 public Graph buildGraphFromRTuple(RTuple fact) { 062 RElem set = fact.getValue(); 063 RElemElements elements = set.getElements(); 064 065 // If the type is supported, create the dataset 066 if (isRelStrStr(fact)) { 067 return convertRelStrStrToDataset(elements); 068 } 069 070 if (isRelTupleTuple(fact)) { 071 return convertRelTupleTupleToDataset(elements); 072 } 073 074 if (isAttributedGraphRelation(fact)) { 075 return convertAttributedGraphDataset(elements); 076 } 077 078 return new Graph(); 079 } 080 081 private Graph convertAttributedGraphDataset(RElemElements elements) { 082 // Setup the new graph. 083 m_nodeCache.clear(); 084 085 DotAdapter graph = new DotAdapter(); 086 087 try { 088 while (elements.hasTail()) { 089 RElem headElement = elements.getHead(); 090 // Replace the current looping set with: ( set - head ). 091 elements = elements.getTail(); 092 093 RElemElements tupleRelation = headElement.getElements(); 094 RElem tuple1 = tupleRelation.getRElemAt(0); 095 096 // Disassemble the tuple into its parts. 097 String from = tuple1.getElements().getRElemAt(0).getStrCon(); 098 Node fromNode = getOrCreateNode(graph, from); 099 100 String to = tupleRelation.getRElemAt(1).getStrCon(); 101 Node toNode = getOrCreateNode(graph, to); 102 103 RElem attributes = tuple1.getElements().getRElemAt(1); 104 if (attributes.isSet()) { 105 RElemElements elems = attributes.getElements(); 106 boolean labelFound = false; 107 108 for (; !elems.isEmpty(); elems = elems.getTail()) { 109 RElem tuple = elems.getHead(); 110 if (!tuple.isTuple() 111 && tuple.getElements().getLength() == 2) { 112 System.err 113 .println("warning: attribute is not a tuple:" 114 + tuple); 115 break; 116 } 117 RElem key = tuple.getElements().getRElemAt(0); 118 RElem value = tuple.getElements().getRElemAt(1); 119 if (value.isStr()) { 120 graph.setNodeAttribute(fromNode, key 121 .getStrCon(), value.getStrCon()); 122 123 if (key.getStrCon() 124 .equals(GraphConstants.LABEL)) { 125 labelFound = true; 126 } 127 } else { 128 System.err 129 .println("warning: attribute value not supported:" 130 + value); 131 } 132 } 133 134 if (!labelFound) { 135 graph.setNodeAttribute(fromNode, GraphConstants.LABEL, 136 from); 137 } 138 } else { 139 System.err.println("warning: ignoring graph attributes:" 140 + attributes); 141 } 142 143 if (toNode != null) { 144 graph.addEdge(fromNode, toNode); 145 } 146 } 147 } catch (UnsupportedOperationException e) { 148 System.err.println("warning: " + e); 149 } 150 151 graph.doDotLayout(); 152 return graph; 153 } 154 155 /** 156 * Convert RTuple relation([tuple([str,loc]),tuple([str,loc])]) into a graph 157 * dataset. and register the Location attributes to allow click through. 158 * 159 * @param elements 160 * RTuple with the data 161 * @return Graph dataset for graph 162 * 163 */ 164 private Graph convertRelTupleTupleToDataset(RElemElements elements) { 165 // Setup the new graph. 166 m_nodeCache.clear(); 167 168 DotAdapter graph = new DotAdapter(); 169 170 try { 171 while (elements.hasTail()) { 172 RElem headElement = elements.getHead(); 173 // Replace the current looping set with: ( set - head ). 174 elements = elements.getTail(); 175 176 // HeadElement itselfs is a tuple <str,int>. 177 RElemElements tupleRelation = headElement.getElements(); 178 RElem tuple1 = tupleRelation.getRElemAt(0); 179 RElem tuple2 = tupleRelation.getRElemAt(1); 180 181 // Disassemble the tuple into its parts. 182 String nameId1 = tuple1.getElements().getRElemAt(0).getStrCon(); 183 Location loc1 = tuple1.getElements().getRElemAt(1) 184 .getLocation(); 185 186 String nameId2 = tuple2.getElements().getRElemAt(0).getStrCon(); 187 Location loc2 = tuple2.getElements().getRElemAt(1) 188 .getLocation(); 189 190 // Get or create nodes. 191 Node nodeId1 = getOrCreateNode(graph, nameId1); 192 Node nodeId2 = getOrCreateNode(graph, nameId2); 193 194 // register the locations in the cache 195 getOrCreateLocation(nameId1, loc1); 196 197 if (nodeId2 != null) { 198 getOrCreateLocation(nameId2, loc2); 199 graph.addEdge(nodeId1, nodeId2); 200 } 201 } 202 } catch (UnsupportedOperationException e) { 203 System.err.println("warning: " + e); 204 } 205 206 graph.doDotLayout(); 207 return graph; 208 } 209 210 /** 211 * Convert RTuple relation([str,str]) into a graph dataset. 212 * 213 * @param fact 214 * RTuple with the data 215 * @return Graph dataset for graph 216 */ 217 private Graph convertRelStrStrToDataset(RElemElements elements) { 218 // Setup the new graph. 219 m_nodeCache.clear(); 220 DotAdapter graph = new DotAdapter(); 221 222 while (elements.hasTail()) { 223 RElem headElement = elements.getHead(); 224 // Replace the current looping set with: ( set - head ). 225 elements = elements.getTail(); 226 227 // HeadElement itselfs is a tuple <str,int>. 228 RElemElements tuple = headElement.getElements(); 229 230 // Disassemble the tuple into its parts. 231 String nameId = tuple.getRElemAt(0).getStrCon(); 232 String nameLabel = tuple.getRElemAt(1).getStrCon(); 233 234 // Get or create nodes. 235 Node nodeId = getOrCreateNode(graph, nameId); 236 Node nodeLabel = getOrCreateNode(graph, nameLabel); 237 238 if (nodeLabel != null) { 239 graph.addEdge(nodeId, nodeLabel); 240 } 241 } 242 243 graph.doDotLayout(); 244 return graph; 245 } 246 247 /** 248 * Create (if it does not exist) or return the node identified by the 249 * nodeName 250 * 251 * @param graph 252 * @param nodeName 253 * @return The node identified by the nodeName or a new node if no node 254 * existed with this name. 255 */ 256 public Node getOrCreateNode(Graph graph, String nodeName) { 257 Node node; 258 259 if (nodeName.length() == 0) { 260 return null; 261 } 262 263 // Check to see if node already exists. 264 if (m_nodeCache.containsKey(nodeName)) { 265 // If the node is already created, return node from cache. 266 node = m_nodeCache.get(nodeName); 267 } else { 268 // Create a new node since it didnt exist. 269 node = graph.addNode(); 270 node.setString(DotAdapter.DOT_ID, nodeName); 271 node.setString(DotAdapter.DOT_LABEL, nodeName); 272 m_nodeCache.put(nodeName, node); 273 } 274 275 return node; 276 } 277 278 /** 279 * Create (if it does not exist) or return the Location identified by the 280 * nodeName. When searching for a location giving a nodeId the nodeLocation 281 * must be zero. Then it returns null when nothing is found. 282 * 283 * @param nodeName 284 * The name of the location we are looking for or creating a 285 * Location for 286 * @return The Location identified by the nodeName 287 * 288 */ 289 public Location getOrCreateLocation(String nodeName, Location nodeLocation) { 290 Location loc; 291 292 // Check to see if node already exists. 293 if (m_locCache.containsKey(nodeName)) { 294 // If the node is already created, return node from cache. 295 loc = m_locCache.get(nodeName); 296 } else if (nodeLocation != null) { 297 // Create a new node since it didnt exist. 298 m_locCache.put(nodeName, nodeLocation); 299 loc = nodeLocation; 300 } else { 301 loc = null; 302 } 303 304 return loc; 305 } 306 307 /** 308 * @param fact 309 * @return true if the RTuple contains a fact of the expected type 310 */ 311 private boolean isAttributedGraphRelation(RTuple fact) { 312 RType rType = m_factory.RTypeFromString(m_attributedGraphTuple); 313 return rType.equals(fact.getRtype()); 314 } 315 316 /** 317 * Check to see if the RTuple is indeed a str,str relation. 318 * 319 * @param fact 320 * RTuple to test. 321 * @return True if it is the correct str,str type. 322 * 323 * @todo Needs better implementation needs to be resolved reflection, 324 * dynamic dispatch... in a the base or utility class for all plugins, 325 * remark by Anton G. 326 */ 327 public boolean isRelStrStr(RTuple fact) { 328 RType rType = m_factory.RTypeFromString(m_relationGraph); 329 return rType.equals(fact.getRtype()); 330 } 331 332 /** 333 * Check to see if the RTuple is indeed a tuple([str,loc]),tuple([str,loc]) 334 * relation. 335 * 336 * @param fact 337 * RTuple to test. 338 * @return True if it is the correct str,str type. 339 */ 340 public boolean isRelTupleTuple(RTuple fact) { 341 RType rType = m_factory.RTypeFromString(m_relationGraphTuple); 342 return rType.equals(fact.getRtype()); 343 } 344 345 }