/* * 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.xenei.jdbc4sparql.sparql.parser; import java.io.Reader; import java.io.StringReader; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.discovery.ResourceClassIterator; import org.apache.commons.discovery.ResourceNameIterator; import org.apache.commons.discovery.resource.ClassLoaders; import org.apache.commons.discovery.resource.classes.DiscoverClasses; import org.apache.commons.discovery.resource.names.DiscoverServiceNames; import org.xenei.jdbc4sparql.iface.Catalog; import org.xenei.jdbc4sparql.iface.Schema; import org.xenei.jdbc4sparql.sparql.SparqlQueryBuilder; import com.hp.hpl.jena.query.Query; import com.hp.hpl.jena.query.QueryException; import com.hp.hpl.jena.sparql.lang.sparql_11.ParseException; import com.hp.hpl.jena.sparql.lang.sparql_11.SPARQLParser11; import com.hp.hpl.jena.sparql.syntax.Element; /** * An interface that defines the SparqlParser. * <p> * The sparql parser converts SQL to a SparqlQueryBuilder. * </p> * <p> * Must have a no argument constructor * </p> * <p> * It is conceivable that different implementations of the parser may be * required for different version of SQL. * </p> * <p> * Implementations of this interface should be listed in the * META-INF/services/org.xenei.jdbc4sparql.sparql.parser.SparqlBuilder file. The * first implementation listed in that file will be the default parser. * </p> * <p> * Implementations should implement <code> * public static final String PARSER_NAME</code> and <code> * public static final String DESCRIPTION </code> * </p> * <p> * if the PARSER_NAME is not specified the simple class name will be used. * </p> * <p> * if two builders have the same name only the first one will be seen. * </p> * <p> * A list of registered SparqlParsers is returned from J4SDriver when it is run * as a java application (e.g. java -jar J4DDriver.jar J4SDriver) * </p> */ public interface SparqlParser { /** * A utility class that correctly handles common operations and provides * common constants. */ static class Util { /** * Get the defulat parser. * * @return SparqlParser */ public static SparqlParser getDefaultParser() { final List<Class<? extends SparqlParser>> lst = Util.getParsers(); if (lst.size() == 0) { throw new IllegalStateException("No default parser defined"); } try { return lst.get(0).newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new IllegalStateException(lst.get(0) + " could not be instantiated.", e); } } /** * Get the parser by parser name. * * The name may be a parser name or a fully qualified class name. * * If a class name is provided the class must implement SparqlParser. * * If the name is null the default parser is returned. * * @param parserName * The name to find. * @return SparqlParser */ public static SparqlParser getParser(final String parserName) { if (parserName == null) { return Util.getDefaultParser(); } try { final Class<?> clazz = Class.forName(parserName); if (SparqlParser.class.isAssignableFrom(clazz)) { try { return (SparqlParser) clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new IllegalStateException(clazz + " could not be instantiated.", e); } } else { throw new IllegalArgumentException(clazz + " does not implement SparqlParser."); } } catch (final ClassNotFoundException e) { throw new IllegalArgumentException(parserName + " is not a vaild SparqlParser"); } } /** * Get the list of parsers. * * @return The list of parsers. */ public static List<Class<? extends SparqlParser>> getParsers() { final List<Class<? extends SparqlParser>> retval = new ArrayList<Class<? extends SparqlParser>>(); final ClassLoaders loaders = ClassLoaders.getAppLoaders( SparqlParser.class, SparqlParser.class, false); final DiscoverClasses<SparqlParser> dc = new DiscoverClasses<SparqlParser>( loaders); final ResourceNameIterator classIter = (new DiscoverServiceNames( loaders)).findResourceNames(SparqlParser.class.getName()); final List<String> lst = new ArrayList<String>(); // we build a list first because the classes can be found by // multiple loaders. while (classIter.hasNext()) { final String className = classIter.nextResourceName(); if (!lst.contains(className)) { lst.add(className); } } // now just load the classes once. for (final String className : lst) { final ResourceClassIterator<SparqlParser> iter = dc .findResourceClasses(className); while (iter.hasNext()) { final Class<? extends SparqlParser> clazz = iter .nextResourceClass().loadClass(); if (!retval.contains(clazz)) { retval.add(clazz); } } } // return the list return retval; } /** * Parse a where clause into an element. * * @param qry * The query. * @return The element * @throws ParseException * on error. */ public static Element parse(final String qstr) throws ParseException, QueryException { final Query query = new Query(); final Reader in = new StringReader(qstr); final SPARQLParser11 parser = new SPARQLParser11(in); query.setStrict(true); parser.setQuery(query); parser.WhereClause(); return query.getQueryPattern(); } } List<String> getSupportedNumericFunctions(); List<String> getSupportedStringFunctions(); List<String> getSupportedSystemFunctions(); /** * Parse the SQL string and then deparse it back into SQL to provide the SQL * string native to the parser. This is used in support of nativeSQL() in * the Driver. * * @param sqlQuery * the original SQL string * @return the native SQL string * @throws SQLException * on error. */ String nativeSQL(String sqlQuery) throws SQLException; /** * Given a catalog and a SQL query create a SparqlQueryBuilder. * * @param catalog * The catalog to run the query against. * @param sqlQuery * The SQL query to execute * @return The SparqlQueryBuilder. * @throws SQLException */ SparqlQueryBuilder parse(Map<String, Catalog> catalogs, Catalog catalog, Schema schema, String sqlQuery) throws SQLException; }