Package de.fuberlin.wiwiss.d2rq.optimizer.expr

Source Code of de.fuberlin.wiwiss.d2rq.optimizer.expr.TransformExprToSQLApplyer

package de.fuberlin.wiwiss.d2rq.optimizer.expr;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.hp.hpl.jena.datatypes.RDFDatatype;
import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.sparql.core.Var;
import com.hp.hpl.jena.sparql.expr.E_Add;
import com.hp.hpl.jena.sparql.expr.E_Datatype;
import com.hp.hpl.jena.sparql.expr.E_Divide;
import com.hp.hpl.jena.sparql.expr.E_Equals;
import com.hp.hpl.jena.sparql.expr.E_GreaterThan;
import com.hp.hpl.jena.sparql.expr.E_GreaterThanOrEqual;
import com.hp.hpl.jena.sparql.expr.E_IsBlank;
import com.hp.hpl.jena.sparql.expr.E_IsIRI;
import com.hp.hpl.jena.sparql.expr.E_IsLiteral;
import com.hp.hpl.jena.sparql.expr.E_Lang;
import com.hp.hpl.jena.sparql.expr.E_LangMatches;
import com.hp.hpl.jena.sparql.expr.E_LessThan;
import com.hp.hpl.jena.sparql.expr.E_LessThanOrEqual;
import com.hp.hpl.jena.sparql.expr.E_LogicalNot;
import com.hp.hpl.jena.sparql.expr.E_LogicalOr;
import com.hp.hpl.jena.sparql.expr.E_Multiply;
import com.hp.hpl.jena.sparql.expr.E_NotEquals;
import com.hp.hpl.jena.sparql.expr.E_SameTerm;
import com.hp.hpl.jena.sparql.expr.E_Str;
import com.hp.hpl.jena.sparql.expr.E_Subtract;
import com.hp.hpl.jena.sparql.expr.E_UnaryMinus;
import com.hp.hpl.jena.sparql.expr.E_UnaryPlus;
import com.hp.hpl.jena.sparql.expr.Expr;
import com.hp.hpl.jena.sparql.expr.ExprAggregator;
import com.hp.hpl.jena.sparql.expr.ExprEvalException;
import com.hp.hpl.jena.sparql.expr.ExprFunction;
import com.hp.hpl.jena.sparql.expr.ExprFunction0;
import com.hp.hpl.jena.sparql.expr.ExprFunction1;
import com.hp.hpl.jena.sparql.expr.ExprFunction2;
import com.hp.hpl.jena.sparql.expr.ExprFunction3;
import com.hp.hpl.jena.sparql.expr.ExprFunctionN;
import com.hp.hpl.jena.sparql.expr.ExprFunctionOp;
import com.hp.hpl.jena.sparql.expr.ExprVar;
import com.hp.hpl.jena.sparql.expr.ExprVisitor;
import com.hp.hpl.jena.sparql.expr.NodeValue;
import com.hp.hpl.jena.sparql.expr.nodevalue.NodeFunctions;
import com.hp.hpl.jena.sparql.expr.nodevalue.NodeValueBoolean;

import de.fuberlin.wiwiss.d2rq.algebra.Attribute;
import de.fuberlin.wiwiss.d2rq.algebra.ExpressionProjectionSpec;
import de.fuberlin.wiwiss.d2rq.algebra.NodeRelation;
import de.fuberlin.wiwiss.d2rq.algebra.ProjectionSpec;
import de.fuberlin.wiwiss.d2rq.algebra.RelationalOperators;
import de.fuberlin.wiwiss.d2rq.expr.Add;
import de.fuberlin.wiwiss.d2rq.expr.Constant;
import de.fuberlin.wiwiss.d2rq.expr.Divide;
import de.fuberlin.wiwiss.d2rq.expr.Equality;
import de.fuberlin.wiwiss.d2rq.expr.Expression;
import de.fuberlin.wiwiss.d2rq.expr.GreaterThan;
import de.fuberlin.wiwiss.d2rq.expr.GreaterThanOrEqual;
import de.fuberlin.wiwiss.d2rq.expr.LessThan;
import de.fuberlin.wiwiss.d2rq.expr.LessThanOrEqual;
import de.fuberlin.wiwiss.d2rq.expr.Multiply;
import de.fuberlin.wiwiss.d2rq.expr.Negation;
import de.fuberlin.wiwiss.d2rq.expr.SQLExpression;
import de.fuberlin.wiwiss.d2rq.expr.Subtract;
import de.fuberlin.wiwiss.d2rq.expr.UnaryMinus;
import de.fuberlin.wiwiss.d2rq.nodes.DetermineNodeType;
import de.fuberlin.wiwiss.d2rq.nodes.FixedNodeMaker;
import de.fuberlin.wiwiss.d2rq.nodes.NodeMaker;
import de.fuberlin.wiwiss.d2rq.nodes.NodeSetConstraintBuilder;
import de.fuberlin.wiwiss.d2rq.nodes.TypedNodeMaker;
import de.fuberlin.wiwiss.d2rq.values.ValueMaker;

/**
* Attempts to transform a SPARQL FILTER Expr to a SQL Expression
*
* Notes:
* <ul>
* <li>Literals using d2rq:pattern cannot be compared.</li>
* <li>No XSD type checking/conversion or constructor functions yet.</li>
* </ul>
*
* @author Herwig Leimer
* @author Giovanni Mels
*/
public final class TransformExprToSQLApplyer implements ExprVisitor {
 
