package org.deri.tarql; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import org.apache.jena.atlas.logging.Log; import org.apache.jena.query.Query; import org.apache.jena.query.QueryException; import org.apache.jena.query.QueryParseException; import org.apache.jena.riot.system.IRIResolver; import org.apache.jena.shared.JenaException; import org.apache.jena.shared.NotFoundException; import org.apache.jena.shared.PrefixMapping; import org.apache.jena.shared.impl.PrefixMappingImpl; import org.apache.jena.sparql.ARQConstants; import org.apache.jena.sparql.lang.SyntaxVarScope; import org.apache.jena.sparql.lang.sparql_11.ParseException; import org.apache.jena.sparql.lang.sparql_11.SPARQLParser11; import org.apache.jena.sparql.lang.sparql_11.TokenMgrError; import org.apache.jena.util.FileManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Parses a {@link TarqlQuery} provided as a string or reader. */ public class TarqlParser { private final static Logger log = LoggerFactory.getLogger(TarqlParser.class); private final static PrefixMapping builtInPrefixes = new PrefixMappingImpl() {{ setNsPrefix("tarql", tarql.NS); setNsPrefix("apf", ARQConstants.ARQPropertyFunctionLibraryURI); }}; private final Reader reader; private final TarqlQuery result = new TarqlQuery(); private boolean done = false; private boolean seenSelectOrAsk = false; public TarqlParser(String filenameOrURL) { this(open(filenameOrURL), FileManager.get().mapURI(filenameOrURL)); } public TarqlParser(String filenameOrURL, String baseIRI) { this(open(filenameOrURL), baseIRI); } public TarqlParser(Reader reader) { this(reader, null); } public TarqlParser(Reader reader, String baseIRI) { this.reader = reader; result.getPrologue().setResolver(IRIResolver.create(baseIRI)); addBuiltInPrefixes(); } private static Reader open(String filenameOrURL) { try { InputStream in = FileManager.get().open(filenameOrURL); if (in == null) throw new NotFoundException(filenameOrURL); return new InputStreamReader(in, "UTF-8"); } catch (UnsupportedEncodingException ex) { // Can't happen, UTF-8 is always supported return null; } } public TarqlQuery getResult() { parse(); return result; } private void parseDo(SPARQLParser11 parser) throws ParseException { do { int beginLine = parser.getToken(1).beginLine; int beginColumn = parser.getToken(1).beginColumn; Query query = new Query(result.getPrologue()); // You'd assume that a query initialized via "new Query(prologue)" // has the IRI resolver from prologue.getResolver(), but that doesn't // appear to be the case in Jena 2.12.0, so we set it manually query.getPrologue().setResolver(result.getPrologue().getResolver()); result.addQuery(query); parser.setQuery(query); parser.Query(); if (query.isSelectType() || query.isAskType()) { seenSelectOrAsk = true; } if (seenSelectOrAsk && result.getQueries().size() > 1) { throw new QueryParseException("" + "Multiple queries per file are only supported for CONSTRUCT", beginLine, beginColumn); } // From Parser.validateParsedQuery, which we can't call directly SyntaxVarScope.check(query); result.getPrologue().usePrologueFrom(query); if (log.isDebugEnabled()) { log.debug(query.toString()); } } while (parser.getToken(1).kind != SPARQLParser11.EOF); removeBuiltInPrefixes(); } // Adapted from ARQ ParserSPARQL11.java private void parse() { if (done) return; done = true; SPARQLParser11 parser = new SPARQLParser11(reader) ; try { parseDo(parser); } catch (ParseException ex) { throw new QueryParseException(ex.getMessage(), ex.currentToken.beginLine, ex.currentToken.beginColumn); } catch (TokenMgrError tErr) { // Last valid token : not the same as token error message - but this should not happen int col = parser.token.endColumn; int line = parser.token.endLine; throw new QueryParseException(tErr.getMessage(), line, col); } catch (QueryException ex) { throw ex; } catch (JenaException ex) { throw new QueryException(ex.getMessage(), ex); } catch (Error err) { // The token stream can throw errors. throw new QueryParseException(err.getMessage(), err, -1, -1); } catch (Throwable th) { Log.warn(TarqlParser.class, "Unexpected throwable: ",th); throw new QueryException(th.getMessage(), th); } } private void addBuiltInPrefixes() { for (String prefix: builtInPrefixes.getNsPrefixMap().keySet()) { if (result.getPrologue().getPrefix(prefix) != null) continue; result.getPrologue().getPrefixMapping().setNsPrefix(prefix, builtInPrefixes.getNsPrefixURI(prefix)); } } private void removeBuiltInPrefixes() { PrefixMapping prefixes = null; for (String prefix: builtInPrefixes.getNsPrefixMap().keySet()) { String uri = builtInPrefixes.getNsPrefixURI(prefix); if (!uri.equals(result.getPrologue().getPrefix(prefix))) continue; if (prefixes == null) { prefixes = new PrefixMappingImpl(); prefixes.setNsPrefixes(result.getPrologue().getPrefixMapping()); } prefixes.removeNsPrefix(prefix); } if (prefixes != null) { result.getPrologue().setPrefixMapping(prefixes); } } }