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