Annotation of java/classes/org/w3c/rdf/examples/ARPServlet.java, revision 1.35
1.16 barstow 1: /***********************************************************************
2: *
3: * ARPServlet - this servlet implements an RDF Validation service. As
4: * of this writing, the following RDF validation service used this
5: * servlet:
6: *
7: * http://www.w3.org/RDF/Validator/
8: *
9: ***********************************************************************
1.1 barstow 10: *
11: * Copyright © World Wide Web Consortium, (Massachusetts Institute of
12: * Technology, Institut National de Recherche en Informatique et en
13: * Automatique, Keio University).
14: *
15: * All Rights Reserved.
16: *
17: * Please see the full Copyright clause at
18: * <http://www.w3.org/Consortium/Legal/copyright-software.html>
19: *
1.16 barstow 20: ***********************************************************************
21: *
22: * This servlet is a wrapper for the ARP RDF parser. See the following
23: * for information about the ARP RDF parser:
24: *
25: * http://www.hpl.hp.co.uk/people/jjc/arp/
26: *
27: ***********************************************************************
28: *
29: * Implementation notes:
30: *
31: * o This servlet supports the HTTP POST operation; it does not
32: * support the HTTP GET operation
33: *
34: * o Depending upon the parameters given to the servlet it may
35: * invoke a GraphViz suprocess to generate a graph of the RDF.
36: * See the following for more information about GraphViz:
1.1 barstow 37: *
1.16 barstow 38: * http://www.research.att.com/sw/tools/graphviz/
1.1 barstow 39: *
1.16 barstow 40: * The servlet assumes version 1.7.4 of GraphViz.
1.1 barstow 41: *
1.16 barstow 42: * o Depending upon the parameters given to the servlet, the RDF
43: * to be validated may be copied to a file. The name of the file
44: * is automatically generated via Java's temporary file APIs. The
45: * location of the directory where the file is stored is configured
46: * via the serverlet's init() method. See below for more information.
47: *
48: * o See the section on Server Initialization for more information.
49: *
50: ***********************************************************************
51: *
52: * HTTP POST parameters - the servlet expects/assumes the following
53: * variables are defined via the HTTP POST request:
54: *
55: * RDF - the RDF (assumed to be in RDF/XML syntax) to be validated
56: *
57: * SAVE_DOT_FILE - if "on", the GraphViz DOT file is saved and a
58: * link to the file is returned; otherwise the DOT file is not saved
59: *
60: * SAVE_RDF - if "on", the RDF will be copied to a file; otherwise
61: * the RDF is not copied to a file
1.1 barstow 62: *
1.10 barstow 63: * EMBEDDED_RDF - if "on", then the RDF is not enclosed in <RDF>...</RDF>
1.30 duerst 64: * tags; otherwise it assumed that the RDF is enclosed in these tags.
1.10 barstow 65: *
1.16 barstow 66: * URI - the URI of the RDF to validate; this parameter may not be
67: * specified; if this parameter and the RDF parameter are both
68: * specified, this parameter takes precedence
1.1 barstow 69: *
70: * ORIENTATION - the graph's orientation (left to right or top to
1.16 barstow 71: * bottom); default is left to right
1.1 barstow 72: *
1.16 barstow 73: * FONT_SIZE - the font size to use (10, 12, 14, 16 and 20 are
74: * supported); the default is 10
1.1 barstow 75: *
1.16 barstow 76: * ANON_NODES_EMPTY - if "on", anonymous nodes are not labeled; otherwise
77: * anonymous nodes are labeled;
1.12 barstow 78: *
79: * TRIPLES_AND_GRAPH - support values are:
80: *
1.16 barstow 81: * PRINT_BOTH - display triples and a graph (the default)
82: * PRINT_TRIPLES - only display the triples
83: * PRINT_GRAPH - only display the graph
1.12 barstow 84: *
1.1 barstow 85: * GRAPH_FORMAT - the graph's output format. Supported values are:
86: *
1.16 barstow 87: * GIF_EMBED - embed the graph as a GIF (the default)
1.1 barstow 88: * GIF_LINK - don't embed the GIF but create a link for it
89: * SVG_LINK - create the graph in SVG format and create a link to the file
90: * PNG_EMBED - create the graph in PNG format and embed the graph in the
91: * document that is returned
92: * PNG_LINK - create the graph in PNG format and create a link to the file
93: * PS_LINK - create a PostScript image of the file and a link to the file
94: * HP_PCL_LINK - create a HPGL/2 - PCL (Laserwriter) image of the file
95: * and a link to the file
96: * HP_GL_LINK - create a HPGL - PCL (pen plotter) image of the file and
97: * a link to the file
98: *
99: * NTRIPLES if "on" the tabular output will be in the NTriples format;
100: * otherwise a table of Subject, Predicate, Objects will be generated
101: *
1.16 barstow 102: ***********************************************************************
103: *
104: * Server Initialization - this servlet requires the following
105: * parameters be set in the servlet's init() method - via the
106: * ServletConfig object:
1.1 barstow 107: *
1.16 barstow 108: * GRAPH_VIZ_ROOT - the absolute path of the top-level directory containing
109: * GraphViz's binary distribution
110: *
111: * GRAPH_VIZ_PATH - the relative path (based on GRAPH_VIZ_ROOT) of
112: * the DOT executable (e.g. dotneato/dot) - the program used to generate
113: * a graph from a DOT file.
114: *
115: * GRAPH_VIZ_FONT_DIR - the relative path (based on GRAPH_VIZ_ROOT) of
116: * the fonts directory used by GraphViz (e.g. Fonts)
117: *
118: * SERVLET_TMP_DIR - the absolute path of the directory to be used to
119: * store temporary files used by the servlet and GraphViz. This
120: * directory must be writable by the servlet.
121: *
122: * NOTE - Some files created by the servlet are not removed by
123: * servlet (e.g. graph image files).
124: *
125: * If any of these parameters are not defined, the servlet will NOT
126: * validate the RDF.
127: *
128: ***********************************************************************
129: *
130: * Dependencies - this servlet requires the following Java packages
131: * as well as GraphViz (described above):
132: *
133: * ARP RDF parser: http://www.hpl.hp.co.uk/people/jjc/arp/download.html
134: *
135: * SAX-based XML parser: e.g. Xerces at http://xml.apache.org/
136: *
137: * Java servlet package: http://java.sun.com/products/servlet/archive.html
138: *
139: * Apache Regular Expression: http://jakarta.apache.org/builds/jakarta-regexp/release/v1.2/
140: *
141: ***********************************************************************
142: *
143: * Author: Art Barstow <barstow@w3.org>
1.30 duerst 144: * Author (internationalization): Martin J. Duerst <duerst@w3.org>
1.16 barstow 145: *
1.21 duerst 146: * $Id: ARPServlet.java,v 1.20 2002/08/05 07:02:43 duerst Exp $
1.16 barstow 147: *
148: ***********************************************************************/
1.1 barstow 149:
1.16 barstow 150: // http://dev.w3.org/cvsweb/java/classes/org/w3c/rdf/examples/
151: package org.w3c.rdf.examples;
1.1 barstow 152:
153: import java.io.*;
154: import java.net.MalformedURLException;
155: import java.net.URL;
1.33 duerst 156: import java.net.URLConnection;
1.1 barstow 157: import java.util.StringTokenizer;
158: import java.util.Enumeration;
1.14 barstow 159: import java.util.Hashtable;
1.16 barstow 160:
161: // http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/package-summary.html
162: import javax.servlet.*;
1.1 barstow 163: import javax.servlet.http.*;
1.32 duerst 164: import javax.mail.internet.ContentType;
1.1 barstow 165:
1.16 barstow 166: // http://xml.apache.org/apiDocs/org/xml/sax/package-summary.html
167: import org.xml.sax.InputSource;
1.1 barstow 168: import org.xml.sax.Parser;
169: import org.xml.sax.SAXException;
170: import org.xml.sax.SAXParseException;
171: import org.xml.sax.ErrorHandler;
172: import org.xml.sax.helpers.*;
173:
1.16 barstow 174: // http://jakarta.apache.org/regexp/apidocs/org/apache/regexp/RE.html
1.3 barstow 175: import org.apache.regexp.RE;
176:
1.16 barstow 177: // http://www.hpl.hp.co.uk/people/jjc/arp/apidocs/index.html
178: import com.hp.hpl.jena.rdf.arp.*;
1.1 barstow 179:
180: public class ARPServlet extends HttpServlet
181: {
1.21 duerst 182: final static public String REVISION = "$Id: ARPServlet.java,v 1.20 2002/08/05 07:02:43 duerst Exp $";
1.1 barstow 183:
184: // The email address for bug reports
1.6 barstow 185: private static final String MAIL_TO = "www-rdf-validator@w3.org";
1.1 barstow 186:
187: // Names of the POST parameters (described above) and their
1.12 barstow 188: // defaults (if applicable)
189: private static final String TEXT = "RDF";
190: private static final String SAVE_DOT_FILE = "SAVE_DOT_FILE";
191: private static final String SAVE_RDF = "SAVE_RDF";
192: private static final String EMBEDDED_RDF = "EMBEDDED_RDF";
193: private static final String URI = "URI";
194: private static final String NTRIPLES = "NTRIPLES";
195: private static final String ANON_NODES_EMPTY = "ANON_NODES_EMPTY";
1.1 barstow 196:
197: private static final String NODE_COLOR = "NODE_COLOR";
198: private static final String DEFAULT_NODE_COLOR = "black";
199:
200: private static final String NODE_TEXT_COLOR = "NODE_TEXT_COLOR";
201: private static final String DEFAULT_NODE_TEXT_COLOR = "black";
202:
203: private static final String EDGE_COLOR = "EDGE_COLOR";
204: private static final String DEFAULT_EDGE_COLOR = "black";
205:
206: private static final String EDGE_TEXT_COLOR = "EDGE_TEXT_COLOR";
207: private static final String DEFAULT_EDGE_TEXT_COLOR = "black";
208:
209: private static final String ORIENTATION = "ORIENTATION";
210: private static final String DEFAULT_ORIENTATION = "TB"; // Top to Bottom
211:
212: private static final String FONT_SIZE = "FONT_SIZE";
213: private static final String DEFAULT_FONT_SIZE = "10";
214:
1.12 barstow 215: // Print graph and/or triples
216: private static final String TRIPLES_AND_GRAPH = "TRIPLES_AND_GRAPH";
217: private static final String PRINT_BOTH = "PRINT_BOTH";
218: private static final String PRINT_TRIPLES = "PRINT_TRIPLES";
219: private static final String PRINT_GRAPH = "PRINT_GRAPH";
220:
221: // Graph formats
1.1 barstow 222: private static final String FORMAT = "FORMAT";
223: private static final String FORMAT_GIF_EMBED = "GIF_EMBED";
224: private static final String FORMAT_GIF_LINK = "GIF_LINK";
225: private static final String FORMAT_SVG_LINK = "SVG_LINK";
226: private static final String FORMAT_PNG_EMBED = "PNG_EMBED";
227: private static final String FORMAT_PNG_LINK = "PNG_LINK";
228: private static final String FORMAT_PS_LINK = "PS_LINK";
229: private static final String FORMAT_HP_PCL_LINK = "HP_PCL_LINK";
230: private static final String FORMAT_HP_GL_LINK = "HP_GL_LINK";
231:
232: // Fonts are not currently configurable
233: private static final String DEFAULT_FONT = "arial";
234:
235: // Names of the servlet's parameters - for Jigsaw web server
236: private static final String SERVLET_TMP_DIR = "SERVLET_TMP_DIR";
237: private static final String GRAPH_VIZ_ROOT = "GRAPH_VIZ_ROOT";
238: private static final String GRAPH_VIZ_PATH = "GRAPH_VIZ_PATH";
239: private static final String GRAPH_VIZ_FONT_DIR = "GRAPH_VIZ_FONT_DIR";
240:
241: // Variables for the servlet's parameters
242: private static String m_ServletTmpDir = null;
243: private static String m_GraphVizPath = null;
244: private static String m_GraphVizFontDir = null;
245:
1.30 duerst 246: // Names of environment variable needed by GraphVis
1.1 barstow 247: private static String DOTFONTPATH = "DOTFONTPATH";
248: private static String LD_LIBRARY_PATH = "LD_LIBRARY_PATH";
249:
250: // Names used for temporary files
251: private static final String TMP_FILE_PREFIX = "servlet_";
252: private static final String SUFFIX_TMP_DIR = ".tmp";
253: private static final String SUFFIX_DOT = ".dot";
254: private static final String SUFFIX_RDF = ".rdf";
255:
256: // Names used for file suffixes and for GraphViz's command line
257: // option
258: private static final String NAME_GIF = "gif";
259: private static final String NAME_HPGL = "hpgl";
260: private static final String NAME_PCL = "pcl";
261: private static final String NAME_PNG = "png";
262: private static final String NAME_PS = "ps";
263: private static final String NAME_SVG = "svg";
264:
265: // Default GraphViz parameter names and their default values
266: // Servlet name
267: private static final String SERVLET_NAME = "ARPServlet";
268:
269: // Name for the DOT file title
270: private static final String DOT_TITLE = "dotfile";
271:
1.30 duerst 272: // The string to use to prefix anonymous nodes.
1.14 barstow 273: private static final String ANON_NODE = "genid:";
274:
1.1 barstow 275: // The string to use for a namespace name when no
276: // namespace is available - e.g. for the RDF that is
277: // directly entered into the input form.
1.14 barstow 278: private static final String DEFAULT_NAMESPACE = "online:";
1.1 barstow 279:
280: /*
1.14 barstow 281: * Create a File object from the given directory and file names
1.1 barstow 282: *
283: *@param directory the file's directory
284: *@param prefix the file's prefix name (not its directory)
285: *@param suffix the file's suffix or extension name
286: *@return a File object if a temporary file is created; null otherwise
287: */
1.4 barstow 288: private File createTempFile (String directory, String prefix, String suffix)
289: {
1.1 barstow 290: File f;
291: try {
292: File d = new File(directory);
293: f = File.createTempFile(prefix, suffix, d);
294: } catch (Exception e) {
295: return null;
296: }
297: return f;
298: }
299:
300: /*
301: * Given a URI string, open it, read its contents into a String
302: * and return the String
303: *
304: *@param uri the URI to open
305: *@return the content at the URI or null if any error occurs
306: */
1.31 duerst 307: private String getRDFfromURI (String uri)
1.4 barstow 308: {
1.1 barstow 309: try {
310: URL url = new URL(uri);
1.32 duerst 311: URLConnection con = url.openConnection();
1.34 duerst 312: con.setRequestProperty("Accept", "application/rdf+xml");
1.32 duerst 313: con.connect();
314: String contentT = con.getContentType();
1.35 ! duerst 315: String HTTPcharset = null;
1.33 duerst 316: if (contentT != null) {
1.32 duerst 317: ContentType contentType = new ContentType(con.getContentType());
1.35 ! duerst 318: HTTPcharset = contentType.getParameter("charset");
1.32 duerst 319: }
320:
1.35 ! duerst 321: // need buffer for lookahead for encoding detection
! 322: BufferedInputStream bis = new BufferedInputStream(con.getInputStream());
! 323: bis.mark(200); // mark start so that we can get back to it
! 324:
1.1 barstow 325: String s = new String("");
326: int c;
327: int numRead = 0;
328:
329: while ((c = is.read()) != -1) {
330: s += (char)c;
1.35 ! duerst 331: if (numRead++ >= 195) break;
1.1 barstow 332: }
333:
334: if (s.equals(""))
335: // Nothing was returned
336: return null;
337:
1.35 ! duerst 338: // A server could return content but not the RDF/XML that
! 339: // we need. Check the beginning of s and if it looks like
! 340: // a generic HTML message, return an error.
! 341: if (s.startsWith("<!DOCTYPE"))
! 342: return null;
! 343:
! 344: String APPFcharset = null; // preliminary 'charset' according to XML APP. F
! 345: int ignoreBytes = 0;
! 346: if (s.startsWith("\u00FE\u00FF")) {
! 347: APPFcharset = "UTF-16BE";
! 348: ignoreBytes = 2;
! 349: }
! 350: if (s.startsWith("\u00FF\u00FE")) {
! 351: APPFcharset = "UTF-16LE";
! 352: ignoreBytes = 2;
! 353: }
! 354: if (s.startsWith("\u00EF\u00BB\u00BF")) {
! 355: APPFcharset = "UTF-8";
! 356: ignoreBytes = 3;
! 357: }
! 358: if (s.startsWith("\u0000<\u0000?")) {
! 359: APPFcharset = "UTF-16BE";
! 360: }
! 361: if (s.startsWith("<\u0000?\u0000")) {
! 362: APPFcharset = "UTF-16LE";
! 363: }
! 364: if (s.startsWith("<?xml")) {
! 365: APPFcharset = "US-ASCII";
! 366: }
! 367: if (s.startsWith("\u004C\u006F\u00A7\u0094")) {
! 368: APPFcharset = "cp037"; // EBCDIC
! 369: }
! 370:
! 371: // convert start of xml input according to APPFcharset
! 372: String xmlstart = new String(s.substring(ignoreBytes).getBytes("iso-8859-1"), APPFcharset);
! 373:
! 374: RE r = new RE("<?xml[ \\t\\n\\r]+version[ \\t\\n\\r]?=[ \\t\\n\\r]?(['\"])[-a-zA-Z0-9_.:]+\\1[ \\t\\n\\r]+encoding[ \\t\\n\\r]?=[ \\t\\n\\r]?(['\"])([A-Za-z][-A-Za-z0-9._]*)\\2");
! 375:
! 376:
! 377: r.setMatchFlags(MATCH_NORMAL | MATCH_MULTILINE)
! 378: String XMLcharset = null;
! 379: if (r.match(xmlstart) && r.getParenStart(0)==0)
! 380: XMLcharset = r.getParen(3);
! 381:
! 382: HTTPcharset = HTTPcharset.toUpperCase();
! 383: APPFcharset = APPFcharset.toUpperCase();
! 384: XMLcharset = XMLcharset.toUpperCase();
! 385:
! 386: if (HTTPcharset != null) {
! 387: if (XMLcharset != null && HTTPcharset != XMLcharset)
! 388: return null;
! 389: finalCharset = HTTPcharset;
! 390: }
! 391: else if (XMLcharset != null)
! 392: finalCharset = XMLcharset;
! 393: else
! 394: finalCharset = "UTF-8";
! 395: if (finalCharset == "UTF-16" && ignoreBytes != 2) // no BOM!
! 396: return null;
! 397:
! 398: bis.reset(); // move back to start of stream
! 399: bis.skip(ignoreBytes); // skip BOM
! 400: InputStreamReader isr = new InputStreamReader(bis, finalCharset);
! 401: s = new String("");
! 402:
! 403: while ((c = is.read()) != -1)
! 404: s += (char)c;
! 405:
! 406: // todo: fix encoding parameter in xml pseudo-PI
! 407:
1.1 barstow 408: return s;
409:
410: } catch (Exception e) {
411: return null;
412: }
413: }
414:
415: /*
1.4 barstow 416: * Copy the given string of RDF to a file in the given directory.
417: * This is only done if the servlet is explictly asked to save
418: * the RDF to a file.
1.1 barstow 419: *
1.14 barstow 420: *@param tmpDir the file's directory
1.1 barstow 421: *@param rdf the string of RDF
422: */
423: private void copyRDFStringToFile(String tmpDir, String rdf)
424: {
425: try {
426: // Generate a unique file name
427: File tmpFile = createTempFile(tmpDir, TMP_FILE_PREFIX, SUFFIX_RDF);
428: if (tmpFile == null) {
429: // Not really a critical error, just return
430: return;
431: }
432:
433: // Create a PrintWriter for the GraphViz consumer
434: FileWriter fw = new FileWriter(tmpFile);
435: PrintWriter pw = new PrintWriter(fw);
436:
437: pw.println(rdf);
438: pw.close();
439: } catch (Exception e) {
1.4 barstow 440: System.err.println(SERVLET_NAME + ": error occured trying to save RDF to file '" + tmpDir + TMP_FILE_PREFIX + SUFFIX_RDF + "'.");
1.1 barstow 441: return;
442: }
443: }
444:
445: /*
446: * Given the graph's format option, return either the corresponding
447: * command line option for that option or the file name suffix for
448: * the graph option. For example GIF files have ".gif" for its
449: * suffix and GraphViz uses "-Tgif" for the command line.
450: *
451: * NOTE: default is GIF.
452: *
453: *@param graphFormat the graph's output format
454: *@param suffix. If true, the name returned is for the graph's
455: * file name suffix; otherwise, the name returned is for the
456: * graph's command line option.
457: *@return the suffix to use for the graph's output file
458: */
459: private String getFormatName(String graphFormat, boolean suffix) {
460:
461: String name = (suffix) ? "." : "-T";
462:
1.4 barstow 463: if (graphFormat.equals(FORMAT_PNG_EMBED)) return name + NAME_PNG;
464: if (graphFormat.equals(FORMAT_PNG_LINK)) return name + NAME_PNG;
465: if (graphFormat.equals(FORMAT_SVG_LINK)) return name + NAME_SVG;
466: if (graphFormat.equals(FORMAT_PS_LINK)) return name + NAME_PS;
467: if (graphFormat.equals(FORMAT_HP_GL_LINK)) return name + NAME_HPGL;
1.1 barstow 468: if (graphFormat.equals(FORMAT_HP_PCL_LINK)) return name + NAME_PCL;
469:
470: return name + NAME_GIF;
471: }
472:
473: /*
474: * Invokes the GraphVis program to create a graph image from the
475: * the given DOT data file
476: *
477: *@param dotFileName the name of the DOT data file
478: *@param outputFileName the name of the output data file
1.14 barstow 479: *@param graphFormat the graph's format
1.1 barstow 480: *@return true if success; false if any failure occurs
481: */
1.8 barstow 482: private boolean generateGraphFile(String dotFileName,
1.4 barstow 483: String outputFileName, String graphFormat)
484: {
1.16 barstow 485: String environment[] = {DOTFONTPATH + "=" + m_GraphVizFontDir};
1.1 barstow 486:
487: String formatOption = getFormatName(graphFormat, false);
488:
489: String cmdArray[] = {m_GraphVizPath, formatOption, "-o", outputFileName, dotFileName};
490: Runtime rt = Runtime.getRuntime();
491: try {
492: Process p = rt.exec(cmdArray, environment);
493: p.waitFor();
1.10 barstow 494:
1.1 barstow 495: } catch (Exception e) {
496: System.err.println("Error: generating OutputFile.");
497: return false;
498: }
499: return true;
500: }
501:
502: /*
503: * Returns a parameter from a request or the parameter's default
504: * value.
505: *
506: *@param req a Servlet request
1.14 barstow 507: *@param param the name of the parameter
508: *@param defString the string returned if the param is not found
1.1 barstow 509: *@return if the request contains the specfied parameter its value
510: * in the request is returned; otherwise its default value is
511: * returned
512: */
513: private String getParameter(HttpServletRequest req, String param,
514: String defString)
515: {
516: String s = req.getParameter(param);
517: return (s == null) ? defString : s;
518: }
519:
520: /*
521: * If the request contains any graph-related parameters, pass them
522: * to the graph consumer for handling
523: *
524: *@param req the response
1.14 barstow 525: *@param pw the PrintWriter
1.1 barstow 526: *@param consumer the GraphViz consumer
527: */
1.14 barstow 528: private void processGraphParameters (HttpServletRequest req, PrintWriter pw)
1.1 barstow 529: {
1.4 barstow 530: // Print the graph header
531: pw.println("digraph " + DOT_TITLE + "{ " );
1.1 barstow 532:
533: // Look for colors
1.4 barstow 534: String nodeColor = getParameter(req, NODE_COLOR,
535: DEFAULT_NODE_COLOR);
536: String nodeTextColor = getParameter(req, NODE_TEXT_COLOR,
537: DEFAULT_NODE_TEXT_COLOR);
538: String edgeColor = getParameter(req, EDGE_COLOR,
539: DEFAULT_EDGE_COLOR);
540: String edgeTextColor = getParameter(req, EDGE_TEXT_COLOR,
541: DEFAULT_EDGE_TEXT_COLOR);
542: String fontSize = getParameter(req, FONT_SIZE,
543: DEFAULT_FONT_SIZE);
1.1 barstow 544:
545: // Orientation must be either
546: String orientation = req.getParameter (ORIENTATION);
547: if (orientation.equals("LR"))
548: orientation = "LR";
549: else
550: orientation = DEFAULT_ORIENTATION;
551:
552: // Add an attribute for all of the graph's nodes
553: pw.println("node [fontname=" + DEFAULT_FONT +
1.7 barstow 554: ",fontsize=" + fontSize +
555: ",color=" + nodeColor +
1.12 barstow 556: ",fontcolor=" + nodeTextColor + "];");
1.1 barstow 557:
558: // Add an attribute for all of the graph's edges
559: pw.println("edge [fontname=" + DEFAULT_FONT +
1.8 barstow 560: ",fontsize=" + fontSize +
561: ",color=" + edgeColor +
562: ",fontcolor=" + edgeTextColor + "];");
1.1 barstow 563:
564: // Add an attribute for the orientation
565: pw.println("rankdir=" + orientation + ";");
566: }
567:
568: private static class SaxErrorHandler implements org.xml.sax.ErrorHandler
569: {
1.22 duerst 570: PrintWriter out;
1.1 barstow 571: boolean silent = false;
1.5 barstow 572: String fatalErrors = "";
573: String errors = "";
574: String warnings = "";
1.1 barstow 575:
576: /*
577: * Constructuor for a SaxErrorHandler
578: *
1.22 duerst 579: *@param out the servlet's PrintWriter
1.1 barstow 580: *@param silent if false, output is suprressed
581: */
1.22 duerst 582: public SaxErrorHandler(PrintWriter out, boolean silent)
1.1 barstow 583: {
584: this.out = out;
585: this.silent = silent;
586: }
587:
588: /*
589: * Create a formatted string from the exception's message
590: *
1.5 barstow 591: *@param e the SAX Parse Exception
1.1 barstow 592: *@return a formatted string
593: */
594: private static String format(org.xml.sax.SAXParseException e)
595: {
596: String msg = e.getMessage();
597: if (msg == null)
598: msg = e.toString();
599: return msg + "[Line = " + e.getLineNumber() + ", Column = " + e.getColumnNumber() + "]";
600: }
601:
602: /*
603: * Handle a parse error
604: *
1.5 barstow 605: *@param e the SAX Parse Exception
1.1 barstow 606: */
607: public void error(org.xml.sax.SAXParseException e)
608: throws org.xml.sax.SAXException
609: {
610: if (this.silent) return;
611:
1.5 barstow 612: this.errors += "Error: " + format(e) + "<br />";
1.1 barstow 613: }
614:
615: /*
616: * Handle a fatal parse error
617: *
1.5 barstow 618: *@param e the SAX Parse Exception
1.1 barstow 619: */
620: public void fatalError(org.xml.sax.SAXParseException e)
621: throws org.xml.sax.SAXException
622: {
623: if (this.silent) return;
624:
1.5 barstow 625: this.fatalErrors += "FatalError: " + format(e) + "<br />";
1.1 barstow 626: }
627:
628: /*
629: * Handle a parse warning
630: *
1.5 barstow 631: *@param e the SAX Parse Exception
1.1 barstow 632: */
633: public void warning(org.xml.sax.SAXParseException e)
634: throws org.xml.sax.SAXException
635: {
636: if (this.silent) return;
637:
1.5 barstow 638: this.warnings += "Warning: " + format(e) + "<br />";
1.1 barstow 639: }
1.5 barstow 640:
641: /*
642: * Return the error messages
643: *
644: *@return the error messages or an empty string if there are
645: * no messages
646: */
647: public String getErrors()
648: {
649: return this.errors;
650: }
651:
652: /*
653: * Return the fatal error messages
654: *
655: *@return the fatal error messages or an empty string if there are
656: * no messages
657: */
658: public String getFatalErrors()
659: {
660: return this.fatalErrors;
661: }
662:
663: /*
664: * Return the warning messages
665: *
666: *@return the warning messages or an empty string if there are
667: * no messages
668: */
669: public String getWarnings()
670: {
671: return this.warnings;
672: }
1.1 barstow 673: }
674:
675: /*
676: * Generate a graph of the RDF data model
677: *
678: *@param out the servlet's output stream
1.4 barstow 679: *@param pw the graph file's PrintWriter
680: *@param dotFile the File handle for the graph file
1.1 barstow 681: *@param rdf the RDF text
682: *@param req a Servlet request
683: *@param graphFormat the graph's format
684: *@param saveRDF the RDF can be cached [saved to the file system]
685: *@param saveDOTFile the DOT file should be cached
686: */
1.22 duerst 687: private void generateGraph(PrintWriter out, PrintWriter pw,
1.4 barstow 688: File dotFile, String rdf, HttpServletRequest req, String graphFormat,
1.1 barstow 689: boolean saveRDF, boolean saveDOTFile)
690: {
691: try {
692: out.println("<hr title=\"visualisation\">");
693: out.println("<h3>Graph of the data model</h3>");
694:
695: // The temporary directory
696: String tmpDir = m_ServletTmpDir;
697:
698: // Add the graph footer
699: pw.println( " }");
700:
1.4 barstow 701: // Close the DOT input file so the GraphViz can
1.1 barstow 702: // open and read it
703: pw.close();
704:
705: // Generate a unique file name for the output file
706: // that will be created
707: String suffix = getFormatName(graphFormat, true);
708: File outputFile = createTempFile(tmpDir, TMP_FILE_PREFIX, suffix);
709: if (outputFile == null) {
710: out.println("Failed to create a temporary file for the graph. A graph cannot be generated.");
711: dotFile.delete();
712: return;
713: }
714:
715: // Pass the DOT data file to the GraphViz dot program
716: // so it can create a graph image of the data model
717: String dotFileName = dotFile.getAbsolutePath();
718: String outputFileName = outputFile.getAbsolutePath();
719:
1.8 barstow 720: if (!generateGraphFile(dotFileName, outputFileName, graphFormat)) {
1.1 barstow 721: out.println("An attempt to create a graph failed.");
722: dotFile.delete();
723: outputFile.delete();
724: return;
725: }
726: // Handle the DOT file
727: if (saveDOTFile) {
728: // Make the DOT file link'able if so requested
729: String dotPath = SERVLET_NAME + SUFFIX_TMP_DIR +
730: File.separator + dotFile.getName();
731: out.println("<a href=\"" + dotPath + "\">Download the DOT file.</a><br /><br />");
732: }
733: else {
734: // Delete it ...
735: dotFile.delete();
736: }
737:
738: // NOTE: Cannot delete the output file here because its
739: // pathname is returned to the client
740: String imagePath = SERVLET_NAME + SUFFIX_TMP_DIR + File.separator +
741: outputFile.getName();
742:
743: // Handle the embedded image formats first
744: if (graphFormat.equals(FORMAT_GIF_EMBED) ||
745: graphFormat.equals(FORMAT_PNG_EMBED)) {
746: if (outputFile.length() > 0)
747: out.println("<img src=\"" + imagePath + "\"/>");
748: else
749: out.println("The graph image file is empty.");
750: } else {
751: if (outputFile.length() > 0)
752: out.println("<a href=\"" + imagePath + "\">Get/view the graph's image file (" + suffix + ").</a><br /><br />");
753: else
754: out.println("The graph image file is empty.");
755: }
756:
757: // One last thing to do before exiting - copy the RDF to a file
758: if (saveRDF)
759: copyRDFStringToFile(tmpDir, rdf);
760:
761: } catch (Exception e) {
762: System.err.println("Exception generating graph: " + e.getMessage());
763: }
764: }
765:
766: /*
767: * Search the given string for substring "key"
768: * and if it is found, replace it with string "replacement"
769: *
770: *@param input the input string
771: *@param key the string to search for
772: *@param replacement the string to replace all occurences of "key"
1.3 barstow 773: *@return if no substitutions are done, input is returned; otherwise
1.1 barstow 774: * a new string is returned.
775: */
776: public static String replaceString(String input, String key,
777: String replacement)
778: {
1.3 barstow 779: try {
780: RE re = new RE(key);
781: return re.subst(input, replacement);
782: } catch (Exception e) {
783: return input;
1.1 barstow 784: }
785: }
786:
787: /*
788: * Print the document's header info
789: *
790: *@param out the servlet's output stream
791: */
1.22 duerst 792: private void printDocumentHeader (PrintWriter out)
1.1 barstow 793: {
794: try {
795:
1.6 barstow 796: out.println( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"" +
797: " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" +
798: "<html><head>" +
799: "<title>RDF Validator</title>" +
800: "<link href='http://www.w3.org/StyleSheets/base.css' rel='stylesheet' type='text/css'/>" +
801: "<style type='text/css'>" +
802: " TD {" +
803: " background:#EEEEEE;" +
804: " font-family:'courier new',courier,serif;" +
805: " }" +
806: "</style>" +
807: "</head>" +
808: "<body>");
1.1 barstow 809:
810: } catch (Exception e) {
1.8 barstow 811: System.err.println("Exception (printDocumentHeader): " + e.getMessage());
1.1 barstow 812: }
813: }
814:
815: /*
816: * Print the rdf listing
817: *
818: *@param out the servlet's output stream
819: *@param rdf the RDF code
1.14 barstow 820: *@param needCR if true, add a CarriageReturn to the output; if false,
821: * do not add it
1.1 barstow 822: */
1.22 duerst 823: private void printListing (PrintWriter out, String rdf,
1.1 barstow 824: boolean needCR)
825: {
826: try {
1.8 barstow 827: out.println("<hr title=\"original source\">" +
1.29 duerst 828: "<h3>The original RDF/XML document</h3>" +
1.8 barstow 829: "<pre>");
1.1 barstow 830:
831: String s = replaceString(rdf, "<", "<");
832:
833: // Now output the RDF one line at a time with line numbers
834: int lineNum = 1;
835: int nl = 0;
836: String terminator = needCR?"\n":"";
837: do {
838: String tok;
839: nl = s.indexOf('\n');
840: if ( nl == -1 ) {
841: tok = s;
842: } else {
843: tok = s.substring(0,nl);
844: s = s.substring(nl+1);
845: }
1.8 barstow 846: out.print("<a name=\"" + lineNum + "\">" + lineNum +
847: "</a>: " + tok + terminator);
1.1 barstow 848: lineNum++;
849: } while ( nl != -1 );
850:
851: out.println("</pre>");
852: } catch (Exception e) {
1.8 barstow 853: System.err.println("Exception (printListing): " + e.getMessage());
1.1 barstow 854: }
855: }
856:
857: /*
858: * Print the header for the triple listing
859: *
860: *@param out the servlet's output stream
1.14 barstow 861: *@param nTriples if true, output is N-Triples syntax
1.1 barstow 862: */
1.22 duerst 863: private void printTripleTableHeader (PrintWriter out, boolean nTriples)
1.1 barstow 864: {
865: try {
866: if (nTriples) {
1.6 barstow 867: out.println("<h3>Triples of the Data Model in " +
868: "<a href=\"http://www.w3.org/2001/sw/RDFCore/ntriples/\">" +
869: "N-Triples</a> Format (Sub, Pred, Obj)</h3>" +
870: "<pre>");
1.1 barstow 871: } else {
872: out.println("<hr title=\"triples\">");
873: out.println("<h3>Triples of the Data Model</h3>");
874: out.println("<table border><tr>" +
875: "<td><b>Number</b></td>" +
876: "<td><b>Subject</b></td>" +
877: "<td><b>Predicate</b></td>" +
878: "<td><b>Object</b></td>" +
879: "</tr>");
880: }
881: } catch (Exception e) {
1.8 barstow 882: System.err.println("Exception (printTripleTableHeader): " + e.getMessage());
1.1 barstow 883: }
884: }
885:
886: /*
887: * Print the footer info for the triple listing
888: *
889: *@param out the servlet's output stream
1.14 barstow 890: *@param nTriples if true, output is N-Triples syntax
1.1 barstow 891: */
1.22 duerst 892: private void printTripleTableFooter (PrintWriter out,
1.1 barstow 893: boolean nTriples)
894: {
895: try {
896: if (nTriples)
897: out.println("</pre>");
898: else
899: out.println("</table>");
900: } catch (Exception e) {
1.8 barstow 901: System.err.println("Exception (printTripleTableFooter): " + e.getMessage());
1.1 barstow 902: }
903: }
904:
905: /*
906: * Print the document's footer info
907: *
908: *@param out the servlet's output stream
909: *@param rdf the RDF code
910: */
1.22 duerst 911: private void printDocumentFooter (PrintWriter out, String rdf)
1.1 barstow 912: {
913: try {
914:
1.8 barstow 915: String s;
916:
917: s = "<hr title=\"Problem reporting\">" +
918: "<h3>Feedback</h3>" +
919: "<p>If you suspect the parser is in error, please enter an explanation below and then press the <b>Submit problem report</b> button, to mail the report (and listing) to <i>" + MAIL_TO + "</i></p>" +
920: "<form enctype='text/plain' method='post' action='mailto:" + MAIL_TO + "'>" +
1.9 barstow 921: "<textarea cols='60' rows='4' name='report'></textarea>";
1.8 barstow 922: out.println(s);
1.1 barstow 923:
1.9 barstow 924: out.println("<input type='hidden' name='RDF' value=\"<?xml version="1.0">");
925:
1.1 barstow 926: // The listing is being passed as a parameter so the '<'
927: // and '"' characters must be replaced with < and ",
928: // respectively
929: if (rdf != null) {
1.9 barstow 930: String s1;
931: s1 = replaceString(rdf, "<", "<");
1.11 barstow 932: s1 = replaceString(s1, ">", ">");
1.9 barstow 933: s1 = replaceString(s1, "\"", """);
934: out.println(s1);
1.1 barstow 935: }
1.9 barstow 936: out.println("\"\\>");
1.1 barstow 937:
1.8 barstow 938: out.println("<input type='submit' value='Submit problem report'\\>" +
1.9 barstow 939: "</form></body></html>");
1.1 barstow 940:
941: } catch (Exception e) {
1.8 barstow 942: System.err.println("Exception (printDocumentFooter): " + e.getMessage());
1.1 barstow 943: }
944: }
945:
946: /*
947: * Servlet's get info method
948: */
949: public String getServletInfo () {
950: return "Servlet wrapper for the ARP RDF parser. This is revision " + REVISION;
951: }
952:
953: /*
954: * Servlet's init method
955: *
956: *@param config the servlet's configuration object
957: *@throws ServletException
958: */
959: public void init(ServletConfig config) throws ServletException
960: {
961: super.init (config);
962:
963: // Cache the parameters
964: m_ServletTmpDir = config.getInitParameter(SERVLET_TMP_DIR);
965:
966: // All of the Graph Viz paths extend from GRAPH_VIZ_ROOT
967: String GraphVizRoot = config.getInitParameter(GRAPH_VIZ_ROOT);
968:
969: m_GraphVizPath = GraphVizRoot + "/" + config.getInitParameter(GRAPH_VIZ_PATH);
970: m_GraphVizFontDir = GraphVizRoot + "/" + config.getInitParameter(GRAPH_VIZ_FONT_DIR);
1.20 duerst 971: System.out.println("GRAPH_VIZ_ROOT = " + GraphVizRoot);
972: System.out.println("GRAPH_VIZ_PATH = " + m_GraphVizPath);
973: System.out.println("GRAPH_VIZ_FNTDIR = " + m_GraphVizFontDir);
974: System.out.println("SERVLET_TMP_DIR = " + m_ServletTmpDir);
1.1 barstow 975:
976: if (m_ServletTmpDir == null || GraphVizRoot == null) {
977: System.err.println (
978: "<html>" +
979: "<h1>Servlet Initialization Error</h1>" +
980: "<h2>One or more of the following parameters has not been initialized: " +
981: SERVLET_TMP_DIR + "," + GRAPH_VIZ_ROOT + "," +
1.16 barstow 982: GRAPH_VIZ_FONT_DIR + "," + GRAPH_VIZ_PATH + "." +
1.1 barstow 983: "</h2>" +
984: "</html>");
985: }
986: }
987:
988: /*
989: * Servlet's destroy info method
990: */
991: public void destroy () {
992: super.destroy ();
993: }
994:
995: /*
996: * Servlet's doGet info method - NOT supported
997: *
998: *@param req the request
999: *@param res the response
1000: *@throws ServletException, IOException
1001: */
1002: public void doGet (HttpServletRequest req, HttpServletResponse res)
1003: throws ServletException, IOException
1004: {
1005: String sRDF = req.getParameter(TEXT);
1006: String sURI = req.getParameter(URI);
1007:
1008: if (sURI == null && sRDF == null) {
1.22 duerst 1009: res.setContentType ("text/html;charset=utf-8");
1010: PrintWriter out = res.getWriter ();
1.1 barstow 1011:
1.6 barstow 1012: out.println("<h1>Data Error</h1>" +
1013: "Must specify the RDF (RDF is a string) or the URI parameter." +
1014: "</h1>");
1.1 barstow 1015: return;
1016: }
1017:
1.20 duerst 1018: try {
1019: process(req, res,
1020: (sRDF != null) ? java.net.URLDecoder.decode(sRDF) : null,
1021: (sURI != null) ? java.net.URLDecoder.decode(sURI) : null);
1022: } catch (Exception e) {
1023: System.err.println("Exception: URLDecoder.decode()");
1024: }
1.1 barstow 1025: }
1026:
1027: /*
1028: * Servlet's doPost method
1029: *
1030: *@param req the request
1031: *@param res the response
1.17 duerst 1032: *@throws ServletException, IOException, java.io.UnsupportedEncodingException
1.1 barstow 1033: */
1034: public void doPost (HttpServletRequest req, HttpServletResponse res)
1035: throws ServletException, IOException
1036: {
1.19 duerst 1037: // String encoding = req.getCharacterEncoding();
1038: // if (encoding == null) {
1039: // req.setCharacterEncoding("UTF-8");
1040: // }
1.35 ! duerst 1041: String sRDF = new String(req.getParameter(TEXT).getBytes("iso-8859-1"), "utf-8");
! 1042: String sURI = new String(req.getParameter(URI).getBytes("iso-8859-1"), "utf-8");
1.1 barstow 1043:
1044: if (sURI == null && sRDF == null) {
1.22 duerst 1045: res.setContentType ("text/html;charset=utf-8");
1046: PrintWriter out = res.getWriter ();
1.1 barstow 1047:
1048: out.println("<h1>Neither the RDF or an URI was specified" +
1049: "and one of them must be specified.</h1>");
1050: return;
1051: }
1052:
1053: process(req,res,sRDF, sURI);
1054: }
1055:
1056: /*
1057: * Output a Resource in NTriples syntax
1058: *
1059: *@param out the servlet's output stream
1060: *@param r the Resource to output
1061: */
1.22 duerst 1062: static private void printResource(PrintWriter out, AResource r)
1.1 barstow 1063: {
1.26 duerst 1064: if (r.isAnonymous() )
1065: out.print("_:j" + r.getAnonymousID() + " ");
1066: else
1067: out.print("<" + r.getURI() + "> ");
1.1 barstow 1068: }
1069:
1070: /*
1.18 duerst 1071: * Convert to Hex and padd left with zeroes
1072: *
1073: *@param in the integer to convert and padd
1074: *@param in the length of the result
1075: *@return the padded string
1076: */
1077: // MJD: is there an easier way to do this?
1.20 duerst 1078: static private String hexPadd (int number, int length)
1.18 duerst 1079: {
1080: String t = Integer.toHexString(number).toUpperCase();
1.20 duerst 1081: int hexlength = t.length();
1.18 duerst 1082:
1083: if ( hexlength > length ) { // too long, truncate
1084: hexlength = length;
1085: }
1086:
1.20 duerst 1087: int zerolength = length - hexlength;
1.18 duerst 1088: String r = "";
1089:
1090: for (int i=0; i < zerolength; i++) {
1091: r += "0";
1092: }
1093: for (int i=0; i < hexlength; i++) {
1.24 duerst 1094: r += t.charAt(i);
1.18 duerst 1095: }
1096: return r;
1097: }
1098:
1099: /*
1.1 barstow 1100: * Output a Literal in NTriples syntax
1101: *
1102: *@param out the servlet's output stream
1103: *@param l the Literal to output
1104: */
1.22 duerst 1105: static private void printNTripleLiteral(PrintWriter out, ALiteral l)
1.1 barstow 1106: {
1.28 duerst 1107: out.print("\"");
1108: char ar[] = l.toString().toCharArray();
1.1 barstow 1109:
1.28 duerst 1110: for (int i=0;i<ar.length;i++) {
1111: switch (ar[i]) {
1112: case '\\':
1113: out.print("\\\\");
1114: break;
1115: case '"':
1116: out.print("\\\"");
1117: break;
1118: case '\n':
1119: out.print("\\n");
1120: break;
1121: case '\r':
1122: out.print("\\r");
1123: break;
1124: case '\t':
1125: out.print("\\t");
1126: break;
1127: default:
1128: if ( ar[i] >= 32 && ar[i] <= 127 )
1129: out.print(ar[i]);
1130: else if ( ar[i] < 0xD800 || ar[i] >= 0xE000)
1131: out.print("\\u" + hexPadd(ar[i], 4) );
1132: else { // deal with surrogates
1133: // check for correct surrogate pair
1134: // this code should probably move somewhere else:
1135: // check when we get the input
1136: if ( ar[i] >= 0xDC00 ) {
1137: out.print("{{{error: lone low surrogate}}}");
1.18 duerst 1138: }
1.28 duerst 1139: else if ( ++i >= ar.length ) {
1140: out.print("{{{error: lone surrogate at end of string}}}");
1141: }
1142: else if ( ar[i] < 0xDC00 || ar[i] >= 0xE000 ) {
1143: out.print("{{{error: high surrogate not followed by low surrogate}}}");
1144: }
1145: // no errors, actually print
1146: else {
1147: int scalarvalue = 0x10000 + (ar[i-1] * 1024) + ar[i];
1148: out.print("\\U" + hexPadd(scalarvalue, 8) );
1149: }
1150: }
1.1 barstow 1151: }
1.28 duerst 1152: }
1153: out.print("\" ");
1.1 barstow 1154: }
1155:
1156: /*
1157: * Control point for outputing an triple in NTriple syntax
1158: *
1159: *@param out the servlet's output stream
1160: *@param subj the subject
1161: *@param pred the predicate
1162: *@param objRes the object as a Resource (may be null)
1163: *@param objLit the object as a Literal (may be null)
1164: */
1.22 duerst 1165: static private void printNTriple(PrintWriter out, AResource subj,
1.1 barstow 1166: AResource pred, AResource objRes, ALiteral objLit)
1167: {
1.27 duerst 1168: printResource(out, subj);
1169: printResource(out, pred);
1170: if (objRes != null)
1171: printResource(out, objRes);
1172: else
1173: printNTripleLiteral(out, objLit);
1174: out.println(".");
1.1 barstow 1175: }
1176:
1177: /*
1178: * Create a HTML anchor from the URI or anonNode of the
1179: * given Resource
1180: *
1.14 barstow 1181: *@param r the Resource
1.1 barstow 1182: *@return the string as an HTML anchor
1183: */
1184: static private String addAnchor(AResource r)
1185: {
1186: if (r.isAnonymous())
1.14 barstow 1187: return ANON_NODE + r.getAnonymousID();
1.1 barstow 1188: else
1189: return "<a href='" + r.getURI() + "'>" + r.getURI() + "</a>";
1190: }
1191:
1192: /*
1193: * Output a triple as a row in HTML
1194: *
1195: *@param out the servlet's output stream
1196: *@param subj the subject
1197: *@param pred the predicate
1198: *@param objRes the object as a Resource (may be null)
1199: *@param objLit the object as a Literal (may be null)
1200: *@param num the statement number
1201: */
1.22 duerst 1202: static private void printTableRow(PrintWriter out, AResource subj,
1.1 barstow 1203: AResource pred, AResource objRes, ALiteral objLit, int num)
1204: {
1.27 duerst 1205: out.println("<tr><td>" + num + "</td>");
1206: out.println("<td>" + addAnchor(subj) + "</td>");
1207: out.println("<td>" + addAnchor(pred) + "</td>");
1208: if (objRes != null)
1209: out.println("<td>" + addAnchor(objRes) + "</td>");
1210: else {
1211: out.println("<td>");
1212: String s1 = objLit.toString().trim();
1213: s1 = replaceString(s1, "<", "<");
1214: s1 = replaceString(s1, ">", ">");
1215: out.println(s1);
1216: out.println("</td>");
1217: }
1218: out.println("</tr>");
1.1 barstow 1219: }
1220:
1.4 barstow 1221: private static class SH implements StatementHandler
1.1 barstow 1222: {
1.22 duerst 1223: PrintWriter out;
1.12 barstow 1224: PrintWriter pw;
1.1 barstow 1225: boolean isNTriples;
1.12 barstow 1226: boolean printTriples;
1227: boolean printGraph;
1228: boolean anonNodesEmpty;
1.14 barstow 1229: int numStatements;
1230: int numLiterals;
1231: Hashtable subjects;
1232: int numSubjects;
1.1 barstow 1233:
1234: /*
1.4 barstow 1235: * Constructuor for the StatementHandler. The primary
1236: * responsiblitly is to cache init variables
1.1 barstow 1237: *
1.22 duerst 1238: *@param out the servlet's PrintWriter
1.4 barstow 1239: *@param pw the Dot file's PrintWriter
1.1 barstow 1240: * syntax; otherwise use HTML syntax
1.12 barstow 1241: *@param isNTriples if true, output using the NTriples
1242: *@param printTriples if true, print the triples
1243: *@param printGraph if true, create the graph file
1244: *@param printGraph if true, anonomyous nodes should be empty
1.1 barstow 1245: */
1.22 duerst 1246: public SH(PrintWriter out, PrintWriter pw, boolean isNTriples,
1.12 barstow 1247: boolean printTriples, boolean printGraph, boolean anonNodesEmpty)
1.1 barstow 1248: {
1249: this.out = out;
1.12 barstow 1250: this.pw = pw;
1.1 barstow 1251: this.isNTriples = isNTriples;
1.12 barstow 1252: this.printTriples = printTriples;
1253: this.printGraph = printGraph;
1254: this.anonNodesEmpty = anonNodesEmpty;
1.14 barstow 1255:
1256: this.numStatements = 0;
1257: this.numLiterals = 0;
1258:
1259: this.subjects = new Hashtable();
1260: this.numSubjects = 0;
1.4 barstow 1261: }
1262:
1263: /*
1264: * Generic handler for a Resource/Resource/Resource triple (S/P/O).
1265: * Dispatches to the methods that do the real work.
1266: *
1267: *@param subj the subject
1268: *@param pred the predicate
1269: *@param obj the object (as a Resource)
1270: */
1271: public void statement(AResource subj, AResource pred, AResource obj)
1272: {
1.12 barstow 1273: if (printTriples)
1274: statementResource(subj, pred, obj);
1275: if (printGraph)
1276: statementDotResource(subj, pred, obj);
1.4 barstow 1277: }
1278:
1279: /*
1280: * Generic handler for a Resource/Resource/Resource triple (S/P/O).
1281: * Dispatches to the methods that do the real work.
1282: *
1283: *@param subj the subject
1284: *@param pred the predicate
1285: *@param obj the object (as a Literal)
1286: */
1287: public void statement(AResource subj, AResource pred, ALiteral lit)
1288: {
1.13 barstow 1289: numLiterals++;
1290:
1.12 barstow 1291: if (printTriples)
1292: statementLiteral(subj, pred, lit);
1293: if (printGraph)
1294: statementDotLiteral(subj, pred, lit);
1.1 barstow 1295: }
1296:
1297: /*
1298: * Handler for a Resource/Resource/Resource triple (S/P/O)
1299: * Outputs the given triple using NTriples or HTML syntax.
1300: *
1301: *@param subj the subject
1302: *@param pred the predicate
1303: *@param obj the object (as a Resource)
1304: */
1.4 barstow 1305: public void statementResource(AResource subj, AResource pred, AResource obj)
1.1 barstow 1306: {
1307: numStatements++;
1308:
1309: if (this.isNTriples)
1310: printNTriple(out, subj, pred, obj, null);
1311: else
1312: printTableRow(out, subj, pred, obj, null, this.numStatements);
1313: }
1.4 barstow 1314:
1.1 barstow 1315: /*
1316: * Handler for a Resource/Resource/Literal triple (S/P/O)
1317: * Outputs the given triple using NTriples or HTML syntax.
1318: *
1319: *@param subj the subject
1320: *@param pred the predicate
1321: *@param obj the object (as a Literal)
1322: */
1.4 barstow 1323: public void statementLiteral(AResource subj, AResource pred, ALiteral lit)
1.1 barstow 1324: {
1325: numStatements++;
1326:
1327: if (this.isNTriples)
1328: printNTriple(out, subj, pred, null, lit);
1329: else
1330: printTableRow(out, subj, pred, null, lit, this.numStatements);
1331: }
1.4 barstow 1332:
1.12 barstow 1333: /*
1334: * Print the first part of a triple's Dot file. See below for
1335: * more info. This is the same regardless if the triple's
1336: * object is a Resource or a Literal
1337: *
1338: *@param subj the subject
1339: */
1340: public void printFirstPart(AResource subj)
1341: {
1.14 barstow 1342: if (subj.isAnonymous()) {
1.12 barstow 1343: if (this.anonNodesEmpty) {
1.14 barstow 1344: Integer n = (Integer) subjects.get(subj.getAnonymousID());
1345: if (n == null) {
1346: this.numSubjects++;
1347: subjects.put(subj.getAnonymousID(), new Integer(this.numSubjects));
1348: this.pw.println("\"" + ANON_NODE +
1349: subj.getAnonymousID() + "\" [label=\" \"];");
1350: }
1.12 barstow 1351: }
1.14 barstow 1352: this.pw.print("\"" + ANON_NODE + subj.getAnonymousID());
1.12 barstow 1353: } else {
1354: this.pw.println("\"" + subj.getURI() + "\" [URL=\"" +
1355: subj.getURI() + "\"];");
1356: this.pw.print("\"" + subj.getURI());
1357: }
1358: }
1359:
1.4 barstow 1360: /*
1361: * Handler for a Resource/Resource/Resource triple (S/P/O).
1362: * Outputs the given triple using Dot syntax.
1.12 barstow 1363: *
1364: * Each triple will be output in three lines of DOT code as
1365: * follows (not including the complication of anon nodes
1366: * and the possiblity that the anon nodes may be named
1367: * with an empty string):
1368: *
1369: * 1. "<subject>" [URL="<subject">];
1370: * 2. "<subject>" -> "<object>" [label="<predicate>",URL="<predicate>"];
1371: * 3. "<object>" [URL="<object>"];
1.4 barstow 1372: *
1373: *@param subj the subject
1374: *@param pred the predicate
1375: *@param obj the object (as a Resource)
1376: */
1377: public void statementDotResource(AResource subj, AResource pred, AResource obj)
1378: {
1379: if (this.pw == null) return;
1380:
1.12 barstow 1381: printFirstPart(subj);
1382:
1383: this.pw.print("\" -> ");
1.4 barstow 1384:
1.7 barstow 1385: if (obj.isAnonymous()) {
1.12 barstow 1386: if (this.anonNodesEmpty) {
1.14 barstow 1387: this.pw.println("\"" + ANON_NODE +
1.15 barstow 1388: obj.getAnonymousID() +
1389: "\" [label=\"" + pred.getURI() + "\",URL=\"" +
1390: pred.getURI() + "\"];");
1.12 barstow 1391: } else {
1.15 barstow 1392: this.pw.println("\"" + ANON_NODE + obj.getAnonymousID() +
1393: "\" [label=\"" + pred.getURI() + "\",URL=\"" +
1394: pred.getURI() + "\"];");
1.12 barstow 1395: }
1.7 barstow 1396: } else {
1.14 barstow 1397: this.pw.println("\"" + obj.getURI() + "\" [label=\"" +
1398: pred.getURI() + "\",URL=\"" + pred.getURI() + "\"];");
1.12 barstow 1399: this.pw.println("\"" + obj.getURI() + "\" [URL=\"" +
1400: obj.getURI() + "\"];");
1.14 barstow 1401: }
1.4 barstow 1402: }
1403:
1404: /*
1405: * Handler for a Resource/Resource/Literal triple (S/P/O).
1406: * Outputs the given triple using Dot syntax.
1.12 barstow 1407: *
1408: * Each triple will be output in three lines of DOT code as
1409: * follows (not including the complication of anon nodes
1410: * and the possiblity that the anon nodes may be named
1411: * with an empty string):
1412: *
1413: * 1. "<subject>" [URL="<subject">];
1414: * 2. "<subject>" -> "<literal>" [label="<predicate>",URL="<predicate>"];
1415: * 3. "<literal>" [shape="box"];
1.4 barstow 1416: *
1417: *@param subj the subject
1418: *@param pred the predicate
1419: *@param obj the object (as a Literal)
1420: */
1421: public void statementDotLiteral(AResource subj, AResource pred, ALiteral lit)
1422: {
1423: if (this.pw == null) return;
1424:
1.12 barstow 1425: printFirstPart(subj); // Same as Res/Res/Res
1.4 barstow 1426:
1427: /*
1428: * Before outputing the object (Literal) do the following:
1429: *
1430: * o GraphViz/DOT cannot handle embedded line terminators characters
1431: * so they must be replaced with spaces
1432: * o Limit the number of chars to make the graph legible
1433: * o Escape double quotes
1434: */
1435: String s1 = new String(lit.toString());
1436: s1 = s1.replace('\n', ' ');
1437: s1 = s1.replace('\f', ' ');
1438: s1 = s1.replace('\r', ' ');
1439: if (s1.indexOf('"') != -1)
1440: s1 = replaceString(s1, "\"", "\\\"");
1441:
1442: // Anything beyond 80 chars makes the graph too large
1443: String tmpObject;
1444: if (s1.length() >= 80)
1445: tmpObject = s1.substring(0, 80) + " ...";
1446: else
1447: tmpObject = s1.substring(0, s1.length());
1448:
1.13 barstow 1449: // Create a temporary label for the literal so that if
1450: // it is duplicated it will be unique in the graph and
1451: // thus have its own node.
1452: String tmpName = "Literal_" + Integer.toString(this.numLiterals);
1453: this.pw.print("\" -> \"" + tmpName);
1.4 barstow 1454:
1.14 barstow 1455: this.pw.println("\" [label=\"" + pred.getURI() +
1456: "\",URL=\"" + pred.getURI() + "\"];");
1.4 barstow 1457:
1.13 barstow 1458: this.pw.println("\"" + tmpName + "\" [shape=box,label=\"" + tmpObject + "\"];");
1.4 barstow 1459: }
1460: }
1461:
1.22 duerst 1462: private void printErrorMessages(PrintWriter out, SaxErrorHandler eh)
1.5 barstow 1463: {
1464: try {
1465: String s;
1466:
1467: s = eh.getFatalErrors();
1468: if (s != null && s.length() >= 1)
1469: out.println("<h2>Fatal Error Messages</h2>" + s);
1470:
1471: s = eh.getErrors();
1472: if (s != null && s.length() >= 1)
1473: out.println("<h2>Error Messages</h2>" + s);
1474:
1475: s = eh.getWarnings();
1476: if (s != null && s.length() >= 1)
1477: out.println("<h2>Warning Messages</h2>" + s);
1478: } catch (Exception e) {
1479: System.err.println(SERVLET_NAME + ": Error printing error messages.");
1480: }
1481: }
1482:
1483: /*
1484: * Initialize the graph output file. If an error occurs, this
1485: * function will print an error message.
1486: *
1487: *@param out the servlet's output stream
1488: *@req the servlet request object
1489: *@return the File object for the graph file; null if an error occurs
1490: */
1.22 duerst 1491: private File initGraphFile(PrintWriter out,
1.4 barstow 1492: HttpServletRequest req)
1493: {
1494: try {
1495: // Stop if any of the parameters are missing
1496: if (m_ServletTmpDir == null ||
1497: m_GraphVizPath == null ||
1.16 barstow 1498: m_GraphVizFontDir == null)
1.4 barstow 1499: {
1500: // Put the paths in a comment in the returned content
1501: out.println("<!-- SERVLET_TMP_DIR = " + m_ServletTmpDir);
1502: out.println("GRAPH_VIZ_PATH = " + m_GraphVizPath);
1503: out.println("GRAPH_FONT_DIR = " + m_GraphVizFontDir + " -->");
1504:
1.12 barstow 1505: out.println("<h1>Servlet initialization failed</h1>");
1506: out.println("Please send a message to <a href='mailto:" + MAIL_TO + "'>" + MAIL_TO + "</a> and mention this problem.");
1.4 barstow 1507: return null;
1508: }
1509: } catch (Exception e) {
1510: System.err.println("Unable to create a temporary graph file. A graph cannot be generated.");
1511: return null;
1512: }
1513:
1514: File dotFile = null;
1515:
1.27 duerst 1516: // Must generate a unique file name that the DOT handler will use
1517: dotFile = createTempFile(m_ServletTmpDir, TMP_FILE_PREFIX, SUFFIX_DOT);
1518: if (dotFile == null) {
1519: out.println("<h1>Failed to create a temporary graph file. A graph cannot be generated.</h1>");
1520: return null;
1521: }
1.4 barstow 1522:
1523: return dotFile;
1.1 barstow 1524: }
1.6 barstow 1525:
1526: /*
1527: * Check if the given URI is supported or not
1528: *
1529: *@param out the servlet's output stream
1530: *@param uri the URI to check
1531: *@return true if the URI is supported; false otherwise
1532: */
1.22 duerst 1533: private boolean isURISupported(PrintWriter out, String uri)
1.6 barstow 1534: {
1535: try {
1536: if (uri.length() >= 4 && uri.substring(0,4).equalsIgnoreCase("file")) {
1537: out.println("<h1>file URI Schemes are NOT Supported</h1>");
1538: out.println("URIs from the 'file' URI scheme are not supported by this servlet.");
1539: return false;
1540: }
1541: } catch (Exception e) {
1542: System.err.println("Exception in isURISupported.");
1543: return false;
1544: }
1545:
1546: return true;
1547: }
1.1 barstow 1548:
1549: /*
1550: * Handle the servlets doGet or doPut request
1551: *
1552: *@param req the servlet's request
1553: *@param res the servlet's response
1554: *@throws SevletException, IOException
1555: */
1556: private void process(HttpServletRequest req, HttpServletResponse res,
1557: String sRDF, String sURI) throws ServletException, IOException
1558: {
1.21 duerst 1559: res.setContentType ("text/html;charset=utf-8");
1.22 duerst 1560: PrintWriter out = res.getWriter ();
1.10 barstow 1561:
1.12 barstow 1562: String sSaveRDF = req.getParameter (SAVE_RDF);
1563: String sSaveDOTFile = req.getParameter (SAVE_DOT_FILE);
1564: String sFormat = req.getParameter (FORMAT);
1565: String sNTriples = req.getParameter (NTRIPLES);
1566: String sEmbedded = req.getParameter (EMBEDDED_RDF);
1567: String sTriplesAndGraph = req.getParameter (TRIPLES_AND_GRAPH);
1568: String sAnonNodesEmpty = req.getParameter (ANON_NODES_EMPTY);
1569:
1570: // Set the print flags
1571: boolean printTriples = true;
1572: boolean printGraph = true;
1573: if (sTriplesAndGraph != null) {
1574: if (sTriplesAndGraph.equals(PRINT_TRIPLES))
1575: printGraph = false;
1576: if (sTriplesAndGraph.equals(PRINT_GRAPH))
1577: printTriples = false;
1578: }
1.1 barstow 1579:
1.12 barstow 1580: // Determine if printing the triples and/or graph
1581: boolean anonNodesEmpty = (sAnonNodesEmpty != null) ? true : false;
1.1 barstow 1582: boolean nTriples = (sNTriples != null) ? true : false;
1.12 barstow 1583:
1.10 barstow 1584: // ARP parser has embedded = true by default so if user
1585: // wants embedding, must set it to false
1586: boolean embedded = (sEmbedded != null) ? false : true;
1.1 barstow 1587:
1588: String sError = null;
1589:
1590: InputStream in = null;
1591: String xmlBase = DEFAULT_NAMESPACE;
1592:
1593: printDocumentHeader (out);
1594:
1595: if (sURI != null && sURI.length() >= 1) {
1.6 barstow 1596:
1597: // First check for unsupported URIs
1598: if (!isURISupported(out, sURI)) {
1599: printDocumentFooter(out, null);
1600: return;
1601: }
1602:
1.1 barstow 1603: xmlBase = sURI;
1604: try {
1605: File ff = new File(sURI);
1606: in = new FileInputStream(ff);
1.6 barstow 1607: } catch (Exception ignore) {
1.1 barstow 1608: try {
1609: URL url;
1610: url=new URL(sURI);
1611: in = url.openStream();
1.31 duerst 1612: sRDF = getRDFfromURI(sURI);
1.1 barstow 1613: if (sRDF == null)
1.6 barstow 1614: sError = "error";
1.1 barstow 1615: } catch (Exception e) {
1.6 barstow 1616: sError = "error";
1.1 barstow 1617: }
1618: }
1619: }
1620:
1621: if (sError != null) {
1.6 barstow 1622: out.println("<h1>RDF Load Error</h1>");
1623: out.println("An attempt to load the RDF from URI '" + sURI + "' failed. (The URI may not exist or the server is down.)");
1.1 barstow 1624: printDocumentFooter(out, null);
1625: return;
1626: }
1.4 barstow 1627:
1628: PrintWriter pw = null; // The writer for the graph file
1629: File dotFile = null; // The graph file
1.12 barstow 1630: if (sFormat != null && printGraph) {
1.4 barstow 1631: dotFile = initGraphFile(out, req);
1632: if (dotFile == null)
1633: // Assume error has been reported
1634: return;
1635:
1636: // Create a PrintWriter for the DOT handler
1637: FileWriter fw = new FileWriter(dotFile);
1638: if (fw != null)
1639: pw = new PrintWriter(fw);
1640: if (pw != null)
1641: // Add the graph header
1642: processGraphParameters (req, pw);
1643: }
1644:
1645: // Create the StatementHandler - it will handle triples for
1646: // the table/ntriples and the graph file
1.12 barstow 1647: SH sh = new SH(out, pw, nTriples, printTriples, printGraph, anonNodesEmpty);
1.4 barstow 1648:
1649: // Create the ErrorHandler
1650: SaxErrorHandler errorHandler = new SaxErrorHandler(out, false);
1651:
1652: // Create and initialize the parser
1653: ARP parser = new com.hp.hpl.jena.rdf.arp.ARP();
1654: parser.setErrorHandler(errorHandler);
1655: parser.setStatementHandler(sh);
1.10 barstow 1656: parser.setEmbedding(embedded);
1.1 barstow 1657:
1658: printListing (out, sRDF, sURI != null && sURI.length() >= 1);
1.12 barstow 1659:
1660: if (printTriples)
1661: printTripleTableHeader (out, nTriples);
1.1 barstow 1662:
1663: try {
1664: if (sURI != null && sURI.length() >= 1) {
1665: parser.load(in, xmlBase);
1666: } else {
1667: StringReader sr = new StringReader (sRDF);
1668: parser.load(sr, xmlBase);
1669: }
1670: } catch (IOException ioe) {
1671: sError = "IOError parsing: " + sURI + ": " + ioe.toString();
1672: } catch (Exception ex) {
1673: sError = "Exception parsing: " + sURI + ": " + ex.toString();
1674: }
1675:
1.12 barstow 1676: if (printTriples)
1677: printTripleTableFooter(out, nTriples);
1.5 barstow 1678:
1679: printErrorMessages(out, errorHandler);
1.1 barstow 1680:
1681: if (sError != null) {
1.6 barstow 1682: out.println ("<h1>Parser Loading Error</h1>");
1683: out.println (sError);
1.1 barstow 1684: printDocumentFooter(out, null);
1685: return;
1686: }
1687:
1.10 barstow 1688: res.flushBuffer();
1.12 barstow 1689: if (sFormat != null && printGraph) {
1.4 barstow 1690: generateGraph(out, pw, dotFile, sRDF, req, sFormat,
1.1 barstow 1691: (sSaveRDF != null) ? true : false,
1692: (sSaveDOTFile != null && sSaveDOTFile.equals ("on") ? true : false));
1693: }
1694:
1695:
1.9 barstow 1696: if (sURI != null && sURI.length() >= 1)
1697: printDocumentFooter(out, null);
1698: else
1699: printDocumentFooter(out, sRDF);
1.1 barstow 1700: }
1701: }
Webmaster