![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.bop.ModifiableBOpBase 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
*/
/*
* Created on Aug 16, 2010
*/
package com.bigdata.bop;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
* Abstract base class for mutable {@link BOp}s. Unlike {@link BOpBase}, this
* class supports destructive mutation. This is the base class for the bigdata
* AST nodes. ASTs are destructively rewritten by optimizers before they are
* turned into a query plan.
*
*
Constructor patterns
*
* {@link ModifiableBOpBase}s should define the following public constructors
*
* -
*
public Class(BOp[] args, Map<String,Object> anns)
* - 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.
* public Class(Class src)
* - A copy constructor.
*
*
* @author Bryan Thompson
* @version $Id$
*/
public class ModifiableBOpBase extends CoreBaseBOp {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* A mutable list containing the argument values (aka children) and never
* null
.
*/
private final ArrayList args;
/**
* A mutable map containing the operator annotations and never
* null
.
*/
private final Map annotations;
/**
* Deep copy constructor (required).
*
* Each {@link BOp} MUST implement a public copy constructor with the
* signature:
*
*
* public Foo(Foo)
*
*
* This construct is invoked by {@link #clone()} using reflection.
*
* The default implementation makes a shallow 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.
*
* @param op
* A deep copy will be made of this {@link BOp}.
*
* @throws NullPointerException
* if the argument is null
.
*/
public ModifiableBOpBase(final ModifiableBOpBase op) {
args = new ArrayList(op.args);
annotations = new LinkedHashMap(op.annotations);
}
/**
* Shallow copy constructor (required).
*
* @param args
* The arguments to the operator.
* @param annotations
* The annotations for the operator (optional). When
* null
, a mutable map with a default capacity of
* {@value CoreBaseBOp#DEFAULT_INITIAL_CAPACITY} is allocated. Do
* NOT specify an immutable map.
*/
public ModifiableBOpBase(final BOp[] args,
final Map annotations) {
if (args == null)
throw new IllegalArgumentException();
checkArgs(args);
this.args = new ArrayList(args.length);
for(BOp t : args)
this.args.add(t);
this.annotations = (annotations == null ? new LinkedHashMap(
DEFAULT_INITIAL_CAPACITY) : annotations);
}
final public Map annotations() {
return annotations;
}
@Override
protected boolean annotationsEqual(final BOp o) {
if (o instanceof ModifiableBOpBase) {
// Fast path when comparing two modifiable bops.
return annotationsEqual(annotations,
((ModifiableBOpBase) o).annotations);
}
return super.annotationsEqual(annotations, o.annotations());
}
public BOp get(final int index) {
return args.get(index);
}
/**
* Return the index of the bop in the args. Returns -1 if bop is not
* present in the args.
*/
public int indexOf(final BOp bop) {
return args.indexOf(bop);
}
/**
* Replace the arguments.
*
* @param args
* The new arguments.
* @return this.
*/
public ModifiableBOpBase setArgs(final BOp[] args) {
if (args == null)
throw new IllegalArgumentException();
this.args.clear(); // FIXME Override might not catch clear of parent ref.
for (BOp arg : args) {
addArg(arg);
}
mutation();
return this;
}
/**
* Replace the value of the argument at the specified index (core mutation
* method).
*
* @param index
* The index of the child expression to be replaced.
* @param newArg
* The new child expression.
*
* @return This {@link ModifiableBOpBase}.
*
* @throws IllegalArgumentException
* if the argument is null
.
* @throws IllegalArgumentException
* if the argument is this.
*/
public ModifiableBOpBase setArg(final int index, final BOp newArg) {
if (newArg == null)
throw new IllegalArgumentException();
if(newArg == this)
throw new IllegalArgumentException();
args.set(index, newArg);
mutation();
return this;
}
/**
* Add a new argument (core mutation method).
*
* @param newArg
* The argument.
*
* @return this.
*
* @throws IllegalArgumentException
* if the argument is null
.
* @throws IllegalArgumentException
* if the argument is this.
*/
public void addArg(final BOp newArg) {
if(newArg == null)
throw new IllegalArgumentException();
if(newArg == this)
throw new IllegalArgumentException();
args.add(newArg);
mutation();
}
/**
* Add a new argument (core mutation method) at the specified index.
*
* @param index
* The index at which the child expression is to be inserted.
* @param newArg
* The argument.
*
* @return this.
*
* @throws IllegalArgumentException
* if the argument is null
.
* @throws IllegalArgumentException
* if the argument is this.
*/
public void addArg(final int index, final BOp newArg) {
if(newArg == null)
throw new IllegalArgumentException();
if(newArg == this)
throw new IllegalArgumentException();
args.add(index, newArg);
mutation();
}
/**
* Add an argument iff it is not already present.
*
* @param arg
* The argument.
*
* @throws IllegalArgumentException
* if the argument is null
.
* @throws IllegalArgumentException
* if the argument is this.
*/
public void addArgIfAbsent(final BOp arg) {
if(arg == null)
throw new IllegalArgumentException();
if(arg == this)
throw new IllegalArgumentException();
if(!args.contains(arg)) {
addArg(arg);
}
}
/**
* Remove the 1st occurrence of the argument (core mutation method).
*
* @param arg
* The argument.
*
* @return true
iff the argument was removed.
*
* @throws IllegalArgumentException
* if the argument is null
.
* @throws IllegalArgumentException
* if the argument is this.
*/
public boolean removeArg(final BOp arg) {
if (arg == null)
throw new IllegalArgumentException();
if (arg == this)
throw new IllegalArgumentException();
if (args.remove(arg)) {
mutation();
return true;
}
return false;
}
/**
* Replace a child of a node with another reference (destructive
* modification). All arguments which point to the oldChild will be replaced
* by references to the newChild.
*
* @param oldChild
* @param newChild
*
* @return The #of references which were replaced.
*/
public int replaceWith(final BOp oldChild, final BOp newChild) {
final ModifiableBOpBase p = this;
final int arity = p.arity();
int nmods = 0;
for (int i = 0; i < arity; i++) {
final BOp child = p.get(i);
if (child == oldChild) {
((ModifiableBOpBase) p).setArg(i, newChild);
nmods++;
}
}
return nmods;
}
public int arity() {
return args.size();
}
/**
* An unmodifiable view of the list of arguments (aka children) of this
* node.
*
* Note: The view is not modifiable in order to preserve the contract that
* {@link #mutation()} will be invoked if there is change in the state of
* this {@link BOp}.
*/
final public List args() {
return new NotifyingList(this);
}
/**
* Class provides notice on mutation events.
*/
static private class NotifyingList implements List {
private final ModifiableBOpBase bop;
private final List delegate;
@SuppressWarnings("unchecked")
NotifyingList(final ModifiableBOpBase bop) {
this(bop, (List) bop.args);
}
NotifyingList(final ModifiableBOpBase bop, final List subList) {
this.bop = bop;
this.delegate = subList;
}
private boolean mutation(boolean modified) {
if(modified) {
bop.mutation();
}
return modified;
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public boolean contains(Object o) {
return delegate.contains(o);
}
public Iterator iterator() {
return new NotifyingListIterator(bop, delegate.listIterator());
}
public Object[] toArray() {
return delegate.toArray();
}
public T1[] toArray(T1[] a) {
return delegate.toArray(a);
}
public boolean add(T e) {
return mutation(delegate.add(e));
}
public boolean remove(Object o) {
return mutation(delegate.remove(o));
}
public boolean containsAll(Collection> c) {
return delegate.containsAll(c);
}
public boolean addAll(Collection extends T> c) {
return mutation (delegate.addAll(c));
}
public boolean addAll(int index, Collection extends T> c) {
return mutation(delegate.addAll(index, c));
}
public boolean removeAll(Collection> c) {
return mutation(delegate.removeAll(c));
}
public boolean retainAll(Collection> c) {
return mutation(delegate.retainAll(c));
}
public void clear() {
delegate.clear();
bop.mutation();
}
public boolean equals(Object o) {
return delegate.equals(o);
}
public int hashCode() {
return delegate.hashCode();
}
public T get(int index) {
return delegate.get(index);
}
public T set(int index, T element) {
final T ret = delegate.set(index, element);
bop.mutation();
return ret;
}
public void add(int index, T element) {
delegate.add(index, element);
bop.mutation();
}
public T remove(int index) {
final T ret = delegate.remove(index);
bop.mutation();
return ret;
}
public int indexOf(Object o) {
return delegate.indexOf(o);
}
public int lastIndexOf(Object o) {
return delegate.lastIndexOf(o);
}
public ListIterator listIterator() {
return new NotifyingListIterator(bop, delegate.listIterator());
}
public ListIterator listIterator(int index) {
return new NotifyingListIterator(bop, delegate.listIterator(index));
}
public List subList(int fromIndex, int toIndex) {
return new NotifyingList(bop, delegate.subList(fromIndex,
toIndex));
}
}
/**
* Iterator with mutation notify pattern.
*/
private static class NotifyingListIterator implements ListIterator {
private final ModifiableBOpBase bop;
private final ListIterator delegate;
NotifyingListIterator(ModifiableBOpBase bop,
ListIterator listIterator) {
this.bop = bop;
this.delegate = listIterator;
}
public boolean hasNext() {
return delegate.hasNext();
}
public T next() {
return delegate.next();
}
public boolean hasPrevious() {
return delegate.hasPrevious();
}
public T previous() {
return delegate.previous();
}
public int nextIndex() {
return delegate.nextIndex();
}
public int previousIndex() {
return delegate.previousIndex();
}
public void remove() {
delegate.remove();
bop.mutation();
}
public void set(T e) {
delegate.set(e);
bop.mutation();
}
public void add(T e) {
delegate.add(e);
bop.mutation();
}
}
/**
* {@inheritDoc}
*
* Note: This {@link Iterator} supports removal.
*/
final public Iterator argIterator() {
return new NotifyingList(this, args).iterator();
}
// shallow copy
public BOp[] toArray() {
return args.toArray(new BOp[args.size()]);
}
// shallow copy
public T[] toArray(final T[] a) {
return args.toArray(a);
}
// @SuppressWarnings("unchecked")
// public T getProperty(final String name) {
//
// return (T) annotations.get(name);
//
// }
public Object getProperty(final String name) {
return annotations.get(name);
}
// public 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;
//
// }
/**
* Copy all annotations from the caller's map.
*
* @param anns
* The annotations to be copied.
*
* @return this
*/
public ModifiableBOpBase copyAll(final Map anns) {
annotations.putAll(anns);
return this;
}
/**
* Set the named property value (destructive mutation).
*
* @param name
* The name.
* @param value
* The new value.
*
* @return this.
*/
public ModifiableBOpBase setProperty(final String name, final Object value) {
if (value == null) {
if (annotations.remove(name) != null) {
mutation();
}
} else {
annotations.put(name, value);
mutation();
}
return this;
}
/**
* Conditionally set the named property iff it is not bound (destructive
* mutation).
*
* @param name
* The name.
* @param value
* The value.
*
* @return this.
*
* @throws IllegalStateException
* if the property is already set.
*/
public ModifiableBOpBase setUnboundProperty(final String name, final Object value) {
final Object oldValue = getProperty(name);
if (oldValue != null)
throw new IllegalStateException("Already set: name=" + name
+ ", value=" + oldValue);
setProperty(name, value);
return this;
}
/**
* Clear the named annotation (destructive mutation).
*
* @param name
* The annotation.
*
* @return this.
*/
public ModifiableBOpBase clearProperty(final String name) {
if (name == null)
throw new IllegalArgumentException();
if (annotations.remove(name) != null) {
mutation();
}
return this;
}
/**
* Unconditionally set the {@link Annotations#BOP_ID}.
*
* @param id
* The id.
*/
public void setId(final int id) {
setProperty(Annotations.BOP_ID, id);
}
// public ModifiableBOpBase deepCopy() {
//
// final ModifiableBOpBase bop = (ModifiableBOpBase) this.clone();
//
// deepCopyArgs(bop.args);
//
// deepCopyAnnotations(bop.annotations);
//
// return bop;
//
// }
//
// /**
// * Deep copy the arguments.
// *
// * 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.
// */
// private void deepCopyArgs() {
// final ArrayList tmp = new ArrayList(args);
//
// 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.
// *
// * 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 deepCopy(final Map a) {
// if (a == BOp.NOANNS) {
// // Fast past for immutable, empty annotations.
// return a;
// }
// // allocate map.
// final Map t = new LinkedHashMap(a
// .size());
// // copy map's entries.
// final Iterator> itr = a.entrySet().iterator();
// while (itr.hasNext()) {
// final Map.Entry 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;
// }
}