/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
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.bop;
import java.util.Collections;
import com.bigdata.rdf.sparql.ast.FilterNode;
/**
* A constant.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
final public class Constant<E> extends ImmutableBOp implements IConstant<E> {
/**
*
*/
private static final long serialVersionUID = -2967861242470442497L;
/** Unique (for all E) Constant representing the error value as in
* https://www.w3.org/TR/sparql11-query/#aggregateAlgebra .
* This allows efficient checking if a value is an error value by using
* the reference equality (val == Constant.ERROR_VALUE).
*/
@SuppressWarnings("rawtypes")
private static final Constant ERROR_VALUE = new Constant();
/** value == null indicates this == errorValue, representing
* an error value as in
* https://www.w3.org/TR/sparql11-query/#aggregateAlgebra
*/
final private E value;
public interface Annotations extends ImmutableBOp.Annotations {
/**
* The {@link IVariable} which is bound to that constant value
* (optional).
* <p>
* {@link BOpContext#bind(IPredicate, IConstraint[], Object, IBindingSet)}
* takes care of propagating the binding onto the variable for solutions
* which join.
* <p>
* Note: The {@link Var} class in bigdata provides a guarantee of
* reference testing for equality, which is why we can not simply attach
* the constant to the variable and have the variable report its bound
* value. *
*/
String VAR = Constant.class.getName() + ".var";
}
@Override
final public boolean isVar() {
return false;
}
@Override
final public boolean isConstant() {
return true;
}
// /**
// * Required shallow copy constructor.
// *
// * @param op
// */
// public Constant(final BOp[] args, final Map<String,Object> ann) {
// super(args,ann);
// this.value = null;
// }
/**
* Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
*
* @param op != Constant.ERROR_VALUE
*/
public Constant(final Constant<E> op) {
super(op);
if (op == Constant.ERROR_VALUE)
throw new IllegalArgumentException();
this.value = op.value;
}
/**
* Create a constant which models a variable bound to that constant. This
* may be used when a variable has an external binding, such as when a
* single binding set is provided as input to a query. By pairing the
* {@link Constant} with the {@link IVariable} and handling this case when
* solutions are joined the resulting solutions will have the variable with
* its constant bound value.
* <p>
* Note: The {@link Var} class in bigdata provides a guarantee of reference
* testing for equality, which is why we can not simply attach the constant
* to the variable and have the variable report its bound value. *
* <p>
* Note: A very similar effect may be achieved by simply binding the
* variable on the {@link IBindingSet} to the constant. However, there are
* some (few) cases where we can not do that because the binding must be
* applied for all solutions and we lack access to the input solutions.
* <p>
* See
* {@link BOpContext#bind(IPredicate, IConstraint[], Object, IBindingSet)},
* which takes care of propagating the binding onto the variable for
* solutions which join.
*
* @param var
* The variable.
* @param value
* The bound value.
*/
public Constant(final IVariable<E> var, final E value) {
super(BOp.NOARGS, Collections.singletonMap(Annotations.VAR,
(Object) var));
// NV.asMap(new NV(Annotations.VAR, var)));
if (var == null)
throw new IllegalArgumentException();
if (value == null)
throw new IllegalArgumentException();
if(value instanceof IConstant<?>) {
// Recursive nesting of Constant is not allowed.
throw new IllegalArgumentException();
}
this.value = value;
}
/**
* Create a constant for the value.
*
* @param value
* The value (may not be <code>null</code>).
*/
public Constant(final E value) {
super(BOp.NOARGS, BOp.NOANNS);
if (value == null)
throw new IllegalArgumentException();
if(value instanceof IConstant<?>) {
// Recursive nesting of Constant is not allowed.
throw new IllegalArgumentException();
}
this.value = value;
}
/** Currently only used to create {@link ERROR_VALUE}. */
private Constant() {
super(BOp.NOARGS, BOp.NOANNS);
value = null;
}
/** Always returns the same constant representing the error value as in
* https://www.w3.org/TR/sparql11-query/#aggregateAlgebra .
* Copies of this Constant cannot be created,
* so comparison of reference is enough for equality checks.
*/
@SuppressWarnings("rawtypes")
public static Constant errorValue() {
return ERROR_VALUE;
}
/**
* Clone is overridden to reduce heap churn.
*/
@Override
final public Constant<E> clone() {
return this;
}
@Override
public String toString() {
if (value == null) {
return "<error value>";
}
@SuppressWarnings("unchecked")
final IVariable<E> var = (IVariable<E>) getProperty(Annotations.VAR);
if(var != null) {
// A constant which is really an as-bound variable.
return value.toString() + "[var=" + var + "]";
}
return value.toString();
}
@SuppressWarnings("rawtypes")
@Override
final public boolean equals(final IVariableOrConstant<E> o) {
if (!o.isConstant()) {
return false;
}
if (value == null) {
return ((Constant) o).value == null;
}
return ((Constant) o).value != null &&
value.equals(((Constant) o).value);
}
@Override
final public boolean equals(final Object o) {
if (this == o)
return true;
if(!(o instanceof IConstant<?>)) {
/*
* Incomparable types.
*
* Note: This used to permit IVariableOrConstant, but it is not
* possible to invoke get() on an IVariable without a bindingSet
* against which to resolve its asBound value.
*
* See https://sourceforge.net/apps/trac/bigdata/ticket/276
*/
return false;
}
final Object otherValue = ((IConstant<?>) o).get();
// handles reference equality, including when both are null.
if (value == otherValue)
return true;
// handles value null when other is non-null.
if (value == null)
return false;
// compares non-null value with the other value.
if(value.equals(otherValue))
return true;
return false;
}
@Override
final public int hashCode() {
// return (int) (id ^ (id >>> 32));
if (value == null) {
return 747282; // arbitrary, not too small
}
return value.hashCode();
}
/** @return possibly null if this Constant represents an error value
* as in https://www.w3.org/TR/sparql11-query/#aggregateAlgebra
*/
@Override
final public E get() {
return value;
}
/** @return possibly null if this Constant represents an error value
* as in https://www.w3.org/TR/sparql11-query/#aggregateAlgebra
*/
@Override
final public E get(final IBindingSet bindingSet) {
return value;
}
@Override
final public String getName() {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
final public IVariable<E> getVar() {
return (IVariable<E>) getProperty(Annotations.VAR);
}
}