  private static final Log logger = LogFactory.getLog(TransformExprToSQLApplyer.class);

  /**
   * Converts a SPARQL filter expression to an SQL expression
   *
   * @param expr The root node of an {@link Expr Expr} tree, contains the SPARQL filter.
   * @param nodeRelation The relation supplying the values to apply the filter on.
   * @return The root node of an {@link Expression Expression} tree, if conversion was successful, <code>null</code> otherwise.
   */
  public static Expression convert(final Expr expr, final NodeRelation nodeRelation) {
    TransformExprToSQLApplyer transformer = new TransformExprToSQLApplyer(nodeRelation);
    expr.visit(transformer);
    return transformer.result();
  }
 
  // TODO Expression.FALSE and Expression.TRUE are not constants
  private static final Expression CONSTANT_FALSE = new ConstantEx("false", NodeValueBoolean.FALSE.asNode());
  private static final Expression CONSTANT_TRUE  = new ConstantEx("true", NodeValueBoolean.TRUE.asNode());
 
  private final NodeRelation nodeRelation;
  private final Stack<Expression> expression = new Stack<Expression>();
 
  private boolean convertable;     // flag if converting was possible
  private String  reason = null;   // reason why converting failed
 
  /**
   * Creates an expression transformer.
   *
   * @param nodeRelation
   */
  public TransformExprToSQLApplyer(NodeRelation nodeRelation)
  { 
    this.convertable  = true;
    this.nodeRelation = nodeRelation;
  }

  /**
   * Returns the sql Expression
   *
   * @return the transformed sql expression if converting was possible, otherwise null.
   */
  public Expression result()
  {
    if (!convertable) {
      logger.debug("filter conversion failed: " + reason);
      return null;
    }
   
    if (expression.size() != 1)
      throw new IllegalStateException("something is seriously wrong");
   
    Expression result = expression.pop();
    logger.debug("Resulting filter = " + result);
    return result;
  }

  public void startVisit()
  {
    logger.debug("transform started");
  }
 
  public void finishVisit()
  {
    logger.debug("transform finished");
  }
 
  public void visit(ExprFunction0 func) {
    visitExprFunction(func);   
  }

  public void visit(ExprFunction1 function) {
    logger.debug("visit ExprFunction " + function);
   
    if (!convertable) {
      expression.push(Expression.FALSE); // prevent stack empty exceptions when conversion
      return;                            // fails in the middle of a multi-arg operator conversion
    }
    convertFunction(function);
  }

  public void visit(ExprFunction2 function) {
    logger.debug("visit ExprFunction " + function);
   
    if (!convertable) {
      expression.push(Expression.FALSE); // prevent stack empty exceptions when conversion
      return;                            // fails in the middle of a multi-arg operator conversion
    }
    convertFunction(function);
  }

  public void visit(ExprFunction3 func) {
    visitExprFunction(func);   
  }

  public void visit(ExprFunctionN func) {
    visitExprFunction(func);   
  }

  public void visit(ExprFunctionOp funcOp) {
    visitExprFunction(funcOp);   
  }

  public void visit(ExprAggregator eAgg) {
    conversionFailed(eAgg);
  }
 
  public void visit(ExprVar var)
  {
    logger.debug("visit ExprVar " + var);
   
    if (!convertable) {
      expression.push(Expression.FALSE); // prevent stack empty exceptions when conversion
      return;                            // fails in the middle of a multi-arg operator conversion
    }
   
    String varName = var.getVarName();
   
    // if expression contains a blank node, no conversion to sql can be done
    if (Var.isBlankNodeVarName(varName)) {
      conversionFailed("blank nodes not supported", var);
      return;
    }
   
    List<Expression> expressions = toExpression(var);
    if (expressions.size() == 1) {
      expression.push(expressions.get(0));
    } else {
      // no single sql-column for sparql-var does exist break up conversion
      // (the case for Pattern ValueMakers)
      conversionFailed("multi column pattern valuemakers not supported", var);
    }
  }
 
  public void visit(NodeValue value)
  {
    logger.debug("visit NodeValue " + value);
   
    if (!convertable) {
      expression.push(Expression.FALSE); // prevent stack empty exceptions when conversion
      return;                            // fails in the middle of a multi-arg operator conversion
    }
   
    if (value.isDecimal() || value.isDouble() || value.isFloat() || value.isInteger() || value.isNumber()) {
      expression.push(new ConstantEx(value.asString(), value.asNode()));
    } else if (value.isDateTime()) {
      // Convert xsd:dateTime: CCYY-MM-DDThh:mm:ss
      // to SQL-92 TIMESTAMP: CCYY-MM-DD hh:mm:ss[.fraction]
      // TODO support time zones (WITH TIME ZONE columns)
      expression.push(new ConstantEx(value.asString().replace("T", " "), value.asNode()));
    } else {
      expression.push(new ConstantEx(value.asString(), value.asNode()));
    }
  }

  private void visitExprFunction(ExprFunction function)
  {
    logger.debug("visit ExprFunction " + function);
   
    if (!convertable) {
      expression.push(Expression.FALSE); // prevent stack empty exceptions when conversion
      return;                            // fails in the middle of a multi-arg operator conversion
    }
    if (!extensionSupports(function)) {
      conversionFailed(function);
      return;
    }
   
    for (int i = 0; i < function.numArgs(); i++)
      function.getArg(i + 1).visit(this);
    List<Expression> args = new ArrayList<Expression>(function.numArgs());
   
    for (int i = 0; i < function.numArgs(); i++)
      args.add(expression.pop());
    Collections.reverse(args);
    extensionConvert(function, args);
  }

