All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jena.sparql.expr.ExprLib Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.sparql.expr;

import org.apache.jena.graph.Node ;
import org.apache.jena.sparql.ARQInternalErrorException ;
import org.apache.jena.sparql.algebra.optimize.ExprTransformConstantFold ;
import org.apache.jena.sparql.algebra.walker.Walker ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.function.FunctionEnv ;

public class ExprLib
{
    /** Evaluate or return null.  
     * 

* This is better (faster) than the simple implementation * which captures {@link ExprEvalException} and returns null. */ public static NodeValue evalOrNull(Expr expr, Binding binding, FunctionEnv functionEnv) { return evalOrElse(expr, binding, functionEnv, null) ; } /** evaluate or throw an exception */ // This post dates a lot of code that uses expr.eval directly. // Placeholder for now. private static NodeValue evalOrException(Expr expr, Binding binding, FunctionEnv functionEnv) { return expr.eval(binding, functionEnv) ; } private static NodeValue evalOrElse(Expr expr, Binding binding, FunctionEnv functionEnv, NodeValue exceptionValue) { // Exceptions in java are expensive if the stack information is // collected which is the default behaviour. The expensive step is // Throwable.fillInStackTrace. // // Otherwise, they are reasonable cheap. It needs special exceptions // which overrides fillInStackTrace to be cheap but they loose the // general information for development. // // Instead, pick out specal cases, the expression being a single variable // being the important one. // // BOUND(?x) is a important case where the expression is often an exception // in general evaluation. See E_Bound - different exception handling // (it handles VariableNotBoundException not a general ExprEvalException). if ( expr.isConstant() ) // Easy case. return expr.getConstant() ; if ( expr.isVariable() ) { // The case of the expr being a single variable. Var v = expr.asVar() ; Node n = binding.get(v) ; if ( n == null ) return exceptionValue ; NodeValue nv = NodeValue.makeNode(n) ; return nv ; } try { return expr.eval(binding, functionEnv) ; } catch (ExprEvalException ex) { return exceptionValue ; } } /** Attempt to fold any sub-expressions of the Expr. * Return an expression that is equivalent to the argument but maybe simpler. * @param expr * @return Expression */ public static Expr foldConstants(Expr expr) { return ExprTransformer.transform(new ExprTransformConstantFold(), expr) ; } /** transform an expression that may involve aggregates into one that just uses the variable for the aggregate */ public static Expr replaceAggregateByVariable(Expr expr) { return ExprTransformer.transform(replaceAgg, expr) ; } // /** transform expressions that may involve aggregates into one that just uses the variable for the aggregate */ // public static ExprList replaceAggregateByVariable(ExprList exprs) // { // return ExprTransformer.transform(replaceAgg, exprs) ; // } private static ExprTransform replaceAgg = new ExprTransformCopy() { @Override public Expr transform(ExprAggregator eAgg) { return eAgg.getAggVar() ; } } ; /** Decide whether an expression is safe for using a graph substitution. * Need to be careful about value-like tests when the graph is not * matched in a value fashion. */ public static boolean isAssignmentSafeEquality(Expr expr) { return isAssignmentSafeEquality(expr, false, false) ; } /** * @param graphHasStringEquality True if the graph triple matching equates xsd:string and plain literal * @param graphHasNumercialValueEquality True if the graph triple matching equates numeric values */ public static boolean isAssignmentSafeEquality(Expr expr, boolean graphHasStringEquality, boolean graphHasNumercialValueEquality) { if ( !(expr instanceof E_Equals) && !(expr instanceof E_SameTerm) ) return false ; // Corner case: sameTerm is false for string/plain literal, // but true in the graph. ExprFunction2 eq = (ExprFunction2)expr ; Expr left = eq.getArg1() ; Expr right = eq.getArg2() ; Var var = null ; NodeValue constant = null ; if ( left.isVariable() && right.isConstant() ) { var = left.asVar() ; constant = right.getConstant() ; } else if ( right.isVariable() && left.isConstant() ) { var = right.asVar() ; constant = left.getConstant() ; } // Not between a variable and a constant if ( var == null || constant == null ) return false ; if ( ! constant.isLiteral() ) // URIs, bNodes. Any bNode will have come from a substitution - not legal syntax in filters return true ; if (expr instanceof E_SameTerm) { if ( graphHasStringEquality && constant.isString() ) // Graph is not same term return false ; if ( graphHasNumercialValueEquality && constant.isNumber() ) return false ; return true ; } // Final check for "=" where a FILTER = can do value matching when the graph does not. if ( expr instanceof E_Equals ) { if ( ! graphHasStringEquality && constant.isString() ) return false ; if ( ! graphHasNumercialValueEquality && constant.isNumber() ) return false ; return true ; } // Unreachable. throw new ARQInternalErrorException() ; } /** Some "functions" are non-deterministic (unstable) - * calling them with the same arguments * does not yields the same answer each time. * Therefore how and when they are called * matters. * * Functions: RAND, UUID, StrUUID, BNode * * NOW() is safe. */ public static boolean isStable(Expr expr) { try { Walker.walk(expr, exprVisitorCheckForNonFunctions) ; return true ; } catch ( ExprUnstable ex ) { return false ; } } private static ExprVisitor exprVisitorCheckForNonFunctions = new ExprVisitorBase() { @Override public void visit(ExprFunction0 func) { if ( func instanceof E_Random || func instanceof E_UUID || func instanceof E_StrUUID) throw new ExprUnstable() ; } @Override public void visit(ExprFunctionN func) { if (func instanceof E_BNode ) throw new ExprUnstable() ; } } ; private static class ExprUnstable extends ExprException { // Filling in the stack trace is the expensive part of // an exception but we don't need it. @Override public Throwable fillInStackTrace() { return this ; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy