package org.aksw.sparqlify.util; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.aksw.commons.collections.MapUtils; import org.aksw.commons.collections.multimaps.IBiSetMultimap; import org.aksw.commons.util.MapReader; import org.aksw.commons.util.XmlUtils; import org.aksw.jena_sparql_api.views.SparqlifyConstants; import org.aksw.sparqlify.algebra.sql.exprs2.S_Add; import org.aksw.sparqlify.algebra.sql.exprs2.S_Constant; import org.aksw.sparqlify.algebra.sql.exprs2.S_Equals; import org.aksw.sparqlify.algebra.sql.exprs2.S_GreaterThan; import org.aksw.sparqlify.algebra.sql.exprs2.S_GreaterThanOrEqual; import org.aksw.sparqlify.algebra.sql.exprs2.S_LessThan; import org.aksw.sparqlify.algebra.sql.exprs2.S_LessThanOrEqual; import org.aksw.sparqlify.algebra.sql.exprs2.S_Multiply; import org.aksw.sparqlify.algebra.sql.exprs2.S_Substract; import org.aksw.sparqlify.backend.postgres.SqlLiteralMapperPostgres; import org.aksw.sparqlify.config.xml.Mapping; import org.aksw.sparqlify.config.xml.SimpleFunction; import org.aksw.sparqlify.config.xml.SparqlifyConfig; import org.aksw.jena_sparql_api.views.RdfTerm; import org.aksw.sparqlify.core.TypeToken; import org.aksw.sparqlify.core.algorithms.DatatypeToString; import org.aksw.sparqlify.core.cast.CoercionSystemImpl3; import org.aksw.sparqlify.core.cast.MethodDeclarationParserSimple; import org.aksw.sparqlify.core.cast.NodeValueToObjectDefault; import org.aksw.sparqlify.core.cast.SqlDatatypeConstant; import org.aksw.sparqlify.core.cast.SqlDatatypeDefault; import org.aksw.sparqlify.core.cast.SqlExprSerializerSystem; import org.aksw.sparqlify.core.cast.SqlExprSerializerSystemImpl; import org.aksw.sparqlify.core.cast.SqlFunctionSerializerStringTemplate; import org.aksw.sparqlify.core.cast.SqlLiteralMapper; import org.aksw.sparqlify.core.cast.SqlTypeMapper; import org.aksw.sparqlify.core.cast.SqlValue; import org.aksw.sparqlify.core.cast.SqlValueTransformerFloat; import org.aksw.sparqlify.core.cast.SqlValueTransformerInteger; import org.aksw.sparqlify.core.cast.TransformUtils; import org.aksw.sparqlify.core.cast.TypeSystem; import org.aksw.sparqlify.core.cast.TypeSystemImpl; import org.aksw.sparqlify.core.datatypes.SparqlFunction; import org.aksw.sparqlify.core.datatypes.SparqlFunctionImpl; import org.aksw.sparqlify.core.jena.functions.BNode; import org.aksw.sparqlify.core.jena.functions.PlainLiteral; import org.aksw.sparqlify.core.jena.functions.RightPad; import org.aksw.sparqlify.core.jena.functions.TypedLiteral; import org.aksw.sparqlify.core.jena.functions.Uri; import org.aksw.sparqlify.core.jena.functions.UrlDecode; import org.aksw.sparqlify.core.jena.functions.UrlEncode; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformer; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerArithmetic; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerCast; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerConcat; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerFunction; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerHasRdfTermType; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerIsNumeric; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerLang; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerLangMatches; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerLogicalConjunction; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerOneOf; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerPassAsTypedLiteral; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerRdfTermComparator; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerRdfTermCtor; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerSparqlFunctionModel; import org.aksw.sparqlify.core.rewrite.expr.transform.ExprTransformerStr; import org.aksw.sparqlify.core.rewrite.expr.transform.RdfTermEliminatorImpl; import org.aksw.sparqlify.core.rewrite.expr.transform.RdfTermEliminatorWriteable; import org.aksw.sparqlify.core.sql.common.serialization.SqlEscaper; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_Arithmetic; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_Compare; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_LogicalAnd; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_LogicalNot; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_LogicalOr; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_ParseDate; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_ParseInt; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_PassThrough; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_UrlDecode; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator_UrlEncode; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializer; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerCase; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerDefault; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerElse; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerOp1; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerOp1Prefix; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerOp2; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerPassThrough; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializerWhen; import org.aksw.sparqlify.core.sql.expr.serialization.SqlFunctionSerializer_Join; import org.aksw.sparqlify.type_system.FunctionModel; import org.aksw.sparqlify.type_system.FunctionModelAliased; import org.aksw.sparqlify.type_system.FunctionModelMeta; import org.aksw.sparqlify.type_system.MethodDeclaration; import org.aksw.sparqlify.type_system.MethodSignature; import org.aksw.sparqlify.type_system.TypeModel; import org.apache.jena.datatypes.TypeMapper; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.sparql.expr.aggregate.AggCount; import org.apache.jena.sparql.expr.aggregate.AggGroupConcat; import org.apache.jena.sparql.expr.aggregate.AggSum; import org.apache.jena.sparql.function.FunctionRegistry; import org.apache.jena.vocabulary.XSD; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; public class SparqlifyCoreInit { public static void initSparqlifyFunctions() { FunctionRegistry.get().put(SparqlifyConstants.rdfTermLabel, RdfTerm.class); FunctionRegistry.get().put(SparqlifyConstants.blankNodeLabel, BNode.class); FunctionRegistry.get().put(SparqlifyConstants.uriLabel, Uri.class); FunctionRegistry.get().put(SparqlifyConstants.plainLiteralLabel, PlainLiteral.class); FunctionRegistry.get().put(SparqlifyConstants.typedLiteralLabel, TypedLiteral.class); FunctionRegistry.get().put("http://aksw.org/sparqlify/urlDecode", UrlDecode.class); // Jena does not yet seem to have this strangely named encode_for_uri function FunctionRegistry.get().put("http://aksw.org/sparqlify/urlEncode", UrlEncode.class); FunctionRegistry.get().put(SparqlifyConstants.rightPadLabel, RightPad.class); } public static SqlExprSerializerSystem createSerializerSystem(TypeSystem typeSystem, DatatypeToString typeSerializer, SqlEscaper sqlEscaper) { //DatatypeToString typeSerializer = new DatatypeToStringCast();//new DatatypeToStringPostgres(); //DatatypeToString typeSerializer = new DatatypeToStringPostgres(); SqlLiteralMapper sqlLiteralMapper = new SqlLiteralMapperPostgres( typeSerializer, sqlEscaper); SqlExprSerializerSystem result = new SqlExprSerializerSystemImpl( typeSerializer, sqlEscaper, sqlLiteralMapper); FunctionModel<TypeToken> sqlModel = typeSystem.getSqlFunctionModel(); { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("+"); result.addSerializer(sqlModel.getIdsByName("numericPlus"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("-"); result.addSerializer(sqlModel.getIdsByName("numericMinus"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("*"); result.addSerializer(sqlModel.getIdsByName("numericMultiply"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("/"); result.addSerializer(sqlModel.getIdsByName("numericDivide"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("="); result.addSerializer(sqlModel.getIdsByName("equal"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializer_Join(" || "); result.addSerializer("concat@str", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("COALESCE"); result.addSerializer("coalesce", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2(">"); result.addSerializer(sqlModel.getIdsByName("greaterThan"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2(">="); result.addSerializer(sqlModel.getIdsByName("greaterThanOrEqual"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("<"); result.addSerializer(sqlModel.getIdsByName("lessThan"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("<="); result.addSerializer(sqlModel.getIdsByName("lessThanOrEqual"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("AND"); result.addSerializer(sqlModel.getIdsByName("logicalAnd"), serializer); result.addSerializer("logicalAnd", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("OR"); result.addSerializer(sqlModel.getIdsByName("logicalOr"), serializer); result.addSerializer("logicalOr", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1("NOT"); result.addSerializer(sqlModel.getIdsByName("logicalNot"), serializer); result.addSerializer("logicalNot", serializer); } // HACK: When isNotNull contraints are added based on the schema, // these expressions are not passed through the SQL rewriting process // Therefore we need this entry { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix(" IS NOT NULL"); result.addSerializer("isNotNull", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix(" IS NOT NULL"); result.addSerializer(sqlModel.getIdsByName("isNotNull"), serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix("::float8"); result.addSerializer("double@str", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix("::text"); result.addSerializer("str@float", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix("::text"); result.addSerializer("str@double", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerPassThrough(); result.addSerializer("str@str", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix("::text"); result.addSerializer("str@int", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix("::text"); result.addSerializer("str@date", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix("::float"); result.addSerializer("float toFloat(int)", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerOp1Prefix("::float"); result.addSerializer("double toDouble(int)", serializer); } // Geo stuff have been converted to extension functions // { // //SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("ST_GeomFromPoint"); // result.addSerializer("geometry ST_GeomFromPoint(float, float)", new SqlFunctionSerializer() { // @Override // public String serialize(List<String> args) { // return "ST_SetSRID(ST_Point(" + args.get(0) + ", " + args.get(1) + "), 4326)"; // } // }); // } // // { // MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.Geometry, "ST_GeomFromText", false, TypeToken.String); // SqlFunctionSerializer serializer = new SqlFunctionSerializer() { // @Override // public String serialize(List<String> args) { // return "ST_SetSRID(ST_GeomFromText(" + args.get(0) + "), 4326)"; // } // }; // // result.addSerializer(decl.toString(), serializer); // } // { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.Boolean, "ST_DWithin", false, TypeToken.Geometry, TypeToken.Geometry, TypeToken.Float); SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("ST_DWithin"); result.addSerializer(decl.toString(), serializer); } { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.Boolean, "ST_Intersects", false, TypeToken.Geometry, TypeToken.Geometry); SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("ST_Intersects"); result.addSerializer(decl.toString(), serializer); } { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.Long, "Count", false); //SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("Count"); SqlFunctionSerializer serializer = new SqlFunctionSerializer() { @Override public String serialize(List<String> args) { return "Count(*)"; } }; result.addSerializer(decl.toString(), serializer); } { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.String, SparqlifyConstants.urlEncode, false, TypeToken.String); //SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("ST_"); result.addSerializer(decl.toString(), new SqlFunctionSerializerPassThrough()); } { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.Int, "Sum", false, TypeToken.Int); SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("Sum"); result.addSerializer(decl.toString(), serializer); } { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.String, "GroupConcat", false, TypeToken.String, TypeToken.String); SqlFunctionSerializer serializer = new SqlFunctionSerializer() { @Override public String serialize(List<String> args) { return "string_agg(" + args.get(0) + ", " + args.get(1) + ")"; } }; result.addSerializer(decl.toString(), serializer); } { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.Double, "Sum", false, TypeToken.Double); SqlFunctionSerializer serializer = new SqlFunctionSerializerDefault("Sum"); result.addSerializer(decl.toString(), serializer); } { //SqlFunctionSerializer serializer = new SqlFunctionSerializerOp2("~*"); result.addSerializer(sqlModel.getIdsByName("regex"), new SqlFunctionSerializer() { @Override public String serialize(List<String> args) { return "(" + args.get(0) + " ~ " + args.get(1) + ")"; } }); //result.addSerializer(sqlModel.getIdsByName("regex"), serializer); } // Cast is built in // { // SqlFunctionSerializer serializer = new SqlFunctionSerializerCast(); // result.addSerializer("cast", serializer); // } { SqlFunctionSerializer serializer = new SqlFunctionSerializerWhen(); //result.addSerializer(sqlModel.getIdsByName("when"), serializer); result.addSerializer("when", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerCase(); //result.addSerializer(sqlModel.getIdsByName("case"), serializer); result.addSerializer("case", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerElse(); //result.addSerializer(sqlModel.getIdsByName("else"), serializer); result.addSerializer("else", serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializerPassThrough(); result.addSerializer(SparqlifyConstants.urlEncode, serializer); } { SqlFunctionSerializer serializer = new SqlFunctionSerializer() { @Override public String serialize(List<String> args) { return "COUNT(*)"; } }; result.addSerializer("org.aksw.sparqlify.algebra.sql.exprs2.S_AggCount", serializer); } return result; } public static RdfTermEliminatorImpl createDefaultTransformer(TypeSystem typeSystem) { RdfTermEliminatorImpl exprTransformer = new RdfTermEliminatorImpl(); Map<String, ExprTransformer> transMap = exprTransformer.getTransformerMap(); transMap.put("concat", new ExprTransformerConcat()); transMap.put("lang", new ExprTransformerLang()); transMap.put("langMatches", new ExprTransformerLangMatches()); transMap.put("=", new ExprTransformerRdfTermComparator(XSD.xboolean)); transMap.put(">", new ExprTransformerRdfTermComparator(XSD.xboolean)); transMap.put(">=", new ExprTransformerRdfTermComparator(XSD.xboolean)); transMap.put("<", new ExprTransformerRdfTermComparator(XSD.xboolean)); transMap.put("<=", new ExprTransformerRdfTermComparator(XSD.xboolean)); //transMap.put("+", new ExprTransformerArithmetic(XSD.decimal)); FunctionModel<String> sparqlModel = typeSystem.getSparqlFunctionModel(); transMap.put("+", new ExprTransformerSparqlFunctionModel(sparqlModel)); transMap.put("-", new ExprTransformerArithmetic(XSD.decimal)); transMap.put("*", new ExprTransformerArithmetic(XSD.decimal)); transMap.put("/", new ExprTransformerArithmetic(XSD.decimal)); transMap.put("bound", new ExprTransformerPassAsTypedLiteral(XSD.xboolean)); transMap.put("cast", new ExprTransformerCast()); transMap.put("str", new ExprTransformerStr()); transMap.put("regex", new ExprTransformerFunction(XSD.xboolean)); transMap.put(SparqlifyConstants.blankNodeLabel, new ExprTransformerRdfTermCtor()); transMap.put(SparqlifyConstants.uriLabel, new ExprTransformerRdfTermCtor()); transMap.put(SparqlifyConstants.plainLiteralLabel, new ExprTransformerRdfTermCtor()); transMap.put(SparqlifyConstants.typedLiteralLabel, new ExprTransformerRdfTermCtor()); transMap.put(SparqlifyConstants.rdfTermLabel, new ExprTransformerRdfTermCtor()); transMap.put("&&", new ExprTransformerLogicalConjunction()); transMap.put("||", new ExprTransformerLogicalConjunction()); transMap.put("!", new ExprTransformerPassAsTypedLiteral(XSD.xboolean)); transMap.put("in", new ExprTransformerOneOf()); //transMap.put("||", new ExprTransformerLogicalAn()); transMap.put(XSD.xdouble.getURI(), new ExprTransformerCast()); transMap.put(SparqlifyConstants.urlEncode, new ExprTransformerFunction(XSD.xstring)); transMap.put(SparqlifyConstants.urlDecode, new ExprTransformerFunction(XSD.xstring)); // Geometry String bif = "http://www.openlinksw.com/schemas/bif#"; Resource virtGeometry = ResourceFactory.createResource("http://www.openlinksw.com/schemas/virtrdf#Geometry"); // transMap.put(bif + "st_intersects", new ExprTransformerFunction(XSD.xboolean)); // transMap.put(bif + "st_geomFromText", new ExprTransformerFunction(virtGeometry)); // transMap.put(bif + "st_point", new ExprTransformerFunction(ResourceFactory.createResource("http://www.opengis.net/ont/geosparql#wktLiteral"))); //)); //typeSystem.get TypeModel<String> sparqlTypeModel = typeSystem.getSparqlTypeModel(); transMap.put("isNumeric", new ExprTransformerIsNumeric(sparqlTypeModel)); transMap.put("isURI", new ExprTransformerHasRdfTermType(1)); transMap.put("isBlank", new ExprTransformerHasRdfTermType(0)); //transMap.put("isDecimal", new ET_IsDecimal(sparqlTypeModel)); // TODO: The return type of this function depends on which signature is used // So i more sophicsticated transformer is needed transMap.put(AggCount.class.getSimpleName(), new ExprTransformerFunction(XSD.xlong)); transMap.put(AggSum.class.getSimpleName(), new ExprTransformerFunction(XSD.xdouble)); transMap.put(AggGroupConcat.class.getSimpleName(), new ExprTransformerSparqlFunctionModel(sparqlModel)); return exprTransformer; } public static void registerSqlOperatorBatchNumeric(FunctionModel<TypeToken> sqlModel, String name) { sqlModel.registerFunction(name + "@boolean", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.Boolean, TypeToken.Boolean)); sqlModel.registerFunction(name + "@int", name, MethodSignature.create(false, TypeToken.Int, TypeToken.Int, TypeToken.Int)); sqlModel.registerFunction(name + "@float", name, MethodSignature.create(false, TypeToken.Float, TypeToken.Float, TypeToken.Float)); sqlModel.registerFunction(name + "@double", name, MethodSignature.create(false, TypeToken.Double, TypeToken.Double, TypeToken.Double)); //sqlModel.registerFunction(name + "@string", name, MethodSignature.create(false, TypeToken.String, TypeToken.String, TypeToken.String)); //sqlModel.registerFunction(name + "@dateTime", name, MethodSignature.create(false, TypeToken.Date, TypeToken.Date, TypeToken.Date)); } public static void registerSqlOperatorBatchCompare(FunctionModel<TypeToken> sqlModel, String name) { sqlModel.registerFunction(name + "@boolean", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.Boolean, TypeToken.Boolean)); sqlModel.registerFunction(name + "@int", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.Int, TypeToken.Int)); sqlModel.registerFunction(name + "@float", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.Float, TypeToken.Float)); sqlModel.registerFunction(name + "@double", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.Double, TypeToken.Double)); sqlModel.registerFunction(name + "@string", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.String, TypeToken.String)); sqlModel.registerFunction(name + "@dateTime", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.DateTime, TypeToken.DateTime)); sqlModel.registerFunction(name + "@date", name, MethodSignature.create(false, TypeToken.Boolean, TypeToken.Date, TypeToken.Date)); } public static TypeSystem createDefaultDatatypeSystem() { // String basePath = "src/main/resources"; try { /* Map<String, String> typeNameToClass = MapReader.read(SparqlifyCoreInit.class.getResourceAsStream("/type-class.tsv")); Map<String, String> typeNameToUri = MapReader.read(SparqlifyCoreInit.class.getResourceAsStream("/type-uri.tsv")); Map<String, String> typeHierarchy = MapReader.read(SparqlifyCoreInit.class.getResourceAsStream("/type-hierarchy.default.tsv")); Map<String, String> physicalTypeMap = MapReader.read(SparqlifyCoreInit.class.getResourceAsStream("/type-map.h2.tsv")); Map<String, String> rdfTypeHierarchyRaw = MapReader.read(SparqlifyCoreInit.class.getResourceAsStream("/rdf-type-hierarchy.tsv")); */ Map<String, String> typeNameToClass = MapReader .readFromResource("/type-class.tsv"); Map<String, String> typeNameToUri = MapReader .readFromResource("/type-uri.tsv"); Map<String, String> typeHierarchy = MapReader .readFromResource("/type-hierarchy.default.tsv"); Map<String, String> physicalTypeMap = MapReader .readFromResource("/type-map.h2.tsv"); Map<String, String> rdfTypeHierarchyRaw = MapReader.readFromResource("/rdf-type-hierarchy.tsv"); IBiSetMultimap<String, String> rdfTypeHierarchy = TypeSystemImpl.toBidiMap(rdfTypeHierarchyRaw); // TODO HACK Do not add types programmatically physicalTypeMap.put("INTEGER", "int"); physicalTypeMap.put("FLOAT", "float"); physicalTypeMap.put("DOUBLE", "double"); physicalTypeMap.put("DATE", "date"); //typeHierarchy.putAll(physicalTypeMap); // Map<String, String> typeNameToClass = MapReader // .readFromResource("/type-class.tsv"); TypeSystemImpl result = TypeSystemImpl.create(typeHierarchy, physicalTypeMap); result.getSparqlTypeHierarchy().putAll(rdfTypeHierarchy); result.getNormSqlTypeToUri().putAll(typeNameToUri); SparqlifyCoreInit.initSparqlModel(result); return result; } catch (Exception e) { throw new RuntimeException(e); } } /** * Create the SPARQL and SQL models * * * */ public static void initSparqlModel(TypeSystem typeSystem) { // NodeValue xxx = NodeValue.makeInteger(1); // RDFDatatype yyy = xxx.asNode().getLiteral().getDatatype(); // System.out.println(yyy); S_Constant x; // SIGH, Jena does not support assigning custom URIs to its default // datatypes. // Register type token datatypes TypeMapper tm = TypeMapper.getInstance(); String xxx = "http://mytype.org/foo/bar"; // RDFDatatype inner = new XSDBaseNumericType("int", BigInteger.class); // RDFDatatype i = new RDFDatatypeCustomUri("int", inner); SqlTypeMapper stm = typeSystem.getSqlTypeMapper(); stm.register(XSD.xstring.getURI(), new SqlDatatypeDefault(TypeToken.String, new NodeValueToObjectDefault())); stm.register(XSD.xboolean.getURI(), new SqlDatatypeDefault(TypeToken.Boolean, new NodeValueToObjectDefault())); stm.register(XSD.integer.getURI(), new SqlDatatypeDefault(TypeToken.Int, new NodeValueToObjectDefault())); stm.register(XSD.xfloat.getURI(), new SqlDatatypeDefault(TypeToken.Float, new NodeValueToObjectDefault())); stm.register(XSD.xdouble.getURI(), new SqlDatatypeDefault(TypeToken.Double, new NodeValueToObjectDefault())); stm.register(XSD.decimal.getURI(), new SqlDatatypeDefault(TypeToken.Int, new NodeValueToObjectDefault())); stm.register(XSD.date.getURI(), new SqlDatatypeDefault(TypeToken.Date, new NodeValueToObjectDefault())); stm.register(XSD.dateTime.getURI(), new SqlDatatypeDefault(TypeToken.Date, new NodeValueToObjectDefault())); stm.register(SparqlifyConstants.nvTypeError.asNode().getLiteralDatatypeURI(), new SqlDatatypeConstant(SqlValue.TYPE_ERROR)); // RDFDatatype i = new XSDBaseNumericType(TypeToken.Int.toString(), // BigInteger.class); // tm.registerDatatype(i); CoercionSystemImpl3 cs = (CoercionSystemImpl3) typeSystem .getCoercionSystem(); cs.registerCoercion(TypeToken.alloc(XSD.integer.toString()), TypeToken.Int, new SqlValueTransformerInteger()); cs.registerCoercion(TypeToken.String, TypeToken.alloc("int8"), new SqlValueTransformerInteger()); cs.registerCoercion(TypeToken.String, TypeToken.alloc("int4"), new SqlValueTransformerInteger()); cs.registerCoercion(TypeToken.String, TypeToken.alloc("int"), new SqlValueTransformerInteger()); cs.registerCoercion(TypeToken.Int, TypeToken.Float, new SqlValueTransformerFloat()); // TODO Finally clean up the TypeSystem // The coercion system is still a hack... cs.registerCoercion(TypeToken.String, TypeToken.alloc("INTEGER"), new SqlValueTransformerInteger()); // cs.registerCoercion(TypeToken.Date, TypeToken., // new SqlValueTransformer()); // FunctionRegistry functionRegistry = new FunctionRegistry(); // ExprBindingSubstitutor exprBindingSubstitutor = new ExprBindingSubstitutorImpl(); // Eliminates rdf terms from Expr (this is datatype independent) // ExprEvaluator exprEvaluator = SqlTranslationUtils // .createDefaultEvaluator(); // Computes types for Expr, thereby yielding SqlExpr // TypedExprTransformer typedExprTransformer = new TypedExprTransformerImpl( // typeSystem); // Obtain DBMS specific string representation for SqlExpr // DatatypeToStringPostgres typeSerializer = new DatatypeToStringPostgres(); // SqlLiteralMapper sqlLiteralMapper = new SqlLiteralMapperDefault( // typeSerializer); // SqlExprSerializerSystem serializerSystem = new SqlExprSerializerSystemImpl( // typeSerializer, sqlEscaper, sqlLiteralMapper); // ExprEvaluator exprEvaluator = new // ExprEvaluatorPartial(functionRegistry, typedExprTransformer) // { // Method m = DefaultCoercions.class.getMethod("toDouble", // Integer.class); // XMethod x = XMethodImpl.createFromMethod("toDouble", ds, null, m); // ds.registerCoercion(x); // } // // /* // * Methods that can only be rewritten // */ // // // // For most of the following functions, we can rely on Jena for their // // evaluation // ExprEvaluator evaluator = new ExprEvaluatorJena(); // // FunctionModel<TypeToken> sqlModel = typeSystem.getSqlFunctionModel(); Multimap<String, String> sparqlSqlDecls = typeSystem.getSparqlSqlDecls(); Map<String, SqlExprEvaluator> sqlImpls = typeSystem.getSqlImpls(); registerSqlOperatorBatchCompare(sqlModel, "lessThan"); registerSqlOperatorBatchCompare(sqlModel, "lessThanOrEqual"); registerSqlOperatorBatchCompare(sqlModel, "equal"); registerSqlOperatorBatchCompare(sqlModel, "greaterThan"); registerSqlOperatorBatchCompare(sqlModel, "greaterThanOrEqual"); registerSqlOperatorBatchNumeric(sqlModel, "numericPlus"); registerSqlOperatorBatchNumeric(sqlModel, "numericMinus"); registerSqlOperatorBatchNumeric(sqlModel, "numericMultiply"); registerSqlOperatorBatchNumeric(sqlModel, "numericDivide"); sqlModel.registerFunction("str@str", "str", MethodSignature.create(false, TypeToken.String, TypeToken.String)); sqlModel.registerFunction("str@double", "str", MethodSignature.create(false, TypeToken.String, TypeToken.Double)); sqlModel.registerFunction("str@float", "str", MethodSignature.create(false, TypeToken.String, TypeToken.Float)); sqlModel.registerFunction("str@int", "str", MethodSignature.create(false, TypeToken.String, TypeToken.Int)); sqlModel.registerFunction("str@date", "str", MethodSignature.create(false, TypeToken.String, TypeToken.Date)); sqlModel.registerFunction("double@str", "double", MethodSignature.create(false, TypeToken.Double, TypeToken.String)); sqlModel.registerFunction("isNotNull@object", "isNotNull", MethodSignature.create(false, TypeToken.Boolean, TypeToken.Object)); sparqlSqlDecls.putAll("<", sqlModel.getIdsByName("lessThan")); sparqlSqlDecls.putAll("<=", sqlModel.getIdsByName("lessThanOrEqual")); sparqlSqlDecls.putAll("=", sqlModel.getIdsByName("equal")); sparqlSqlDecls.putAll(">", sqlModel.getIdsByName("greaterThan")); sparqlSqlDecls.putAll(">=", sqlModel.getIdsByName("greaterThanOrEqual")); sparqlSqlDecls.putAll("+", sqlModel.getIdsByName("numericPlus")); sparqlSqlDecls.putAll("-", sqlModel.getIdsByName("numericMinus")); sparqlSqlDecls.putAll("/", sqlModel.getIdsByName("numericMultiply")); sparqlSqlDecls.putAll("*", sqlModel.getIdsByName("numericDivide")); sparqlSqlDecls.put("str", "str@str"); sparqlSqlDecls.put("str", "str@double"); sparqlSqlDecls.put("str", "str@float"); sparqlSqlDecls.put("str", "str@int"); sparqlSqlDecls.put(XSD.xdouble.getURI(), "double@str"); sparqlSqlDecls.put("str", "str@date"); sparqlSqlDecls.put("bound", "isNotNull@object"); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("lessThan"), new SqlExprEvaluator_Compare(typeSystem, S_LessThan::new)); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("lessThanOrEqual"), new SqlExprEvaluator_Compare(typeSystem, S_LessThanOrEqual::new)); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("equal"), new SqlExprEvaluator_Compare(typeSystem, S_Equals::new)); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("greaterThan"), new SqlExprEvaluator_Compare(typeSystem, S_GreaterThan::new)); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("greaterThanOrEqual"), new SqlExprEvaluator_Compare(typeSystem, S_GreaterThanOrEqual::new)); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("numericPlus"), new SqlExprEvaluator_Arithmetic()); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("+"), new SqlExprEvaluator_Compare(typeSystem, S_Add::new)); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("-"), new SqlExprEvaluator_Compare(typeSystem, S_Substract::new)); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("*"), new SqlExprEvaluator_Compare(typeSystem, S_Multiply::new)); // putForAll(sqlImpls, sqlModel.getIdsByName("/"), new SqlExprEvaluator_Compare(typeSystem, SqlExprFactoryUtils.factoryNumericDivide)); sqlModel.registerFunction("logicalAnd@boolean", "logicalAnd", MethodSignature.create(false, TypeToken.Boolean, TypeToken.Boolean, TypeToken.Boolean)); sparqlSqlDecls.putAll("&&", sqlModel.getIdsByName("logicalAnd")); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("logicalAnd"), new SqlExprEvaluator_LogicalAnd()); sqlModel.registerFunction("logicalOr@boolean", "logicalOr", MethodSignature.create(false, TypeToken.Boolean, TypeToken.Boolean, TypeToken.Boolean)); sqlModel.registerFunction("logicalOr@booleanError", "logicalOr", MethodSignature.create(false, TypeToken.Boolean, TypeToken.Boolean, TypeToken.TypeError)); sqlModel.registerFunction("logicalOr@errorBoolean", "logicalOr", MethodSignature.create(false, TypeToken.Boolean, TypeToken.TypeError, TypeToken.Boolean)); sparqlSqlDecls.putAll("||", sqlModel.getIdsByName("logicalOr")); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("logicalOr"), new SqlExprEvaluator_LogicalOr()); sqlModel.registerFunction("logicalNot@boolean", "logicalNot", MethodSignature.create(false, TypeToken.Boolean, TypeToken.Boolean)); sparqlSqlDecls.putAll("!", sqlModel.getIdsByName("logicalNot")); MapUtils.putForAll(sqlImpls, sqlModel.getIdsByName("logicalNot"), new SqlExprEvaluator_LogicalNot()); sqlModel.registerFunction("concat@str", "concat", MethodSignature.create(true, TypeToken.String, TypeToken.String)); sparqlSqlDecls.put("concat", "concat@str"); // register a parse int function sqlModel.registerFunction("parseInt@str", "parseInt", MethodSignature.create(false, TypeToken.Int, TypeToken.String)); sqlModel.registerFunction("parseDate@str", "parseDate", MethodSignature.create(false, TypeToken.Date, TypeToken.String)); //sparqlSqlDecls.put("concat", "concat@object"); FunctionModelMeta sqlMetaModel = typeSystem.getSqlFunctionMetaModel(); sqlMetaModel.getInverses().put("str@int", "parseInt@str"); sqlMetaModel.getInverses().put("str@date", "parseDate@str"); sqlMetaModel.getComparators().addAll(sqlModel.getIdsByName("lessThan")); sqlMetaModel.getComparators().addAll(sqlModel.getIdsByName("lessThanOrEqual")); sqlMetaModel.getComparators().addAll(sqlModel.getIdsByName("equal")); sqlMetaModel.getComparators().addAll(sqlModel.getIdsByName("greaterThanOrEqual")); sqlMetaModel.getComparators().addAll(sqlModel.getIdsByName("greaterThan")); sqlMetaModel.getLogicalAnds().addAll(sqlModel.getIdsByName("logicalAnd")); sqlMetaModel.getLogicalOrs().addAll(sqlModel.getIdsByName("logicalOr")); sqlMetaModel.getLogicalNots().addAll(sqlModel.getIdsByName("logicalNot")); //sqlModel.getInverses().put(); sqlImpls.put("parseInt@str", new SqlExprEvaluator_ParseInt()); sqlImpls.put("parseDate@str", new SqlExprEvaluator_ParseDate()); //sqlMetaModel.getInverses().put(key, value) // Geographic TypeToken typeGeometry = TypeToken.alloc("geometry"); String bif = "http://www.openlinksw.com/schemas/bif#"; // sqlModel.registerFunction("geometry ST_GeomFromPoint(float, float)", "ST_GeomFromPoint", MethodSignature.create(false, typeGeometry, TypeToken.Float, TypeToken.Float)); // sparqlSqlDecls.putAll(bif + "st_point", sqlModel.getIdsByName("ST_GeomFromPoint")); // sqlImpls.put("geometry ST_GeomFromPoint(float, float)", new SqlExprEvaluator_PassThrough(typeGeometry, "ST_GeomFromPoint")); // //String stIntersectsName = bif +7/ // MethodDeclaration<TypeToken> stIntersectsDecl1 = MethodDeclaration.create(TypeToken.Boolean, "ST_Intersects", false, typeGeometry, typeGeometry); // MethodDeclaration<TypeToken> stIntersectsDecl2 = MethodDeclaration.create(TypeToken.Boolean, "ST_DWithin", false, typeGeometry, typeGeometry, TypeToken.Float); // sqlModel.registerFunction(stIntersectsDecl1); // sqlModel.registerFunction(stIntersectsDecl2); // // sparqlSqlDecls.put(bif + "st_intersects", stIntersectsDecl1.toString()); // sparqlSqlDecls.put(bif + "st_intersects", stIntersectsDecl2.toString()); //sqlImpls.put(stIntersectsDecl1.toString(), new SqlExprEvaluator_PassThrough(TypeToken.Boolean, "ST_Intersects")); //sqlImpls.put(stIntersectsDecl2.toString(), new SqlExprEvaluator_PassThrough(TypeToken.Boolean, "ST_Intersects")); //sqlImpls.put(urlEncodeDecl.toString(), new SqlExprEvaluator_UrlEncode()); //sqlModel.registerFunction("boolean ST_Intersects(geometry, geometry, float)", "ST_Intersects", MethodSignature.create(false, TypeToken.Boolean, typeGeometry, typeGeometry, TypeToken.Float)); //sparqlSqlDecls.putAll(bif + "st_intersects", sqlModel.getIdsByName("ST_Intersects")); //sqlImpls.put("boolean ST_Intersects(geometry, geometry, float)", new SqlExprEvaluator_PassThrough(TypeToken.Boolean, "ST_Intersects")); // TODO: This coercion seems to get applied too often - why? sqlModel.registerCoercion("float toFloat(int)", "toFloat", MethodSignature.create(false, TypeToken.Float, TypeToken.Int)); sqlModel.registerCoercion("double toDouble(int)", "toDouble", MethodSignature.create(false, TypeToken.Double, TypeToken.Int)); //sqlImpls.put("float toFloat(int)", new SqlE); // Regex: Which function to use depends on the flags given the SPARQL function // Postgres: // ~ -> case sensitive // ~* -> case insensitive // However, we could also create a virtual SQL function, and process the regex flags in the SQL impl, or even the serializer // Put differently: Where is the best place to handle this? // - The SQL model should actually model what's there, so a fake SQL model doesn't really make sense. sqlModel.registerFunction("boolean regex(string, string)", "regex", MethodSignature.create(false, TypeToken.Boolean, TypeToken.String, TypeToken.String)); sqlModel.registerFunction("boolean regex(string, string, string)", "regex", MethodSignature.create(false, TypeToken.Boolean, TypeToken.String, TypeToken.String, TypeToken.String)); sparqlSqlDecls.putAll("regex", sqlModel.getIdsByName("regex")); sqlImpls.put("boolean regex(string, string)", new SqlExprEvaluator_PassThrough(TypeToken.Boolean, "regex")); sqlImpls.put("boolean regex(string, string, string)", new SqlExprEvaluator_PassThrough(TypeToken.Boolean, "regex")); // MethodDeclaration<TypeToken> stGeomFromTextDecl = MethodDeclaration.create(typeGeometry, "ST_GeomFromText", false, TypeToken.String); // sqlModel.registerFunction(stGeomFromTextDecl); // sparqlSqlDecls.put(bif + "st_geomFromText", stGeomFromTextDecl.toString()); //sqlImpls.put(stGeomFromTextDecl.toString(), new SqlExprEvaluator_PassThrough(TypeToken.Boolean, "ST_GeomFromText")); MethodDeclaration<TypeToken> urlEncodeDecl = MethodDeclaration.create(TypeToken.String, SparqlifyConstants.urlEncode, false, TypeToken.String); sqlModel.registerFunction(urlEncodeDecl); sparqlSqlDecls.put(SparqlifyConstants.urlEncode, urlEncodeDecl.toString()); sqlImpls.put(urlEncodeDecl.toString(), new SqlExprEvaluator_UrlEncode()); MethodDeclaration<TypeToken> urlDecodeDecl = MethodDeclaration.create(TypeToken.String, SparqlifyConstants.urlDecode, false, TypeToken.String); sqlModel.registerFunction(urlDecodeDecl); sparqlSqlDecls.put(SparqlifyConstants.urlDecode, urlDecodeDecl.toString()); sqlImpls.put(urlDecodeDecl.toString(), new SqlExprEvaluator_UrlDecode()); sqlMetaModel.getInverses().put(urlEncodeDecl.toString(), urlDecodeDecl.toString()); sqlMetaModel.getInverses().put(urlDecodeDecl.toString(), urlEncodeDecl.toString()); // Maps a sparql symbol to a set of implementing method declarations // This mapping allows the following: // the + symbol maps to { int op:plus_int (int, int), double op:plus_double (double, double) ,... } // Upon transformation, '+' is replaced with the name of a matching declaration // i.e. the name may change // afterwards, each sparql function name may have a set of backing sql declarations. // Note that the set of backing sql declarations is independent of the existing // overload signatures for that name - i.e. it only depends on the name! // // It seems to me, that we should somehow bundle up this MultiMap<FunctionSymbol, FunctionName> map. // Yet again, on the SQL level, we assign a unique ID to each overload // // // // Multimap<String, String> symbolSparqlDecls = HashMultimap.create(); // Aggregate function declarations // Note: These function have a hidden 'flags' field (string), e.g. for distinct // MethodDeclaration<TypeToken> aggCountDecl1 = MethodDeclaration.create(TypeToken.Long, "Count"); // MethodDeclaration<TypeToken> aggCountDecl2 = MethodDeclaration.create(TypeToken.Long, "Count", false, TypeToken.Object); // sqlModel.registerFunction(aggCountDecl1); // sqlModel.registerFunction(aggCountDecl2); // sparqlSqlDecls.put("count(*)", aggCountDecl1.toString()); // sparqlSqlDecls.put("count(*)", aggCountDecl2.toString()); /* * TODO Think of some builder pattern... * but maybe a declarative approach would be even better... * yet the problem is that we need beans in the xml, which means we need to mix it * with spring * * builder.getSparqlDecl(bif + "foo").addSqlDecl(..).setImpl().addSqlDecl(...) * */ // try { // InputStream in = NewWorldTest.class.getClassLoader().getResourceAsStream("functions.xml"); // SparqlifyConfig config = XmlUtils.unmarshallXml(SparqlifyConfig.class, in); // // //System.out.println(config); // // // for(SimpleFunction simpleFunction : config.getSimpleFunctions().getSimpleFunction()) { // String functionName = simpleFunction.getName(); // // for(Mapping mapping : simpleFunction.getMappings().getMapping()) { // String decStr = mapping.getSignature(); // String patternStr = mapping.getPattern(); // // MethodDeclaration<String> dec = MethodDeclarationParserSimple.parse(decStr); // // SqlFunctionSerializer serializer; // if(patternStr == null) { // serializer = new SqlFunctionSerializerDefault(functionName); // } else { // serializer = SqlFunctionSerializerStringTemplate.create(patternStr, dec); // } // // // TODO Add to serializer system // // TODO Auto create SPARQL declaration // } // } // // } catch(Exception e) { // throw new RuntimeException(e); // } // TODO We need to find the best overload based on a set of types: // http://www.postgresql.org/docs/9.1/static/functions-aggregate.html // Expample: sum is declared for: smallint, int, bigint, real, double precision, numeric, or interval // So given: [smallint, text, decimal, double, geometry] we need to figure out that there is no match // for geometry and text, but there are matches for smallint, decimal and double. // // Well, I guess decimal and double only have numeric as a common base // /* * Algo: * For each expression type, find all candidates * By this we rule out the types for which no candidates exist * * For the remaining ones, we need to find the best matching candidate out of those that we already found. * * So we compute the distance for each type and for each candidate, and take the best one... seems easy. * * * For group by: * * Group by is being done by expressions, so we can use the same expression as * generated by order by for the grouping, rather than duplicating the views and * increasing the joins. * * */ MethodDeclaration<TypeToken> aggCountDecl = MethodDeclaration.create(TypeToken.Long, "Count"); sqlModel.registerFunction(aggCountDecl); sparqlSqlDecls.put(AggCount.class.getSimpleName(), aggCountDecl.toString()); MethodDeclaration<TypeToken> aggSumDecl1 = MethodDeclaration.create(TypeToken.Long, "Sum", false, TypeToken.Long); MethodDeclaration<TypeToken> aggSumDecl2 = MethodDeclaration.create(TypeToken.Double, "Sum", false, TypeToken.Double); sqlModel.registerFunction(aggSumDecl1); sqlModel.registerFunction(aggSumDecl2); sparqlSqlDecls.put(AggSum.class.getSimpleName(), aggSumDecl1.toString()); sparqlSqlDecls.put(AggSum.class.getSimpleName(), aggSumDecl2.toString()); MethodDeclaration<TypeToken> aggGroupConcatDecl = MethodDeclaration.create(TypeToken.String, "GroupConcat", false, TypeToken.String); sqlModel.registerFunction(aggGroupConcatDecl); sparqlSqlDecls.put(AggGroupConcat.class.getSimpleName(), aggGroupConcatDecl.toString()); //sqlImpls.put(urlEncodeDecl.toString(), new SqlExprEvaluator_UrlEncode()); FunctionModelAliased<String> sparqlModel = typeSystem.getSparqlFunctionModel(); String fn = "http://www.w3.org/2005/xpath-functions#"; String op = "http://www.w3.org/2005/xpath-functions#"; String xsdInt = XSD.xint.toString(); String xsdString = XSD.xstring.toString(); String xsdFloat = XSD.xfloat.toString(); String xsdDouble = XSD.xdouble.toString(); String xsdDecimal = XSD.decimal.toString(); MethodDeclaration<String> numericAddInt = MethodDeclaration.create("+", MethodSignature.create(false, xsdInt, xsdInt, xsdInt)); sparqlModel.registerFunction("+", numericAddInt); MethodDeclaration<String> numericAddDouble = MethodDeclaration.create("+", MethodSignature.create(false, xsdDouble, xsdDouble, xsdDouble)); sparqlModel.registerFunction("+", numericAddDouble); sparqlModel.registerCoercion(MethodDeclaration.create(xsdDouble, MethodSignature.create(false, xsdDouble, xsdInt))); // Decimal -> Float // Decimal -> Double sparqlModel.registerCoercion(MethodDeclaration.create(xsdFloat, MethodSignature.create(false, xsdFloat, xsdDecimal))); sparqlModel.registerCoercion(MethodDeclaration.create(xsdDouble, MethodSignature.create(false, xsdDouble, xsdDecimal))); MethodDeclaration<String> groupConcat = MethodDeclaration.create(AggGroupConcat.class.getSimpleName(), MethodSignature.create(false, xsdString)); sparqlModel.registerFunction(AggGroupConcat.class.getSimpleName(), groupConcat); //sparqlModel.registerCoercion(MethodDeclaration.create(xsdDouble, MethodSignature.create(false, xsdDouble, xsdInt))); //sqlImpls.put() // SparqlFunction f = new SparqlFunctionImpl(decl, null, null); // typeSystem.registerSparqlFunction(f); /** * Ok, seems like we need one more iteration to get the type system right: * - Initially, we start with a SPARQL expression, such as typedLit(?foo, xsd:int) + typedLit(?bar, xsd:float) * The xsd types must be compatible with the underlying sql type. * What compatible means needs to be formalized, but essentially it means * we are mapping to the closest semantic type - and that we are not mapping e.g. strings to integers. * * - Now the TypedExprTransformer does a bottom up evaluation of the expression, and turns each node into an RdfTerm expression * typed literal constants and column references are automatically converted to typed literals * * - When the TypedExprTransformer hits an operator, such as '+', '||', '&&' and so on, it invokes any registered * ExprTransformer* --- its signature is: E_RdfTerm transform(Expr orig, List<E_RdfTerm> exprs); * * The expr transformer can now yield a now RdfTerm expression * and has now the chance to detect type errors, or compute an appropriate datatype language tag, etc. * * This step is independent of the SQL datatypes - its purpose is to fulfill the functions' and operators' contracts * that are set forth by the SPARQL standard. * Note that for simplicity we do not allow xsd:datatypes to be dynamic. * In theory, we could push down the conditions into the SQL, but this is * rather cumbersome and there is no probably no use case that can't be solved in a better way. * * The resulting expression for a function may be a constant or an arbitray new expression with its own function name. * However, in practice, the function name should stay the same as the original one, as the main purpose is to * create the appropriate E_RdfTerm object for the encountered symbol. * * * After the TypedExprTransformer is done, we end up with a new expression which does not contain any E_RdfTer * objects anymore except for the root of the expression. * This means, that we eleminated the RDF specific RDF terms and instead have expressions that only make use of * plain old SQL datatypes. * * - The function symbols now no longer refer to the original SPARQL functions, which have to cope with RdfTerm semantics, * but rather, their arguments are now SQL types. * * We can now declare for each function symbol which combinations of SQL typed parameters are valid, * thereby *overloading* the symbol with SQL symbols. * The plus operator could for instance have the overloads int + (int, int), float + (float, float), etc... * * [TODO] We already store literals as java objects, can we just map them to corresponding java methods using reflection? * On the other hand, reflection is so fucking expensive which is bad for benchmarks. * * * - We could now provide java implementations the evaluate these functions * * - Finally, for each SQL function symbol, the appropriate serializer needs to be used * e.g. double(foo) -> foo::double for postgres, float(bar) -> (cast bar as float) for mysql, ... * * * */ // tag the comparators as comparators... // urlEncode { MethodSignature<TypeToken> sig = MethodSignature.create(false, TypeToken.String, TypeToken.String); SparqlFunction f = new SparqlFunctionImpl(SparqlifyConstants.urlEncode, sig, null, null); typeSystem.registerSparqlFunction(f); } { MethodDeclaration<TypeToken> decl = MethodDeclaration.create(TypeToken.String, SparqlifyConstants.urlEncode, false, TypeToken.String); SparqlFunction f = new SparqlFunctionImpl(decl, null, null); typeSystem.registerSparqlFunction(f); } // // { // MethodSignature<String> sig = MethodSignature.create(false, // SparqlifyConstants.numericTypeLabel, // SparqlifyConstants.numericTypeLabel, // SparqlifyConstants.numericTypeLabel); // SqlExprEvaluator evaluator = new SqlExprEvaluator_LogicalOr(); // // SparqlFunction f = new SparqlFunctionImpl("or", sig, evaluator, // null); // typeSystem.registerSparqlFunction(f); // SqlFunctionSerializer serializer = new // SqlFunctionSerializerOp2("OR"); // serializerSystem.addSerializer("or", serializer); // // } { // MethodSignature<String> sig = MethodSignature.create(false, // SparqlifyConstants.numericTypeLabel, // SparqlifyConstants.numericTypeLabel, // SparqlifyConstants.numericTypeLabel); // SqlExprEvaluator evaluator = new // SqlExprEvaluator_Arithmetic(typeSystem); // // XSDFuncOp.add(nv1, nv2); // XSDFuncOp.classifyNumeric(fName, nv); // // // As a fallback where Jena can't evaluate it, register a // transformation to an SQL expression. // SparqlFunction f = new SparqlFunctionImpl("+", sig, evaluator, // null); // typeSystem.registerSparqlFunction(f); // SqlFunctionSerializer serializer = new // SqlFunctionSerializerOp2("+"); // serializerSystem.addSerializer("+", serializer); } } public static void loadExtensionFunctions(TypeSystem typeSystem, RdfTermEliminatorWriteable exprTransformer, SqlExprSerializerSystem serializerSystem) { // transMap.put(bif + "st_intersects", new ExprTransformerFunction(XSD.xboolean)); // transMap.put(bif + "st_geomFromText", new ExprTransformerFunction(virtGeometry)); // transMap.put(bif + "st_point", new ExprTransformerFunction(ResourceFactory.createResource("http://www.opengis.net/ont/geosparql#wktLiteral"))); //)); try { Map<String, String> typeNameToUri = MapReader.readFromResource("/type-uri.tsv"); Function<String, String> fnTypeToUri = Functions.forMap(typeNameToUri); InputStream in = SparqlifyCoreInit.class.getClassLoader().getResourceAsStream("functions.xml"); SparqlifyConfig config = XmlUtils.unmarshallXml(SparqlifyConfig.class, in); FunctionModelAliased<String> sparqlModel = typeSystem.getSparqlFunctionModel(); Multimap<String, String> sparqlSqlDecls = typeSystem.getSparqlSqlDecls(); FunctionModel<TypeToken> sqlModel = typeSystem.getSqlFunctionModel(); //System.out.println(config); for(SimpleFunction simpleFunction : config.getSimpleFunctions().getSimpleFunction()) { String sparqlName = simpleFunction.getName(); for(Mapping mapping : simpleFunction.getMappings().getMapping()) { String decStr = mapping.getSignature(); String patternStr = mapping.getPattern(); MethodDeclaration<String> dec = MethodDeclarationParserSimple.parse(decStr); SqlFunctionSerializer serializer; if(patternStr == null) { serializer = new SqlFunctionSerializerDefault(dec.getName()); } else { serializer = SqlFunctionSerializerStringTemplate.create(patternStr, dec); } MethodDeclaration<TypeToken> sqlDec = transform(dec, TransformUtils.toTypeToken); String translationName = sparqlName + "@" + sqlDec.getSignature().getReturnType(); MethodDeclaration<String> sparqlDec = MethodDeclaration.create(translationName, transform(dec.getSignature(), fnTypeToUri)); sparqlModel.registerFunction(sparqlName, sparqlDec); //Resource resReturnType = ResourceFactory.createResource(sparqlDec.getSignature().getReturnType()); //ExprTransformer et = new ExprTransformerFunction(resReturnType); //transMap.put(bif + "st_intersects", ); // TODO Add to serializer system // TODO Auto create SPARQL declaration String sqlDescriptor = sqlDec.toString(); sparqlSqlDecls.put(translationName, sqlDescriptor); sqlModel.registerFunction(sqlDec); serializerSystem.addSerializer(sqlDescriptor, serializer); } ExprTransformer et = new ExprTransformerSparqlFunctionModel(sparqlModel); exprTransformer.register(sparqlName, et); } } catch(Exception e) { throw new RuntimeException(e); } } // TODO Move these transform methods to an appropriate place public static <I, O> MethodDeclaration<O> transform(MethodDeclaration<I> dec, Function<I, O> fn) { MethodSignature<O> s = transform(dec.getSignature(), fn); MethodDeclaration<O> result = MethodDeclaration.create(dec.getName(), s); return result; } public static <I, O> MethodSignature<O> transform(MethodSignature<I> sig, Function<I, O> fn) { O returnType = fn.apply(sig.getReturnType()); List<I> items = sig.getParameterTypes(); List<O> paramTypes = new ArrayList<O>(items.size()); for(I item : items) { O paramType = fn.apply(item); paramTypes.add(paramType); } I vat = sig.getVarArgType(); O varArgType = vat == null ? null : fn.apply(vat); MethodSignature<O> result = MethodSignature.create(returnType, paramTypes, varArgType); return result; } }