Annotation of java/classes/org/w3c/rdf/VisRDF.java, revision 1.2
1.1 jsaarela 1: /**
2: * Visualize RDF descriptions and allow user to navigate within the
3: * the RDF data model
4: *
5: * Copyright © World Wide Web Consortium, (Massachusetts Institute of
6: * Technology, Institut National de Recherche en Informatique et en
7: * Automatique, Keio University).
8: *
9: * All Rights Reserved.
10: *
11: * Please see the full Copyright clause at
12: * <http://www.w3.org/Consortium/Legal/copyright-software.html>
13: *
1.2 ! jsaarela 14: * $Log: VisRDF.java,v $
! 15: * Revision 1.1 1999/01/13 15:00:40 jsaarela
! 16: * Finished conformance testing with PR-rdf-syntax-19990105 version
! 17: * of the RDF M&S spec.
! 18: *
1.1 jsaarela 19: *
20: * @author Janne Saarela <jsaarela@w3.org>
1.2 ! jsaarela 21: * @version $Id: VisRDF.java,v 1.1 1999/01/13 15:00:40 jsaarela Exp $
1.1 jsaarela 22: */
23: package org.w3c.rdf;
24:
25: import java.awt.*;
26: import java.util.Vector;
27: import java.util.Hashtable;
28: import java.util.Enumeration;
29: import java.io.*;
30: import java.net.URL;
31:
32: public class VisRDF extends java.applet.Applet {
1.2 ! jsaarela 33: public static final String VERSION = "$Id: VisRDF.java,v 1.1 1999/01/13 15:00:40 jsaarela Exp $";
1.1 jsaarela 34:
35: public boolean bApplication = false;
36:
37: /* storage elements */
38: private Node m_selectedNode = null;
39: private Arc m_selectedArc = null;
40: private Hashtable m_prop = new Hashtable ();
41: private Hashtable m_propobj = new Hashtable ();
42: private Hashtable m_propvalue = new Hashtable ();
43: private Vector m_vNodes = new Vector ();
44: private Vector m_vArcs = new Vector ();
45:
46: /* GUI elements */
47: private Panel m_options = new Panel ();
48: private Panel m_nodes = new Panel ();
49: private Panel m_buttons = new Panel ();
50: private Panel m_checkboxes = new Panel ();
51: private Label m_urlLabel = new Label ("Type in an ID");
52: public TextField m_url = new TextField("", 20);
53: private Button m_start = new Button ("Start query");
54: private Button m_clear = new Button ("Clear canvas");
55:
56: private Checkbox m_genidsCB = new Checkbox ("Show generated IDs");
57: private Checkbox m_shortnamesCB = new Checkbox ("Show long names");
58:
59: private List m_schemas = new List (5, true);
60:
61: String m_input = new String ();
62:
63: public VisRDF () {
64: }
65:
66: public String getAppletInfo() {
67: String info = "This is RDF visualization applet version "+VERSION+
68: "\nby Janne Saarela/W3c <jsaarela@w3.org>. You can use it to\n" +
69: "interactively browse RDF data models with the mouse.";
70: return info;
71: }
72:
73: public void init () {
74: if (!bApplication) {
75: String sTriples = getParameter ("triples");
76: if (sTriples != null && sTriples.length() > 0)
77: parseTriples (new StringBufferInputStream (sTriples));
78: String sStart = getParameter ("start");
79: if (sStart != null && sStart.length() > 0)
80: m_url.setText (sStart);
81: }
82:
83: setLayout (new BorderLayout());
84:
85: m_nodes.setLayout (null);
86: m_nodes.setBackground (Color.white);
87:
88: m_buttons.setLayout (new GridLayout (1, 3));
89: m_checkboxes.setLayout (new GridLayout (2, 1));
90:
91: add("West", m_options);
92: add("Center", m_nodes);
93: add("South", m_buttons);
94:
95: m_options.setLayout (new GridLayout (4,1));
96:
97: m_options.add (m_urlLabel);
98: m_options.add (m_url);
99: m_options.add (new Label ("Select visualized schemas"));
100: m_options.add (m_schemas);
101:
102: m_buttons.add (m_start);
103: m_buttons.add (m_clear);
104: m_buttons.add (m_checkboxes);
105:
106: m_checkboxes.add (m_genidsCB);
107: m_checkboxes.add (m_shortnamesCB);
108: }
109:
110: public void parseTriples (InputStream is) {
1.2 ! jsaarela 111: Vector vTriples = new Vector ();
! 112:
1.1 jsaarela 113: try {
114: StreamTokenizer st = new StreamTokenizer (is);
115: st.whitespaceChars(',', ',');
116: st.quoteChar('\'');
117:
118: int token1, token2, token3, token4, token5, token6, token7;
119: while ((token1 = st.nextToken()) != StreamTokenizer.TT_EOF) {
120: token2 = st.nextToken ();
121: token3 = st.nextToken ();
122: String sPredicate = st.sval;
123: token4 = st.nextToken ();
124: String sSubject = st.sval;
125: token5 = st.nextToken ();
126: String sObject = st.sval;
127: token6 = st.nextToken ();
128: token7 = st.nextToken ();
129:
1.2 ! jsaarela 130: /**
! 131: * Check an identical triple has not been added, yet
! 132: */
! 133: boolean bFound = false;
! 134: for (int x = 0; x < vTriples.size(); x++) {
! 135: Triple oldT = (Triple)vTriples.elementAt(x);
! 136: if (oldT.predicate().toString().equals (sPredicate) &&
! 137: oldT.subject().toString().equals (sSubject) &&
! 138: oldT.object().toString().equals (sObject)) {
! 139: bFound = true;
! 140: break;
! 141: }
! 142: }
! 143: if (bFound) continue;
! 144:
! 145: Triple t = new Triple (new Property(sPredicate),
! 146: new Resource(sSubject),
! 147: new Literal(sObject));
! 148: vTriples.addElement (t);
1.1 jsaarela 149: index (t);
150:
151: addSchema (sPredicate);
152: }
153: } catch (Exception e) {
154: e.printStackTrace();
155: }
156:
1.2 ! jsaarela 157: vTriples = null; // allow garbage collection of this vector
! 158:
1.1 jsaarela 159: for (int x = 0; x < m_schemas.countItems(); x++) {
160: m_schemas.select (x);
161: }
162: }
163:
164: /**
165: * Figure out if a schema name can be extracted and added
166: * to the list box
167: */
168: public void addSchema (String sPredicate) {
169: int iHash = sPredicate.indexOf ('#');
170: if (iHash > -1) {
171: String sSchema = sPredicate.substring (0, iHash);
172: int iItems = m_schemas.countItems ();
173: for (int x = 0; x < iItems; x++) {
174: if (m_schemas.getItem (x).equals (sSchema))
175: return;
176: }
177: m_schemas.addItem (sSchema, 0);
178: return;
179: }
180:
181: int iSlash = sPredicate.indexOf ('/');
182: if (iSlash > -1) {
183: String sSchema = sPredicate.substring (0, iSlash);
184: int iItems = m_schemas.countItems ();
185: for (int x = 0; x < iItems; x++) {
186: if (m_schemas.getItem (x).equals (sSchema))
187: return;
188: }
189: m_schemas.addItem (sSchema, 0);
190: }
191: }
192:
193: /**
194: * Return a Vectorful of items that point to
195: * the given URL
196: */
197: public Vector matchTO (String sURL) {
198: Vector v = (Vector)m_propvalue.get (sURL);
199: Vector v2 = new Vector ();
200: if (v == null)
201: return new Vector ();
202:
1.2 ! jsaarela 203: for (int y = 0; y < v.size(); y++) {
! 204: Triple t = (Triple)v.elementAt(y);
! 205:
! 206: for (int x = 0; x < m_schemas.countItems(); x++) {
! 207: if (m_schemas.isSelected (x)) {
! 208: String sSchema = m_schemas.getItem (x);
! 209:
! 210: if (t.predicate().toString().startsWith (sSchema)) {
! 211: // require the predicate().toString() is followed by # or /
1.1 jsaarela 212: // character
1.2 ! jsaarela 213: if (t.predicate().toString().substring(sSchema.length(), sSchema.length()+1).equals ("#") ||
! 214: t.predicate().toString().substring(sSchema.length(), sSchema.length()+1).equals ("/")) {
1.1 jsaarela 215: v2.addElement (t);
1.2 ! jsaarela 216: break;
! 217: }
1.1 jsaarela 218: }
219: }
220: }
221: }
222:
223: return v2;
224: }
225:
226: /**
227: * Return a Vectorful of items that point from
228: * the given URL
229: */
230: public Vector matchFROM (String sURL) {
231: Vector v = (Vector)m_propobj.get (sURL);
232: Vector v2 = new Vector ();
233: if (v == null)
234: return new Vector();
235:
1.2 ! jsaarela 236: for (int y = 0; y < v.size(); y++) {
! 237: Triple t = (Triple)v.elementAt(y);
! 238:
! 239: for (int x = 0; x < m_schemas.countItems(); x++) {
! 240: if (m_schemas.isSelected (x)) {
! 241: String sSchema = m_schemas.getItem (x);
! 242:
! 243: if (t.predicate().toString().startsWith (sSchema)) {
! 244: // require the predicate().toString() is followed by # or /
1.1 jsaarela 245: // character
1.2 ! jsaarela 246: if (t.predicate().toString().substring(sSchema.length(), sSchema.length()+1).equals ("#") ||
! 247: t.predicate().toString().substring(sSchema.length(), sSchema.length()+1).equals ("/")) {
1.1 jsaarela 248: v2.addElement (t);
1.2 ! jsaarela 249: break;
! 250: }
1.1 jsaarela 251: }
252: }
253: }
254: }
255:
256: return v2;
257: }
258:
259: /**
260: * Return a Vectorful of items that have a given property
261: */
262: public Vector matchPredicate (String sPredicate) {
263: Vector v = (Vector)m_prop.get (sPredicate);
264: if (v == null)
265: return new Vector();
266:
267: return v;
268: }
269:
270: /**
271: * Index each Triple instance according to each element
272: */
273: public void index (Triple t) {
1.2 ! jsaarela 274: String sType = t.predicate().toString();
1.1 jsaarela 275: Vector v = (Vector)m_prop.get (sType);
276: if (v == null) {
277: v = new Vector ();
278: }
1.2 ! jsaarela 279: v.addElement (t);
! 280: m_prop.put (sType, v);
1.1 jsaarela 281:
1.2 ! jsaarela 282: String sSubject = t.subject().toString ();
1.1 jsaarela 283: v = (Vector)m_propobj.get (sSubject);
284: if (v == null) {
285: v = new Vector ();
286: }
1.2 ! jsaarela 287: v.addElement (t);
! 288: m_propobj.put (sSubject, v);
1.1 jsaarela 289:
1.2 ! jsaarela 290: String sObject = t.object().toString();
1.1 jsaarela 291: v = (Vector)m_propvalue.get (sObject);
292: if (v == null) {
293: v = new Vector ();
294: }
1.2 ! jsaarela 295: v.addElement (t);
! 296: m_propvalue.put (sObject, v);
1.1 jsaarela 297: }
298:
299: /*
300: * manage the button events
301: */
302: public boolean handleEvent (Event e) {
303: if (e.id == Event.ACTION_EVENT) {
304: if (m_start == e.target) {
305: query (null);
306: return true;
307: } else if (m_clear == e.target) {
308: m_nodes.removeAll();
309: m_vNodes.removeAllElements();
310: m_vArcs.removeAllElements();
311: repaint();
312: return true;
313: } else if (m_genidsCB == e.target) {
314: if (m_genidsCB.getState() == true)
315: Node.GENIDS = true;
316: else
317: Node.GENIDS = false;
318: repaint ();
319: return true;
320: } else if (m_shortnamesCB == e.target) {
1.2 ! jsaarela 321: if (m_shortnamesCB.getState() == true) {
1.1 jsaarela 322: Arc.SHORTNAMES = false;
1.2 ! jsaarela 323: Node.SHORTNAMES = false;
! 324: } else {
1.1 jsaarela 325: Arc.SHORTNAMES = true;
1.2 ! jsaarela 326: Node.SHORTNAMES = true;
! 327: }
1.1 jsaarela 328: repaint ();
329: return true;
330: }
331: } else if (e.id == Event.MOUSE_MOVE &&
332: e.target == m_nodes) {
333: Rectangle r = m_options.bounds();
334: Point p = new Point (e.x - r.width, e.y);
335: boolean bRepaint = false;
336:
337: /**
338: * Try to figure out the closest
339: * 1. Node -> make it selected
340: * 2. Arc -> make it selected along with the originating node
341: */
342:
343: Node potentialNode = null;
344: Arc potentialArc = null;
345:
346: Enumeration enum = m_vNodes.elements();
347: while (enum.hasMoreElements()) {
348: Node n = (Node)enum.nextElement();
349: if (n.distance(p) < 150) {
350: potentialNode = n;
351: }
352: }
353:
354: enum = m_vArcs.elements ();
355: while (enum.hasMoreElements()) {
356: Arc a = (Arc)enum.nextElement();
357: if (a.distance(p) < 150) {
358: potentialArc = a;
359: potentialNode = null; // make sure it is null
360: }
361: }
362:
363: if (potentialNode != null) {
364: if (m_selectedArc != null) {
365: m_selectedArc.setColor (Arc.DEFAULT_COLOR);
366: m_selectedArc = null;
367: }
368: if (potentialNode != m_selectedNode)
369: bRepaint = true;
370:
371: if (m_selectedNode != null)
372: m_selectedNode.setColor (Node.DEFAULT_COLOR);
373: m_selectedNode = potentialNode;
374: m_selectedNode.setColor (Node.SELECTED_COLOR);
375:
1.2 ! jsaarela 376: // showStatus ("Click on the node to follow it further");
1.1 jsaarela 377: } else if (potentialArc != null) {
378: if (m_selectedNode != null) {
379: m_selectedNode.setColor (Node.DEFAULT_COLOR);
380: m_selectedNode = null;
381: }
382: if (potentialArc != m_selectedArc)
383: bRepaint = true;
384:
385: if (m_selectedArc != null)
386: m_selectedArc.setColor (Arc.DEFAULT_COLOR);
387: m_selectedArc = potentialArc;
388: m_selectedArc.setColor (Arc.SELECTED_COLOR);
389:
1.2 ! jsaarela 390: // showStatus ("Click on the arc to fetch similar properties");
1.1 jsaarela 391: } else {
1.2 ! jsaarela 392: // showStatus ("Place the mouse over a node or an arc");
1.1 jsaarela 393: }
394:
395: if (bRepaint)
396: repaint ();
397: return true;
398: } else if (e.id == Event.MOUSE_UP) {
399: if (m_selectedNode != null) {
400: String sOldText = m_url.getText ();
401: m_url.setText (m_selectedNode.name());
402:
403: /**
404: * If the text within the central node was a valid URL, bring
405: * it up on the browser. If not, continue with the
406: * applet execution
407: */
408: boolean bValidURL = true;
409: URL url = null;
410: try {
411: url = new URL (m_selectedNode.name());
412: } catch (Exception ex) {
413: bValidURL = false;
414: }
415: if (bValidURL &&
416: sOldText.equals (m_selectedNode.name())) {
417: getAppletContext().showDocument (url);
418: } else {
419: query (m_selectedNode.name());
420: }
421: return true;
422: } else if (m_selectedArc != null) {
423: queryPredicate (m_selectedArc.relation());
424: return true;
425: }
426: } else if (e.id == Event.LIST_SELECT ||
427: e.id == Event.LIST_DESELECT) {
428: query (null);
429: }
430:
431:
432: return false;
433: }
434:
435: /**
436: * query
437: *
438: * 1. reads the new Triple from m_url
439: * 2. create a new Node for the found item
440: * 3. create new Nodes for all other Nodes associaed with it
441: * 4. adds all these to the m_nodes Panel
442: * (5. nodes draw themselves using paint() method)
443: */
444:
445: public void query (String sObject) {
446: m_vNodes.removeAllElements ();
447: m_vArcs.removeAllElements ();
448:
449: /**
450: * 1.
451: */
452: String sURL;
453: if (sObject == null)
454: sURL = m_url.getText ();
455: else
456: sURL = sObject;
457:
458: /**
459: * 2.
460: */
461: Rectangle r = m_nodes.bounds ();
462: Dimension d = new Dimension (r.width, r.height);
463: Point center = new Point (d.width/2, d.height/2);
464:
465: Node centerNode = new Node (sURL, center);
466: centerNode.setColor (Node.SELECTED_COLOR);
467: m_selectedNode = centerNode;
468: m_vNodes.addElement (centerNode);
469:
470: /**
471: * 3.
472: */
473: Vector v = matchTO (sURL);
474:
475: for (int i = 0; i < v.size(); i++) {
476: Triple triple = (Triple)v.elementAt(i);
477:
478: double dAngle = - (i * Math.PI / (v.size() + 1) + Math.PI / (v.size() + 1));
479: int iRadius = d.height/2 - 50;
480:
481: Point p = new Point (d.width/2 + (int)(iRadius * Math.cos(dAngle)),
482: d.height/2 + (int)(iRadius * Math.sin(dAngle)));
1.2 ! jsaarela 483: Node n = new Node (triple.subject().toString(), p);
1.1 jsaarela 484:
485: n.addNextNode (centerNode);
486:
487: m_vNodes.addElement (n);
488:
1.2 ! jsaarela 489: m_vArcs.addElement (new Arc (triple.predicate().toString(),
1.1 jsaarela 490: p, center,
491: n, centerNode));
492: }
493:
494: v = matchFROM (sURL);
495:
496: for (int i = 0; i < v.size(); i++) {
497: Triple triple = (Triple)v.elementAt(i);
498:
499: double dAngle = - Math.PI - ( i * Math.PI / (v.size() + 1) + Math.PI / (v.size() + 1));
500: int iRadius = d.height/2 - 50;
501:
502: Point p = new Point (d.width/2 + (int)(iRadius * Math.cos(dAngle)),
503: d.height/2 + (int)(iRadius * Math.sin(dAngle)));
1.2 ! jsaarela 504: Node n = new Node (triple.object().toString(), p);
! 505:
1.1 jsaarela 506: centerNode.addNextNode (n);
507:
508: m_vNodes.addElement (n);
509:
1.2 ! jsaarela 510: m_vArcs.addElement (new Arc (triple.predicate().toString(),
1.1 jsaarela 511: center, p,
512: centerNode, n));
513: }
514:
515: repaint ();
516: }
517:
518: /**
519: * queryPredicate
520: *
521: */
522:
523: public void queryPredicate (String sPredicate) {
524: m_vNodes.removeAllElements ();
525: m_vArcs.removeAllElements ();
526:
527: Rectangle r = m_nodes.bounds ();
528: Dimension d = new Dimension (r.width, r.height);
529:
530: Vector v = matchPredicate (sPredicate);
531:
532: for (int i = 0; i < v.size(); i++) {
533: Triple triple = (Triple)v.elementAt(i);
534:
535: Point sourcePoint = new Point ((i + 1) * r.width / (v.size()+1), (int)(0.2 * r.height));
536: Point destPoint = new Point ((i + 1) * r.width / (v.size()+1), (int)(0.8 * r.height));
537:
1.2 ! jsaarela 538: Node sourceNode = new Node (triple.subject().toString(), sourcePoint);
! 539: Node destNode = new Node (triple.object().toString(), destPoint);
1.1 jsaarela 540:
541: m_vNodes.addElement (sourceNode);
542: m_vNodes.addElement (destNode);
543:
1.2 ! jsaarela 544: m_vArcs.addElement (new Arc (triple.predicate().toString(),
1.1 jsaarela 545: sourcePoint, destPoint,
546: sourceNode, destNode));
547: }
548:
549: repaint ();
550: }
551:
552: public void paint (Graphics g) {
553: Font f1 = new Font ("Helvetica", Font.PLAIN, 12);
554: Font f2 = new Font ("Helvetica", Font.PLAIN, 10);
555: Rectangle r = m_nodes.bounds ();
556: Dimension d = new Dimension (r.width, r.height);
557: Graphics g2 = m_nodes.getGraphics();
558:
559: g2.clearRect (0, 0, d.width, d.height);
560:
561: Enumeration e = m_vNodes.elements();
562:
563: while (e.hasMoreElements()) {
564: Node n = (Node)e.nextElement();
565: g2.setColor (n.color());
566: g2.setFont (f1);
567: g2.drawString (n.visualName(), n.x()-40, n.y());
568: g2.fillOval (n.x(), n.y(), 6, 6);
569: }
570:
571: e = m_vArcs.elements ();
572: while (e.hasMoreElements()) {
573: Arc a = (Arc)e.nextElement ();
574:
575: int sourceX = a.start().x;
576: int sourceY = a.start().y;
577: int destX = a.end().x;
578: int destY = a.end().y;
579:
580: if (sourceX > destX) {
581: sourceX -= 10;
582: destX += 10;
583: } else if (sourceX < destX) {
584: sourceX += 10;
585: destX -= 10;
586: }
587: if (sourceY > destY) {
588: sourceY -= 10;
589: destY += 10;
590: } else if (sourceY < destY) {
591: sourceY += 10;
592: destY -= 10;
593: }
594:
595: g2.setColor(a.color());
596:
597: g2.drawLine (sourceX, sourceY, destX, destY);
598:
599: /**
600: * Try to make the line look like an arrow
601: */
602: int dY = destY - sourceY;
603: int dX = destX - sourceX;
604: double dLength = Math.sqrt (dX*dX + dY*dY);
605: double dP = 10.0;
606: double dQ = 0.6;
607:
608: int iX1 = (int)(destX - (dX + dQ * dY) * dP/dLength);
609: int iY1 = (int)(destY - (dY - dQ * dX) * dP/dLength);
610:
611: int iX2 = (int)(destX - (dX - dQ * dY) * dP/dLength);
612: int iY2 = (int)(destY - (dY + dQ * dX) * dP/dLength);
613:
614: g2.drawLine (destX, destY, iX1, iY1);
615: g2.drawLine (destX, destY, iX2, iY2);
616:
617: /**
618: * Draw the relation
619: */
620: g2.setFont (f2);
621: g2.drawString (a.visualRelation(),
622: (sourceX + destX) / 2 - 10,
623: (sourceY + destY) / 2);
624: }
625: }
626:
627: public static void main (String argv[]) {
1.2 ! jsaarela 628: Frame FirstWindow = new Frame ("Visual RDF (C)1997-1999 Janne Saarela/World Wide Web Consortium (W3C)");
1.1 jsaarela 629:
630: VisRDF vs = new VisRDF ();
631:
632: FirstWindow.setLayout (new BorderLayout());
633: FirstWindow.add ("Center", vs);
634: FirstWindow.resize (700, 650);
635:
636: vs.m_input = argv[0];
637: if (argv.length > 1)
638: vs.m_url.setText (argv[1]);
639:
640: try {
641: vs.parseTriples (new FileInputStream (argv[0]));
642: } catch (Exception e) {
643: e.printStackTrace ();
644: }
645:
646: vs.bApplication = true;
647: vs.init ();
648: vs.start ();
649:
650: FirstWindow.show ();
651: }
652: }
Webmaster