001: /*
002: * (c) Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: * [See end of file]
004: */
005:
006: package com.hp.hpl.jena.n3;
007:
008: import antlr.collections.AST;
009: import java.util.*;
010: import com.hp.hpl.jena.rdf.model.*;
011: import com.hp.hpl.jena.shared.*;
012:
013: import com.hp.hpl.jena.vocabulary.*;
014: import org.apache.commons.logging.Log;
015: import org.apache.commons.logging.LogFactory;
016:
017: /**
018: * @author Andy Seaborne
019: * @version $Id: N3toRDF.java,v 1.38 2008/01/02 12:04:49 andy_seaborne Exp $
020: */
021: public class N3toRDF implements N3ParserEventHandler {
022: protected static Log log = LogFactory.getLog(N3toRDF.class);
023:
024: Model model;
025:
026: // Maps URIref or a _:xxx bNode to a Resource
027: Map resourceRef = new HashMap();
028: // Maps URIref to Property
029: Map propertyRef = new HashMap();
030:
031: // A more liberal prefix mapping map.
032: Map myPrefixMapping = new HashMap();
033:
034: boolean allowPropertySymbols = true;
035: boolean allowKeywordA = true;
036:
037: // Well known namespaces
038:
039: static final String NS_rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
040: static final String NS_rdfs = "http://www.w3.org/2000/01/rdf-schema#";
041:
042: static final String NS_W3_log = "http://www.w3.org/2000/10/swap/log#";
043: static final String LOG_IMPLIES = NS_W3_log + "implies";
044: static final String LOG_MEANS = NS_W3_log + "means";
045:
046: static final String XMLLiteralURI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral";
047:
048: //private String base = null ;
049: private IRIResolver resolver = null;
050: //private String basedir = null ;
051: final String anonPrefix = "_";
052:
053: public N3toRDF(Model model, String base) {
054: resolver = new IRIResolver(base);
055: this .model = model;
056: }
057:
058: public void startDocument() {
059: }
060:
061: public void endDocument() {
062: }
063:
064: // When Jena exceptions are runtime, we will change this
065: public void error(Exception ex, String message) {
066: throw new N3Exception(message);
067: }
068:
069: public void error(String message) {
070: error(null, message);
071: }
072:
073: public void warning(Exception ex, String message) {
074: log.warn(message, ex);
075: }
076:
077: public void warning(String message) {
078: log.warn(message);
079: }
080:
081: public void deprecated(Exception ex, String message) {
082: throw new N3Exception(message);
083: }
084:
085: public void deprecated(String message) {
086: deprecated(null, message);
087: }
088:
089: public void startFormula(int line, String context) {
090: error("Line "
091: + line
092: + ": N3toRDF: All statements are asserted - no formulae in RDF");
093: }
094:
095: public void endFormula(int line, String context) {
096: }
097:
098: public void directive(int line, AST directive, AST[] args,
099: String context) {
100: if (directive.getType() == N3Parser.AT_PREFIX) {
101: // @prefix now
102: if (args[0].getType() != N3Parser.QNAME) {
103: error("Line "
104: + line
105: + ": N3toRDF: Prefix directive does not start with a prefix! "
106: + args[0].getText() + "["
107: + N3Parser.getTokenNames()[args[0].getType()]
108: + "]");
109: return;
110: }
111:
112: String prefix = args[0].getText();
113: if (prefix.endsWith(":"))
114: prefix = prefix.substring(0, prefix.length() - 1);
115:
116: if (args[1].getType() != N3Parser.URIREF) {
117: error("Line "
118: + line
119: + ": N3toRDF: Prefix directive does not supply a URIref! "
120: + args[1].getText());
121: return;
122: }
123:
124: String uriref = args[1].getText();
125: uriref = expandURIRef(uriref, line);
126:
127: if (uriref == null)
128: error("Line "
129: + line
130: + ": N3toRDF: Relative URI can't be resolved in in @prefix directive");
131:
132: setPrefixMapping(model, prefix, uriref);
133: return;
134: }
135:
136: warning("Line " + line
137: + ": N3toRDF: Directive not recongized and ignored: "
138: + directive.getText());
139: return;
140: }
141:
142: public void quad(int line, AST subj, AST prop, AST obj,
143: String context) {
144: // Syntax that reverses subject and object is done in the grammar
145:
146: if (context != null)
147: error("Line "
148: + line
149: + ": N3toRDF: All statement are asserted - no formulae");
150:
151: try {
152: // Converting N3 to RDF:
153: // subject: must be a URIref or a bNode name
154: // property: remove sugaring and then must be a URIref
155: // object: can be a literal or a URIref or a bNode name
156: // context must be zero (no formulae)
157:
158: // Lists: The parser creates list elements as sequences of triples:
159: // anon list:first ....
160: // anon list:rest resource
161: // Where "resource" is nil for the last element of the list (generated first).
162:
163: // The properties are in a unique namespace to distinguish them
164: // from lists encoded explicitly, not with the () syntax.
165:
166: int pType = prop.getType();
167: String propStr = prop.getText();
168: Property pNode = null;
169:
170: switch (pType) {
171: case N3Parser.ARROW_R:
172: if (!allowPropertySymbols)
173: error("Line "
174: + line
175: + ": N3toRDF: Property symbol '=>' not allowed");
176: propStr = LOG_IMPLIES;
177: break;
178: case N3Parser.ARROW_MEANS:
179: if (!allowPropertySymbols)
180: error("Line "
181: + line
182: + ": N3toRDF: Property symbol '<=>' not allowed");
183: propStr = LOG_MEANS;
184: break;
185: case N3Parser.ARROW_L:
186: if (!allowPropertySymbols)
187: error("Line "
188: + line
189: + ": N3toRDF: Property symbol '<=' not allowed");
190: // Need to reverse subject and object
191: propStr = LOG_IMPLIES;
192: AST tmp = obj;
193: obj = subj;
194: subj = tmp;
195: break;
196: case N3Parser.EQUAL:
197: //propStr = NS_DAML + "equivalentTo";
198: //propStr = damlVocab.equivalentTo().getURI() ;
199: if (!allowPropertySymbols)
200: error("Line "
201: + line
202: + ": N3toRDF: Property symbol '=' not allowed");
203: pNode = OWL.sameAs;
204: break;
205: case N3Parser.KW_A:
206: if (!allowKeywordA)
207: error("Line "
208: + line
209: + ": N3toRDF: Property symbol 'a' not allowed");
210: pNode = RDF.type;
211: break;
212: case N3Parser.QNAME:
213:
214: if (prop.getText().startsWith("_:"))
215: error("Line "
216: + line
217: + ": N3toRDF: Can't have properties with labelled bNodes in RDF");
218:
219: String uriref = expandPrefix(model, propStr);
220: if (uriref == propStr) {
221: // Failed to expand ...
222: error("Line " + line
223: + ": N3toRDF: Undefined qname namespace: "
224: + propStr);
225: return;
226: }
227: pNode = model.createProperty(uriref);
228: break;
229: case N3Parser.URIREF:
230: propStr = expandURIRef(propStr, line);
231: break;
232: case N3Parser.TK_LIST_FIRST:
233: pNode = RDF.first;
234: break;
235: case N3Parser.TK_LIST_REST:
236: pNode = RDF.rest;
237: break;
238: // Literals, parser generated bNodes (other bnodes handled as QNAMEs)
239: // and tokens that can't be properties.
240: case N3Parser.ANON:
241: error("Line "
242: + line
243: + ": N3toRDF: Can't have anon. properties in RDF");
244: break;
245: default:
246: error("Line " + line + ": N3toRDF: Shouldn't see "
247: + N3EventPrinter.formatSlot(prop)
248: + " at this point!");
249: break;
250: }
251:
252: // Didn't find an existing one above so make it ...
253: if (pNode == null)
254: pNode = model.createProperty(propStr);
255: else
256: propStr = pNode.getURI();
257:
258: RDFNode sNode = createNode(line, subj);
259: // Must be a resource
260: if (sNode instanceof Literal)
261: error("Line " + line
262: + ": N3toRDF: Subject can't be a literal: "
263: + subj.getText());
264:
265: RDFNode oNode = createNode(line, obj);
266:
267: Statement stmt = model.createStatement((Resource) sNode,
268: pNode, oNode);
269: model.add(stmt);
270: } catch (JenaException rdfEx) {
271: error("Line " + line + ": JenaException: " + rdfEx);
272: }
273: }
274:
275: private Map bNodeMap = new HashMap();
276:
277: private RDFNode createNode(int line, AST thing) {
278: //String tokenType = N3AntlrParser._tokenNames[thing.getType()] ;
279: //System.out.println("Token type: "+tokenType) ;
280: String text = thing.getText();
281: switch (thing.getType()) {
282: case N3Parser.NUMBER:
283: Resource xsdType = XSD.integer;
284: if (text.indexOf('.') >= 0)
285: // The choice of XSD:decimal is for compatibility with N3/cwm.
286: xsdType = XSD.decimal;
287: if (text.indexOf('e') >= 0 || text.indexOf('E') >= 0)
288: xsdType = XSD.xdouble;
289: return model.createTypedLiteral(text, xsdType.getURI());
290:
291: case N3Parser.LITERAL:
292: // Literals have three part: value (string), lang tag, datatype
293: // Can't have a lang tag and a data type - if both, just use the datatype
294:
295: AST a1 = thing.getNextSibling();
296: AST a2 = (a1 == null ? null : a1.getNextSibling());
297: AST datatype = null;
298: AST lang = null;
299:
300: if (a2 != null) {
301: if (a2.getType() == N3Parser.DATATYPE)
302: datatype = a2.getFirstChild();
303: else
304: lang = a2;
305: }
306: // First takes precedence over second.
307: if (a1 != null) {
308: if (a1.getType() == N3Parser.DATATYPE)
309: datatype = a1.getFirstChild();
310: else
311: lang = a1;
312: }
313:
314: // Chop leading '@'
315: String langTag = (lang != null) ? lang.getText().substring(
316: 1) : null;
317:
318: if (datatype == null)
319: return model.createLiteral(text, langTag);
320:
321: // If there is a datatype, it takes predence over lang tag.
322: String typeURI = datatype.getText();
323:
324: if (datatype.getType() != N3Parser.QNAME
325: && datatype.getType() != N3Parser.URIREF) {
326: error("Line "
327: + line
328: + ": N3toRDF: Must use URIref or QName datatype URI: "
329: + text + "^^" + typeURI + "("
330: + N3Parser.getTokenNames()[datatype.getType()]
331: + ")");
332: return model.createLiteral("Illegal literal: " + text
333: + "^^" + typeURI);
334:
335: }
336:
337: // Can't have bNodes here so the code is slightly different for expansion
338:
339: if (datatype.getType() == N3Parser.QNAME) {
340: if (typeURI.startsWith("_:")
341: || typeURI.startsWith("=:")) {
342: error("Line "
343: + line
344: + ": N3toRDF: Can't use bNode for datatype URI: "
345: + text + "^^" + typeURI);
346: return model.createLiteral("Illegal literal: "
347: + text + "^^" + typeURI);
348: }
349:
350: String typeURI2 = expandPrefix(model, typeURI);
351: if (typeURI2 == typeURI) {
352: error("Line "
353: + line
354: + ": N3toRDF: Undefined qname namespace in datatype: "
355: + typeURI);
356: }
357:
358: typeURI = typeURI2;
359: }
360:
361: typeURI = expandURIRef(typeURI, line);
362: // 2003-08 - Ignore lang tag when there is an type.
363: return model.createTypedLiteral(text, typeURI);
364:
365: case N3Parser.QNAME:
366: // Is it a labelled bNode?
367: // Check if _ has been defined.
368: if (text.startsWith("_:")
369: && (model.getNsPrefixURI("_") == null)) {
370: if (!bNodeMap.containsKey(text))
371: bNodeMap.put(text, model.createResource());
372: return (Resource) bNodeMap.get(text);
373: }
374:
375: String uriref = expandPrefix(model, text);
376: if (uriref == text) {
377: error("Line " + line
378: + ": N3toRDF: Undefined qname namespace: "
379: + text);
380: return null;
381: }
382: return model.createResource(expandURIRef(uriref, line));
383:
384: // Normal URIref - may be <> or <#>
385: case N3Parser.URIREF:
386: return model.createResource(expandURIRef(text, line));
387:
388: // Lists
389: case N3Parser.TK_LIST_NIL:
390: return RDF.nil;
391: case N3Parser.TK_LIST:
392: return RDF.List;
393:
394: case N3Parser.ANON: // bNodes via [] or [:- ] QNAME starts "=:"
395: if (!bNodeMap.containsKey(text))
396: bNodeMap.put(text, model.createResource());
397: return (Resource) bNodeMap.get(text);
398:
399: case N3Parser.UVAR:
400: error("Line " + line
401: + ": N3toRDF: Can't map variables to RDF: " + text);
402: break;
403:
404: default:
405: error("Line " + line
406: + ": N3toRDF: Can't map to a resource or literal: "
407: + AntlrUtils.ast(thing));
408: break;
409: }
410: return null;
411: }
412:
413: private String expandURIRef(String text, int line) {
414: try {
415: return resolver.resolve(text);
416: } catch (Exception ex) {
417: error("Line " + line + ": N3toRDF: Bad URI: " + text + " ("
418: + ex.getMessage() + ")");
419: }
420: return null;
421: }
422:
423: private boolean hasURIscheme(String text) {
424: for (int i = 0; i < text.length(); i++) {
425: char ch = text.charAt(i);
426: if (ch == ':')
427: return true;
428: if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
429: || (ch >= '0' && ch <= '9') || ch == '+'
430: || ch == '-' | ch == '.')
431: continue;
432: return false;
433: }
434: return false;
435: }
436:
437: private void setPrefixMapping(Model m, String prefix, String uriref) {
438: try {
439: model.setNsPrefix(prefix, uriref);
440: } catch (PrefixMapping.IllegalPrefixException ex) {
441: warning("Prefix mapping '" + prefix
442: + "' illegal: used but not recorded in model");
443: }
444: myPrefixMapping.put(prefix, uriref);
445: }
446:
447: private String expandPrefix(Model m, String prefixed) {
448: // Code from shared.impl.PrefixMappingImpl ...
449: // Needed a copy as we store unchecked prefixes for N3.
450: int colon = prefixed.indexOf(':');
451: if (colon < 0)
452: return prefixed;
453: else {
454: String prefix = prefixed.substring(0, colon);
455: String uri = (String) myPrefixMapping.get(prefix);
456: if (uri == null)
457: return prefixed;
458: return uri + prefixed.substring(colon + 1);
459: }
460: // Not this - model may already have prefixes defined;
461: // we allow "illegal" prefixes (e.g. starts with a number)
462: // for compatibility
463: //return model.expandPrefix(prefix) ;
464: }
465: }
466:
467: /*
468: * (c) Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
469: * All rights reserved.
470: *
471: * Redistribution and use in source and binary forms, with or without
472: * modification, are permitted provided that the following conditions
473: * are met:
474: * 1. Redistributions of source code must retain the above copyright
475: * notice, this list of conditions and the following disclaimer.
476: * 2. Redistributions in binary form must reproduce the above copyright
477: * notice, this list of conditions and the following disclaimer in the
478: * documentation and/or other materials provided with the distribution.
479: * 3. The name of the author may not be used to endorse or promote products
480: * derived from this software without specific prior written permission.
481: *
482: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
483: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
484: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
485: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
486: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
487: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
488: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
489: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
490: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
491: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
492: */
|