/**
* Copyright 2004-2016 Riccardo Solmi. All rights reserved.
* This file is part of the Whole Platform.
*
* The Whole Platform is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Whole Platform 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whole.lang.queries.iterators;
import java.util.Collections;
import java.util.NoSuchElementException;
import java.util.Set;
import org.whole.lang.bindings.AbstractFilterScope;
import org.whole.lang.bindings.BindingManagerFactory;
import org.whole.lang.bindings.IBindingManager;
import org.whole.lang.exceptions.WholeIllegalStateException;
import org.whole.lang.iterators.AbstractLazyCloneableIterator;
import org.whole.lang.iterators.IEntityIterator;
import org.whole.lang.matchers.Matcher;
import org.whole.lang.model.IEntity;
import org.whole.lang.operations.ICloneContext;
import org.whole.lang.visitors.MissingVariableException;
/**
* @author Riccardo Solmi
*/
public class SelectIterator<E extends IEntity> extends AbstractLazyCloneableIterator<E> {
private AbstractFilterScope lookaheadScope;
private IEntityIterator<?> fromIterator;
private IEntityIterator<E> selectIterator;
private IEntityIterator<?> whereIterator;
private boolean lazyCloneSelect, lazyCloneWhere;
private E nextEntity = null;
private E lastEntity;
private Set<String> namesToBind = Collections.emptySet();
private boolean withNamesComplement;
protected SelectIterator(IEntityIterator<E> selectIterator, IEntityIterator<?> fromIterator, IEntityIterator<? extends IEntity> whereIterator) {
this.selectIterator = selectIterator;
this.fromIterator = fromIterator;
this.whereIterator = whereIterator;
}
@Override
public IEntityIterator<E> clone(ICloneContext cc) {
SelectIterator<E> iterator = (SelectIterator<E>) super.clone(cc);
iterator.fromIterator = cc.clone(fromIterator);
iterator.lazyCloneSelect = lazyCloneSelect = iterator.lazyCloneWhere = lazyCloneWhere = true;
return iterator;
}
protected boolean isLazyCloneEmpty() {
return !lazyCloneSelect && !lazyCloneWhere;
}
public IEntityIterator<?> getFromIterator() {
return fromIterator;
}
public IEntityIterator<E> getSelectIterator() {
if (lazyCloneSelect) {
selectIterator = getCloneContext().clone(selectIterator);
lazyCloneSelect = false;
updateCloneContext();
selectIterator.setBindings(getBindings());
}
return selectIterator;
}
public IEntityIterator<?> getWhereIterator() {
if (lazyCloneWhere) {
whereIterator = getCloneContext().clone(whereIterator);
lazyCloneWhere = false;
updateCloneContext();
whereIterator.setBindings(getBindings());
}
return whereIterator;
}
public Set<String> namesToBind() {
return namesToBind;
}
public SelectIterator<E> withNamesToBind(Set<String> namesToBind) {
this.namesToBind = namesToBind;
return this;
}
public boolean withNamesComplement() {
return withNamesComplement;
}
public SelectIterator<E> withNamesComplement(boolean withNamesComplement) {
this.withNamesComplement = withNamesComplement;
return this;
}
public boolean hasNext() {
return lookahead() != null;
}
public E lookahead() {
if (nextEntity != null)
return nextEntity;
IEntity selfEntityOld = getBindings().wGet("self");
lookaheadScope().setFilterEnabled(false);
clearLookaheadScope();
IEntity selfEntity = getFromIterator().lookahead();
if (selfEntity == null) {
lookaheadScope().setFilterEnabled(true);
return null;
}
getBindings().wEnterScope(lookaheadScope(), true);
getSelectIterator().reset(selfEntity);
lookaheadScope().wAddAll(getFromIterator().lookaheadScope());
Set<String> filterNames = lookaheadScope().getFilterNames();
for (String name : getFromIterator().lookaheadScope().wLocalNames())
filterNames.add(name);
lookaheadScope().wDef("self", selfEntity);
filterNames.add("self");
try {
nextEntity = getSelectIterator().next();
} catch (NoSuchElementException e) {
throw new WholeIllegalStateException("Select clause must return a value", e).withSourceEntity(getSourceEntity()).withBindings(getBindings());
}
lookaheadScope().wAddAll(getSelectIterator().lookaheadScope());
//enable select names hidden by from iterator
filterNames.removeAll(getSelectIterator().lookaheadScope().wLocalNames());
applyWhereClause(nextEntity, selfEntity, getBindings());
filterNames.addAll(getWhereIterator().lookaheadScope().wLocalNames());
lookaheadScope().setFilterEnabled(true);
getBindings().wExitScope();
if (!Matcher.removeVars(nextEntity, namesToBind(), withNamesComplement())) {
Set<String> unboundedNames = Matcher.vars(nextEntity, false);
if (withNamesComplement())
unboundedNames.removeAll(namesToBind());
else
unboundedNames.retainAll(namesToBind());
throw new MissingVariableException("Unbounded names in select clause: ", null, unboundedNames.toArray(new String[unboundedNames.size()])).withSourceEntity(getSourceEntity()).withBindings(getBindings());
}
if (selfEntityOld != null)
getBindings().wDef("self", selfEntityOld);
return nextEntity;
}
protected void applyWhereClause(E pattern, IEntity selfEntity, IBindingManager bindings) {
getWhereIterator().reset(selfEntity);
bindings.wDef("self", selfEntity);
for (IEntity e : getWhereIterator()) {
bindings.wDef("self", e);
Matcher.substitute(pattern, bindings, true);
bindings.wDef("self", selfEntity);
}
}
public E next() {
E result = lookahead();
if (result == null)
throw new NoSuchElementException();
getBindings().wEnterScope();
getFromIterator().next();
getBindings().wExitScope();
getBindings().wAddAll(lookaheadScope());
nextEntity = null;
return lastEntity = result;
}
public void reset(IEntity entity) {
getFromIterator().reset(entity);
clearLookaheadScope();
lastEntity = null;
nextEntity = null;
}
protected void setChildrenBindings(IBindingManager bindings) {
super.setChildrenBindings(bindings);
fromIterator.setBindings(bindings);
if (!lazyCloneSelect)
selectIterator.setBindings(bindings);
if (!lazyCloneWhere)
whereIterator.setBindings(bindings);
}
public AbstractFilterScope lookaheadScope() {
if (lookaheadScope == null)
lookaheadScope = BindingManagerFactory.instance.createExcludeFilterSimpleScope();
return lookaheadScope;
}
protected void clearLookaheadScope() {
if (lookaheadScope != null) {
for (String name : lookaheadScope.wLocalNames())
getBindings().wUnset(name);
lookaheadScope.wClear();
lookaheadScope.getFilterNames().clear();
}
}
public void prune() {
}
public void add(E entity) {
throw new UnsupportedOperationException();
}
public void set(E entity) {
if (lastEntity == null)
throw new IllegalStateException();
lastEntity.wGetParent().wSet(lastEntity, entity);
lastEntity = entity;
}
public void remove() {
if (lastEntity == null)
throw new IllegalStateException();
lastEntity.wGetParent().wRemove(lastEntity);
lastEntity = null;
}
@Override
public void toString(StringBuilder sb) {
sb.append("select ");
sb.append(selectIterator);
sb.append(" from ");
sb.append(fromIterator);
sb.append(" where ");
sb.append(whereIterator);
}
}