/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.capedwarf.datastore.query; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Query; import com.google.common.collect.ForwardingIterator; import org.infinispan.query.CacheQuery; import org.infinispan.query.FetchOptions; import org.infinispan.query.ResultIterator; import org.jboss.capedwarf.datastore.EntityUtils; /** * @author <a href="mailto:mluksa@redhat.com">Marko Luksa</a> * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ public class EntityLoader { private static enum Type { NONE, KEYS_ONLY, PROJECTIONS } private final Query query; private final CacheQuery cacheQuery; public EntityLoader(Query query, CacheQuery cacheQuery) { this.query = query; this.cacheQuery = cacheQuery; } public List<Object> getList() { boolean conversionNeeded = mustConvertResultsToEntities(); Set<EntityWrapper> distinct = (getType() == Type.PROJECTIONS && query.getDistinct() ? new HashSet<EntityWrapper>() : null); List<Object> results = cacheQuery.list(); List<Object> list = new ArrayList<Object>(results.size()); for (Object result : results) { if (conversionNeeded) { Entity entity = Projections.convertToEntity(query, result); if (distinct == null || distinct.add(new EntityWrapper(entity))) { list.add(entity); } } else { list.add(EntityUtils.cloneEntity((Entity) result)); } } return list; } public Iterator<Object> getIterator(Integer chunkSize) { // TODO -- in-memory distinct if (query.getDistinct()) { return getList().iterator(); } final Iterator<Object> iterator; if (chunkSize == null) { iterator = toClosingIterator(cacheQuery.iterator()); } else if (chunkSize == Integer.MAX_VALUE) { iterator = cacheQuery.list().iterator(); } else { iterator = toClosingIterator(cacheQuery.iterator(new FetchOptions().fetchSize(chunkSize))); } if (mustConvertResultsToEntities()) { return new WrappingIterator(iterator); } else { return new CloningIterator(iterator); } } private Iterator<Object> toClosingIterator(ResultIterator iterator) { return new ClosingIterator(iterator); } private Type getType() { if (query.isKeysOnly()) { return Type.KEYS_ONLY; } else if (query.getProjections().size() > 0) { return Type.PROJECTIONS; } else { return Type.NONE; } } private boolean mustConvertResultsToEntities() { return (getType() != Type.NONE); } private class WrappingIterator implements Iterator<Object> { private final Iterator iterator; public WrappingIterator(Iterator iterator) { this.iterator = iterator; } public boolean hasNext() { return iterator.hasNext(); } public Object next() { return Projections.convertToEntity(query, iterator.next()); } public void remove() { iterator.remove(); } } private class CloningIterator extends ForwardingIterator<Object> { private final Iterator<Object> iterator; public CloningIterator(Iterator<Object> iterator) { this.iterator = iterator; } @Override protected Iterator<Object> delegate() { return iterator; } public Object next() { Entity entity = (Entity) iterator.next(); return EntityUtils.cloneEntity(entity); } } private static class ClosingIterator extends ForwardingIterator<Object> { private final ResultIterator delegate; private ClosingIterator(ResultIterator delegate) { this.delegate = delegate; } protected Iterator<Object> delegate() { return delegate; } protected void finalize() throws Throwable { try { delegate.close(); // any better way to do this? } catch (Throwable t) { System.err.println(t); } finally { super.finalize(); } } } private static class EntityWrapper { private Entity entity; private EntityWrapper(Entity entity) { this.entity = entity; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EntityWrapper that = (EntityWrapper) o; if (!entity.getProperties().equals(that.entity.getProperties())) return false; return true; } @Override public int hashCode() { return entity.getProperties().hashCode(); } } }