package com.bigdata.rdf.sparql.ast; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.openrdf.model.URI; import org.openrdf.model.impl.URIImpl; import org.openrdf.model.vocabulary.FN; import org.openrdf.model.vocabulary.XMLSchema; import org.openrdf.query.algebra.Compare.CompareOp; import com.bigdata.bop.BOp; import com.bigdata.bop.BOpContextBase; import com.bigdata.bop.Constant; import com.bigdata.bop.ContextBindingSet; import com.bigdata.bop.IBindingSet; import com.bigdata.bop.IConstant; import com.bigdata.bop.IValueExpression; import com.bigdata.bop.IVariable; import com.bigdata.bop.IVariableOrConstant; import com.bigdata.bop.ImmutableBOp; import com.bigdata.bop.NV; import com.bigdata.bop.aggregate.AggregateBase; import com.bigdata.bop.aggregate.IAggregate; import com.bigdata.bop.rdf.aggregate.GROUP_CONCAT; import com.bigdata.rdf.internal.ILexiconConfiguration; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.internal.constraints.AndBOp; import com.bigdata.rdf.internal.constraints.BNodeBOp; import com.bigdata.rdf.internal.constraints.CoalesceBOp; import com.bigdata.rdf.internal.constraints.CompareBOp; import com.bigdata.rdf.internal.constraints.ComputedIN; import com.bigdata.rdf.internal.constraints.ConcatBOp; import com.bigdata.rdf.internal.constraints.DatatypeBOp; import com.bigdata.rdf.internal.constraints.DateBOp; import com.bigdata.rdf.internal.constraints.DateBOp.DateOp; import com.bigdata.rdf.internal.constraints.DigestBOp; import com.bigdata.rdf.internal.constraints.DigestBOp.DigestOp; import com.bigdata.rdf.internal.constraints.EncodeForURIBOp; import com.bigdata.rdf.internal.constraints.FalseBOp; import com.bigdata.rdf.internal.constraints.FuncBOp; import com.bigdata.rdf.internal.constraints.IfBOp; import com.bigdata.rdf.internal.constraints.InHashBOp; import com.bigdata.rdf.internal.constraints.IriBOp; import com.bigdata.rdf.internal.constraints.IsBNodeBOp; import com.bigdata.rdf.internal.constraints.IsBoundBOp; import com.bigdata.rdf.internal.constraints.IsLiteralBOp; import com.bigdata.rdf.internal.constraints.IsNumericBOp; import com.bigdata.rdf.internal.constraints.IsURIBOp; import com.bigdata.rdf.internal.constraints.LangBOp; import com.bigdata.rdf.internal.constraints.LangMatchesBOp; import com.bigdata.rdf.internal.constraints.LcaseBOp; import com.bigdata.rdf.internal.constraints.MathBOp; import com.bigdata.rdf.internal.constraints.MathBOp.MathOp; import com.bigdata.rdf.internal.constraints.NotBOp; import com.bigdata.rdf.internal.constraints.NowBOp; import com.bigdata.rdf.internal.constraints.NumericBOp; import com.bigdata.rdf.internal.constraints.NumericBOp.NumericOp; import com.bigdata.rdf.internal.constraints.OrBOp; import com.bigdata.rdf.internal.constraints.RandBOp; import com.bigdata.rdf.internal.constraints.RegexBOp; import com.bigdata.rdf.internal.constraints.ReplaceBOp; import com.bigdata.rdf.internal.constraints.SameTermBOp; import com.bigdata.rdf.internal.constraints.SparqlTypeErrorBOp; import com.bigdata.rdf.internal.constraints.StrAfterBOp; import com.bigdata.rdf.internal.constraints.StrBOp; import com.bigdata.rdf.internal.constraints.StrBeforeBOp; import com.bigdata.rdf.internal.constraints.StrcontainsBOp; import com.bigdata.rdf.internal.constraints.StrdtBOp; import com.bigdata.rdf.internal.constraints.StrendsBOp; import com.bigdata.rdf.internal.constraints.StrlangBOp; import com.bigdata.rdf.internal.constraints.StrlenBOp; import com.bigdata.rdf.internal.constraints.StrstartsBOp; import com.bigdata.rdf.internal.constraints.SubstrBOp; import com.bigdata.rdf.internal.constraints.TrueBOp; import com.bigdata.rdf.internal.constraints.UUIDBOp; import com.bigdata.rdf.internal.constraints.UcaseBOp; import com.bigdata.rdf.internal.constraints.XsdLongBOp; import com.bigdata.rdf.internal.constraints.XsdStrBOp; import com.bigdata.rdf.internal.constraints.XsdUnsignedLongBOp; import com.bigdata.rdf.lexicon.LexiconRelation; import com.bigdata.rdf.sparql.ast.eval.AST2BOpUtility; import com.bigdata.rdf.sparql.ast.eval.ASTSearchInSearchOptimizer; import com.bigdata.rdf.sparql.ast.optimizers.IASTOptimizer; import com.bigdata.rdf.store.BD; /** * Registry for built-in and external SPARQL functions. * <p> * Note: Use an alternative namespace for functions which do not have official * namespaces. * * @see <a href="http://www.w3.org/2005/xpath-functions/">XPath functions</a> * @see <a * href="http://lists.w3.org/Archives/Public/public-rdf-dawg/2006AprJun/att-0138/sparql-function-uri.html">Sparql * functions</a> * * TODO We should restrict registration of functions in the * {@link BD#NAMESPACE} to static initialization. The code * already does not permit replacement of registered functions. */ @SuppressWarnings("rawtypes") public class FunctionRegistry { public interface Annotations extends AggregateBase.Annotations{ } private static ConcurrentMap<URI, Factory> factories = new ConcurrentHashMap<URI, Factory>(); public static final String SPARQL_FUNCTIONS = "http://www.w3.org/2006/sparql-functions#"; public static final String XPATH_FUNCTIONS = "http://www.w3.org/2005/xpath-functions#"; /** * Functions in SPARQL 1.0 for which there is not yet any official URI. */ public static final String SPARQL10_UNDEFINED_FUNCTIONS = "http://www.bigdata.com/sparql-1.0-undefined-functions"; /** * Functions in SPARQL 1.1 for which there is not yet any official URI. */ public static final String SPARQL11_UNDEFINED_FUNCTIONS = "http://www.bigdata.com/sparql-1.1-undefined-functions"; public static final URI BOUND = new URIImpl(SPARQL_FUNCTIONS+"bound"); public static final URI IS_LITERAL = new URIImpl(SPARQL_FUNCTIONS+"isLiteral"); public static final URI IS_BLANK = new URIImpl(SPARQL_FUNCTIONS+"isBlank"); public static final URI IS_URI = new URIImpl(SPARQL_FUNCTIONS+"isURI"); public static final URI IS_IRI = new URIImpl(SPARQL_FUNCTIONS+"isIRI"); public static final URI IS_NUMERIC = new URIImpl(SPARQL_FUNCTIONS+"isNumeric"); public static final URI STR = new URIImpl(SPARQL_FUNCTIONS+"str"); public static final URI LANG = new URIImpl(SPARQL_FUNCTIONS+"lang"); public static final URI DATATYPE = new URIImpl(SPARQL_FUNCTIONS+"datatype"); public static final URI LANG_MATCHES = new URIImpl(SPARQL_FUNCTIONS+"langMatches"); public static final URI REGEX = new URIImpl(XPATH_FUNCTIONS+"matches"); public static final URI OR = new URIImpl(SPARQL_FUNCTIONS+"logical-or"); public static final URI AND = new URIImpl(SPARQL_FUNCTIONS+"logical-and"); public static final URI NOT = new URIImpl(XPATH_FUNCTIONS+"not"); public static final URI SAME_TERM = new URIImpl(SPARQL_FUNCTIONS+"sameTerm"); public static final URI CONCAT = new URIImpl(SPARQL_FUNCTIONS+"concat"); public static final URI COALESCE = new URIImpl(SPARQL_FUNCTIONS+"coalesce"); public static final URI IN = new URIImpl(SPARQL_FUNCTIONS+"in"); public static final URI NOT_IN = new URIImpl(SPARQL_FUNCTIONS+"notIn"); public static final URI IF = new URIImpl(SPARQL_FUNCTIONS+"if"); public static final URI NOW = new URIImpl(XPATH_FUNCTIONS+"now"); public static final URI YEAR = new URIImpl(XPATH_FUNCTIONS+"year-from-dateTime"); public static final URI MONTH = new URIImpl(XPATH_FUNCTIONS+"month-from-dateTime"); public static final URI DAY = new URIImpl(XPATH_FUNCTIONS+"day-from-dateTime"); public static final URI HOURS = new URIImpl(XPATH_FUNCTIONS+"hours-from-dateTime"); public static final URI MINUTES = new URIImpl(XPATH_FUNCTIONS+"minutes-from-dateTime"); public static final URI SECONDS = new URIImpl(XPATH_FUNCTIONS+"seconds-from-dateTime"); public static final URI TZ = new URIImpl(XPATH_FUNCTIONS+"tz"); public static final URI TIMEZONE = new URIImpl(XPATH_FUNCTIONS+"timezone-from-dateTime"); public static final URI MD5 = new URIImpl(SPARQL_FUNCTIONS+"md5"); public static final URI SHA1 = new URIImpl(SPARQL_FUNCTIONS+"sha1"); public static final URI SHA224 = new URIImpl(SPARQL_FUNCTIONS+"sha224"); public static final URI SHA256 = new URIImpl(SPARQL_FUNCTIONS+"sha256"); public static final URI SHA384 = new URIImpl(SPARQL_FUNCTIONS+"sha384"); public static final URI SHA512 = new URIImpl(SPARQL_FUNCTIONS+"sha512"); public static final URI UUID = new URIImpl(SPARQL_FUNCTIONS+"uuid"); public static final URI STRUUID = new URIImpl(SPARQL_FUNCTIONS+"struuid"); public static final URI STR_DT = new URIImpl(SPARQL_FUNCTIONS+"strdt"); public static final URI STR_LANG = new URIImpl(SPARQL_FUNCTIONS+"strlang"); public static final URI LCASE = FN.LOWER_CASE;//new URIImpl(XPATH_FUNCTIONS+"lower-case"); public static final URI UCASE = FN.UPPER_CASE;//new URIImpl(XPATH_FUNCTIONS+"upper-case"); public static final URI ENCODE_FOR_URI = FN.ENCODE_FOR_URI;//new URIImpl(SPARQL_FUNCTIONS+"encodeForUri"); public static final URI STR_LEN = FN.STRING_LENGTH;//new URIImpl(XPATH_FUNCTIONS+"string-length"); public static final URI SUBSTR = FN.SUBSTRING;//new URIImpl(SPARQL_FUNCTIONS+"substr"); public static final URI CONTAINS = FN.CONTAINS; public static final URI STARTS_WITH = FN.STARTS_WITH; public static final URI ENDS_WITH = FN.ENDS_WITH; public static final URI STR_AFTER = FN.SUBSTRING_AFTER; // FIXME implement. See StrAfter public static final URI STR_BEFORE = FN.SUBSTRING_BEFORE; // FIXME implement. See StrBefore public static final URI REPLACE = FN.REPLACE; // FIXME implement. See Replace. /** * The IRI function, as defined in <a * href="http://www.w3.org/TR/sparql11-query/#SparqlOps">SPARQL 1.1 Query * Language for RDF</a>. */ public static final URI IRI = new URIImpl(SPARQL11_UNDEFINED_FUNCTIONS + "iri"); /** * The BNODE()/BNODE(Literal) function as defined in <a * href="http://www.w3.org/TR/sparql11-query/#SparqlOps">SPARQL 1.1 Query * Language for RDF</a>. */ public static final URI BNODE = new URIImpl(SPARQL11_UNDEFINED_FUNCTIONS+ "bnode"); /** * The EXISTS(graphPattern) function as defined in <a * href="http://www.w3.org/TR/sparql11-query/#SparqlOps">SPARQL 1.1 Query * Language for RDF</a>. */ public static final URI EXISTS = new URIImpl(SPARQL11_UNDEFINED_FUNCTIONS + "exists"); /** * The NOT EXISTS(graphPattern) function as defined in <a * href="http://www.w3.org/TR/sparql11-query/#SparqlOps">SPARQL 1.1 Query * Language for RDF</a>. */ public static final URI NOT_EXISTS = new URIImpl( SPARQL11_UNDEFINED_FUNCTIONS + "not-exists"); public static final URI EQ = new URIImpl(XPATH_FUNCTIONS+"equal-to"); public static final URI NE = new URIImpl(XPATH_FUNCTIONS+"not-equal-to"); public static final URI GT = new URIImpl(XPATH_FUNCTIONS+"greater-than"); public static final URI GE = new URIImpl(XPATH_FUNCTIONS+"greater-than-or-equal-to"); public static final URI LT = new URIImpl(XPATH_FUNCTIONS+"less-than"); public static final URI LE = new URIImpl(XPATH_FUNCTIONS+"less-than-or-equal-to"); public static final URI ADD = new URIImpl(XPATH_FUNCTIONS+"numeric-add"); public static final URI SUBTRACT = new URIImpl(XPATH_FUNCTIONS+"numeric-subtract"); public static final URI MULTIPLY = new URIImpl(XPATH_FUNCTIONS+"numeric-multiply"); public static final URI DIVIDE = new URIImpl(XPATH_FUNCTIONS+"numeric-divide"); public static final URI ABS = FN.NUMERIC_ABS;//new URIImpl(XPATH_FUNCTIONS+"numeric-abs"); public static final URI ROUND = FN.NUMERIC_ROUND;//new URIImpl(SPARQL_FUNCTIONS+"numeric-round"); public static final URI CEIL = FN.NUMERIC_CEIL;//new URIImpl(SPARQL_FUNCTIONS+"numeric-ceil"); public static final URI FLOOR = FN.NUMERIC_FLOOR;//new URIImpl(SPARQL_FUNCTIONS+"numeric-floor"); public static final URI RAND = new URIImpl(SPARQL_FUNCTIONS+"numeric-rand"); public static final URI AVERAGE = new URIImpl(SPARQL_FUNCTIONS+"average"); public static final URI COUNT = new URIImpl(SPARQL_FUNCTIONS+"count"); public static final URI GROUP_CONCAT = new URIImpl(SPARQL_FUNCTIONS+"groupConcat"); public static final URI MAX = new URIImpl(SPARQL_FUNCTIONS+"max"); public static final URI MIN = new URIImpl(SPARQL_FUNCTIONS+"min"); public static final URI SAMPLE = new URIImpl(SPARQL_FUNCTIONS+"sample"); public static final URI SUM = new URIImpl(SPARQL_FUNCTIONS+"sum"); public static final URI XSD_BOOL = XMLSchema.BOOLEAN; public static final URI XSD_DT = XMLSchema.DATETIME; public static final URI XSD_DEC = XMLSchema.DECIMAL; public static final URI XSD_DBL = XMLSchema.DOUBLE; public static final URI XSD_FLT = XMLSchema.FLOAT; public static final URI XSD_INT = XMLSchema.INTEGER; public static final URI XSD_STR = XMLSchema.STRING; public static final URI XSD_DATE = XMLSchema.DATE; public static final URI XSD_LONG = XMLSchema.LONG; public static final URI XSD_UNSIGNED_LONG = XMLSchema.UNSIGNED_LONG; static { add(AVERAGE, new AggregateFactory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression ve = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context, globals, args[0]); return new com.bigdata.bop.rdf.aggregate.AVERAGE(new BOp[]{ve}, scalarValues); } }); add(COUNT, new AggregateFactory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression ve = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context, globals, args[0]); return new com.bigdata.bop.rdf.aggregate.COUNT(new BOp[]{ve}, scalarValues); } }); add(GROUP_CONCAT, new GroupConcatFactory()); add(MAX, new AggregateFactory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); IValueExpression<? extends IV> expressions[]=new IValueExpression[args.length]; for(int i=0;i<args.length;i++){ expressions[i] = AST2BOpUtility.toVE(context, globals, args[i]); } return new com.bigdata.bop.rdf.aggregate.MAX(expressions, scalarValues); } }); add(MIN, new AggregateFactory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); IValueExpression<? extends IV> expressions[]=new IValueExpression[args.length]; for(int i=0;i<args.length;i++){ expressions[i] = AST2BOpUtility.toVE(context, globals, args[i]); } return new com.bigdata.bop.rdf.aggregate.MIN(expressions, scalarValues); } }); add(SAMPLE, new AggregateFactory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression ve = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context, globals, args[0]); return new com.bigdata.bop.rdf.aggregate.SAMPLE(false,ve); } }); add(SUM, new AggregateFactory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); IValueExpression<? extends IV> expressions[]=new IValueExpression[args.length]; for(int i=0;i<args.length;i++){ expressions[i] = AST2BOpUtility.toVE(context, globals, args[i]); } return new com.bigdata.bop.rdf.aggregate.SUM(expressions, scalarValues); } }); // add the bigdata built-ins add(BOUND, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, VarNode.class); final IVariable<IV> var = ((VarNode) args[0]) .getValueExpression(); return new IsBoundBOp(var); } }); add(IS_LITERAL, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression ve = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context, globals, args[0]); return new IsLiteralBOp(ve); } }); add(IS_BLANK, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression ve = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context, globals, args[0]); return new IsBNodeBOp(ve); } }); add(IS_URI, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context, globals, args[0]); return new IsURIBOp(ve); } }); add(IS_IRI, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new IsURIBOp(ve); } }); add(IS_NUMERIC, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new IsNumericBOp(ve); } }); add(STR, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new StrBOp(ve, globals); } }); add(ENCODE_FOR_URI,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new EncodeForURIBOp(ve, globals); } }); add(LCASE,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> ve = AST2BOpUtility.toVE(context,globals, args[0]); return new LcaseBOp(ve, globals); } }); add(UCASE,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> ve = AST2BOpUtility.toVE(context,globals, args[0]); return new UcaseBOp(ve, globals); } }); add(STR_DT,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class,ValueExpressionNode.class); final IValueExpression<? extends IV> ve = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> type = AST2BOpUtility.toVE(context,globals, args[1]); return new StrdtBOp(ve,type ,globals); } }); add(STR_LANG,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class,ValueExpressionNode.class); final IValueExpression<? extends IV> var = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> lang = AST2BOpUtility.toVE(context,globals, args[1]); return new StrlangBOp(var,lang ,globals); } }); add(STR_LEN,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> var = AST2BOpUtility.toVE(context,globals, args[0]); return new StrlenBOp(var,globals); } }); add(SUBSTR,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class,ValueExpressionNode.class); final IValueExpression<? extends IV> var = AST2BOpUtility.toVE(context, globals, args[0]); // if (args.length == 2) { // // } else { final IValueExpression<? extends IV> start = AST2BOpUtility .toVE(context, globals, args[1]); final IValueExpression<? extends IV> length = args.length >= 3 ? AST2BOpUtility .toVE(context, globals, args[2]) : null; return new SubstrBOp(var, start, length, globals); // } } }); add(CONTAINS,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> x = AST2BOpUtility.toVE(context, globals, args[0]); final IValueExpression<? extends IV> y = AST2BOpUtility.toVE(context, globals, args[1]); return new StrcontainsBOp(x, y); } }); add(LANG, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> ve = AST2BOpUtility.toVE(context,globals, args[0]); return new LangBOp(ve, globals); } }); add(CONCAT, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); IValueExpression<? extends IV> expressions[]=new IValueExpression[args.length]; for(int i=0;i<args.length;i++){ expressions[i] = AST2BOpUtility.toVE(context,globals, args[i]); } return new ConcatBOp(globals,expressions); } }); add(COALESCE, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); IValueExpression<? extends IV> expressions[]=new IValueExpression[args.length]; for(int i=0;i<args.length;i++){ expressions[i] = AST2BOpUtility.toVE(context,globals, args[i]); } return new CoalesceBOp(globals,expressions); } }); add(IF, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class,ValueExpressionNode.class,ValueExpressionNode.class); final IValueExpression<? extends IV> conditional = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> expression1 = AST2BOpUtility.toVE(context,globals, args[1]); final IValueExpression<? extends IV> expression2 = AST2BOpUtility.toVE(context,globals, args[2]); return new IfBOp(conditional,expression1,expression2); } }); add(DATATYPE, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> ve = AST2BOpUtility.toVE(context,globals, args[0]); return new DatatypeBOp(ve, globals); } }); add(RAND,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { if(args!=null&&args.length>0){ throw new IllegalArgumentException("wrong # of args"); } return new RandBOp(); } }); add(LANG_MATCHES, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> right = AST2BOpUtility.toVE(context,globals, args[1]); return new LangMatchesBOp(left, right); } }); add(REGEX, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> var = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> pattern = AST2BOpUtility.toVE(context,globals, args[1]); final RegexBOp regex; if (args.length == 2) { regex = new RegexBOp(var, pattern); } else { final IValueExpression<? extends IV> flags = AST2BOpUtility.toVE(context,globals, args[2]); regex = new RegexBOp(var, pattern, flags); } return regex; } }); add(AND, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> right = AST2BOpUtility.toVE(context,globals, args[1]); return new AndBOp(left, right); } }); add(OR, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context, globals, args[0]); final IValueExpression<? extends IV> right = AST2BOpUtility.toVE(context, globals, args[1]); return new OrBOp(left, right); } }); add(NOT, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> arg = AST2BOpUtility.toVE(context, globals, args[0]); return new NotBOp(arg); } }); add(IRI,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); final String baseURI = (String) scalarValues.get(IriBOp.Annotations.BASE_URI); return new IriBOp(ve, baseURI, globals); } }); add(BNODE,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { if (args.length == 0) { return new BNodeBOp(globals); } else { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new BNodeBOp(ve, globals); } } }); add(STARTS_WITH,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class,ValueExpressionNode.class); final IValueExpression<? extends IV> var = AST2BOpUtility.toVE(context, globals, args[0]); final IValueExpression<? extends IV> token = AST2BOpUtility .toVE(context, globals, args[1]); return new StrstartsBOp(var, token); } }); add(ENDS_WITH,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class,ValueExpressionNode.class); final IValueExpression<? extends IV> var = AST2BOpUtility.toVE(context, globals, args[0]); final IValueExpression<? extends IV> token = AST2BOpUtility .toVE(context, globals, args[1]); return new StrendsBOp(var, token); } }); add(STR_BEFORE,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> arg1 = AST2BOpUtility.toVE(context, globals, args[0]); final IValueExpression<? extends IV> arg2 = AST2BOpUtility.toVE(context, globals, args[1]); return new StrBeforeBOp(arg1, arg2, globals); } }); add(STR_AFTER,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> arg1 = AST2BOpUtility.toVE(context, globals, args[0]); final IValueExpression<? extends IV> arg2 = AST2BOpUtility.toVE(context, globals, args[1]); return new StrAfterBOp(arg1, arg2, globals); } }); add(REPLACE,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> var = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> pattern = AST2BOpUtility.toVE(context,globals, args[1]); final IValueExpression<? extends IV> replacement = AST2BOpUtility.toVE(context,globals, args[2]); if (args.length == 3) { return new ReplaceBOp(var, pattern, replacement, globals); } else { final IValueExpression<? extends IV> flags = AST2BOpUtility.toVE(context,globals, args[3]); return new ReplaceBOp(var, pattern, replacement, flags, globals); } } }); add(IN, new InFactory(false/*not*/)); add(NOT_IN, new InFactory(true/*not*/)); add(SAME_TERM, SameTermFactory.INSTANCE); add(EQ, new CompareFactory(CompareOp.EQ)); add(NE, new CompareFactory(CompareOp.NE)); add(GT, new CompareFactory(CompareOp.GT)); add(GE, new CompareFactory(CompareOp.GE)); add(LT, new CompareFactory(CompareOp.LT)); add(LE, new CompareFactory(CompareOp.LE)); add(ADD, new MathFactory(MathOp.PLUS)); add(SUBTRACT, new MathFactory(MathOp.MINUS)); add(MULTIPLY, new MathFactory(MathOp.MULTIPLY)); add(DIVIDE, new MathFactory(MathOp.DIVIDE)); add(ABS, new NumericFactory(NumericOp.ABS)); add(ROUND, new NumericFactory(NumericOp.ROUND)); add(CEIL, new NumericFactory(NumericOp.CEIL)); add(FLOOR, new NumericFactory(NumericOp.FLOOR)); add(XSD_BOOL, new CastFactory(XMLSchema.BOOLEAN.toString())); add(XSD_DT, new CastFactory(XMLSchema.DATETIME.toString())); add(XSD_DEC, new CastFactory(XMLSchema.DECIMAL.toString())); add(XSD_DBL, new CastFactory(XMLSchema.DOUBLE.toString())); add(XSD_FLT, new CastFactory(XMLSchema.FLOAT.toString())); add(XSD_INT, new CastFactory(XMLSchema.INTEGER.toString())); add(XSD_LONG, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new XsdLongBOp(ve, globals); } }); add(XSD_UNSIGNED_LONG, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new XsdUnsignedLongBOp(ve, globals); } }); /* * Changed the xsd:string cast operator to use XsdStrBOp instead of the * Sesame cast function. */ // add(XSD_STR, new CastFactory(XMLSchema.STRING.toString())); add(XSD_STR, new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); // final IValueExpression<? extends IV> var = args[0].getValueExpression(); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new XsdStrBOp(ve, globals); } }); add(YEAR,new DateFactory(DateOp.YEAR)); add(MONTH,new DateFactory(DateOp.MONTH)); add(DAY,new DateFactory(DateOp.DAY)); add(HOURS,new DateFactory(DateOp.HOURS)); add(MINUTES,new DateFactory(DateOp.MINUTES)); add(SECONDS,new DateFactory(DateOp.SECONDS)); add(TIMEZONE,new DateFactory(DateOp.TIMEZONE)); add(XSD_DATE, new DateFactory(DateOp.DATE)); add(TZ,new DateFactory(DateOp.TZ)); add(NOW,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { if (args != null && args.length > 0) throw new IllegalArgumentException("no args for NOW()"); return new NowBOp(globals); } }); add(UUID,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { if (args != null && args.length > 0) throw new IllegalArgumentException("no args for UUID()"); return new UUIDBOp(globals, false); } }); add(STRUUID,new Factory() { public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { if (args != null && args.length > 0) throw new IllegalArgumentException("no args for STRUUID()"); return new UUIDBOp(globals, true); } }); add(MD5,new DigestFactory(DigestOp.MD5)); add(SHA1,new DigestFactory(DigestOp.SHA1)); add(SHA224,new DigestFactory(DigestOp.SHA224)); add(SHA256,new DigestFactory(DigestOp.SHA256)); add(SHA384,new DigestFactory(DigestOp.SHA384)); add(SHA512,new DigestFactory(DigestOp.SHA512)); add(EXISTS, new ExistsFactory(true)); add(NOT_EXISTS, new ExistsFactory(false)); } public static boolean containsFunction(URI functionUri){ return factories.containsKey(functionUri); } public static boolean isAggregate(final URI functionUri) { final Factory f = factories.get(functionUri); return f != null && f instanceof AggregateFactory; } /** * Verify type constraints. * * @param args * The arguments to some function. * @param types * The type constraints. If there are more arguments given than * constraints, then the last constraint in this vararg parameter * will be used to validate the additional arguments. * @throws IllegalArgumentException * if the type constraints are violated. */ public static final void checkArgs(final ValueExpressionNode[] args, final Class... types) { if (args.length < types.length) { throw new IllegalArgumentException("wrong # of args"); } for (int i = 0; i < args.length; i++) { if (!types[i >= types.length ? types.length - 1 : i] .isAssignableFrom(args[i].getClass())) { throw new IllegalArgumentException("wrong type for arg# " + i + ": " + args[i].getClass()); } } } /** * Convert a {@link FunctionNode} into an {@link IValueExpression}. * * @param globals * The global annotations, including the lexicon namespace. * @param functionURI * The function URI. * @param scalarValues * Scalar values for the function (optional). This is used for * things like the <code>separator</code> in GROUP_CONCAT. * @param args * The function arguments. * * @return The {@link IValueExpression}. */ public static final IValueExpression<? extends IV> toVE( final BOpContextBase context,// BLZG-1343 final GlobalAnnotations globals, final URI functionURI, final Map<String,Object> scalarValues, final ValueExpressionNode... args) { if (functionURI == null) throw new IllegalArgumentException("functionURI is null"); // final Factory f = factories.get(functionURI); // // if (f == null) { // /* // * TODO If we eagerly translate FunctionNodes in the AST to IV value // * expressions then we should probably attach a function which will // * result in a runtime type error when it encounters value // * expression for a function URI which was not known to the backend. // * However, if we handle this translation lazily then this might not // * be an issue. // */ // throw new IllegalArgumentException("unknown function: " // + functionURI); // } final Factory f; if (factories.containsKey(functionURI)) { f = factories.get(functionURI); } else { f = new UnknownFunctionFactory(functionURI); } return f.create(context, globals, scalarValues, args); } /** * Register a factory for a function. * * @param functionURI * The function URI. * @param factory * The factory. * * @throws UnsupportedOperationException * if there is already a {@link Factory} registered for that * URI. */ public static final void add(final URI functionURI, final Factory factory) { if (factories.putIfAbsent(functionURI, factory) != null) { throw new UnsupportedOperationException("Already declared."); } } /** * Register an alias for a functionURI which has already been declared. * * @param functionURI * The function URI. * @param aliasURI * The alias. * * @throws UnsupportedOperationException * if the function URI has not been declared. * @throws UnsupportedOperationException * if the alias URI has already been declared. */ public static final void addAlias(final URI functionURI, final URI aliasURI) { if (!factories.containsKey(functionURI)) { throw new UnsupportedOperationException("FunctionURI:"+functionURI+ " not present."); } if (factories.putIfAbsent(aliasURI, factories.get(functionURI)) != null) { throw new UnsupportedOperationException("Already declared."); } } /** * Remove a registered function {@link Factory}. * * @param functionURI * The {@link URI} of the function. * * @return The factory -or- <code>null</code> if there was no function * registered for that {@link URI}. */ public static Factory remove(final URI functionURI) { return factories.remove(functionURI); } /** * An interface for creating {@link IValueExpression}s from a function URI * and its arguments. */ public static interface Factory { /** * Create an {@link IValueExpression} instance. * * @param context * The {@link BOpContextBase} required to evaluate * {@link IValueExpression}s. This is used if we need to * statically evaluate an {@link IValueExpression} during * query optimization. During query execution the * {@link ContextBindingSet} will convey this information. * @param globals * The global annotations, including the lexicon namespace. * @param scalarValues * The scalar arguments (used by some {@link IAggregate}s). * @param args * The function arguments. * @return The {@link IValueExpression}. * @see BLZG-1372 create() was refactored to pass in the * {@link BOpContextBase} to allow correct resolution of the * {@link LexiconRelation} and {@link ILexiconConfiguration} in * order to properly evaluate {@link IValueExpression}s during * query optimization. */ IValueExpression<? extends IV> create( final BOpContextBase context,//BLZG-1343 final GlobalAnnotations globals,// final Map<String,Object> scalarValues,// final ValueExpressionNode... args); } /** * Marker interface for aggregate functions. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> */ public static interface AggregateFactory extends Factory { } public static class CompareFactory implements Factory { private final CompareOp op; public CompareFactory(final CompareOp op) { this.op = op; } public IValueExpression<? extends IV> create( final BOpContextBase context, final GlobalAnnotations globals, final Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context, globals, args[0]); final IValueExpression<? extends IV> right = AST2BOpUtility.toVE(context, globals, args[1]); /* * If we are dealing with a URI constant we can use * SparqlTypeErrorBOp for any operator other than EQ, NE. * */ if (!(op == CompareOp.EQ || op == CompareOp.NE)) { if (left instanceof Constant && ((Constant<? extends IV>) left).get().isURI()) { return SparqlTypeErrorBOp.INSTANCE; } if (right instanceof Constant && ((Constant<? extends IV>) right).get().isURI()) { return SparqlTypeErrorBOp.INSTANCE; } } /* * If we have a variable or constant for both operands and they * are equal, we can optimize to true */ if (left instanceof IVariableOrConstant && right instanceof IVariableOrConstant && left.equals(right)) { if (op == CompareOp.EQ || op == CompareOp.LE || op == CompareOp.GE) { return TrueBOp.INSTANCE; } else { return FalseBOp.INSTANCE; } } return new CompareBOp(left, right, op); } } public static class SameTermFactory implements Factory { public static final SameTermFactory INSTANCE = new SameTermFactory(); public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> right = AST2BOpUtility.toVE(context,globals, args[1]); return new SameTermBOp(left, right, CompareOp.EQ); } } public static class MathFactory implements Factory { private final MathOp op; public MathFactory(final MathOp op) { this.op = op; } public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context,globals, args[0]); final IValueExpression<? extends IV> right = AST2BOpUtility.toVE(context,globals, args[1]); return new MathBOp(left, right, op,globals); } } public static class NumericFactory implements Factory { private final NumericOp op; public NumericFactory(final NumericOp op) { this.op = op; } public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context,globals, args[0]); return new NumericBOp(left, op); } } public static class DigestFactory implements Factory { private final DigestOp op; public DigestFactory(final DigestOp op) { this.op = op; } public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context,globals, args[0]); return new DigestBOp(left, op, globals); } } public static class DateFactory implements Factory { private final DateOp op; public DateFactory(final DateOp op) { this.op = op; } public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final IValueExpression<? extends IV> left = AST2BOpUtility.toVE(context,globals, args[0]); return new DateBOp(left, op, globals); } } public static class CastFactory implements Factory { private final String uri; public CastFactory(final String uri) { this.uri = uri; } public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, Map<String, Object> scalarValues, final ValueExpressionNode... args) { final IValueExpression<? extends IV>[] bops = new IValueExpression[args.length]; for (int i = 0; i < args.length; i++) { bops[i] = AST2BOpUtility.toVE(context,globals, args[i]); } return new FuncBOp(bops, uri, globals); } } public static class GroupConcatFactory implements AggregateFactory { public interface Annotations extends GROUP_CONCAT.Annotations{ } public GroupConcatFactory() { } public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, final Map<String,Object> scalarValues, final ValueExpressionNode... args) { checkArgs(args, ValueExpressionNode.class); final LinkedHashMap<String, Object> tmp = new LinkedHashMap<String, Object>( scalarValues); if (!scalarValues.containsKey(Annotations.SEPARATOR)) { // Per the spec. tmp.put(Annotations.SEPARATOR, " "); } tmp.put(Annotations.NAMESPACE, globals.lex); final IValueExpression ve = AST2BOpUtility.toVE(context,globals, args[0]); return new com.bigdata.bop.rdf.aggregate.GROUP_CONCAT( new BOp[] { ve }, tmp); } } /** * <code>NumericExpression IN ArgList</code> is an infix operator. The left * argument in the syntax must be provided as the first argument to this * factory. The ArgList must be provided as the [1...nargs] arguments to the * factory. * <p> * This optimizes IN/0 (foo IN()) as FALSE. * <p> * This optimizes IN/1 (foo IN(valueExpr) as SameTerm. * <p> * This optimizes expr IN Constants using a hash set over the constants. */ public static class InFactory implements Factory { public static interface Annotations { /** * Literals are not allowed in an IN clause in SPARQL, but sometimes * this operator is used as part of an optimized re-write where * literals should be allowed. * * @see ASTSearchInSearchOptimizer */ String ALLOW_LITERALS = Annotations.class.getName() + ".allowLiterals"; } private final boolean not; /** * * @param not * <code>true</code> iff this is NOT IN. */ public InFactory(final boolean not) { this.not = not; } public IValueExpression<? extends IV> create(final BOpContextBase context, final GlobalAnnotations globals, final Map<String, Object> scalarValues, final ValueExpressionNode... args) { if (args.length == 0) { /* * The first argument is the left hand side of the infix IN * operator. */ throw new IllegalArgumentException(); } if (args.length == 1) { /* * "foo IN()" is always false. * * "foo NOT IN()" is always true. */ return not ? TrueBOp.INSTANCE : FalseBOp.INSTANCE; } final boolean allowLiterals; if (scalarValues != null && scalarValues.containsKey(Annotations.ALLOW_LITERALS)) { allowLiterals = (Boolean) scalarValues.get(Annotations.ALLOW_LITERALS); } else { allowLiterals = false; } if (args.length == 2) { /* * "foo IN(bar)" is SameTerm(foo,bar) if bar is a URI, otherwise CompareBOp. */ // final IValueExpression<? extends IV> val = AST2BOpUtility.toVE(context,globals, args[1]); // final IValueExpression ret = SameTermFactory.INSTANCE.create( // globals, scalarValues, args); /* * MP: Changed this to check for allowLiterals. When we are allowing * literals, chances are we want to bypass the CompareOp logic * for literal comparison. */ if (allowLiterals) { final IValueExpression ret = SameTermFactory.INSTANCE.create(context, globals, scalarValues, args); if (not) return new NotBOp(ret); return ret; } // compare factory is smart enough to optimize for SameTerm when necessary final IValueExpression ret = new CompareFactory(CompareOp.EQ).create(context, globals, scalarValues, args); if (not) return new NotBOp(ret); return ret; } try { /* * First, attempt to use an optimized variant. The args MUST be * [var,constant(s)]. */ checkArgs(args, ValueExpressionNode.class, ConstantNode.class); final IValueExpression<? extends IV> arg = AST2BOpUtility.toVE(context,globals, args[0]); final IConstant<? extends IV> set[] = new IConstant[args.length - 1]; try { for (int i = 1; i < args.length; i++) { set[i - 1] = ((ConstantNode) args[i]).getValueExpression(); final IV iv = set[i - 1].get(); if (!allowLiterals && iv.isLiteral()) { throw new IllegalArgumentException("must use CompareBOps for literals"); } } return new InHashBOp(not, arg, set); } catch (IllegalArgumentException ex) { IValueExpression ret = null; final CompareFactory compare = new CompareFactory(CompareOp.EQ); for (int i = 1; i < args.length; i++) { if (ret == null) { ret = compare.create(context, globals, scalarValues, args[0], args[i]); } else { ret = new OrBOp(ret, compare.create(context, globals, scalarValues, args[0], args[i])); } } if (not) return new NotBOp(ret); return ret; } } catch (IllegalArgumentException iae) { /* * Use a variant which handles value expressions for the members * of the set. The first member of the list is taken to be the * valueExpr which is then tested against each other member of * the list. */ checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class); final IValueExpression<? extends IV> set[] = new IValueExpression[args.length]; for (int i = 0; i < args.length; i++) { set[i] = AST2BOpUtility.toVE(context,globals, args[i]); } return new ComputedIN(not, set); } } } /** * Factory for EXISTS() and NOT EXISTS(). The EXISTS() node in the AST must * be marked with the group graph pattern as an annotation. The * {@link FunctionNode} will be a simple test of an anonymous variable. * EXISTS tests the variable for <code>true</code>. Not exists tests the * variable for <code>false</code>. The name of that anonymous variable is * the sole {@link ValueExpressionNode} passed into this factory. * <p> * The "guts" of the EXISTS logic is translated by an {@link IASTOptimizer} * into an ASK subquery (an instance of {@link SubqueryRoot}). This will be * executed as a special BOp that bind the success or failure of that ASK * subquery on the anonymous variable. The "ASK" subquery does not fail, it * just binds the truth state of the ASK subquery on the anonymous variable. * The {@link IValueExpression} returned by this factory just tests the * truth state of that anonymous variable. */ public static class ExistsFactory implements Factory { final private boolean exists; public ExistsFactory(boolean exists) { this.exists = exists; } @Override public IValueExpression<? extends IV> create( final BOpContextBase context, final GlobalAnnotations globals, final Map<String, Object> scalarValues, final ValueExpressionNode... args) { if (args.length != 1) throw new IllegalArgumentException(); final VarNode anonvar = (VarNode) args[0]; return exists ? anonvar.getValueExpression() : new NotBOp( anonvar.getValueExpression()); } } private static class UnknownFunctionFactory implements Factory { private URI functionURI; public UnknownFunctionFactory(final URI functionURI) { this.functionURI = functionURI; } @Override public IValueExpression<? extends IV> create( final BOpContextBase context, final GlobalAnnotations globals, final Map<String, Object> scalarValues, final ValueExpressionNode... args) { return new UnknownFunctionBOp(functionURI); } } public static class UnknownFunctionBOp extends ImmutableBOp implements IValueExpression<IV> { private static final long serialVersionUID = 1L; private static final String FUNCTION_URI = "FUNCTION_URI"; public UnknownFunctionBOp(final URI functionURI) { this(BOp.NOARGS, NV.asMap(FUNCTION_URI, functionURI)); } /** * Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}. * * @param op */ public UnknownFunctionBOp(final UnknownFunctionBOp op) { super(op); } /** * Required shallow copy constructor. * * @param args * The operands. * @param op * The operation. */ public UnknownFunctionBOp(final BOp[] args, Map<String, Object> anns) { super(args, anns); } public IV get(final IBindingSet bindingSet) { throw new UnsupportedOperationException( "unknown function: " + getRequiredProperty(FUNCTION_URI)); } } }