io.konig.core.path.PathParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of konig-core Show documentation
Show all versions of konig-core Show documentation
A library for core classes (Graph, Vertex, Edge, etc.)
package io.konig.core.path;
/*
* #%L
* Konig Core
* %%
* Copyright (C) 2015 - 2017 Gregory McFall
* %%
* Licensed 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.
* #L%
*/
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import org.openrdf.model.BNode;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.helpers.RDFHandlerBase;
import io.konig.core.Context;
import io.konig.core.NameMap;
import io.konig.core.NamespaceManager;
import io.konig.core.Path;
import io.konig.core.vocab.VAR;
import io.konig.rio.turtle.NamespaceMap;
import io.konig.rio.turtle.SeaTurtleParser;
public class PathParser extends SeaTurtleParser {
private NamespaceManager nsManager;
public PathParser(NamespaceManager nsManager) {
this(nsManager==null ? null : new NamespaceMapAdapter(nsManager), null);
this.nsManager = nsManager;
}
public PathParser(NamespaceMap map, PushbackReader reader) {
super(map);
setRDFHandler(new Handler());
this.reader = reader;
}
public PathParser(NameMap nameMap) {
this.nameMap = nameMap;
}
public Path path(Reader input) throws PathParseException {
super.initParse(input, "");
return path();
}
/**
* Here's the official Turtle 1.1 Syntax
*
* PrefixedName ::= PNAME_LN | PNAME_NS
* ::= (PNAME_NS PN_LOCAL) | PNAME_NS
* ::= PNAME_NS PN_LOCAL?
* ::= PN_PREFIX? ':' PN_LOCAL?
*
*
* We customize the Turtle syntax by redefining PrefixedName as follows.
*
* PrefixedName ::= (PN_PREFIX? ':' PN_LOCAL?) | bareLocalName
* bareLocalName ::= PN_PREFIX
*
* Notice that this customization requires that a bareLocalName is allowed only if it matches
* the syntax of a namespace prefix.
*
* We also modify the production rules for PN_LOCAL and PN_PREFIX to exclude '.' characters.
*/
// protected URI prefixedName(int c) throws IOException, RDFParseException {
// unread(c);
//
// String prefix = pn_prefix();
//
// c = read();
//
// if (c != ':') {
// unread(c);
// // Treat the prefix as a bare local name.
// if (localNameService != null) {
// Set set = localNameService.lookupLocalName(prefix);
// if (set.isEmpty()) {
// StringBuilder err = err();
// err.append("No URI found with local name '");
// err.append(prefix);
// err.append("'");
// fail(err);
// }
//
// if (set.size()>1) {
// StringBuilder err = err();
// err.append("Local name '");
// err.append(prefix);
// err.append("' is ambgious. Matching values include ");
//
// int count = 0;
// for (URI uri : set) {
// if (count > 0) {
// err.append(", ");
// }
// err.append('<');
// err.append(uri.stringValue());
// err.append('>');
// count++;
// if (count >= 3) {
// break;
// }
// }
// if (set.size()>3) {
// err.append(" ...");
// }
// fail(err);
// }
// return set.iterator().next();
// } else {
//
// StringBuilder err = err();
// err.append("Bare local names not supported. ");
// err.append("Use a fully-qualified IRI or a prefixed name, ");
// err.append("or assign a LocalNameService to this PathParser.");
// fail(err);
// }
//
// }
//
// String localName = pn_local();
//
// String namespace = namespaceMap.get(prefix);
// if (namespace == null) {
// fail("Namespace not defined for prefix '" + prefix + "'");
// }
//
// return valueFactory.createURI(namespace + localName);
// }
/**
*
* path ::= step+
* step ::= namedIndividual | in | out | filter
*
*
* @param path
*/
private Path path() throws PathParseException {
Path path = new PathImpl();
try {
prologue();
path.setContext(getContext());
int c;
while (!done(c=next())) {
unread(c);
Step step=null;
switch (c) {
case '.' :
case '/' :
step = out();
break;
case '^' :
step = in();
break;
case '[' :
step = filter();
break;
case '?' :
step = variable();
break;
default :
step = namedIndividual();
}
path.asList().add(step);
}
} catch (IOException | RDFParseException | RDFHandlerException e) {
throw new PathParseException(e);
}
if (nameMap != null ) {
Context context = path.getContext();
if (context.asList().isEmpty() && nsManager != null) {
PathContextBuilder.buildContext(path, nsManager);
}
}
return path;
}
private Step variable() throws RDFParseException, RDFHandlerException, IOException {
read();
String name = pn_local();
URI predicate = new URIImpl(VAR.NAMESPACE + "?" + name);
return new OutStep(predicate);
}
protected boolean done(int c) throws IOException {
return c==-1;
}
/**
* Don't allow dots ('.') in the middle of a local name
*/
public String pn_local() throws IOException, RDFParseException {
StringBuilder builder = buffer();
int c = read();
if (
!pn_chars_u(c) &&
(c != ':') &&
!inRange(c, '0', '9') &&
!plx(c)
) {
unread(c);
} else {
builder.appendCodePoint(c);
int last = -1;
for (;;) {
c = read();
if (
!pn_chars(c) &&
(c != ':') &&
!plx(c)
) {
break;
}
builder.appendCodePoint(c);
last = c;
}
unread(c);
}
return builder.toString();
}
/**
*
* namedIndividual ::= iri
*
* @throws RDFHandlerException
*/
private Step namedIndividual() throws RDFParseException, IOException, RDFHandlerException {
URI iri = iri();
return new VertexStep(iri);
}
private Handler handler() {
return (Handler) getRDFHandler();
}
/**
*
* filter ::= blankNodePropertyList
*
*
* The blankNodePropertyList
rule is defined in the
* Turtle 1.1 specification.
*/
private Step filter() throws RDFParseException, RDFHandlerException, IOException {
BNode bnode = blankNodePropertyList();
if (bnode == null) {
StringBuilder err = err();
err.append("BNode property list cannot be empty");
fail(err);
}
BlankNodeStep top = handler().peek();
return top.step;
}
/**
*
* in ::= '^' iri
*
* @throws RDFHandlerException
*/
private Step in() throws RDFParseException, IOException, RDFHandlerException {
read('^');
URI predicate = iri();
return new InStep(predicate);
}
/**
*
* out ::= '/' iri
*
* @throws RDFHandlerException
*/
private Step out() throws RDFParseException, IOException, RDFHandlerException {
int delim = read();
if (delim!='/' && delim!='.') {
throw new RDFParseException("Expected '.' or '/'");
}
URI predicate = iri();
return new OutStep(predicate);
}
/**
* PN_PREFIX ::= PN_CHARS_BASE PN_CHARS*
*/
protected void pn_prefix(int c) throws IOException, RDFParseException {
if (pn_chars_base(c)) {
buffer.appendCodePoint(c);
for (;;) {
c = read();
if (pn_chars(c)) {
buffer.appendCodePoint(c);
} else {
break;
}
}
}
unread(c);
}
private static class Handler extends RDFHandlerBase {
List stack = new ArrayList<>();
@Override
public void handleStatement(Statement st) throws RDFHandlerException {
BlankNodeStep top = peek();
Resource subject = st.getSubject();
URI predicate = st.getPredicate();
Value object = st.getObject();
if (!(subject instanceof BNode)) {
throw new RDFHandlerException(
"Expected subject to be a BNode, but found: " +
subject.toString());
}
BNode bnode = (BNode) subject;
if (top == null) {
top = new BlankNodeStep(bnode, new HasStep());
stack.add(top);
}
if (bnode != top.bnode) {
throw new RDFHandlerException("Nested BNodes not supported yet.");
// TODO: support nested BNodes.
}
top.step.add(predicate, object);
}
private BlankNodeStep peek() {
return stack.isEmpty() ? null : stack.get(stack.size()-1);
}
}
private static class BlankNodeStep {
private BNode bnode;
private HasStep step;
public BlankNodeStep(BNode bnode, HasStep step) {
this.bnode = bnode;
this.step = step;
}
}
}