22 |
package nongnu.cashews.rdf; |
package nongnu.cashews.rdf; |
23 |
|
|
24 |
import java.io.File; |
import java.io.File; |
|
import java.io.FileInputStream; |
|
25 |
import java.io.FileNotFoundException; |
import java.io.FileNotFoundException; |
26 |
import java.io.IOException; |
import java.io.IOException; |
|
import java.net.URI; |
|
|
import java.net.URISyntaxException; |
|
|
import java.util.HashSet; |
|
|
import java.util.Set; |
|
27 |
import java.util.logging.ConsoleHandler; |
import java.util.logging.ConsoleHandler; |
28 |
import java.util.logging.Handler; |
import java.util.logging.Handler; |
29 |
import java.util.logging.Level; |
import java.util.logging.Level; |
|
import java.util.logging.Logger; |
|
30 |
|
|
31 |
import nongnu.cashews.commons.Pair; |
import nongnu.cashews.xml.Parser; |
|
import nongnu.cashews.xml.XmlBaseHandler; |
|
32 |
|
|
|
import org.xml.sax.Attributes; |
|
|
import org.xml.sax.InputSource; |
|
33 |
import org.xml.sax.SAXException; |
import org.xml.sax.SAXException; |
|
import org.xml.sax.XMLReader; |
|
|
import org.xml.sax.helpers.XMLReaderFactory; |
|
34 |
|
|
35 |
/** |
/** |
36 |
* This class deals with the parsing of RDF in XML form, as defined by the |
* This class is simply a wrapper around the existing parser to enable |
37 |
* <a href="http://www.w3.org/TR/rdf-syntax-grammar/">RDFXML </a> syntax |
* the testing of RDF parsing. |
|
* standard. It turns an XML-based representation of an RDF graph into a |
|
|
* <code>Graph</code> object. |
|
38 |
* |
* |
39 |
* @author Andrew John Hughes (gnu_andrew@member.fsf.org) |
* @author Andrew John Hughes (gnu_andrew@member.fsf.org) |
|
* @see Graph |
|
40 |
*/ |
*/ |
41 |
public class XMLParser |
public class XMLParser |
42 |
|
extends Parser |
43 |
{ |
{ |
44 |
|
|
45 |
/** |
/** |
46 |
* The RDF namespace. |
* Constructs a new RDF-based parser, using the specified handler |
|
*/ |
|
|
public static final String |
|
|
RDF_NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; |
|
|
|
|
|
/** |
|
|
* The reader, which interprets the XML file. |
|
|
* |
|
|
* @serial the XML reader. |
|
|
*/ |
|
|
private XMLReader reader; |
|
|
|
|
|
/** |
|
|
* The resulting graph. |
|
|
* |
|
|
* @serial the graph of RDF triples. |
|
|
*/ |
|
|
private Graph graph; |
|
|
|
|
|
/** |
|
|
* The handler for the XML. |
|
|
* |
|
|
* @serial the XML handler. |
|
|
*/ |
|
|
private RDFHandler rdfHandler; |
|
|
|
|
|
/** |
|
|
* Constructs a new XML-based RDF parser, using the specified handler |
|
47 |
* for messages. |
* for messages. |
48 |
* |
* |
49 |
* @param handler the handler to use for log messages. |
* @param logHandler the handler to use for log messages. |
50 |
* @throws SAXException if the parser fails to obtain an |
* @throws SAXException if the parser fails to obtain an |
51 |
* <code>XMLReader</code> instance. |
* <code>XMLReader</code> instance. |
52 |
*/ |
*/ |
53 |
public XMLParser(Handler handler) |
public XMLParser(Handler handler) |
54 |
throws SAXException |
throws SAXException |
55 |
{ |
{ |
56 |
reader = XMLReaderFactory.createXMLReader(); |
super(handler, new RDFHandler(handler)); |
|
rdfHandler = new RDFHandler(handler); |
|
|
reader.setContentHandler(rdfHandler); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Parse an RDF document from the specified system identifier (URI). |
|
|
* |
|
|
* @param systemId the URI of the RDF document. |
|
|
* @return the graph generated by the parsing process. |
|
|
* @throws SAXException if an error occurs in parsing. |
|
|
* @throws IOException if an error occurs in the underlying input. |
|
|
*/ |
|
|
public Graph parse(String systemId) |
|
|
throws IOException, SAXException |
|
|
{ |
|
|
return parse(new InputSource(systemId)); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Parse an RDF document from the specified input source. |
|
|
* |
|
|
* @param source the source of the RDF document. |
|
|
* @return the graph generated by the parsing process. |
|
|
* @throws SAXException if an error occurs in parsing. |
|
|
* @throws IOException if an error occurs in the underlying input. |
|
|
*/ |
|
|
public Graph parse(InputSource source) |
|
|
throws IOException, SAXException |
|
|
{ |
|
|
graph = new Graph(); |
|
|
rdfHandler.setBaseURI(source.getSystemId()); |
|
|
reader.parse(source); |
|
|
return graph; |
|
57 |
} |
} |
58 |
|
|
59 |
/** |
/** |
60 |
* Parse an RDF document from the specified file. |
* Retrieves the RDF handler used by the parser. |
61 |
* |
* |
62 |
* @param file the file containing the RDF document. |
* @return the XML event handler. |
|
* @return the graph generated by the parsing process. |
|
|
* @throws SAXException if an error occurs in parsing. |
|
|
* @throws IOException if an error occurs in the underlying input. |
|
63 |
*/ |
*/ |
64 |
public Graph parse(File file) |
public RDFHandler getRDFHandler() |
|
throws IOException, SAXException |
|
65 |
{ |
{ |
66 |
return parse(new InputSource(new FileInputStream(file))); |
return (RDFHandler) getHandler(); |
67 |
} |
} |
68 |
|
|
69 |
/** |
/** |
84 |
handler.setLevel(Level.FINE); |
handler.setLevel(Level.FINE); |
85 |
XMLParser parser = new XMLParser(handler); |
XMLParser parser = new XMLParser(handler); |
86 |
for (int a = 0; a < args.length; ++a) |
for (int a = 0; a < args.length; ++a) |
|
System.out.println(parser.parse(new File(args[a]))); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Handles capturing the XML events and turning them into an RDF |
|
|
* <code>Graph</code>. |
|
|
* |
|
|
* @author Andrew John Hughes (gnu_andrew@member.fsf.org) |
|
|
*/ |
|
|
private class RDFHandler |
|
|
extends XmlBaseHandler |
|
|
{ |
|
|
|
|
|
/** |
|
|
* A <code>Logger</code> instance to log events generated by the |
|
|
* parsing process. |
|
|
*/ |
|
|
private Logger rdfLogger; |
|
|
|
|
|
/** |
|
|
* Flag to indicate whether we are inside an RDF document |
|
|
* or not. There is not a one-to-one correspondence between |
|
|
* an RDF document and the XML document. RDF is to usually |
|
|
* to be expected inside an RDF element. However, a single |
|
|
* <code>Description</code> element may occur on its own, outside |
|
|
* this context. In either of these cases, this flag should |
|
|
* be set. |
|
|
*/ |
|
|
private boolean inRDF; |
|
|
|
|
|
/** |
|
|
* Flag to indicate whether we are inside a subject node or |
|
|
* not. |
|
|
*/ |
|
|
private boolean inSubject; |
|
|
|
|
|
/** |
|
|
* Flag to indicate whether we are inside a predicate node or |
|
|
* not. |
|
|
*/ |
|
|
private boolean inPredicate; |
|
|
|
|
|
/** |
|
|
* The current triple. |
|
|
*/ |
|
|
private Triple triple; |
|
|
|
|
|
/** |
|
|
* The current subject in the current RDF triple. |
|
|
*/ |
|
|
private Subject subject; |
|
|
|
|
|
/** |
|
|
* The current predicate in the current RDF triple. |
|
|
*/ |
|
|
private Predicate predicate; |
|
|
|
|
|
/** |
|
|
* The current object in the current RDF triple. |
|
|
*/ |
|
|
private RDFObject object; |
|
|
|
|
|
/** |
|
|
* The String form of the URI of the current predicate. |
|
|
*/ |
|
|
private String predicateURI; |
|
|
|
|
|
/** |
|
|
* The type of the current literal. |
|
|
*/ |
|
|
private Type type; |
|
|
|
|
|
/** |
|
|
* The set of ids used in the RDF document. RDF IDs must be unique |
|
|
* within a particular base URI. |
|
|
*/ |
|
|
private Set<Pair<URI,String>> ids; |
|
|
|
|
|
/** |
|
|
* Constructs a new <code>RDFHandler</code>, using the specified |
|
|
* handler for log messages. |
|
|
* |
|
|
* @param handler the handler to use for log messages. |
|
|
*/ |
|
|
public RDFHandler(Handler handler) |
|
|
{ |
|
|
super(handler); |
|
|
rdfLogger = Logger.getLogger("nongnu.cashews.rdf.XMLParser"); |
|
|
rdfLogger.addHandler(handler); |
|
|
rdfLogger.setLevel(handler.getLevel()); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Captures the start of the document and sets up the initial |
|
|
* state. |
|
|
*/ |
|
|
public void startDocument() |
|
|
{ |
|
|
super.startDocument(); |
|
|
inSubject = false; |
|
|
inRDF = false; |
|
|
inPredicate = false; |
|
|
predicateURI = null; |
|
|
type = null; |
|
|
subject = null; |
|
|
predicate = null; |
|
|
object = null; |
|
|
ids = new HashSet<Pair<URI,String>>(); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Captures the start of an XML element. |
|
|
* |
|
|
* @param uri the namespace URI. |
|
|
* @param localName the local name of the element inside the namespace. |
|
|
* @param qName the local name qualified with the namespace URI. This |
|
|
* may or may not be provided, as we don't ask for namespace |
|
|
* prefixes. |
|
|
* @param attributes the attributes of this element. |
|
|
* @throws SAXException if some error occurs in parsing. |
|
|
*/ |
|
|
public void startElement(String uri, String localName, |
|
|
String qName, Attributes attributes) |
|
|
throws SAXException |
|
|
{ |
|
|
super.startElement(uri, localName, qName, attributes); |
|
|
if (uri.equals(RDF_NAMESPACE)) |
|
|
{ |
|
|
if (localName.equals("RDF")) |
|
|
{ |
|
|
/* rdf:RDF */ |
|
|
inRDF = true; |
|
|
rdfLogger.finer("Start of RDF block"); |
|
|
} |
|
|
else if (localName.equals("Description")) |
|
|
{ |
|
|
/* rdf:Description */ |
|
|
rdfLogger.finer("Start of RDF description block"); |
|
|
inRDF = true; |
|
|
if (!inSubject) |
|
|
{ |
|
|
/* Check for RDF URI subject */ |
|
|
String value = attributes.getValue(RDF_NAMESPACE, "about"); |
|
|
if (value != null) |
|
|
subject = parseRDFURI(value); |
|
|
else |
|
|
{ |
|
|
value = attributes.getValue(RDF_NAMESPACE, "ID"); |
|
|
if (value != null) |
|
|
{ |
|
|
boolean added = |
|
|
ids.add(new Pair<URI,String>(getBaseURI(), value)); |
|
|
if (added) |
|
|
subject = new |
|
|
RDFURI(getBaseURI().resolve("#" + value)); |
|
|
} |
|
|
else |
|
|
{ |
|
|
/* Check for blank node subject */ |
|
|
value = attributes.getValue(RDF_NAMESPACE, "nodeID"); |
|
|
if (value != null) |
|
|
subject = new Blank(value); |
|
|
} |
|
|
} |
|
|
if (subject == null) |
|
|
rdfLogger.severe("No subject found in RDF " + |
|
|
"description."); |
|
|
else |
|
|
{ |
|
|
inSubject = true; |
|
|
rdfLogger.fine("Created subject: " + subject); |
|
|
} |
|
|
} |
|
|
else |
|
|
rdfLogger.warning("Invalid use of RDF namespace: " + uri + |
|
|
localName); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
if (inRDF) |
|
|
{ |
|
|
if (inSubject) |
|
|
{ |
|
|
/* Predicate element */ |
|
|
predicateURI = uri + localName; |
|
|
rdfLogger.finer("Start of predicate: " + predicateURI); |
|
|
predicate = parseRDFURI(predicateURI); |
|
|
rdfLogger.fine("Created predicate: " + predicate); |
|
|
inSubject = false; |
|
|
inPredicate = true; |
|
|
/* Check for RDF URI object */ |
|
|
String value = attributes.getValue(RDF_NAMESPACE, |
|
|
"resource"); |
|
|
if (value != null) |
|
|
{ |
|
|
object = parseRDFURI(value); |
|
|
rdfLogger.fine("Created object: " + object); |
|
|
} |
|
|
else |
|
|
{ |
|
|
/* Check for blank node object */ |
|
|
value = attributes.getValue(RDF_NAMESPACE, "nodeID"); |
|
|
if (value != null) |
|
|
{ |
|
|
object = new Blank(value); |
|
|
rdfLogger.fine("Created object: " + object); |
|
|
} |
|
|
} |
|
|
/* Check for a type */ |
|
|
value = attributes.getValue(RDF_NAMESPACE, "datatype"); |
|
|
if (value != null) |
|
|
type = TypeFactory.getInstance(value); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Attempts to returns the RDF URI parsed from the specified |
|
|
* <code>String</code>. |
|
|
* |
|
|
* @param value the value to parse. |
|
|
* @return an RDF URI. |
|
|
* @throws SAXException if the URI can't be parsed. |
|
|
*/ |
|
|
private RDFURI parseRDFURI(String value) |
|
|
throws SAXException |
|
|
{ |
|
|
try |
|
|
{ |
|
|
return new RDFURI(new URI(value)); |
|
|
} |
|
|
catch (URISyntaxException e) |
|
|
{ |
|
|
throw new SAXException("Failed to parse URI: " + e, e); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Captures characters within an XML element. |
|
|
* |
|
|
* @param ch the array of characters. |
|
|
* @param start the start index of the characters to use. |
|
|
* @param length the number of characters to use from the start index on. |
|
|
* @throws SAXException if some error occurs in parsing. |
|
|
*/ |
|
|
public void characters(char[] ch, int start, int length) |
|
|
throws SAXException |
|
|
{ |
|
|
super.characters(ch, start, length); |
|
|
String value = new String(ch, start, length).trim(); |
|
|
if (value.length() == 0) |
|
|
return; |
|
|
rdfLogger.finer("Characters: " + value); |
|
|
if (inPredicate) |
|
|
{ |
|
|
if (type == null) |
|
|
object = new Literal(value); |
|
|
else |
|
|
object = new Literal(value, type); |
|
|
rdfLogger.fine("Created object: " + object); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Captures the end of an XML element. |
|
|
* |
|
|
* @param uri the namespace URI. |
|
|
* @param localName the local name of the element inside the namespace. |
|
|
* @param qName the local name qualified with the namespace URI. This |
|
|
* may or may not be provided, as we don't ask for namespace |
|
|
* prefixes. |
|
|
* @throws SAXException if some error occurs in parsing. |
|
|
*/ |
|
|
public void endElement(String uri, String localName, |
|
|
String qName) |
|
|
throws SAXException |
|
|
{ |
|
|
super.endElement(uri, localName, qName); |
|
|
if (uri.equals(RDF_NAMESPACE)) |
|
|
{ |
|
|
if (localName.equals("RDF")) |
|
|
{ |
|
|
inRDF = false; |
|
|
rdfLogger.finer("End of RDF block"); |
|
|
} |
|
|
else if (localName.equals("Description")) |
|
|
{ |
|
|
rdfLogger.finer("End of description block"); |
|
|
inSubject = false; |
|
|
} |
|
|
} |
|
|
else if (inPredicate && predicateURI.equals(uri + localName)) |
|
87 |
{ |
{ |
88 |
inPredicate = false; |
parser.parse(new File(args[a])); |
89 |
inSubject = true; |
System.out.println(parser.getRDFHandler().getGraph()); |
|
predicateURI = null; |
|
|
type = null; |
|
|
triple = new Triple(subject, predicate, object); |
|
|
rdfLogger.fine("Created triple: " + triple); |
|
|
graph.addTriple(triple); |
|
|
rdfLogger.finer("End of predicate block"); |
|
90 |
} |
} |
|
} |
|
|
|
|
91 |
} |
} |
92 |
|
|
93 |
} |
} |