/** * Copyright (c) 2016, All Contributors (see CONTRIBUTORS file) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.eventsourcing.queries; import com.eventsourcing.Entity; import com.eventsourcing.EntityHandle; import com.googlecode.cqengine.attribute.Attribute; import com.googlecode.cqengine.attribute.SimpleAttribute; import com.googlecode.cqengine.query.Query; import com.googlecode.cqengine.query.option.QueryOptions; import com.googlecode.cqengine.query.simple.SimpleQuery; import lombok.Getter; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; public class Scoped<O extends Entity> extends SimpleQuery<EntityHandle<O>, Boolean> { @Getter private final Query<EntityHandle<O>> scope; @Getter private final Query<EntityHandle<O>> query; public Scoped(Query<EntityHandle<O>> scope, Query<EntityHandle<O>> query) { super(new SimpleAttribute<EntityHandle<O>, Boolean>() { @Override public Boolean getValue(EntityHandle<O> object, QueryOptions queryOptions) { return false; } }); this.scope = scope; this.query = query; } @Override protected boolean matchesSimpleAttribute(SimpleAttribute<EntityHandle<O>, Boolean> attribute, EntityHandle<O> object, QueryOptions queryOptions) { return matchesNonSimpleAttribute(attribute, object, queryOptions); } @Override protected boolean matchesNonSimpleAttribute(Attribute<EntityHandle<O>, Boolean> attribute, EntityHandle<O> object, QueryOptions queryOptions) { if (!scope.matches(object, queryOptions)) { return false; } Iterable<EntityHandle<O>> iterable = queryOptions.get(Iterable.class); Map<Object, Object> options = new HashMap<>(queryOptions.getOptions()); options.put(Iterable.class, new FilteringIterable<>(scope, iterable, queryOptions)); return query.matches(object, new QueryOptions(options)); } @Override protected int calcHashCode() { return scope.hashCode() + 31 * query.hashCode(); } private static class FilteringIterable<O extends Entity> implements Iterable<EntityHandle<O>> { private final Query<EntityHandle<O>> scope; private final Iterable<EntityHandle<O>> iterable; private final QueryOptions queryOptions; public FilteringIterable( Query<EntityHandle<O>> scope, Iterable<EntityHandle<O>> iterable, QueryOptions queryOptions) { this.scope = scope; this.iterable = iterable; this.queryOptions = queryOptions; } @Override public Iterator<EntityHandle<O>> iterator() { return new FilteringIterator<>(this, scope, iterable, queryOptions); } private static class FilteringIterator<O extends Entity> implements Iterator<EntityHandle<O>> { private final Query<EntityHandle<O>> scope; private final Iterable<EntityHandle<O>> iterable; private final QueryOptions queryOptions; private Iterator<EntityHandle<O>> iterator; private EntityHandle<O> next; public FilteringIterator(FilteringIterable<O> filteringIterable, Query<EntityHandle<O>> scope, Iterable<EntityHandle<O>> iterable, QueryOptions queryOptions) { this.scope = scope; this.iterable = iterable; this.queryOptions = new QueryOptions(new HashMap<>(queryOptions.getOptions())); this.queryOptions.put(Iterable.class, filteringIterable); } private void prepareIterator() { if (iterator == null) { iterator = iterable.iterator(); } } @Override public boolean hasNext() { prepareIterator(); if (!iterator.hasNext()) { next = null; return false; } do { next = iterator.next(); if (scope.matches(next, queryOptions)) { return true; } } while (iterator.hasNext()); next = null; return false; } @Override public EntityHandle<O> next() { prepareIterator(); if (next == null && !hasNext()) { throw new NoSuchElementException(); } EntityHandle<O> result = next; next = null; return result; } } } }