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    }