/**
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
*/
/*
* Created on Aug 16, 2010
*/
package com.bigdata.bop;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* Abstract base class for copy-on-write {@link BOp}s. The {@link BOpBase} class
* is used for query evaluation operators. The copy-on-write contract provides a
* safety margin during concurrent evaluation of query plans by ensuring that
* all references are fully published.
* <p>
* Instances of this class are effectively immutable (mutation APIs always
* return a deep copy of the operator to which the mutation has been applied),
* {@link Serializable} to facilitate distributed computing, and
* {@link Cloneable} to facilitate non-destructive tree rewrites.
* <p>
* <h2>Constructor patterns</h2>
* <p>
* {@link BOp}s should define the following public constructors
* <dl>
* <dt>
* <code>public <i>Class</i>(BOp[] args, Map<String,Object> anns)</code></dt>
* <dd>A shallow copy constructor. This is used when initializing a {@link BOp}
* from the caller's data or when generated a query plan from Prolog. There are
* some exceptions to this rule. For example, {@link Constant} does not define a
* shallow copy constructor because that would not provide a means to set the
* constant's value.</dd>
* <dt><code>public <i>Class</i>(<i>Class</i> src)</code></dt>
* <dd>A deep copy constructor. Mutation methods make a deep copy of the
* {@link BOp}, apply the mutation to the copy, and then return the copy. This
* is the "effectively immutable" contract. Again, there are some exceptions.
* For example, {@link Var} provides a canonicalized mapping such that reference
* tests may be used to determine if two {@link Var}s are the same. In order to
* support that contract it overrides {@link Var#clone()}.</dd>
* </dl>
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class BOpBase extends CoreBaseBOp {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* The argument values - <strong>direct access to this field is
* discouraged</strong> - the field is protected to support
* <em>mutation</em> APIs and should not be relied on for other purposes.
* <p>
* Note: This field is reported out as a {@link List} so we can make it
* thread safe and, if desired, immutable. However, it is internally a
* simple array. Subclasses can implement mutation operations which return
* deep copies in which the argument values have been modified using
* {@link #_set(int, BOp)}.
* <p>
* If we allowed mutation of the arguments (outside of the object creation
* pattern) then caching of the arguments (or annotations) by classes such
* as EQ will cause {@link #clone()} to fail because (a) it will do
* a field-by-field copy on the concrete implementation class; and (b) it
* will not consistently update the cached references. In order to "fix"
* this problem, any classes which cache arguments or annotations would have
* to explicitly overrides {@link #clone()} in order to set those fields
* based on the arguments on the cloned {@link BOpBase} class.
* <p>
* Note: This must be at least "effectively" final per the effectively
* immutable contract for {@link BOp}s.
*/
private final BOp[] args;
/**
* The operator annotations.
* <p>
* Note: This must be at least "effectively" final per the effectively
* immutable contract for {@link BOp}s.
*/
private final Map<String,Object> annotations;
/**
* Deep copy constructor (required).
* <p>
* Each {@link BOp} MUST implement a public copy constructor with the
* signature:
*
* <pre>
* public Foo(Foo)
* </pre>
*
* This construct is invoked by {@link #clone()} using reflection and is
* responsible for the deep copy semantics for the {@link BOp}.
* <p>
* The default implementation makes a deep copy of {@link #args()} and
* {@link #annotations()} but DOES NOT perform field-by-field copying.
* Subclasses may simply delegate the constructor to their super class
* unless they have additional fields which need to be copied.
* <p>
* This design pattern was selected because it preserves the immutable
* contract of the {@link BOp} which gives us our thread safety and
* visibility guarantees. Since the deep copy is realized by the {@link BOp}
* implementation classes, it is important that each class take
* responsibility for the deep copy semantics of any fields it may declare.
*
* @param op
* A deep copy will be made of this {@link BOp}.
*
* @throws NullPointerException
* if the argument is <code>null</code>.
*/
public BOpBase(final BOpBase op) {
// Note: only shallow copy is required to achieve immutable semantics!
if (op.args == BOp.NOARGS || op.args.length == 0) {
// fast path for zero arity operators.
args = BOp.NOARGS;
} else {
args = Arrays.copyOf(op.args, op.args.length);
}
annotations = new LinkedHashMap<String, Object>(op.annotations);
}
/**
* Shallow copy constructor (required).
*
* @param args
* The arguments to the operator.
* @param annotations
* The annotations for the operator (optional).
*/
public BOpBase(final BOp[] args,
final Map<String, Object> annotations) {
if (args == null)
throw new IllegalArgumentException();
checkArgs(args);
this.args = args;
this.annotations = (annotations == null ? new LinkedHashMap<String, Object>(
DEFAULT_INITIAL_CAPACITY)
: annotations);
}
@Override
final public Map<String, Object> annotations() {
return Collections.unmodifiableMap(annotations);
}
@Override
protected boolean annotationsEqual(final BOp o) {
if (o instanceof BOpBase) {
// Fast path when comparing two immutable bops.
return annotationsEqual(annotations, ((BOpBase) o).annotations);
}
return super.annotationsEqual(annotations, o.annotations());
}
/**
* A copy of the args[] array.
*/
final protected BOp[] argsCopy() {
final BOp[] tmp = new BOp[args.length];
for (int i = 0; i < args.length; i++) {
tmp[i] = args[i];
}
return tmp;
}
/**
* A copy of the annotations.
*/
final protected Map<String, Object> annotationsCopy() {
return new LinkedHashMap<String, Object>(annotations);
}
/**
* A reference to the actual annotations map object. This is used in some
* hot spots to avoid creating a new annotations map when we know that the
* annotations will not be modified (annotations are always set within the
* context in which the {@link BOpBase} instance is created so we can know
* this locally by inspection of the code).
*/
final protected Map<String,Object> annotationsRef() {
return annotations;
}
@Override
public BOp get(final int index) {
return args[index];
}
/**
* Set the value of an operand.
* <p>
* Note: This is protected to facilitate copy-on-write patterns. It is not
* public to prevent arbitrary changes to operators outside of methods which
* clone the operator and return the modified version. This is part of the
* effectively immutable contract for {@link BOp}s.
*
* @param index
* The index.
* @param op
* The operand.
*
* @return The old value.
*/
final protected void _set(final int index, final BOp op) {
this.args[index] = op;
}
/**
* Return a new {@link BOpBase} in which the child operand has been replaced
* by the given expression.
*
* @param index
* The index of the child expression to be replaced.
* @param newArg
* The new child expression.
*
* @return A copy of this {@link BOpBase} in which the child operand has
* been replaced.
*/
public BOpBase setArg(final int index, final BOp newArg) {
if (newArg == null)
throw new IllegalArgumentException();
final BOpBase tmp = (BOpBase) this.clone();
tmp._set(index, newArg);
return tmp;
}
/**
* Effectively overwrites the specified argument with the provided value.<p>
* WARNING: this method could break logic of the code, which relies on immutability of the arguments list.
* It is introduced while fixing issues with deferred IV resolution and intended to be used only before IV resolution completed.
* This method triggers mutation notification.
* @see https://jira.blazegraph.com/browse/BLZG-1755 (Date literals in complex FILTER not properly resolved)
* @param index
* The index of the child expression to be replaced.
* @param newArg
* The new child expression.
*/
public void __replaceArg(final int index, final BOp newArg) {
args[index] = newArg;
mutation();
}
@Override
public int arity() {
return args.length;
}
/**
* {@inheritDoc}
* <p>
* Note: This is much less efficient than {@link #argIterator()}.
*/
@Override
final public List<BOp> args() {
return Collections.unmodifiableList(Arrays.asList(args));
}
/**
* {@inheritDoc}
* <p>
* The iterator does not support removal. (This is more efficient than
* #args()).
*/
@Override
final public Iterator<BOp> argIterator() {
return new ArgIterator();
}
/**
* An iterator visiting the arguments which does not support removal.
*/
private class ArgIterator implements Iterator<BOp> {
private int i = 0;
public boolean hasNext() {
return i < args.length;
}
public BOp next() {
if (!hasNext())
throw new NoSuchElementException();
return args[i++];
}
public void remove() {
throw new UnsupportedOperationException();
}
}
// shallow copy
@Override
public BOp[] toArray() {
final BOp[] a = new BOp[args.length];
return Arrays.copyOf(args, args.length, a.getClass());
}
// shallow copy
@SuppressWarnings("unchecked")
public <T> T[] toArray(final T[] a) {
if (a.length < args.length)
return (T[]) Arrays.copyOf(args, args.length, a.getClass());
System.arraycopy(args, 0, a, 0, args.length);
if (a.length > args.length)
a[args.length] = null;
return a;
}
// /**
// * Deep copy of a {@link BOpBase}.
// *
// * @return The deep copy.
// */
// public BOpBase deepCopy() {
//
// final BOpBase bop = (BOpBase) this.clone();
//
// bop.args = deepCopy(bop.args);
//
// bop.annotations = deepCopy(bop.annotations);
//
// return bop;
//
// }
/**
* Deep copy the arguments.
* <p>
* Note: As long as we stick to the immutable semantics for bops, we can
* just make a shallow copy of the arguments in the "copy" constructor and
* then modify them within the specific operator constructor before
* returning control to the caller. This would result in less heap churn.
*/
static protected BOp[] deepCopy(final BOp[] a) {
if (a == BOp.NOARGS || a.length == 0) {
// fast path for zero arity operators.
return BOp.NOARGS;
}
final BOp[] t = new BOp[a.length];
for (int i = 0; i < a.length; i++) {
t[i] = a[i] == null ? null : a[i].clone();
}
return t;
}
/**
* Deep copy the annotations.
* <p>
* Note: This does not know how to deep copy annotations which are not
* {@link BOp}s or immutable objects such as {@link String}s or
* {@link Number}s. Such objects should not be used as annotations.
*
* @todo When attaching large data sets to a query plan they should be
* attached using a light weight reference object which allows them to
* be demanded by a node so deep copy remains a light weight
* operation. This also has the advantage that the objects are
* materialized on a node only when they are needed, which keeps the
* query plan small. Examples would be sending a temporary graph
* containing an ontology or some conditional assertions with a query
* plan.
*/
static protected Map<String, Object> deepCopy(final Map<String, Object> a) {
if (a == BOp.NOANNS) {
// Fast past for immutable, empty annotations.
return a;
}
// allocate map.
final Map<String, Object> t = new LinkedHashMap<String, Object>(a
.size());
// copy map's entries.
final Iterator<Map.Entry<String, Object>> itr = a.entrySet().iterator();
while (itr.hasNext()) {
final Map.Entry<String, Object> e = itr.next();
if (e.getValue() instanceof BOp) {
// deep copy bop annotations.
t.put(e.getKey(), ((BOp) e.getValue()).clone());
} else {
// shallow copy anything else.
t.put(e.getKey(), e.getValue());
}
}
// return the copy.
return t;
}
// @SuppressWarnings("unchecked")
// public <T> T getProperty(final String name, final T defaultValue) {
//
// if (!annotations.containsKey(name))
// return defaultValue;
//
// final Object val = annotations.get(name);
//
// if (defaultValue != null && val.getClass() != defaultValue.getClass()) {
//
// /*
// * Attempt to convert to the correct target type.
// */
//
// if (defaultValue.getClass() == Integer.class) {
// return (T) Integer.valueOf("" + val);
// }
// if (defaultValue.getClass() == Long.class) {
// return (T) Long.valueOf("" + val);
// }
// if (defaultValue.getClass() == Float.class) {
// return (T) Float.valueOf("" + val);
// }
// if (defaultValue.getClass() == Double.class) {
// return (T) Double.valueOf("" + val);
// }
//
// }
//
// return (T) val;
//
// }
// @SuppressWarnings("unchecked")
// public <T> T getProperty(final String name) {
//
// return (T) annotations.get(name);
//
// }
@Override
public Object getProperty(final String name) {
return annotations.get(name);
}
// public <T> T getRequiredProperty(final String name) {
//
// @SuppressWarnings("unchecked")
// final T tmp = (T) annotations.get(name);
//
// if (tmp == null)
// throw new IllegalArgumentException("Required property: " + name);
//
// return tmp;
//
// }
// public Object getRequiredProperty(final String name) {
//
// final Object tmp = annotations.get(name);
//
// if (tmp == null)
// throw new IllegalStateException("Required property: " + name
// + " : " + this);
//
// return tmp;
//
// }
/**
* Set an annotation.
* <p>
* Note: This is protected to facilitate copy-on-write patterns. It is not
* public to prevent arbitrary changes to operators outside of methods which
* clone the operator and return the modified version. This is part of the
* effectively immutable contract for {@link BOp}s.
*
* @param name
* The name.
* @param value
* The value.
*
* @return The old value.
*/
protected Object _setProperty(final String name, final Object value) {
return annotations.put(name,value);
}
/**
* Clear an annotation.
* <p>
* Note: This is protected to facilitate copy-on-write patterns. It is not
* public to prevent arbitrary changes to operators outside of methods which
* clone the operator and return the modified version. This is part of the
* effectively immutable contract for {@link BOp}s.
*
* @param name
* The name.
*/
protected void _clearProperty(final String name) {
annotations.remove(name);
}
@Override
public BOpBase setProperty(final String name, final Object value) {
final BOpBase tmp = (BOpBase) this.clone();
tmp._setProperty(name, value);
return tmp;
}
/**
* Conditionally sets the property.
*
* @param name
* The name.
* @param value
* The value.
*
* @return A copy of this {@link BOp} on which the property has been set.
*
* @throws IllegalStateException
* if the property is already set.
*/
public BOpBase setUnboundProperty(final String name, final Object value) {
final BOpBase tmp = (BOpBase) this.clone();
if (tmp._setProperty(name, value) != null)
throw new IllegalStateException("Already set: name=" + name
+ ", value=" + value);
return tmp;
}
/**
* Clear the named annotation.
*
* @param name
* The annotation.
*
* @return A copy of this {@link BOp} in which the named annotation has been
* removed.
*/
public BOpBase clearProperty(final String name) {
if (name == null)
throw new IllegalArgumentException();
final BOpBase tmp = (BOpBase) this.clone();
tmp._clearProperty(name);
return tmp;
}
/**
* Strips off the named annotations.
*
* @param names
* The annotations to be removed.
*
* @return A copy of this {@link BOp} in which the specified annotations do
* not appear.
*/
public BOp clearAnnotations(final String[] names) {
final BOpBase tmp = (BOpBase) this.clone();
for(String name : names) {
tmp._clearProperty(name);
}
return tmp;
}
}