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

com.bigdata.rdf.internal.constraints.CompareBOp Maven / Gradle / Ivy

/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package com.bigdata.rdf.internal.constraints;

import java.util.Map;

import org.apache.log4j.Logger;
import org.openrdf.model.Literal;
import org.openrdf.query.algebra.Compare.CompareOp;
import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
import org.openrdf.query.algebra.evaluation.util.QueryEvaluationUtil;

import com.bigdata.bop.BOp;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IValueExpression;
import com.bigdata.bop.NV;
import com.bigdata.bop.solutions.IVComparator;
import com.bigdata.rdf.error.SparqlTypeErrorException;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.impl.literal.AbstractLiteralIV;
import com.bigdata.rdf.internal.impl.literal.LiteralExtensionIV;
import com.bigdata.rdf.internal.impl.literal.PackedLongIV;
import com.bigdata.rdf.internal.impl.literal.XSDIntegerIV;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.sparql.ast.FilterNode;

/**
 * Perform open-world value comparison operations per the SPARQL spec (the LT
 * operator). This does NOT implement the broader ordering for ORDER BY. That is
 * handled by {@link IVComparator}.
 * 
 * @see <
 * 
 * @see IVComparator
 */
public class CompareBOp extends XSDBooleanIVValueExpression
		implements INeedsMaterialization {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5661497748051783499L;
	
    private static final transient Logger log = Logger
            .getLogger(CompareBOp.class);
	
    public interface Annotations extends XSDBooleanIVValueExpression.Annotations {

        /**
         * The compare operator, which is a {@link CompareOp} enum value.
         */
        String OP = CompareBOp.class.getName() + ".op";

    }

    @SuppressWarnings("rawtypes")
    public CompareBOp(final IValueExpression left,
            final IValueExpression right, final CompareOp op) {

        this(new BOp[] { left, right }, NV.asMap(Annotations.OP, op));

    }
    
    /**
     * Required shallow copy constructor.
     */
    public CompareBOp(final BOp[] args, final Map anns) {

    	super(args, anns);
    	
        if (args.length != 2 || args[0] == null || args[1] == null
        		|| getProperty(Annotations.OP) == null) {

			throw new IllegalArgumentException();
		
		}
        
        if (log.isDebugEnabled()) {
        	log.debug(toString());
        }

    }

    /**
     * Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
     */
    public CompareBOp(final CompareBOp op) {
        super(op);
    }
    
    public CompareOp op() {
    	return (CompareOp) getRequiredProperty(Annotations.OP);
    }

    public static boolean compare( 
    		final IV left,
            final IV right,
            final CompareOp op) {

        if (log.isDebugEnabled()) {
            log.debug(left);
            log.debug(right);
        }

//    	final BigdataValue val1 = left.getValue();
//    	final BigdataValue val2 = right.getValue();
//    	
//    	try {
//    	
//    		// use the Sesame implementation directly
//    		final boolean accept = QueryEvaluationUtil.compare(val1, val2, op);
//    		
//    		if (log.isDebugEnabled()) {
//    			log.debug(accept);
//    		}
//    		
//    		return accept;
//    		
//    	} catch (Exception ex) {
//    		
//    		if (log.isDebugEnabled()) {
//    			log.debug("exception: " + ex);
//    		}
//    		
//    		throw new SparqlTypeErrorException();
//    		
//    	}

    	
		if (left.isLiteral() && right.isLiteral()) {
			// Both left and right argument is a Literal
			return compareLiterals(left, right, op);
		}
		else {
			// All other value combinations
			switch (op) {
				case EQ:
					return valuesEqual(left, right);
				case NE:
					return !valuesEqual(left, right);
				default:
					throw new SparqlTypeErrorException();
			}
		}
		
    }
    
    private static boolean isRealTermId(final IV iv) {
    	
    	return !iv.isInline() && !iv.isNullIV();
    	
    }
    
    private static boolean compareLiterals(
    		final IV left,
    		final IV right,
    		final CompareOp op) {
    	
    	/*
    	 * Handle the special case where we have exact termId equality.  This
    	 * only works if both are "real" termIds (i.e. not mock IVs).
    	 */
    	if (op == CompareOp.EQ && isRealTermId(left) && isRealTermId(right)) {

            if (left.equals(right)) {
            	
                return true;
                
            }
    		
    	}
    	
    	/*
    	 * We want to special case the LiteralExtensionIV, which
    	 * handles xsd:dateTime.  If we defer to Sesame for this, we will be
    	 * forced into materialization because the first step in Sesame's
    	 * evaluation is to get the datatype (requires materialization for
    	 * LiteralExtensionIV).
    	 */
    	if (left instanceof LiteralExtensionIV &&
    			right instanceof LiteralExtensionIV) {
    	
    		@SuppressWarnings("rawtypes")
            final IV leftDatatype = ((LiteralExtensionIV) left).getExtensionIV();
    		
            @SuppressWarnings("rawtypes")
    		final IV rightDatatype = ((LiteralExtensionIV) right).getExtensionIV();
    		
    		if (leftDatatype.equals(rightDatatype)) {
    		
        		final boolean accept = 
        			_accept(left.compareTo(right), op);
        		
	    		if (log.isDebugEnabled()) {
	    			log.debug(accept);
	    		}
	    		
	    		return accept;
    			
    		}
    		
    	}
    	
    	/**
    	 * Handle LiteralExtensionIVs that support comparison with built-in datatypes.
         * These types are handled by conversion into a built-in datatype, which can be checked
         * by Sesame's QueryEvaluationUtil.
    	 */
    	Literal l1 = null;
        Literal l2 = null;
    	if (left instanceof LiteralExtensionIV || right instanceof LiteralExtensionIV) { 
    	    
    	    /**
    	     * We caught the case where both left and right are LiteralExtensionIVs
    	     * earlier, so we can be sure that exactly one of the is a LiteralExtensionIVs.
    	     */
    	    boolean leftIsLiteralExtensionIV = left instanceof LiteralExtensionIV;
    	    
    	    @SuppressWarnings("rawtypes")
            final AbstractLiteralIV delegate = 
                leftIsLiteralExtensionIV ?
                ((LiteralExtensionIV) left).getDelegate():
                ((LiteralExtensionIV) right).getDelegate();
                    
    	    if (delegate !=null) {
    	        
    	        Literal delegateAsBuiltInLiteral = null;
    	        if (delegate instanceof PackedLongIV) {
    	            
    	            @SuppressWarnings("rawtypes")
                    final PackedLongIV iv = (PackedLongIV)delegate;
    	            delegateAsBuiltInLiteral = new XSDIntegerIV<>(iv.integerValue());

    	        } // else: add other cases here in future

    	        // set left-hand or right-hand side expression
                if (leftIsLiteralExtensionIV) {
                    l1 = delegateAsBuiltInLiteral; // may remain null
                } else {
                    l2 = delegateAsBuiltInLiteral; // may remanin null
                }    	        
    	    }
    	}

		/*
		 * Now that the IVs implement the right openrdf interfaces,
		 * we should be able to mix and match inline with non-inline,
		 * using either the IV directly or its materialized value.
		 */
		l1 = l1==null ? asLiteral(left) : l1;
		l2 = l2==null ? asLiteral(right) : l2;
		
        if (log.isDebugEnabled()) {
            log.debug(l1);
            log.debug(l2);
        }

        try {

    		// use the Sesame implementation directly
    		final boolean accept = 
    				QueryEvaluationUtil.compareLiterals(l1, l2, op);
    		
    		if (log.isDebugEnabled()) {
    			log.debug(accept);
    		}
    		
    		return accept;
    		
    	} catch (ValueExprEvaluationException ex) {
    		
    		if (log.isDebugEnabled()) {
    			log.debug("exception: " + ex);
    		}
    		
    		throw new SparqlTypeErrorException();
    		
    	}
    	
    }
    
	private static boolean valuesEqual(
			final IV left, final IV right) {
		
    	// must be the same type of value (e.g. both URIs, both BNodes, etc)
    	if (left.getVTE() != right.getVTE()) {
    		return false;
    	}

		/*
		 * We can't use IV.equals() when we have a null IV. A null IV might
		 * have a materialized value that matches a term id in the database.
		 * Different IVs, same materialized value (this can happen via the
		 * datatype or str operator, anything that mints a new Value during
		 * query evaluation). 
		 */
		if (left.isNullIV() || right.isNullIV()) {
			
			return asValue(left).equals(asValue(right));
			
		} else {
			
			return left.equals(right);
			
		}
		
	}

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public boolean accept(final IBindingSet s) {
        
    	final IV left = get(0).get(s);
    	final IV right = get(1).get(s);
    	
        // not yet bound
    	if (left == null || right == null)
        	throw new SparqlTypeErrorException();
    	
    	if (log.isDebugEnabled()) {
    		log.debug("left: " + left);
    		log.debug("right: " + right);
    		log.debug("left value: " + (left.hasValue() ? left.getValue() : null));
    		log.debug("right value: " + (right.hasValue() ? right.getValue() : null));
    	}
    
    	return compare(left, right, op());
    	
    }
    	
    static protected boolean _accept(final int compare, final CompareOp op) {
    	
    	switch(op) {
    	case EQ:
    		return compare == 0; 
    	case NE:
    		return compare != 0;
    	case GT:
    		return compare > 0;
    	case GE:
    		return compare >= 0;
    	case LT:
    		return compare < 0;
    	case LE:
    		return compare <= 0;
    	default:
    		throw new UnsupportedOperationException();
    	}
    	
    }
    
    /**
     * The CompareBOp can work with non-materialized terms in the case of
     * inline numerical compare operations.  It is only when the bop encounters
     * non-inlined numerics or needs to compare strings that it needs
     * materialized terms.  
     */
    public Requirement getRequirement() {
    	
    	return INeedsMaterialization.Requirement.SOMETIMES;
    	
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy