blob: a3f3d3a7392c89e09f3d806fccf800962251d15c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.riot.system;
import org.apache.jena.atlas.lib.Cache;
import org.apache.jena.atlas.lib.CacheFactory;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.*;
import org.apache.jena.iri.IRI;
import org.apache.jena.irix.*;
import org.apache.jena.query.ARQ;
import org.apache.jena.riot.RiotException;
import org.apache.jena.riot.tokens.Token;
import org.apache.jena.riot.tokens.TokenType;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.util.Context;
import org.apache.jena.sparql.util.FmtUtils;
/**
* {@link ParserProfileStd} uses a {@link FactoryRDF} to create items in the parsing
* process.
*/
public class ParserProfileStd implements ParserProfile {
private final FactoryRDF factory;
private final ErrorHandler errorHandler;
private final Context context;
private IRIxResolver resolver;
private final PrefixMap prefixMap;
private final boolean strictMode;
private final boolean checking;
private static int DftCacheSize = 500;
private final Cache<String, IRI> iriCache;
private boolean allowNodeExtentions;
public ParserProfileStd(FactoryRDF factory, ErrorHandler errorHandler, IRIxResolver resolver, PrefixMap prefixMap, Context context,
boolean checking, boolean strictMode) {
this.factory = factory;
this.errorHandler = errorHandler;
this.resolver = resolver;
this.prefixMap = prefixMap;
this.context = context;
this.checking = checking;
this.iriCache = checking ? CacheFactory.createCache(DftCacheSize) : null;
this.strictMode = strictMode;
this.allowNodeExtentions = true; // (context.isTrue(RIOT.ALLOW_NODE_EXT)) ;
}
@Override
public FactoryRDF getFactorRDF() {
return factory;
}
@Override
public ErrorHandler getErrorHandler() {
return errorHandler;
}
@Override
public boolean isStrictMode() {
return strictMode;
}
@Override
public String resolveIRI(String uriStr, long line, long col) {
return internalMakeIRI(uriStr, line, col).toString();
}
@Override
public void setBaseIRI(String baseIRIstr) {
IRIx newBase = resolver.resolve(baseIRIstr);
this.resolver = resolver.resetBase(newBase);
}
private IRIx internalMakeIRI(String uriStr, long line, long col) {
if ( uriStr.contains(" ") ) {
// Specific check for spaces.
errorHandler.warning("Bad IRI: <" + uriStr + "> Spaces are not legal in URIs/IRIs.", line, col);
return IRIx.createAny(uriStr);
}
// Relative IRIs.
// jena-iri : these are errors on the
try {
IRIx iri = resolver.resolve(uriStr);
if ( checking )
doChecking(iri, iri.str(), line, col);
return iri;
} catch (RelativeIRIException ex ) {
errorHandler.error("Relative IRI: " + uriStr, line, col);
return IRIx.createAny(uriStr);
} catch (IRIException ex) {
// Same code as Checker.iriViolations
String msg = ex.getMessage();
Checker.iriViolationMessage(uriStr, true, msg, line, col, errorHandler);
return IRIx.createAny(uriStr);
}
}
private void doChecking(IRIx irix, String uriStr, long line, long col) {
IRI iri;
if ( irix instanceof IRIProviderJenaIRI.IRIxJena )
iri = (IRI)irix.getImpl();
else
iri = iriCache.getOrFill(uriStr, () -> SetupJenaIRI.iriCheckerFactory().create(uriStr));
Checker.iriViolations(iri, errorHandler, false, true, line, col);
}
/**
* Create a triple - this operation call {@link #checkTriple} if checking is
* enabled.
*/
@Override
public Triple createTriple(Node subject, Node predicate, Node object, long line, long col) {
if ( checking )
checkTriple(subject, predicate, object, line, col);
return factory.createTriple(subject, predicate, object);
}
private boolean allowSpecialNode(Node node) {
return allowNodeExtentions && node instanceof Node_Triple;
}
protected void checkTriple(Node subject, Node predicate, Node object, long line, long col) {
if ( subject == null || (!subject.isURI() && !subject.isBlank()) ) {
if ( !allowSpecialNode(subject) ) {
errorHandler.error("Subject is not a URI or blank node", line, col);
throw new RiotException("Bad subject: " + subject);
}
}
if ( predicate == null || (!predicate.isURI()) ) {
errorHandler.error("Predicate not a URI", line, col);
throw new RiotException("Bad predicate: " + predicate);
}
if ( object == null || (!object.isURI() && !object.isBlank() && !object.isLiteral()) ) {
if ( !allowSpecialNode(object) ) {
errorHandler.error("Object is not a URI, blank node or literal", line, col);
throw new RiotException("Bad object: " + object);
}
}
}
/**
* Create a quad - this operation call {@link #checkTriple} if checking is
* enabled.
*/
@Override
public Quad createQuad(Node graph, Node subject, Node predicate, Node object, long line, long col) {
if ( checking )
checkQuad(graph, subject, predicate, object, line, col);
return factory.createQuad(graph, subject, predicate, object);
}
protected void checkQuad(Node graph, Node subject, Node predicate, Node object, long line, long col) {
// Allow blank nodes - syntax may restrict more.
if ( graph != null && !graph.isURI() && !graph.isBlank() ) {
errorHandler.error("Graph name is not a URI or blank node: " + FmtUtils.stringForNode(graph), line, col);
throw new RiotException("Bad graph name: " + graph);
}
checkTriple(subject, predicate, object, line, col);
}
@Override
public Node createURI(String x, long line, long col) {
// Special cases that don't resolve.
// <_:....> is a blank node.
// <::...> is "don't touch" used for a fixed-up prefix name
if ( !RiotLib.isBNodeIRI(x) && !RiotLib.isPrefixIRI(x) )
// Really is an URI!
x = resolveIRI(x, line, col);
return factory.createURI(x);
}
@Override
public Node createTypedLiteral(String lexical, RDFDatatype datatype, long line, long col) {
if ( checking )
Checker.checkLiteral(lexical, datatype, errorHandler, line, col);
return factory.createTypedLiteral(lexical, datatype);
}
@Override
public Node createLangLiteral(String lexical, String langTag, long line, long col) {
if ( checking )
Checker.checkLiteral(lexical, langTag, errorHandler, line, col);
return factory.createLangLiteral(lexical, langTag);
}
@Override
public Node createStringLiteral(String lexical, long line, long col) {
// No checks
return factory.createStringLiteral(lexical);
}
@Override
public Node createBlankNode(Node scope, String label, long line, long col) {
// No checks
return factory.createBlankNode(label);
}
@Override
public Node createBlankNode(Node scope, long line, long col) {
// No checks
return factory.createBlankNode();
}
@Override
public Node createTripleNode(Node subject, Node predicate, Node object, long line, long col) {
return NodeFactory.createTripleNode(subject, predicate, object);
}
@Override
public Node createTripleNode(Triple triple, long line, long col) {
return NodeFactory.createTripleNode(triple);
}
@Override
public Node createGraphNode(Graph graph, long line, long col) {
return NodeFactory.createGraphNode(graph);
}
/** Special token forms */
@Override
public Node createNodeFromToken(Node scope, Token token, long line, long col) {
// OFF - Don't produce Node.ANY by default.
if ( false && token.getType() == TokenType.KEYWORD ) {
if ( Token.ImageANY.equals(token.getImage()) )
return Node.ANY;
}
return null;
}
@Override
public Node create(Node currentGraph, Token token) {
// Dispatches to the underlying ParserFactory operation
long line = token.getLine();
long col = token.getColumn();
String str = token.getImage();
switch (token.getType()) {
case BNODE :
return createBlankNode(currentGraph, str, line, col);
case IRI :
return createURI(str, line, col);
case PREFIXED_NAME : {
String prefix = str;
String suffix = token.getImage2();
String expansion = expandPrefixedName(prefix, suffix, token);
return createURI(expansion, line, col);
}
case DECIMAL :
return createTypedLiteral(str, XSDDatatype.XSDdecimal, line, col);
case DOUBLE :
return createTypedLiteral(str, XSDDatatype.XSDdouble, line, col);
case INTEGER :
return createTypedLiteral(str, XSDDatatype.XSDinteger, line, col);
case LITERAL_DT : {
Token tokenDT = token.getSubToken2();
String uriStr;
switch (tokenDT.getType()) {
case IRI :
uriStr = tokenDT.getImage();
break;
case PREFIXED_NAME : {
String prefix = tokenDT.getImage();
String suffix = tokenDT.getImage2();
uriStr = expandPrefixedName(prefix, suffix, tokenDT);
break;
}
default :
throw new RiotException("Expected IRI for datatype: " + token);
}
uriStr = resolveIRI(uriStr, tokenDT.getLine(), tokenDT.getColumn());
RDFDatatype dt = NodeFactory.getType(uriStr);
return createTypedLiteral(str, dt, line, col);
}
case LITERAL_LANG :
return createLangLiteral(str, token.getImage2(), line, col);
case STRING :
return createStringLiteral(str, line, col);
case BOOLEAN :
return createTypedLiteral(str, XSDDatatype.XSDboolean, line, col);
default : {
Node x = createNodeFromToken(currentGraph, token, line, col);
if ( x != null )
return x;
errorHandler.fatal("Not a valid token for an RDF term: " + token, line, col);
return null;
}
}
}
@Override
public PrefixMap getPrefixMap() {
return prefixMap;
}
private String expandPrefixedName(String prefix, String localPart, Token token) {
String expansion = prefixMap.expand(prefix, localPart);
if ( expansion == null ) {
if ( ARQ.isTrue(ARQ.fixupUndefinedPrefixes) )
return RiotLib.fixupPrefixIRI(prefix, localPart);
errorHandler.fatal("Undefined prefix: " + prefix, token.getLine(), token.getColumn());
}
return expansion;
}
}