/*
* Copyright (C) 2016 Mobile Jazz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mobilejazz.cacheio;
import com.mobilejazz.cacheio.query.Query;
import rx.Single;
import rx.functions.Func1;
import java.util.*;
import java.util.concurrent.*;
import static com.mobilejazz.cacheio.helper.Preconditions.checkArgument;
import static com.mobilejazz.cacheio.helper.Preconditions.checkIsEmpty;
public class StringKeyedRxRepository<M extends HasId<String>, Q extends Query>
implements RxRepository<String, M, Q> {
private Builder<M, Q> proto;
public StringKeyedRxRepository(Builder<M, Q> proto) {
this.proto = proto;
}
@Override public Single<List<M>> find(Q query) {
checkArgument(query, "Query == null");
return proto.queryCache.get(query).flatMap(new Func1<StringList, Single<List<M>>>() {
@Override public Single<List<M>> call(StringList stringIdList) {
final List<String> ids =
stringIdList == null ? Collections.<String>emptyList() : stringIdList.getIds();
Single<Map<String, M>> valueLookup = proto.cache.getAll(ids);
return valueLookup.map(new Func1<Map<String, M>, List<M>>() {
@Override public List<M> call(Map<String, M> map) {
List<M> values = new ArrayList<>(map.size());
for (String id : ids) {
values.add(map.get(id));
}
return values;
}
});
}
});
}
@Override public Single<M> findById(String id) {
checkIsEmpty(id, "Id == null OR empty");
return proto.cache.get(id);
}
@Override public Single<List<M>> put(Q query, List<M> models) {
checkArgument(query, "Query == null");
checkArgument(models, "Models == null");
Map<String, M> values = new HashMap<>(models.size());
final List<String> keys = new ArrayList<>(models.size());
for (M model : models) {
String id = model.getId();
keys.add(id);
values.put(id, model);
}
// Save the query with the ids
StringList idList = new StringList(keys);
proto.queryCache.put(query, idList, Long.MAX_VALUE, TimeUnit.DAYS)
.toObservable()
.toBlocking()
.first();
// Save the values with the ids
Single<Map<String, M>> results = proto.cache.putAll(values, Long.MAX_VALUE, TimeUnit.DAYS);
return results.map(new Func1<Map<String, M>, List<M>>() {
@Override public List<M> call(Map<String, M> values) {
List<M> models = new ArrayList<>(values.size());
for (String key : keys) {
models.add(values.get(key));
}
return models;
}
});
}
@Override public Single<M> put(M model) {
checkArgument(model, "Model == null");
return proto.cache.put(model.getId(), model, Long.MAX_VALUE, TimeUnit.DAYS);
}
@Override public Single<String> removeById(String id) {
checkIsEmpty(id, "Id == null OR empty");
return proto.cache.remove(id);
}
@Override public Single<Collection<String>> removeByQuery(Q query) {
checkArgument(query, "Query == null");
return proto.queryCache.get(query)
.flatMap(new Func1<StringList, Single<? extends Collection<String>>>() {
@Override public Single<? extends Collection<String>> call(StringList stringList) {
return proto.cache.removeAll(stringList.getIds());
}
});
}
public static final class Builder<M extends HasId<String>, Q extends Query> {
private RxCache<String, M> cache;
private RxCache<Q, StringList> queryCache;
public Builder() {
}
public Builder(Builder<M, Q> proto) {
this.cache = proto.cache;
this.queryCache = proto.queryCache;
}
public Builder<M, Q> setCache(RxCache<String, M> cache) {
this.cache = cache;
return this;
}
public Builder<M, Q> setQueryCache(RxCache<Q, StringList> queryCache) {
this.queryCache = queryCache;
return this;
}
public StringKeyedRxRepository<M, Q> build() {
checkArgument(cache, "cache cannot be null");
checkArgument(queryCache, "query cache cannot be null");
return new StringKeyedRxRepository<>(new Builder<>(this));
}
}
}