  /**
   * Delivers the corresponding sql-expression for a sparql-var
   * @param exprVar - a sparql-expr-var
   * @return List<Expression> - the equivalent sql-expressions
   */
  private List<Expression> toExpression(ExprVar exprVar)
  {
    ArrayList<Expression> result = new ArrayList<Expression>();
   
    if (this.nodeRelation != null && exprVar != null)
    {
      // get the nodemaker for the expr-var
      NodeMaker nodeMaker = nodeRelation.nodeMaker(exprVar.asVar());
      if (nodeMaker instanceof TypedNodeMaker) {
        TypedNodeMaker typedNodeMaker = (TypedNodeMaker) nodeMaker;
        Iterator<ProjectionSpec> it = typedNodeMaker.projectionSpecs().iterator();
        if (!it.hasNext()) {
          logger.debug("no projection spec for " + exprVar + ", assuming constant");
          Node node = typedNodeMaker.makeNode(null);
          result.add(new ConstantEx(NodeValue.makeNode(node).asString(), node));
        }
        while (it.hasNext()) {
          ProjectionSpec projectionSpec = it.next();
         
          if (projectionSpec == null)
            return Collections.emptyList();
         
          if (projectionSpec instanceof Attribute) {
            result.add(new AttributeExprEx((Attribute) projectionSpec, nodeMaker));
          } else {
            // projectionSpec is a ExpressionProjectionSpec
            ExpressionProjectionSpec expressionProjectionSpec = (ExpressionProjectionSpec) projectionSpec;
            Expression expression = expressionProjectionSpec.toExpression();
            if (expression instanceof SQLExpression)
              result.add(((SQLExpression)expression));
            else
              return Collections.emptyList();
          }
        }
      } else if (nodeMaker instanceof FixedNodeMaker) {
        FixedNodeMaker fixedNodeMaker = (FixedNodeMaker) nodeMaker;
        Node node = fixedNodeMaker.makeNode(null);
        result.add(new ConstantEx(NodeValue.makeNode(node).asString(), node));
      }
    }
   
    return result;
  }
 
  private void convertFunction(ExprFunction1 expr)
  {
    logger.debug("convertFunction " + expr.toString());
   
    if (expr instanceof E_Str) {
      convertStr((E_Str) expr);
    } else if (expr instanceof E_IsIRI) {
      convertIsIRI((E_IsIRI) expr);
    } else if (expr instanceof E_IsBlank) {
      convertIsBlank((E_IsBlank) expr);
    } else if (expr instanceof E_IsLiteral) {
      convertIsLiteral((E_IsLiteral) expr);
    } else if (expr instanceof E_Datatype) {
      convertDataType((E_Datatype) expr);
    } else if (expr instanceof E_Lang) {
      convertLang((E_Lang) expr);
    } else if (expr instanceof E_LogicalNot) {
      convertLogicalNot((E_LogicalNot) expr);
    } else if (expr instanceof E_UnaryPlus) {
      convert((E_UnaryPlus) expr);
    } else if (expr instanceof E_UnaryMinus) {
      convert((E_UnaryMinus) expr);
    } else if (extensionSupports(expr)) {
      expr.getArg(1).visit(this) ;
      Expression e1 = expression.pop();
      List<Expression> args = Collections.singletonList(e1);
      extensionConvert(expr, args);
    } else {
      conversionFailed(expr);
    }
  }
 
