package net.enilink.komma.em.internal.query;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import net.enilink.commons.iterator.IExtendedIterator;
import net.enilink.composition.mappers.RoleMapper;
import net.enilink.komma.core.IBindings;
import net.enilink.komma.core.IBooleanResult;
import net.enilink.komma.core.IGraphResult;
import net.enilink.komma.core.IQuery;
import net.enilink.komma.core.IReferenceable;
import net.enilink.komma.core.IStatement;
import net.enilink.komma.core.ITupleResult;
import net.enilink.komma.core.IValue;
import net.enilink.komma.core.LockModeType;
import net.enilink.komma.core.NoResultException;
import net.enilink.komma.core.NonUniqueResultException;
import net.enilink.komma.core.URI;
import net.enilink.komma.dm.IDataManagerQuery;
import net.enilink.komma.em.internal.IEntityManagerInternal;
import com.google.inject.Inject;
public class Query<R> extends QueryBase<IQuery<R>> implements IQuery<R> {
protected IEntityManagerInternal manager;
private WeakHashMap<IExtendedIterator<?>, Object> opened = new WeakHashMap<IExtendedIterator<?>, Object>(
4);
protected IDataManagerQuery<?> query;
@Inject
RoleMapper<URI> roleMapper;
public Query(IEntityManagerInternal manager, IDataManagerQuery<?> query) {
this.manager = manager;
this.query = query;
}
public void close() {
for (IExtendedIterator<?> c : opened.keySet()) {
c.close();
}
}
public IExtendedIterator<R> evaluate() {
return evaluateQuery(null, resultInfos);
}
public <T> IExtendedIterator<T> evaluate(Class<T> resultType,
Class<?>... resultTypes) {
// special case where a binding set should be returned as IBindings or
// as Object[]
if (IBindings.class.isAssignableFrom(resultType)
|| resultType.isArray()) {
return evaluateQuery(resultType, resultInfos);
}
ResultInfo resultInfo = new ResultInfo(false, new ArrayList<Class<?>>());
resultInfo.types.add(resultType);
for (Class<?> type : resultTypes) {
resultInfo.types.add(type);
}
Map<String, ResultInfo> resultInfos;
if (this.resultInfos == null) {
resultInfos = Collections.singletonMap((String) null, resultInfo);
} else {
resultInfos = new HashMap<String, ResultInfo>(this.resultInfos);
resultInfos.put(null, resultInfo);
}
return evaluateQuery(resultType, resultInfos);
}
@SuppressWarnings("unchecked")
private <T> IExtendedIterator<T> evaluateQuery(Class<T> resultType,
Map<String, ResultInfo> resultInfos) {
IExtendedIterator<?> iter;
IExtendedIterator<?> result = query.evaluate();
int max = maxResults <= 0 ? 0 : maxResults + firstResult;
if (result instanceof ITupleResult) {
List<String> names = ((ITupleResult<?>) result).getBindingNames();
if (resultType != null
&& IBindings.class.isAssignableFrom(resultType)
|| (resultType == null && names.size() > 1)) {
// returns an iterator of IBindings objects
// this is the default behavior in case of multiple bindings
iter = new TupleBindingsIterator(manager,
(ITupleResult<IBindings<IValue>>) result, max,
resultInfos);
} else if (resultType != null && resultType.isArray()) {
// returns an iterator of Object[]
iter = new TupleArrayIterator(manager,
(ITupleResult<IBindings<IValue>>) result, max,
resultInfos);
} else {
ResultInfo info = null;
if (resultInfos != null) {
// only one binding is selected in the projection clause,
// try to use the information about this binding
if (names.size() == 1) {
info = resultInfos.get(names.get(0));
}
// use info for all bindings as fallback
if (info == null) {
info = resultInfos.get(null);
}
}
iter = new ProjectedTupleIterator(manager,
(ITupleResult<IBindings<IValue>>) result, max, info);
}
} else if (result instanceof IGraphResult) {
if (resultType == null || IStatement.class.equals(resultType)) {
iter = new GraphIterator(manager, (IGraphResult) result, max,
resultInfos == null
|| !resultInfos.get(null).typeRestricted);
} else {
iter = new ProjectedGraphIterator(manager,
(IGraphResult) result, max,
resultInfos != null ? resultInfos.get(null) : null);
}
} else {
iter = new BooleanIterator(((IBooleanResult) result).asBoolean());
}
opened.put(iter, Boolean.TRUE);
// skip elements if limit is used
if (firstResult > 0) {
for (int i = 0; i < firstResult && iter.hasNext(); i++) {
iter.next();
}
}
return (IExtendedIterator<T>) iter;
}
public <T> IExtendedIterator<T> evaluateRestricted(Class<T> resultType,
Class<?>... resultTypes) {
ResultInfo resultInfo = new ResultInfo(true, new ArrayList<Class<?>>());
resultInfo.types.add(resultType);
for (Class<?> type : resultTypes) {
resultInfo.types.add(type);
}
return evaluateQuery(resultType,
Collections.singletonMap((String) null, resultInfo));
}
@Override
public boolean getBooleanResult() {
Object result = getSingleResult();
return Boolean.TRUE.equals(result);
}
@Override
public Map<String, Object> getProperties() {
return query.getProperties();
}
@Override
public LockModeType getLockMode() {
return LockModeType.NONE;
}
public List<R> getResultList() {
return evaluate().toList();
}
public R getSingleResult() {
return getSingleResult(null);
}
@SuppressWarnings("unchecked")
public <T> T getSingleResult(Class<T> resultType) {
IExtendedIterator<?> iter = resultType != null ? evaluate(resultType)
: evaluateQuery(null, resultInfos);
try {
if (!iter.hasNext())
throw new NoResultException("No results");
T result = (T) iter.next();
if (iter.hasNext())
throw new NonUniqueResultException("More than one result");
return result;
} finally {
iter.close();
}
}
@Override
public Set<String> getSupportedProperties() {
return query.getSupportedProperties();
}
private void doSetParameter(String name, IValue value) {
query.setParameter(name, value);
}
public IQuery<R> setProperty(String propertyName, Object value) {
query.setProperty(propertyName, value);
return this;
}
@Override
public IQuery<R> setLockMode(LockModeType lockMode) {
return this;
}
public IQuery<R> setParameter(String name, Object value) {
if (value == null) {
doSetParameter(name, null);
} else {
if (value instanceof IReferenceable) {
value = ((IReferenceable) value).getReference();
}
doSetParameter(name, value instanceof IValue ? (IValue) value
: manager.toValue(value));
}
return this;
}
public IQuery<R> setTypeParameter(String name, Class<?> concept) {
doSetParameter(name, roleMapper.findType(concept));
return this;
}
@Override
public String toString() {
if (query == null) {
return super.toString();
}
return query.toString();
}
@Override
public <T> IQuery<T> bindResultType(Class<T> resultType,
Class<?>... resultTypes) {
return super.<IQuery<T>> doBindResultType(resultType, resultTypes);
}
@Override
public <T> IQuery<T> restrictResultType(Class<T> resultType,
Class<?>... resultTypes) {
return super.<IQuery<T>> doRestrictResultType(resultType, resultTypes);
}
}