/**
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.rdf.sparql.ast;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.bigdata.bop.BOp;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.ModifiableBOpBase;
import com.bigdata.rdf.sparql.ast.eval.AST2BOpBase;
/**
* Base class for AST group nodes.
*/
public abstract class GroupNodeBase<E extends IGroupMemberNode> extends
GroupMemberNodeBase<E> implements IGroupNode<E> {
/**
*
*/
private static final long serialVersionUID = 1L;
interface Annotations extends GroupMemberNodeBase.Annotations,
IJoinNode.Annotations {
}
/**
* Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
*/
public GroupNodeBase(final GroupNodeBase<E> op) {
super(op);
}
/**
* Required shallow copy constructor.
*/
public GroupNodeBase(final BOp[] args, final Map<String, Object> anns) {
super(args, anns);
}
// /**
// * Note: Uses the default for the "optional" annotation.
// */
protected GroupNodeBase() {
}
// protected GroupNodeBase(final boolean optional) {
//
// setOptional( optional );
//
// }
@SuppressWarnings({ "unchecked", "rawtypes" })
public Iterator<E> iterator() {
return (Iterator) argIterator();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<E> getChildren() {
return (List) args();
}
// /**
// * {@inheritDoc}
// * <p>
// * Force the maintenance of the parent reference on the children.
// */
// @Override
// protected void mutation() {
// super.mutation();
// final int arity = arity();
// for (int i = 0; i < arity; i++) {
// final BOp child = get(i);
// ((E) child).setParent((IGroupNode<IGroupMemberNode>) this);
// }
// }
/**
* {@inheritDoc}
* <p>
* Overridden to set the parent reference on the child.
*/
@SuppressWarnings("unchecked")
@Override
public ModifiableBOpBase setArg(final int index, final BOp newArg) {
super.setArg(index, newArg);
((E) newArg).setParent((IGroupNode<IGroupMemberNode>) this);
return this;
}
/**
* {@inheritDoc}
* <p>
* Overridden to set the parent reference on the child
*/
@SuppressWarnings("unchecked")
@Override
public void addArg(final BOp newArg) {
super.addArg(newArg);
((E) newArg).setParent((IGroupNode<IGroupMemberNode>) this);
}
/**
* {@inheritDoc}
* <p>
* Overridden to set the parent reference on the child
*/
@SuppressWarnings("unchecked")
@Override
public void addArg(final int index, final BOp newArg) {
super.addArg(index, newArg);
((E) newArg).setParent((IGroupNode<IGroupMemberNode>) this);
}
/**
* {@inheritDoc}
* <p>
* Overridden to clear the parent reference on the child.
*/
@SuppressWarnings("unchecked")
@Override
public boolean removeArg(final BOp child) {
if (super.removeArg(child)) {
((E) child).setParent(null);
return true;
}
return false;
}
@Override
public IGroupNode<E> addChild(final E child) {
addArg((BOp) child);
// assert child.getParent() == this;
return this;
}
@Override
public IGroupNode<E> removeChild(final E child) {
removeArg((BOp) child);
// assert child.getParent() == null;
return this;
}
@Override
public boolean isEmpty() {
return arity() == 0;
}
@Override
public int size() {
return arity();
}
final public List<FilterNode> getAttachedJoinFilters() {
@SuppressWarnings("unchecked")
final List<FilterNode> filters = (List<FilterNode>) getProperty(Annotations.FILTERS);
if (filters == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(filters);
}
final public void setAttachedJoinFilters(final List<FilterNode> filters) {
setProperty(Annotations.FILTERS, filters);
}
/**
* {@inheritDoc}
* <p>
* Overridden to also clone the children and then set the parent reference
* on the cloned children.
*/
@Override
public GroupNodeBase<E> clone() {
@SuppressWarnings("unchecked")
final GroupNodeBase<E> tmp = (GroupNodeBase<E>) super.clone();
final int size = size();
for (int i = 0; i < size; i++) {
IGroupMemberNode aChild = (IGroupMemberNode) tmp.get(i);
aChild = (IGroupMemberNode) ((ASTBase) aChild).clone();
tmp.setArg(i, (ASTBase) aChild);
}
return tmp;
}
/**
* Simple but robust version of to-String
*/
@Override
public String toString(final int indent) {
final String s = indent(indent);
final StringBuilder sb = new StringBuilder();
sb.append("\n").append(s).append(getClass().getSimpleName());
if (this instanceof IJoinNode) {
final IJoinNode joinNode = (IJoinNode) this;
if (joinNode.isOptional())
sb.append(" [optional]");
if (joinNode.isMinus())
sb.append(" [minus]");
}
if (this instanceof JoinGroupNode) {
final JoinGroupNode joinGroup = (JoinGroupNode) this;
if (joinGroup.getContext() != null) {
sb.append(" [context=" + joinGroup.getContext() + "]");
}
if (joinGroup.getProperty(JoinGroupNode.Annotations.OPTIMIZER) != null) {
// Show non-default value.
sb.append(" [" + JoinGroupNode.Annotations.OPTIMIZER + "="
+ joinGroup.getQueryOptimizer() + "]");
}
}
if (this instanceof GraphPatternGroup) {
final GraphPatternGroup<?> t = (GraphPatternGroup<?>) this;
final IVariable<?>[] joinVars = t.getJoinVars();
if (joinVars != null)
sb.append(" [joinVars=" + Arrays.toString(joinVars) + "]");
final IVariable<?>[] projectInVars = t.getProjectInVars();
if (projectInVars != null)
sb.append(" [projectInVars=" + Arrays.toString(projectInVars) + "]");
}
sb.append(" {");
for (IQueryNode n : this) {
if(n instanceof AssignmentNode) {
/*
* Note: Otherwise no newline before an AssignmentNode since
* also used in a ProjectionNode.
*/
sb.append("\n");
}
sb.append(n.toString(indent + 1));
if (((IGroupMemberNode) n).getParent() != this) {
sb.append(" : ERROR : parent not [this]");
throw new RuntimeException("Parent not this: child=" + n
+ ", this=" + toShortString() + ", but parent="
+ ((IGroupMemberNode) n).getParent());
}
}
sb.append("\n").append(s).append("}");
if (this instanceof GraphPatternGroup) {
final IVariable<?>[] joinVars = ((GraphPatternGroup<?>) this)
.getJoinVars();
if (joinVars != null && joinVars.length > 0) {
sb.append(" JOIN ON (");
boolean first = true;
for (IVariable<?> var : joinVars) {
if (!first)
sb.append(",");
sb.append(var);
first = false;
}
sb.append(")");
}
}
final List<FilterNode> filters = getAttachedJoinFilters();
if(!filters.isEmpty()) {
for (FilterNode filter : filters) {
sb.append(filter.toString(indent + 1));
}
}
if (getProperty(AST2BOpBase.Annotations.ESTIMATED_CARDINALITY) != null) {
sb.append(" AST2BOpBase.estimatedCardinality=");
sb.append(getProperty(AST2BOpBase.Annotations.ESTIMATED_CARDINALITY).toString());
}
if (getQueryHints() != null && !getQueryHints().isEmpty()) {
sb.append("\n");
sb.append(indent(indent));
sb.append(Annotations.QUERY_HINTS);
sb.append("=");
sb.append(getQueryHints().toString());
}
return sb.toString();
}
/**
* {@inheritDoc}
* <p>
* Overridden to set the parent reference on the new child and clear the
* parent reference on the old child.
*/
@SuppressWarnings("unchecked")
@Override
public int replaceWith(final BOp oldChild, final BOp newChild) {
final int i = super.replaceWith(oldChild, newChild);
if (i > 0) {
if (((E) oldChild).getParent() == this) {
((E) oldChild).setParent(null);
}
((E) newChild).setParent((IGroupNode<IGroupMemberNode>) this);
}
return i;
}
}