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 }