  private void convertFunction(ExprFunction2 expr)
  {
    logger.debug("convertFunction " + expr.toString());
   
    if (expr instanceof E_LogicalOr) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(e1.or(e2));
    } else if (expr instanceof E_LessThan) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new LessThan(e1, e2));
    } else if (expr instanceof E_LessThanOrEqual) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new LessThanOrEqual(e1, e2));
    } else if (expr instanceof E_GreaterThan) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new GreaterThan(e1, e2));
    } else if (expr instanceof E_GreaterThanOrEqual) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new GreaterThanOrEqual(e1, e2));
    } else if (expr instanceof E_Add) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new Add(e1, e2));
    } else if (expr instanceof E_Subtract) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new Subtract(e1, e2));
    } else if (expr instanceof E_Multiply) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new Multiply(e1, e2));
    } else if (expr instanceof E_Divide) {
      expr.getArg1().visit(this) ;
      expr.getArg2().visit(this) ;
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
      expression.push(new Divide(e1, e2));
    } else if (expr instanceof E_Equals) {
      convertEquals((E_Equals) expr);
    } else if (expr instanceof E_NotEquals) {
      convertNotEquals((E_NotEquals) expr);
    } else if (expr instanceof E_LangMatches) {
      convertLangMatches((E_LangMatches) expr);
    } else if (expr instanceof E_SameTerm) {
      convertSameTerm((E_SameTerm) expr);
    } else if (extensionSupports(expr)) {
      expr.getArg(1).visit(this);
      expr.getArg(2).visit(this);
      Expression e2 = expression.pop();
      Expression e1 = expression.pop();
     
      List<Expression> args = new ArrayList<Expression>(2);
     
      args.add(e1);
      args.add(e2);
     
      extensionConvert(expr, args);
    } else {
      conversionFailed(expr);
    }
  }
 
  private void convertEquals(E_Equals expr)
  {
    logger.debug("convertEquals " + expr.toString());
   
    convertEquality(expr);
  }
 
  private void convertEquality(ExprFunction2 expr)
  {
    expr.getArg1().visit(this);
    expr.getArg2().visit(this);
    Expression e2 = expression.pop();
    Expression e1 = expression.pop();
   
    // TODO Expression.FALSE and Expression.TRUE are not constants
    if (e1.equals(Expression.FALSE))
      e1 = CONSTANT_FALSE;
    else if (e1.equals(Expression.TRUE))
      e1 = CONSTANT_TRUE;
    if (e2.equals(Expression.FALSE))
      e2 = CONSTANT_FALSE;
    else if (e2.equals(Expression.TRUE))
      e2 = CONSTANT_TRUE;
   
    if (e1 instanceof AttributeExprEx && e2 instanceof Constant || e2 instanceof AttributeExprEx && e1 instanceof Constant) {
     
      AttributeExprEx variable;
      ConstantEx      constant;
     
      if (e1 instanceof AttributeExprEx) {
        variable = (AttributeExprEx) e1;
        constant = (ConstantEx) e2;
      } else {
        variable = (AttributeExprEx) e2;
        constant = (ConstantEx) e1;
      }
     
      logger.debug("isEqual(" + variable + ", " + constant + ")");
     
      NodeMaker nm = variable.getNodeMaker();
     
      if (nm instanceof TypedNodeMaker) {
        ValueMaker vm = ((TypedNodeMaker) nm).valueMaker();
        Node node = constant.getNode();
        logger.debug("checking " + node + " with " + nm);
       
        if (XSD.isNumeric(node)) {
          DetermineNodeType filter = new DetermineNodeType();
          nm.describeSelf(filter);
          RDFDatatype datatype = filter.getDatatype();
          if (datatype != null && XSD.isNumeric(datatype)) {
            RDFDatatype numericType = XSD.getNumericType(datatype, node.getLiteralDatatype());
            nm = cast(nm, numericType);
            node = XSD.cast(node, numericType);
          }
        }
       
        boolean empty = nm.selectNode(node, RelationalOperators.DUMMY).equals(NodeMaker.EMPTY);
        logger.debug("result " + new Boolean(empty));
        if (!empty) {
          if (node.isURI())
            expression.push(vm.valueExpression(node.getURI()));
          else if (node.isLiteral()) {
            if (XSD.isSupported(node.getLiteralDatatype()))
              expression.push(vm.valueExpression(constant.value()));
            else
              conversionFailed("cannot compare values of type " + node.getLiteralDatatypeURI(), expr);
          } else
            conversionFailed(expr); // TODO blank nodes?
          return;
        } else {
          expression.push(Expression.FALSE);
          return;
        }
      } else {
        logger.warn("nm is not a TypedNodemaker");
      }
    } else if (e1 instanceof ConstantEx && e2 instanceof ConstantEx) {
      logger.debug("isEqual(" + e1 + ", " + e2 + ")");
      Node c1 = ((ConstantEx) e1).getNode();
      Node c2 = ((ConstantEx) e2).getNode();
      boolean equals;
      if (XSD.isNumeric(c1) && XSD.isNumeric(c2)) {
        RDFDatatype datatype = XSD.getNumericType(c1.getLiteralDatatype(), c2.getLiteralDatatype());
        equals = XSD.cast(c1, datatype).equals(XSD.cast(c2, datatype));
      } else if (isSimpleLiteral(c1) && isSimpleLiteral(c2)) {
        equals = c1.getLiteralValue().equals(c2.getLiteralValue());
      } else if (XSD.isString(c1) && XSD.isString(c2)) {
        equals = c1.getLiteralValue().equals(c2.getLiteralValue());
      } else {
        try {
          equals = NodeFunctions.rdfTermEquals(c1, c2);
        } catch (ExprEvalException e) {
          equals = false;
        }
      }
      logger.debug("constants equal? " + new Boolean(equals));
      expression.push(equals ? Expression.TRUE : Expression.FALSE);
      return;
    } else if (e1 instanceof AttributeExprEx && e2 instanceof AttributeExprEx) {
      logger.debug("isEqual(" + e1 + ", " + e2 + ")");
      AttributeExprEx variable1 = (AttributeExprEx) e1;
      AttributeExprEx variable2 = (AttributeExprEx) e2;
     
      NodeMaker nm1 = variable1.getNodeMaker();
      NodeMaker nm2 = variable2.getNodeMaker();
     
      DetermineNodeType filter1 = new DetermineNodeType();
      nm1.describeSelf(filter1);
      RDFDatatype datatype1 = filter1.getDatatype();
     
      DetermineNodeType filter2 = new DetermineNodeType();
      nm2.describeSelf(filter2);
      RDFDatatype datatype2 = filter2.getDatatype();
     
      if (datatype1 != null && XSD.isNumeric(datatype1) && datatype2 != null && XSD.isNumeric(datatype2)) {
        RDFDatatype numericType = XSD.getNumericType(filter1.getDatatype(), filter2.getDatatype());
        nm1 = cast(nm1, numericType);
        nm2 = cast(nm2, numericType);
      }
      NodeSetConstraintBuilder nodeSet = new NodeSetConstraintBuilder();
      nm1.describeSelf(nodeSet);
      nm2.describeSelf(nodeSet);
     
      if (nodeSet.isEmpty()) {
        logger.debug("nodes " + nm1 + " " + nm2 + " incompatible");
        expression.push(Expression.FALSE);
        return;
      }
    }
   
    expression.push(Equality.create(e1, e2));
  }
 
  private void convertNotEquals(E_NotEquals expr)
  {
    logger.debug("convertNotEquals " + expr.toString());
   
    expr.getArg1().visit(this);
    expr.getArg2().visit(this);
    Expression e2 = expression.pop();
    Expression e1 = expression.pop();
   
    // TODO Expression.FALSE and Expression.TRUE are not constants
    if (e1.equals(Expression.FALSE))
      e1 = CONSTANT_FALSE;
    else if (e1.equals(Expression.TRUE))
      e1 = CONSTANT_TRUE;
    if (e2.equals(Expression.FALSE))
      e2 = CONSTANT_FALSE;
    else if (e2.equals(Expression.TRUE))
      e2 = CONSTANT_TRUE;
   
    if (e1 instanceof AttributeExprEx && e2 instanceof Constant || e2 instanceof AttributeExprEx && e1 instanceof Constant) {
      AttributeExprEx variable;
      ConstantEx      constant;
     
      if (e1 instanceof AttributeExprEx) {
        variable = (AttributeExprEx) e1;
        constant = (ConstantEx) e2;
      } else {
        variable = (AttributeExprEx) e2;
        constant = (ConstantEx) e1;
      }
     
      logger.debug("isNotEqual(" + variable + ", " + constant + ")");
     
      NodeMaker nm = variable.getNodeMaker();
     
      if (nm instanceof TypedNodeMaker) {
        ValueMaker vm = ((TypedNodeMaker) nm).valueMaker();
        Node node = constant.getNode();
        logger.debug("checking " + node + " with " + nm);
        if (XSD.isNumeric(node)) {
          DetermineNodeType filter = new DetermineNodeType();
          nm.describeSelf(filter);
          RDFDatatype datatype = filter.getDatatype();
          if (datatype != null && XSD.isNumeric(datatype)) {
            RDFDatatype numericType = XSD.getNumericType(datatype, node.getLiteralDatatype());
            nm = cast(nm, numericType);
            node = XSD.cast(node, numericType);
          }
        }
        boolean empty = nm.selectNode(node, RelationalOperators.DUMMY).equals(NodeMaker.EMPTY);
        logger.debug("result " + new Boolean(empty));
        if (!empty) {
          if (node.isURI())
            expression.push(new Negation(vm.valueExpression(node.getURI())));
          else if (node.isLiteral()) {
            if (XSD.isSupported(node.getLiteralDatatype()))
              expression.push(new Negation(vm.valueExpression(constant.value())));
            else // type = boolean or an unknown type
              conversionFailed("cannot compare values of type " + node.getLiteralDatatypeURI(), expr);
          } else
            conversionFailed(expr); // TODO blank nodes?
          return;
        } else {
          expression.push(Expression.TRUE);
          return;
        }
      }
    } else if (e1 instanceof ConstantEx && e2 instanceof ConstantEx) {
      logger.debug("isNotEqual(" + e1 + ", " + e2 + ")");
      Node c1 = ((ConstantEx) e1).getNode();
      Node c2 = ((ConstantEx) e2).getNode();
      boolean equals;
      if (XSD.isNumeric(c1) && XSD.isNumeric(c2)) {
        RDFDatatype datatype = XSD.getNumericType(c1.getLiteralDatatype(), c2.getLiteralDatatype());
        equals = XSD.cast(c1, datatype).equals(XSD.cast(c2, datatype));
      } else if (isSimpleLiteral(c1) && isSimpleLiteral(c2)) {
        equals = c1.getLiteralValue().equals(c2.getLiteralValue());
      } else if (XSD.isString(c1) && XSD.isString(c2)) {
        equals = c1.getLiteralValue().equals(c2.getLiteralValue());
      } else {
        try {
          equals = NodeFunctions.rdfTermEquals(c1, c2);
        } catch (ExprEvalException e) {
          equals = false;
        }
      }
      logger.debug("constants equal? " + new Boolean(equals));
      expression.push(equals ? Expression.FALSE : Expression.TRUE);
      return;
    } else if (e1 instanceof AttributeExprEx && e2 instanceof AttributeExprEx) {
      logger.debug("isNotEqual(" + e1 + ", " + e2 + ")");
      AttributeExprEx variable1 = (AttributeExprEx) e1;
      AttributeExprEx variable2 = (AttributeExprEx) e2;
     
      NodeMaker nm1 = variable1.getNodeMaker();
      NodeMaker nm2 = variable2.getNodeMaker();
     
      DetermineNodeType filter1 = new DetermineNodeType();
      nm1.describeSelf(filter1);
      RDFDatatype datatype1 = filter1.getDatatype();
     
      DetermineNodeType filter2 = new DetermineNodeType();
      nm2.describeSelf(filter2);
      RDFDatatype datatype2 = filter2.getDatatype();
     
      if (datatype1 != null && XSD.isNumeric(datatype1) && datatype2 != null && XSD.isNumeric(datatype2)) {
        RDFDatatype numericType = XSD.getNumericType(filter1.getDatatype(), filter2.getDatatype());
        nm1 = cast(nm1, numericType);
        nm2 = cast(nm2, numericType);
      }
     
      NodeSetConstraintBuilder nodeSet = new NodeSetConstraintBuilder();
      nm1.describeSelf(nodeSet);
      nm2.describeSelf(nodeSet);
     
      if (nodeSet.isEmpty()) {
        logger.debug("nodes " + nm1 + " " + nm2 + " incompatible");
        expression.push(Expression.TRUE);
        return;
      }
    }
   
    expression.push(new Negation(Equality.create(e1, e2)));
  }
 
  private void convertLogicalNot(E_LogicalNot expr)
  {
    expr.getArg().visit(this);
    Expression e1 = expression.pop();
    if (e1 instanceof Negation)
      expression.push(((Negation) e1).getBase());
    else
      expression.push(new Negation(e1));
  }
 
  private void convert(E_UnaryPlus expr)
  {
    expr.getArg().visit(this);
  }
 
  private void convert(E_UnaryMinus expr)
  {
    expr.getArg().visit(this);
    Expression e1 = expression.pop();
    if (e1 instanceof UnaryMinus)
      expression.push(((UnaryMinus) e1).getBase());
    else
      expression.push(new UnaryMinus(e1));
  }
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.2
   *
   * "(ISIRI) Returns true if term is an IRI. Returns false otherwise."
   *
   * @see http://www.w3.org/TR/rdf-sparql-query
   */
  private void convertIsIRI(E_IsIRI expr)
  {
    logger.debug("convertIsIRI " + expr.toString());
   
    expr.getArg().visit(this);
   
    Expression arg = expression.pop();
   
    if (arg instanceof AttributeExprEx) {
      AttributeExprEx variable = (AttributeExprEx) arg;
      NodeMaker nm = variable.getNodeMaker();
      DetermineNodeType filter = new DetermineNodeType();
      nm.describeSelf(filter);
      expression.push(filter.isLimittedToURIs() ? Expression.TRUE : Expression.FALSE);
    } else if (arg instanceof ConstantEx) {
      ConstantEx constant = (ConstantEx) arg;
      Node node = constant.getNode();
      expression.push(node.isURI() ? Expression.TRUE : Expression.FALSE);
    } else {
      conversionFailed(expr);
    }
  }
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.3
   *
   * "(ISBLANK) Returns true if term is a blank node. Returns false otherwise."
   *
   * @see http://www.w3.org/TR/rdf-sparql-query
   */
  private void convertIsBlank(E_IsBlank expr)
  {
    logger.debug("convertIsBlank " + expr.toString());
   
    expr.getArg().visit(this);
   
    Expression arg = expression.pop();
   
    if (arg instanceof AttributeExprEx) {
      AttributeExprEx variable = (AttributeExprEx) arg;
      NodeMaker nm = variable.getNodeMaker();
      DetermineNodeType filter = new DetermineNodeType();
      nm.describeSelf(filter);
      expression.push(filter.isLimittedToBlankNodes() ? Expression.TRUE : Expression.FALSE);
    } else if (arg instanceof ConstantEx) {
      ConstantEx constant = (ConstantEx) arg;
      Node node = constant.getNode();
      expression.push(node.isBlank() ? Expression.TRUE : Expression.FALSE);
    } else {
      conversionFailed(expr);
    }
  }
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.4
   *
   * "(ISLITERAL) Returns true if term is a literal. Returns false otherwise."
   *
   * @see http://www.w3.org/TR/rdf-sparql-query
   */
  private void convertIsLiteral(E_IsLiteral expr)
  {
    logger.debug("convertIsLiteral " + expr.toString());
   
    expr.getArg().visit(this);
   
    Expression arg = expression.pop();
   
    logger.debug("arg " + arg);
   
    if (arg instanceof AttributeExprEx) {
      AttributeExprEx variable = (AttributeExprEx) arg;
      NodeMaker nm = variable.getNodeMaker();
     
      DetermineNodeType filter = new DetermineNodeType();
      nm.describeSelf(filter);
      expression.push(filter.isLimittedToLiterals() ? Expression.TRUE : Expression.FALSE);
    } else if (arg instanceof ConstantEx) {
      ConstantEx constant = (ConstantEx) arg;
      Node node = constant.getNode();
      expression.push(node.isLiteral() ? Expression.TRUE : Expression.FALSE);
    } else {
      conversionFailed(expr);
    }
  }
 
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.4
   *
   * "(STR) Returns the lexical form of a literal; returns the codepoint representation of an IRI."
   *
   * @see http://www.w3.org/TR/rdf-sparql-query
   */
  private void convertStr(E_Str expr)
  {
    logger.debug("convertStr " + expr.toString());
   
    expr.getArg().visit(this);
   
    Expression arg = expression.pop();
   
    if (arg instanceof AttributeExprEx) {
      // make a new AttributeExprEx with changed NodeMaker, which returns plain literal
      // TODO this seems to work, but needs more testing.
      AttributeExprEx attribute = (AttributeExprEx) arg;
      TypedNodeMaker nodeMaker = (TypedNodeMaker) attribute.getNodeMaker();
      TypedNodeMaker newNodeMaker = new TypedNodeMaker(TypedNodeMaker.PLAIN_LITERAL, nodeMaker.valueMaker(), nodeMaker.isUnique());
      logger.debug("changing nodemaker " + nodeMaker + " to " + newNodeMaker);
      expression.push(new AttributeExprEx((Attribute) attribute.attributes().iterator().next(), newNodeMaker));
    } else if (arg instanceof ConstantEx) {
      ConstantEx constant = (ConstantEx) arg;
      Node node = constant.getNode();
      String lexicalForm = node.getLiteral().getLexicalForm();
      node = Node.createLiteral(lexicalForm);
      ConstantEx constantEx = new ConstantEx(NodeValue.makeNode(node).asString(), node);
      logger.debug("pushing " + constantEx);
      expression.push(constantEx);
    } else {
      conversionFailed(expr);
    }
  }
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.6
   *
   * "(LANG) Returns the language tag of ltrl, if it has one. It returns "" if ltrl has no language tag."
   * @see http://www.w3.org/TR/rdf-sparql-query
   */
  private void convertLang(E_Lang expr)
  {
    logger.debug("convertLang " + expr.toString());
   
    expr.getArg().visit(this);
   
    Expression arg = expression.pop();
   
    if (arg instanceof AttributeExprEx) {
      AttributeExprEx variable = (AttributeExprEx) arg;
      NodeMaker nm = variable.getNodeMaker();
      DetermineNodeType filter = new DetermineNodeType();
      nm.describeSelf(filter);
      String lang = filter.getLanguage();
      logger.debug("lang " + lang);
      if (lang == null)
        lang = "";
     
      // NodeValue.makeString(lang); TODO better?
      Node node = Node.createLiteral(lang);
     
      ConstantEx constantEx = new ConstantEx(NodeValue.makeNode(node).asString(), node);
      logger.debug("pushing " + constantEx);
      expression.push(constantEx);
    } else if (arg instanceof ConstantEx) {
      ConstantEx constant = (ConstantEx) arg;
      Node node = constant.getNode();
      if (!node.isLiteral()) {
        // type error, return false?
        logger.warn("type error: " + node + " is not a literal, returning FALSE");
        expression.push(Expression.FALSE);
        return;
      }
      String lang = node.getLiteralLanguage();
      logger.debug("lang " + lang);
      if (lang == null)
        lang = "";
     
      node = Node.createLiteral(lang);
     
      ConstantEx constantEx = new ConstantEx(NodeValue.makeNode(node).asString(), node);
      logger.debug("pushing " + constantEx);
      expression.push(constantEx);
    } else {
      conversionFailed(expr);
    }
  }
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.7
   *
   * "(DATATYPE) Returns the datatype IRI of typedLit; returns xsd:string if the parameter is a simple literal."
   *
   * @see http://www.w3.org/TR/rdf-sparql-query
   */
  private void convertDataType(E_Datatype expr)
  {
    logger.debug("convertDataType " + expr.toString());
   
    expr.getArg().visit(this);
   
    Expression arg = expression.pop();
   
    if (arg instanceof AttributeExprEx) {
      AttributeExprEx variable = (AttributeExprEx) arg;
      NodeMaker nm = variable.getNodeMaker();
      DetermineNodeType filter = new DetermineNodeType();
      nm.describeSelf(filter);
      if (!filter.isLimittedToLiterals()) {
        // type error, return false?
        logger.warn("type error: " + variable + " is not a literal, returning FALSE");
        expression.push(Expression.FALSE);
        return;
      }
      RDFDatatype datatype = filter.getDatatype();
      logger.debug("datatype " + datatype);
     
      Node node = Node.createURI((datatype != null) ? datatype.getURI() : XSDDatatype.XSDstring.getURI());
     
      ConstantEx constantEx = new ConstantEx(NodeValue.makeNode(node).asString(), node);
      logger.debug("pushing " + constantEx);
      expression.push(constantEx);
    } else if (arg instanceof ConstantEx) {
      ConstantEx constant = (ConstantEx) arg;
      Node node = constant.getNode();
      if (!node.isLiteral()) {
        // type error, return false?
        logger.warn("type error: " + node + " is not a literal, returning FALSE");
        expression.push(Expression.FALSE);
        return;
      }
      RDFDatatype datatype = node.getLiteralDatatype();
      logger.debug("datatype " + datatype);
      node = Node.createURI((datatype != null) ? datatype.getURI() : XSDDatatype.XSDstring.getURI());
      ConstantEx constantEx = new ConstantEx(NodeValue.makeNode(node).asString(), node);
      logger.debug("pushing " + constantEx);
      expression.push(constantEx);
    } else {
      conversionFailed(expr);
    }
  }
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.11
   *
   * "(SAMETERM) Returns TRUE if term1 and term2 are the same RDF term as defined
   * in Resource Description Framework (RDF): Concepts and Abstract Syntax [CONCEPTS]; returns FALSE otherwise."
   *
   * @see http://www.w3.org/TR/rdf-sparql-query
   * @see http://www.w3.org/TR/rdf-concepts/
   */
  private void convertSameTerm(E_SameTerm expr)
  {
    logger.debug("convertSameTerm " + expr.toString());
   
    expr.getArg1().visit(this);
    expr.getArg2().visit(this);
    Expression e2 = expression.pop();
    Expression e1 = expression.pop();
   
    // TODO Expression.FALSE and Expression.TRUE are not constants
    if (e1.equals(Expression.FALSE))
      e1 = CONSTANT_FALSE;
    else if (e1.equals(Expression.TRUE))
      e1 = CONSTANT_TRUE;
    if (e2.equals(Expression.FALSE))
      e2 = CONSTANT_FALSE;
    else if (e2.equals(Expression.TRUE))
      e2 = CONSTANT_TRUE;
   
    if (e1 instanceof AttributeExprEx && e2 instanceof Constant || e2 instanceof AttributeExprEx && e1 instanceof Constant) {
     
      AttributeExprEx variable;
      ConstantEx      constant;
     
      if (e1 instanceof AttributeExprEx) {
        variable = (AttributeExprEx) e1;
        constant = (ConstantEx) e2;
      } else {
        variable = (AttributeExprEx) e2;
        constant = (ConstantEx) e1;
      }
     
      logger.debug("isEqual(" + variable + ", " + constant + ")");
     
      NodeMaker nm = variable.getNodeMaker();
     
      if (nm instanceof TypedNodeMaker) {
        ValueMaker vm = ((TypedNodeMaker) nm).valueMaker();
        Node node = constant.getNode();
        logger.debug("checking " + node + " with " + nm);
               
        boolean empty = nm.selectNode(node, RelationalOperators.DUMMY).equals(NodeMaker.EMPTY);
        logger.debug("result " + new Boolean(empty));
        if (!empty) {
          if (node.isURI())
            expression.push(vm.valueExpression(node.getURI()));
          else if (node.isLiteral())
            expression.push(vm.valueExpression(constant.value()));
          else
            conversionFailed(expr); // TODO blank nodes?
          return;
        } else {
          expression.push(Expression.FALSE);
          return;
        }
      } else {
        logger.warn("nm is not a TypedNodemaker");
      }
    } else if (e1 instanceof ConstantEx && e2 instanceof ConstantEx) {
      logger.debug("isEqual(" + e1 + ", " + e2 + ")");
      ConstantEx constant1 = (ConstantEx) e1;
      ConstantEx constant2 = (ConstantEx) e2;
      boolean equals = NodeFunctions.sameTerm(constant1.getNode(), constant2.getNode());
      logger.debug("constants same? " + new Boolean(equals));
      expression.push(equals ? Expression.TRUE : Expression.FALSE);
      return;
    } else if (e1 instanceof AttributeExprEx && e2 instanceof AttributeExprEx) {
      logger.debug("isEqual(" + e1 + ", " + e2 + ")");
      AttributeExprEx variable1 = (AttributeExprEx) e1;
      AttributeExprEx variable2 = (AttributeExprEx) e2;
     
      NodeMaker nm1 = variable1.getNodeMaker();
      NodeMaker nm2 = variable2.getNodeMaker();

      NodeSetConstraintBuilder nodeSet = new NodeSetConstraintBuilder();
      nm1.describeSelf(nodeSet);
      nm2.describeSelf(nodeSet);
     
      if (nodeSet.isEmpty()) {
        logger.debug("nodes " + nm1 + " " + nm2 + " incompatible");
        expression.push(Expression.FALSE);
        return;
      }
    }
   
    expression.push(Equality.create(e1, e2));
  }
 
  /*
   * See http://www.w3.org/TR/rdf-sparql-query paragraph 11.4.12
   *
   * "(LANGMATCHES) returns true if language-tag (first argument) matches language-range (second argument)
   * per the basic filtering scheme defined in [RFC4647] section 3.3.1. language-range is a basic language
   * range per Matching of Language Tags [RFC4647] section 2.1. A language-range of "*" matches any non-empty
   * language-tag string."
   *
   * @see http://www.w3.org/TR/rdf-sparql-query
   */
  private void convertLangMatches(E_LangMatches expr)
  {
    logger.debug("convertLangMatches " + expr.toString());
   
    expr.getArg1().visit(this);
    expr.getArg2().visit(this);
    Expression e2 = expression.pop();
    Expression e1 = expression.pop();
   
    if (e1 instanceof ConstantEx && e2 instanceof ConstantEx) {
      ConstantEx lang1 = (ConstantEx) e1;
      ConstantEx lang2 = (ConstantEx) e2;
      NodeValue nv1 = NodeValue.makeString(lang1.getNode().getLiteral().getLexicalForm());
      NodeValue nv2 = NodeValue.makeString(lang2.getNode().getLiteral().getLexicalForm());
      NodeValue match = NodeFunctions.langMatches(nv1, nv2);
      expression.push(match.equals(NodeValue.TRUE) ? Expression.TRUE : Expression.FALSE);
    } else {
      expression.push(Expression.FALSE);
    }
  }
 
 
  private void conversionFailed(Expr unconvertableExpr)
  {
    // prevent stack empty exceptions when conversion fails in the middle of a multi-arg operator conversion
    expression.push(Expression.FALSE);
    convertable = false;
    if (reason == null)
      reason = "cannot convert " + unconvertableExpr.toString();
  }
 
  private void conversionFailed(String message, Expr unconvertableExpr)
  {
    // prevent stack empty exceptions when conversion fails in the middle of a multi-arg operator conversion
    expression.push(Expression.FALSE);
    convertable = false;
    if (reason == null)
      reason = "cannot convert " + unconvertableExpr.toString() + ": " + message;
  }
 

 
  static NodeMaker cast(NodeMaker nodeMaker, RDFDatatype datatype)
  {
    if (nodeMaker instanceof TypedNodeMaker)
      return new TypedNodeMaker(TypedNodeMaker.typedLiteral(datatype), ((TypedNodeMaker) nodeMaker).valueMaker(), nodeMaker.isUnique());
   
    if (nodeMaker instanceof FixedNodeMaker) {
      Node node = nodeMaker.makeNode(null);
     
      return new FixedNodeMaker(XSD.cast(node, datatype), nodeMaker.isUnique());
    }
   
    throw new RuntimeException("unknown nodeMaker type");
  }
 
  static boolean isSimpleLiteral(Node node)
  {
    if (!node.isLiteral())
      return false;
   
    return node.getLiteralDatatype() == null && "".equals(node.getLiteralLanguage());
  }
 
  // extension mechanism
 

  protected boolean extensionSupports(ExprFunction function)
  {
    return false;
  }
 
  protected void extensionConvert(ExprFunction function, List<Expression> args)
  {
  }

}
TOP

Related Classes of de.fuberlin.wiwiss.d2rq.optimizer.expr.TransformExprToSQLApplyer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.