Merge pull request #22 from TouchInstinct/new_storable

New storable
This commit is contained in:
Gavriil 2017-03-09 17:25:02 +03:00 committed by GitHub
commit 3549b33924
28 changed files with 639 additions and 1595 deletions

View File

@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'me.tatarka.retrolambda'
android {
compileSdkVersion 24
compileSdkVersion 25
buildToolsVersion '25.0.2'
compileOptions {
@ -12,11 +12,11 @@ android {
defaultConfig {
minSdkVersion 9
targetSdkVersion 24
targetSdkVersion 25
}
}
dependencies {
provided 'com.android.support:support-annotations:24.2.1'
provided 'com.android.support:support-annotations:25.2.0'
provided 'io.reactivex:rxandroid:1.2.1'
}

View File

@ -41,6 +41,8 @@ public class ConsoleLogProcessor extends LogProcessor {
}
@Override
@SuppressWarnings("WrongConstant")
//WrongConstant: level.getPriority() is not wrong constant!
public void processLogMessage(@NonNull final LcGroup group, @NonNull final LcLevel level,
@NonNull final String tag, @NonNull final String message, @Nullable final Throwable throwable) {
final String messageToLog = normalize(message + (throwable != null ? '\n' + Log.getStackTraceString(throwable) : ""));
@ -50,7 +52,6 @@ public class ConsoleLogProcessor extends LogProcessor {
newline = newline != -1 ? newline : length;
do {
final int end = Math.min(newline, i + MAX_LOG_LENGTH);
//noinspection WrongConstant
Log.println(level.getPriority(), tag, messageToLog.substring(i, end));
i = end;
}

View File

@ -264,7 +264,9 @@ public final class Lc {
public static void printStackTrace(@NonNull final String tag) {
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
Log.d(tag, TextUtils.join("\n", Arrays.copyOfRange(stackTrace, STACK_TRACE_CODE_DEPTH, stackTrace.length)));
if (Log.isLoggable(tag, Log.DEBUG)) {
Log.d(tag, TextUtils.join("\n", Arrays.copyOfRange(stackTrace, STACK_TRACE_CODE_DEPTH, stackTrace.length)));
}
}
private Lc() {

View File

@ -15,8 +15,8 @@ public class ObservableResult<T> {
@NonNull
private final List<T> items = new LinkedList<>();
@NonNull
private final List<Throwable> errors = new LinkedList<>();
@Nullable
private Throwable error;
/**
* Passes item to collect.
@ -33,7 +33,7 @@ public class ObservableResult<T> {
* @param error Emitted error.
*/
public void onError(@NonNull final Throwable error) {
errors.add(error);
this.error = error;
}
/**
@ -47,13 +47,13 @@ public class ObservableResult<T> {
}
/**
* Returns list of collected errors.
* Returns collected error.
*
* @return Errors.
* @return Error.
*/
@NonNull
public List<Throwable> getErrors() {
return new ArrayList<>(errors);
@Nullable
public Throwable getError() {
return error;
}
}

View File

@ -0,0 +1,214 @@
/**
* Copyright 2014 Netflix, Inc.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 ru.touchin.roboswag.core.observables;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import rx.Observable.OnSubscribe;
import rx.Scheduler;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.observables.ConnectableObservable;
import rx.schedulers.Schedulers;
import rx.subscriptions.CompositeSubscription;
import rx.subscriptions.Subscriptions;
/**
* Returns an observable sequence that stays connected to the source as long as
* there is at least one subscription to the observable sequence and also it stays connected
* for cache time after everyone unsubscribe.
*
* @param <T> the value type
*/
@SuppressWarnings({"PMD.AvoidUsingVolatile", "PMD.CompareObjectsWithEquals"})
//AvoidUsingVolatile,CompareObjectsWithEquals: from OnSubscribeRefCount code
public final class OnSubscribeRefCountWithCacheTime<T> implements OnSubscribe<T> {
@NonNull
private final ConnectableObservable<? extends T> source;
@NonNull
private volatile CompositeSubscription baseSubscription = new CompositeSubscription();
@NonNull
private final AtomicInteger subscriptionCount = new AtomicInteger(0);
@NonNull
private final Scheduler scheduler = Schedulers.computation();
private final long cacheTime;
@NonNull
private final TimeUnit cacheTimeUnit;
@Nullable
private Scheduler.Worker worker;
/**
* Use this lock for every subscription and disconnect action.
*/
@NonNull
private final ReentrantLock lock = new ReentrantLock();
public OnSubscribeRefCountWithCacheTime(@NonNull final ConnectableObservable<? extends T> source,
final long cacheTime, @NonNull final TimeUnit cacheTimeUnit) {
this.source = source;
this.cacheTime = cacheTime;
this.cacheTimeUnit = cacheTimeUnit;
}
@Override
public void call(@NonNull final Subscriber<? super T> subscriber) {
lock.lock();
if (subscriptionCount.incrementAndGet() == 1) {
if (worker != null) {
worker.unsubscribe();
worker = null;
}
final AtomicBoolean writeLocked = new AtomicBoolean(true);
try {
// need to use this overload of connect to ensure that
// baseSubscription is set in the case that source is a
// synchronous Observable
source.connect(onSubscribe(subscriber, writeLocked));
} finally {
// need to cover the case where the source is subscribed to
// outside of this class thus preventing the Action1 passed
// to source.connect above being called
if (writeLocked.get()) {
// Action1 passed to source.connect was not called
lock.unlock();
}
}
} else {
try {
// ready to subscribe to source so do it
doSubscribe(subscriber, baseSubscription);
} finally {
// release the read lock
lock.unlock();
}
}
}
@NonNull
private Action1<Subscription> onSubscribe(@NonNull final Subscriber<? super T> subscriber,
@NonNull final AtomicBoolean writeLocked) {
return new Action1<Subscription>() {
@Override
public void call(@NonNull final Subscription subscription) {
try {
baseSubscription.add(subscription);
// ready to subscribe to source so do it
doSubscribe(subscriber, baseSubscription);
} finally {
// release the write lock
lock.unlock();
writeLocked.set(false);
}
}
};
}
private void doSubscribe(@NonNull final Subscriber<? super T> subscriber, @NonNull final CompositeSubscription currentBase) {
subscriber.add(disconnect(currentBase));
source.unsafeSubscribe(new Subscriber<T>(subscriber) {
@Override
public void onError(@NonNull final Throwable throwable) {
cleanup();
subscriber.onError(throwable);
}
@Override
public void onNext(@Nullable final T item) {
subscriber.onNext(item);
}
@Override
public void onCompleted() {
cleanup();
subscriber.onCompleted();
}
private void cleanup() {
// on error or completion we need to unsubscribe the base subscription
// and set the subscriptionCount to 0
lock.lock();
try {
if (baseSubscription == currentBase) {
if (worker != null) {
worker.unsubscribe();
worker = null;
}
baseSubscription.unsubscribe();
baseSubscription = new CompositeSubscription();
subscriptionCount.set(0);
}
} finally {
lock.unlock();
}
}
});
}
@NonNull
private Subscription disconnect(@NonNull final CompositeSubscription current) {
return Subscriptions.create(new Action0() {
@Override
public void call() {
lock.lock();
try {
if (baseSubscription == current && subscriptionCount.decrementAndGet() == 0) {
if (worker != null) {
worker.unsubscribe();
} else {
worker = scheduler.createWorker();
}
worker.schedule(new Action0() {
@Override
public void call() {
lock.lock();
try {
if (subscriptionCount.get() == 0) {
baseSubscription.unsubscribe();
// need a new baseSubscription because once
// unsubscribed stays that way
worker.unsubscribe();
worker = null;
baseSubscription = new CompositeSubscription();
}
} finally {
lock.unlock();
}
}
}, cacheTime, cacheTimeUnit);
}
} finally {
lock.unlock();
}
}
});
}
}

View File

@ -1,41 +0,0 @@
package ru.touchin.roboswag.core.observables;
import android.support.annotation.NonNull;
import java.util.concurrent.CountDownLatch;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import rx.Observable;
/**
* Created by Gavriil Sitnikov on 21/05/2016.
* Some helper methods to work with JavaRx.
*/
public final class RxUtils {
/**
* Subscribes to specific {@link Observable} and waits for it's onCompleted event
* and then returns {@link ObservableResult} with all collected items and errors during subscription.
* You should NOT use such method normally. It is safer than {@link Observable#toBlocking()} but it is also like a hack.
*
* @param observable {@link Observable} to be executed;
* @param <T> Type of {@link Observable}'s items;
* @return {@link ObservableResult} which contains all items and errors collected during execution.
*/
@NonNull
public static <T> ObservableResult<T> executeSync(@NonNull final Observable<T> observable) {
final ObservableResult<T> result = new ObservableResult<>();
final CountDownLatch waiter = new CountDownLatch(1);
observable.subscribe(result::onNext, result::onError, waiter::countDown);
try {
waiter.await();
} catch (final InterruptedException exception) {
throw new ShouldNotHappenException(exception);
}
return result;
}
private RxUtils() {
}
}

View File

@ -22,6 +22,8 @@ package ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.reflect.Type;
/**
* Created by Gavriil Sitnikov on 04/10/2015.
* Interface that is providing logic to convert value from specific type to type allowed to store in {@link Store} object and back.
@ -32,29 +34,29 @@ import android.support.annotation.Nullable;
public interface Converter<TObject, TStoreObject> {
/**
* Converts specific object of objectClass to object of storeObjectClass allowed to store.
* Converts specific object of objectType to object of storeObjectClass allowed to store.
*
* @param objectClass Class of object;
* @param storeObjectClass Class of store object allowed to store;
* @param objectType Type of object;
* @param storeObjectType Type of store object allowed to store;
* @param object Object to be converted to store object;
* @return Object that is allowed to store into specific {@link Store};
* @throws ConversionException Exception during conversion. Usually it indicates illegal state.
*/
@Nullable
TStoreObject toStoreObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TObject object)
TStoreObject toStoreObject(@NonNull Type objectType, @NonNull Type storeObjectType, @Nullable TObject object)
throws ConversionException;
/**
* Converts specific store object of storeObjectClass to object of objectClass.
* Converts specific store object of storeObjectClass to object of objectType.
*
* @param objectClass Class of object;
* @param storeObjectClass Class of store object allowed to store;
* @param objectType Type of object;
* @param storeObjectType Type of store object allowed to store;
* @param storeObject Object from specific {@link Store};
* @return Object converted from store object;
* @throws ConversionException Exception during conversion. Usually it indicates illegal state.
*/
@Nullable
TObject toObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TStoreObject storeObject)
TObject toObject(@NonNull Type objectType, @NonNull Type storeObjectType, @Nullable TStoreObject storeObject)
throws ConversionException;
class ConversionException extends Exception {

View File

@ -20,11 +20,15 @@
package ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
import rx.Completable;
import rx.Observable;
import rx.Single;
import rx.exceptions.OnErrorThrowable;
/**
* Created by Gavriil Sitnikov on 06/10/2015.
* Object that allows to migrate some store objects from one version to another by migrators passed into constructor.
@ -51,60 +55,102 @@ public class Migration<TKey> {
this.migrators = Arrays.asList(migrators);
}
private long loadCurrentVersion(@NonNull final TKey key) throws MigrationException {
final Long result;
try {
result = versionsStore.loadObject(Long.class, key);
} catch (final Store.StoreException throwable) {
throw new MigrationException(String.format("Can't get version of '%s' from %s", key, versionsStore), throwable);
}
return result != null ? result : DEFAULT_VERSION;
@NonNull
private Single<Long> loadCurrentVersion(@NonNull final TKey key) {
return versionsStore.loadObject(Long.class, key)
.map(version -> version != null ? version : DEFAULT_VERSION)
.onErrorResumeNext(throwable
-> Single.error(new MigrationException(String.format("Can't get version of '%s' from %s", key, versionsStore), throwable)));
}
private void checkMigrationResult(@NonNull final TKey key, final long oldVersion, final long currentVersion, @Nullable final Migrator migrator)
throws MigrationException {
if (oldVersion > currentVersion) {
throw new MigrationException(String.format("Version of '%s' downgraded from %s to %s [from %s by %s]",
key, oldVersion, currentVersion, versionsStore, migrator));
}
if (currentVersion > latestVersion) {
throw new MigrationException(String.format("Version of '%s' is %s and higher than latest version %s [from %s by %s]",
key, oldVersion, currentVersion, versionsStore, migrator));
}
if (oldVersion == currentVersion && migrator != null) {
throw new MigrationException(String.format("Version of '%s' is %s and stood same [from %s by %s]",
key, currentVersion, versionsStore, migrator));
@NonNull
private Single<Long> makeMigrationChain(@NonNull final TKey key, @NonNull final VersionUpdater versionUpdater) {
Single<Long> chain = Single.fromCallable(() -> versionUpdater.initialVersion);
for (final Migrator<TKey, ?, ?> migrator : migrators) {
chain = chain.flatMap(updatedVersion ->
migrator.canMigrate(key, updatedVersion)
.flatMap(canMigrate -> canMigrate
? migrator.migrate(key, updatedVersion)
.doOnSuccess(newVersion
-> versionUpdater.updateVersion(newVersion, latestVersion, migrator))
: Single.just(updatedVersion)));
}
return chain;
}
/**
* Migrates some object by key to latest version.
*
* @param key Key of object to migrate;
* @throws MigrationException Exception during object migration. Usually it indicates illegal state.
* @param key Key of object to migrate.
*/
public void migrateToLatestVersion(@NonNull final TKey key) throws MigrationException {
long currentVersion = loadCurrentVersion(key);
while (currentVersion != latestVersion) {
final long oldVersion = currentVersion;
for (final Migrator<TKey, ?, ?> migrator : migrators) {
if (migrator.supportsMigrationFor(currentVersion) && migrator.canMigrate(key, currentVersion)) {
currentVersion = migrator.migrate(key, currentVersion);
checkMigrationResult(key, oldVersion, currentVersion, migrator);
}
checkMigrationResult(key, oldVersion, currentVersion, null);
}
}
try {
versionsStore.storeObject(Long.class, key, latestVersion);
} catch (final Store.StoreException throwable) {
throw new MigrationException(String.format("Can't store version %s of '%s' into %s", key, currentVersion, versionsStore), throwable);
}
@NonNull
public Completable migrateToLatestVersion(@NonNull final TKey key) {
return loadCurrentVersion(key)
.flatMap(currentVersion -> {
final VersionUpdater versionUpdater = new VersionUpdater<>(key, versionsStore, currentVersion);
return makeMigrationChain(key, versionUpdater)
.doOnSuccess(lastUpdatedVersion -> {
if (lastUpdatedVersion < latestVersion) {
throw OnErrorThrowable.from(new NextLoopMigrationException());
}
if (versionUpdater.initialVersion == versionUpdater.oldVersion) {
throw new MigrationException(String.format("Version of '%s' not updated from %s",
key, versionUpdater.initialVersion));
}
})
.retryWhen(attempts -> attempts.switchMap(throwable -> throwable instanceof NextLoopMigrationException
? Observable.just(null) : Observable.error(throwable)));
})
.toCompletable()
.andThen(versionsStore.storeObject(Long.class, key, latestVersion))
.onErrorResumeNext(throwable -> {
if (throwable instanceof MigrationException) {
return Completable.error(throwable);
}
return Completable.error(new MigrationException(String.format("Can't migrate '%s'", key), throwable));
});
}
public static class MigrationException extends Exception {
private static class VersionUpdater<TKey> {
@NonNull
private final TKey key;
@NonNull
private final Store versionsStore;
private long oldVersion;
private long initialVersion;
public VersionUpdater(@NonNull final TKey key, @NonNull final Store versionsStore, final long initialVersion) {
this.key = key;
this.versionsStore = versionsStore;
this.oldVersion = initialVersion;
this.initialVersion = initialVersion;
}
public void updateVersion(final long updateVersion, final long latestVersion, @NonNull final Migrator migrator) {
if (initialVersion > updateVersion) {
throw new MigrationException(String.format("Version of '%s' downgraded from %s to %s [from %s by %s]",
key, initialVersion, updateVersion, versionsStore, migrator));
}
if (updateVersion > latestVersion) {
throw new MigrationException(
String.format("Version of '%s' is %s and higher than latest version %s [from %s by %s]",
key, initialVersion, updateVersion, versionsStore, migrator));
}
if (updateVersion == initialVersion) {
throw new MigrationException(String.format("Update version of '%s' equals current version '%s' [from %s by %s]",
key, updateVersion, versionsStore, migrator));
}
oldVersion = initialVersion;
initialVersion = updateVersion;
}
}
private static class NextLoopMigrationException extends Exception {
}
public static class MigrationException extends RuntimeException {
public MigrationException(@NonNull final String message) {
super(message);

View File

@ -21,6 +21,8 @@ package ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import rx.Single;
/**
* Created by Gavriil Sitnikov on 05/10/2015.
* Abstract class of objects which are able to migrate some values from one version to another.
@ -52,49 +54,44 @@ public abstract class Migrator<TKey, TOldStoreObject, TNewStoreObject> {
public abstract boolean supportsMigrationFor(long version);
/**
* Returns if specific object with key of specific version could be migrated by this migrator.
* Returns {@link Single} that emits if specific object with key of specific version could be migrated by this migrator.
*
* @param key Key of object to migrate;
* @param version Current version of object;
* @return True if object with such key and version could be migrated;
* @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state.
* @return {@link Single} that emits true if object with such key and version could be migrated.
*/
public boolean canMigrate(@NonNull final TKey key, final long version) throws Migration.MigrationException {
try {
return oldStore.contains(key);
} catch (final Store.StoreException exception) {
throw new Migration.MigrationException("Version " + version + " not supported by " + this, exception);
}
@NonNull
public Single<Boolean> canMigrate(@NonNull final TKey key, final long version) {
return supportsMigrationFor(version) ? oldStore.contains(key) : Single.just(false);
}
/**
* Migrates object with specific key from some version to migrator's version.
* Single that migrates object with specific key from some version to migrator's version.
*
* @param key Key of object to migrate;
* @param version Current version of object;
* @return New version of object after migration process;
* @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state.
* @return {@link Single} that emits new version of object after migration process.
*/
public long migrate(@NonNull final TKey key, final long version) throws Migration.MigrationException {
if (!supportsMigrationFor(version)) {
throw new Migration.MigrationException(String.format("Version %s of '%s' is not supported by %s", version, key, this));
}
return migrateInternal(key, version, oldStore, newStore);
@NonNull
public Single<Long> migrate(@NonNull final TKey key, final long version) {
return supportsMigrationFor(version)
? migrateInternal(key, version, oldStore, newStore)
: Single.error(new Migration.MigrationException(String.format("Version %s of '%s' is not supported by %s", version, key, this)));
}
/**
* Internal migration logic specified by implementation.
* Single that represents internal migration logic specified by implementation.
*
* @param key Key of object to migrate;
* @param version Current version of object;
* @param oldStore Old store of object;
* @param newStore new store of object;
* @return New version of object after migration process;
* @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state.
* @return {@link Single} that emits new version of object after migration process.
*/
protected abstract long migrateInternal(@NonNull TKey key,
final long version,
@NonNull Store<TKey, TOldStoreObject> oldStore,
@NonNull Store<TKey, TNewStoreObject> newStore) throws Migration.MigrationException;
@NonNull
protected abstract Single<Long> migrateInternal(@NonNull TKey key,
long version,
@NonNull Store<TKey, TOldStoreObject> oldStore,
@NonNull Store<TKey, TNewStoreObject> newStore);
}

View File

@ -1,43 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Created by Gavriil Sitnikov on 04/10/2015.
* Interface that is providing logic to convert value from specific type to type allowed to store in {@link Store} object and back.
* The only difference is that it is not throwing exceptions during conversion. It is safe to convert objects with it.
*
* @param <TObject> Type of original objects;
* @param <TStoreObject> Type of objects in store.
*/
public interface SafeConverter<TObject, TStoreObject> extends Converter<TObject, TStoreObject> {
@Nullable
@Override
TStoreObject toStoreObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TObject object);
@Nullable
@Override
TObject toObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TStoreObject storeObject);
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Created by Gavriil Sitnikov on 04/10/2015.
* Interface that is providing access to abstract object which can store (e.g. in file, database, remote store)
* some type of objects (e.g. String, byte[], Integer) by key.
* The only difference is that it is not throwing exceptions during access to store. It is safe to work with such store.
*
* @param <TKey> Type of keys for values;
* @param <TStoreObject> Type of values stored in store.
*/
public interface SafeStore<TKey, TStoreObject> extends Store<TKey, TStoreObject> {
@Override
boolean contains(@NonNull TKey key);
@Override
void storeObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key, @Nullable TStoreObject storeObject);
@Nullable
@Override
TStoreObject loadObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key);
}

View File

@ -3,22 +3,24 @@ package ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.reflect.Type;
/**
* Simple safe converter that is doing nothing on conversion.
*
* @param <T> Same type.
*/
public class SameTypesConverter<T> implements SafeConverter<T, T> {
public class SameTypesConverter<T> implements Converter<T, T> {
@Nullable
@Override
public T toStoreObject(@NonNull final Class<T> class1, @NonNull final Class<T> class2, @Nullable final T object) {
public T toStoreObject(@NonNull final Type type1, @NonNull final Type type2, @Nullable final T object) {
return object;
}
@Nullable
@Override
public T toObject(@NonNull final Class<T> class1, @NonNull final Class<T> class2, @Nullable final T object) {
public T toObject(@NonNull final Type type1, @NonNull final Type type2, @Nullable final T object) {
return object;
}

View File

@ -22,18 +22,18 @@ package ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import ru.touchin.roboswag.core.log.LcGroup;
import ru.touchin.roboswag.core.observables.ObservableResult;
import ru.touchin.roboswag.core.observables.RxUtils;
import ru.touchin.roboswag.core.observables.storable.builders.MigratableStorableBuilder;
import ru.touchin.roboswag.core.observables.OnSubscribeRefCountWithCacheTime;
import ru.touchin.roboswag.core.observables.storable.builders.NonNullStorableBuilder;
import ru.touchin.roboswag.core.observables.storable.builders.SafeStorableBuilder;
import ru.touchin.roboswag.core.utils.ObjectUtils;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import rx.Completable;
import rx.Observable;
import rx.Scheduler;
import rx.exceptions.OnErrorThrowable;
import rx.functions.Actions;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
@ -54,12 +54,25 @@ public class Storable<TKey, TObject, TStoreObject> {
public static final LcGroup STORABLE_LC_GROUP = new LcGroup("STORABLE");
private static final long DEFAULT_CACHE_TIME_MILLIS = TimeUnit.SECONDS.toMillis(5);
@NonNull
private static ObserveStrategy getDefaultObserveStrategyFor(@NonNull final Type objectType, @NonNull final Type storeObjectType) {
if (objectType instanceof Class && ObjectUtils.isSimpleClass((Class) objectType)) {
return ObserveStrategy.CACHE_ACTUAL_VALUE;
}
if (objectType instanceof Class && ObjectUtils.isSimpleClass((Class) storeObjectType)) {
return ObserveStrategy.CACHE_STORE_VALUE;
}
return ObserveStrategy.NO_CACHE;
}
@NonNull
private final TKey key;
@NonNull
private final Class<TObject> objectClass;
private final Type objectType;
@NonNull
private final Class<TStoreObject> storeObjectClass;
private final Type storeObjectType;
@NonNull
private final Store<TKey, TStoreObject> store;
@NonNull
@ -67,113 +80,103 @@ public class Storable<TKey, TObject, TStoreObject> {
@NonNull
private final PublishSubject<TStoreObject> newStoreValueEvent = PublishSubject.create();
@NonNull
private final Observable<TStoreObject> storeValueObservable;
@NonNull
private final Observable<TObject> valueObservable;
@NonNull
private final Scheduler scheduler;
public Storable(@NonNull final BuilderCore<TKey, TObject, TStoreObject> builderCore) {
this(builderCore.key, builderCore.objectClass, builderCore.storeObjectClass,
this(builderCore.key, builderCore.objectType, builderCore.storeObjectType,
builderCore.store, builderCore.converter, builderCore.observeStrategy,
builderCore.migration, builderCore.defaultValue, builderCore.storeScheduler);
builderCore.migration, builderCore.defaultValue, builderCore.storeScheduler, builderCore.cacheTimeMillis);
}
public Storable(@NonNull final TKey key,
@NonNull final Class<TObject> objectClass,
@Nullable final Class<TStoreObject> storeObjectClass,
@Nullable final Store<TKey, TStoreObject> store,
@Nullable final Converter<TObject, TStoreObject> converter,
@Nullable final ObserveStrategy observeStrategy,
@Nullable final Migration<TKey> migration,
@Nullable final TObject defaultValue,
@Nullable final Scheduler storeScheduler) {
if (storeObjectClass == null || store == null || converter == null) {
throw new ShouldNotHappenException();
}
@SuppressWarnings("PMD.ExcessiveParameterList")
//ExcessiveParameterList: that's why we are using builder to create it
private Storable(@NonNull final TKey key,
@NonNull final Type objectType,
@NonNull final Type storeObjectType,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter,
@Nullable final ObserveStrategy observeStrategy,
@Nullable final Migration<TKey> migration,
@Nullable final TObject defaultValue,
@Nullable final Scheduler storeScheduler,
final long cacheTimeMillis) {
this.key = key;
this.objectClass = objectClass;
this.storeObjectClass = storeObjectClass;
this.objectType = objectType;
this.storeObjectType = storeObjectType;
this.store = store;
this.converter = converter;
final ObserveStrategy nonNullObserveStrategy = observeStrategy != null ? observeStrategy : getDefaultGetStrategy();
final Observable<TStoreObject> storeValueObservable
= createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue, storeScheduler);
valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy, storeScheduler);
}
@NonNull
private ObserveStrategy getDefaultGetStrategy() {
if (ObjectUtils.isSimpleClass(objectClass)) {
return ObserveStrategy.CACHE_ACTUAL_VALUE;
}
if (ObjectUtils.isSimpleClass(storeObjectClass)) {
return ObserveStrategy.CACHE_STORE_VALUE;
}
return ObserveStrategy.NO_CACHE;
final ObserveStrategy nonNullObserveStrategy
= observeStrategy != null ? observeStrategy : getDefaultObserveStrategyFor(objectType, storeObjectType);
scheduler = storeScheduler != null ? storeScheduler : Schedulers.from(Executors.newSingleThreadExecutor());
storeValueObservable
= createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue, cacheTimeMillis);
valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy, cacheTimeMillis);
}
@Nullable
private TStoreObject returnDefaultValueIfNull(@Nullable final TStoreObject storeObject, @Nullable final TObject defaultValue) {
if (storeObject == null && defaultValue != null) {
try {
return converter.toStoreObject(objectClass, storeObjectClass, defaultValue);
} catch (final Converter.ConversionException exception) {
STORABLE_LC_GROUP.w(exception, "Exception while converting default value of '%s' from '%s' from store %s",
key, defaultValue, store);
throw OnErrorThrowable.from(exception);
} catch (final RuntimeException throwable) {
STORABLE_LC_GROUP.assertion(throwable);
}
if (storeObject != null || defaultValue == null) {
return storeObject;
}
try {
return converter.toStoreObject(objectType, storeObjectType, defaultValue);
} catch (final Converter.ConversionException exception) {
STORABLE_LC_GROUP.w(exception, "Exception while converting default value of '%s' from '%s' from store %s",
key, defaultValue, store);
throw OnErrorThrowable.from(exception);
} catch (final RuntimeException throwable) {
STORABLE_LC_GROUP.assertion(throwable);
throw OnErrorThrowable.from(throwable);
}
return storeObject;
}
@NonNull
private Observable<TStoreObject> createStoreValueObservable(@NonNull final ObserveStrategy observeStrategy,
@Nullable final Migration<TKey> migration,
@Nullable final TObject defaultValue,
@Nullable final Scheduler storeScheduler) {
final Observable<TStoreObject> result = Observable
.<TStoreObject>create(subscriber -> {
try {
if (migration != null) {
migration.migrateToLatestVersion(key);
}
subscriber.onNext(store.loadObject(storeObjectClass, key));
subscriber.onCompleted();
} catch (final Store.StoreException storeException) {
STORABLE_LC_GROUP.w(storeException, "Exception while trying to get value of '%s' from store %s", key, store);
subscriber.onError(storeException);
} catch (final Migration.MigrationException migrationException) {
STORABLE_LC_GROUP.assertion(migrationException);
subscriber.onError(migrationException);
} catch (final RuntimeException throwable) {
final long cacheTimeMillis) {
final Observable<TStoreObject> result = (migration != null
? migration.migrateToLatestVersion(key).subscribeOn(scheduler)
: Completable.complete())
.andThen(store.loadObject(storeObjectType, key).toObservable().subscribeOn(scheduler))
.doOnError(throwable -> {
if (throwable instanceof RuntimeException) {
STORABLE_LC_GROUP.assertion(throwable);
} else {
STORABLE_LC_GROUP.w(throwable, "Exception while trying to load value of '%s' from store %s", key, store);
}
})
.subscribeOn(storeScheduler != null ? storeScheduler : Schedulers.io())
.concatWith(newStoreValueEvent)
.map(storeObject -> returnDefaultValueIfNull(storeObject, defaultValue));
return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE ? result.replay(1).refCount() : result;
return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE
? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS))
: result;
}
@NonNull
private Observable<TObject> createValueObservable(@NonNull final Observable<TStoreObject> storeValueObservable,
@NonNull final ObserveStrategy observeStrategy,
@Nullable final Scheduler storeScheduler) {
final long cacheTimeMillis) {
final Observable<TObject> result = storeValueObservable
.map(storeObject -> {
try {
return converter.toObject(objectClass, storeObjectClass, storeObject);
} catch (final Converter.ConversionException exception) {
STORABLE_LC_GROUP.w(exception, "Exception while converting value of '%s' from '%s' from store %s",
key, storeObject, store);
throw OnErrorThrowable.from(exception);
} catch (final RuntimeException throwable) {
STORABLE_LC_GROUP.assertion(throwable);
throw OnErrorThrowable.from(throwable);
}
})
.subscribeOn(storeScheduler != null ? storeScheduler : Schedulers.computation());
return observeStrategy == ObserveStrategy.CACHE_ACTUAL_VALUE ? result.replay(1).refCount() : result;
.switchMap(storeObject -> Observable
.fromCallable(() -> converter.toObject(objectType, storeObjectType, storeObject))
.subscribeOn(scheduler)
.doOnError(throwable -> {
if (throwable instanceof RuntimeException) {
STORABLE_LC_GROUP.assertion(throwable);
} else {
STORABLE_LC_GROUP.w(throwable, "Exception while trying to converting value of '%s' from store %s by %s",
key, storeObject, store, converter);
}
}));
return observeStrategy == ObserveStrategy.CACHE_ACTUAL_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE
? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS))
: result;
}
/**
@ -187,23 +190,23 @@ public class Storable<TKey, TObject, TStoreObject> {
}
/**
* Returns class of actual object.
* Returns type of actual object.
*
* @return Class of actual object.
* @return Type of actual object.
*/
@NonNull
public Class<TObject> getObjectClass() {
return objectClass;
public Type getObjectType() {
return objectType;
}
/**
* Returns class of store object.
* Returns type of store object.
*
* @return Class of store object.
* @return Type of store object.
*/
@NonNull
public Class<TStoreObject> getStoreObjectClass() {
return storeObjectClass;
public Type getStoreObjectType() {
return storeObjectType;
}
/**
@ -226,68 +229,81 @@ public class Storable<TKey, TObject, TStoreObject> {
return converter;
}
@NonNull
private Completable internalSet(@Nullable final TObject newValue, final boolean checkForEqualityBeforeSet) {
return (checkForEqualityBeforeSet ? storeValueObservable.first() : Observable.just(null))
.switchMap(oldStoreValue -> Observable
.fromCallable(() -> converter.toStoreObject(objectType, storeObjectType, newValue))
.subscribeOn(scheduler)
.switchMap(newStoreValue -> {
if (checkForEqualityBeforeSet && ObjectUtils.equals(newStoreValue, oldStoreValue)) {
return Observable.empty();
}
return store.storeObject(storeObjectType, key, newStoreValue)
.doOnCompleted(() -> {
newStoreValueEvent.onNext(newStoreValue);
if (checkForEqualityBeforeSet) {
STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, oldStoreValue, newStoreValue);
} else {
STORABLE_LC_GROUP.i("Value of '%s' force changed to '%s'", key, newStoreValue);
}
})
.toObservable();
}))
.doOnError(throwable -> {
if (throwable instanceof RuntimeException) {
STORABLE_LC_GROUP.assertion(throwable);
} else {
STORABLE_LC_GROUP.w(throwable, "Exception while trying to store value of '%s' from store %s by %s",
key, newValue, store, converter);
}
})
.toCompletable();
}
/**
* Creates observable which is async setting value to store.
* NOTE: It could emit ONLY completed and errors events. It is not providing onNext event!
* Errors won't be emitted if {@link #getStore()} implements {@link SafeStore} and {@link #getConverter()} implements {@link SafeConverter}.
* It is not checking if stored value equals new value.
* In result it will be faster to not get value from store and compare but it will emit item to {@link #observe()} subscribers.
* NOTE: It could emit ONLY completed and errors events. It is not providing onNext event! //TODO: it's Completable :(
*
* @param newValue Value to set;
* @return Observable of setting process.
*/
@NonNull
public Observable<?> forceSet(@Nullable final TObject newValue) {
return internalSet(newValue, false).toObservable();
}
/**
* Creates observable which is async setting value to store.
* It is checking if stored value equals new value.
* In result it will take time to get value from store and compare
* but it won't emit item to {@link #observe()} subscribers if stored value equals new value.
* NOTE: It could emit ONLY completed and errors events. It is not providing onNext event! //TODO: it's Completable :(
*
* @param newValue Value to set;
* @return Observable of setting process.
*/
@NonNull
public Observable<?> set(@Nullable final TObject newValue) {
return valueObservable
.first()
.switchMap(value -> ObjectUtils.equals(value, newValue)
? Observable.empty()
: Observable
.<TStoreObject>create(subscriber -> {
try {
final TStoreObject storeObject = converter.toStoreObject(objectClass, storeObjectClass, newValue);
store.storeObject(storeObjectClass, key, storeObject);
newStoreValueEvent.onNext(storeObject);
STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, value, newValue);
subscriber.onCompleted();
} catch (final Converter.ConversionException conversionException) {
STORABLE_LC_GROUP.w(conversionException, "Exception while converting value of '%s' from '%s' to store object",
key, newValue, store);
subscriber.onError(conversionException);
} catch (final Store.StoreException storeException) {
STORABLE_LC_GROUP.w(storeException, "Exception while trying to store value of '%s' to store %s", key, store);
subscriber.onError(storeException);
} catch (final RuntimeException throwable) {
STORABLE_LC_GROUP.assertion(throwable);
}
}));
}
/**
* Setting value in background. If error emits then it will raise assertion.
*
* @param newValue Value to set.
*/
public void setCalm(@Nullable final TObject newValue) {
set(newValue).subscribe(Actions.empty(), STORABLE_LC_GROUP::assertion);
return internalSet(newValue, true).toObservable();
}
/**
* Sets value synchronously. You should NOT use this method normally. Use {@link #set(Object)} asynchronously instead.
*
* @param newValue Value to set;
* @throws Store.StoreException Throws if {@link Store} threw exception during storing;
* @throws Converter.ConversionException Throws if {@link Converter} threw exception during conversion;
* @throws Migration.MigrationException Throws if {@link Migration} threw exception during migration.
*/
public void setSync(@Nullable final TObject newValue)
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
final ObservableResult<?> setResult = RxUtils.executeSync(set(newValue));
checkStorableObservableResult(setResult);
@Deprecated
//deprecation: it should be used for debug only and in very rare cases.
public void setSync(@Nullable final TObject newValue) {
set(newValue).toBlocking().subscribe();
}
/**
* Returns Observable which is emitting item on subscribe and every time when someone have changed value.
* It could emit next and error events but not completed.
* Errors won't be emitted if {@link #getStore()} implements {@link SafeStore} and {@link #getConverter()} implements {@link SafeConverter}.
*
* @return Returns observable of value.
*/
@ -297,9 +313,8 @@ public class Storable<TKey, TObject, TStoreObject> {
}
/**
* Returns Observable which is emitting only one item on subscribe.
* Returns Observable which is emitting only one item on subscribe. //TODO: it's Single :(
* It could emit next and error events but not completed.
* Errors won't be emitted if {@link #getStore()} implements {@link SafeStore} and {@link #getConverter()} implements {@link SafeConverter}.
*
* @return Returns observable of value.
*/
@ -312,38 +327,16 @@ public class Storable<TKey, TObject, TStoreObject> {
* Gets value synchronously. You should NOT use this method normally. Use {@link #get()} or {@link #observe()} asynchronously instead.
*
* @return Returns value;
* @throws Store.StoreException Throws if {@link Store} threw exception during getting from store;
* @throws Converter.ConversionException Throws if {@link Converter} threw exception during conversion;
* @throws Migration.MigrationException Throws if {@link Migration} threw exception during migration.
*/
@Deprecated
//deprecation: it should be used for debug only and in very rare cases.
@Nullable
public TObject getSync()
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
final ObservableResult<TObject> getResult = RxUtils.executeSync(get());
checkStorableObservableResult(getResult);
if (getResult.getItems().size() != 1) {
throw new ShouldNotHappenException();
}
return getResult.getItems().get(0);
}
private void checkStorableObservableResult(@NonNull final ObservableResult<?> result)
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
for (final Throwable throwable : result.getErrors()) {
if (throwable instanceof Store.StoreException) {
throw (Store.StoreException) throwable;
}
if (throwable instanceof Converter.ConversionException) {
throw (Converter.ConversionException) throwable;
}
if (throwable instanceof Migration.MigrationException) {
throw (Migration.MigrationException) throwable;
}
}
public TObject getSync() {
return get().toBlocking().first();
}
/**
* Class that is representing strategy of observing item from store.
* Enum that is representing strategy of observing item from store.
*/
public enum ObserveStrategy {
@ -358,10 +351,19 @@ public class Storable<TKey, TObject, TStoreObject> {
CACHE_STORE_VALUE,
/**
* Caching value so it won't spend time for getting value from {@link #getStore()} and converts it by {@link #getConverter()}.
* Do not use such strategy if object could be big (like byte-array of file).
* But it will take time for getting value from {@link #getStore()} to set value.
* Do not use such strategy if object could be big (like Bitmap or long string).
* Do not use such strategy if object is mutable because multiple subscribers could then change it's state.
*/
CACHE_ACTUAL_VALUE
CACHE_ACTUAL_VALUE,
/**
* Caching value so it won't spend time for getting value from {@link #getStore()} and converts it by {@link #getConverter()}.
* It won't take time or getting value from {@link #getStore()} to set value.
* Do not use such strategy if store object could be big (like byte-array of file).
* Do not use such strategy if object could be big (like Bitmap or long string).
* Do not use such strategy if object is mutable because multiple subscribers could then change it's state.
*/
CACHE_STORE_AND_ACTUAL_VALUE
}
@ -377,112 +379,81 @@ public class Storable<TKey, TObject, TStoreObject> {
@NonNull
protected final TKey key;
@NonNull
protected final Class<TObject> objectClass;
protected final Type objectType;
@NonNull
private final Type storeObjectType;
@NonNull
private final Store<TKey, TStoreObject> store;
@NonNull
private final Converter<TObject, TStoreObject> converter;
@Nullable
private Class<TStoreObject> storeObjectClass;
@Nullable
private Store<TKey, TStoreObject> store;
@Nullable
private Converter<TObject, TStoreObject> converter;
@Nullable
protected final ObserveStrategy observeStrategy;
private ObserveStrategy observeStrategy;
@Nullable
private Migration<TKey> migration;
@Nullable
private TObject defaultValue;
@Nullable
protected final Scheduler storeScheduler;
private Scheduler storeScheduler;
private long cacheTimeMillis;
protected BuilderCore(@NonNull final TKey key,
@NonNull final Class<TObject> objectClass,
@Nullable final ObserveStrategy observeStrategy,
@Nullable final Scheduler storeScheduler) {
this(key, objectClass, null, null, null, observeStrategy, null, null, storeScheduler);
@NonNull final Type objectType,
@NonNull final Type storeObjectType,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter) {
this(key, objectType, storeObjectType, store, converter, null, null, null, null, DEFAULT_CACHE_TIME_MILLIS);
}
protected BuilderCore(@NonNull final BuilderCore<TKey, TObject, TStoreObject> sourceBuilder) {
this(sourceBuilder.key, sourceBuilder.objectClass, sourceBuilder.storeObjectClass,
this(sourceBuilder.key, sourceBuilder.objectType, sourceBuilder.storeObjectType,
sourceBuilder.store, sourceBuilder.converter, sourceBuilder.observeStrategy,
sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler);
sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler, sourceBuilder.cacheTimeMillis);
}
@SuppressWarnings("CPD-START")
//CPD: it is ok that builder copy-pasted parent constructor parameters
@SuppressWarnings({"PMD.ExcessiveParameterList", "CPD-START"})
//CPD: it is same code as constructor of Storable
//ExcessiveParameterList: that's why we are using builder to create it
private BuilderCore(@NonNull final TKey key,
@NonNull final Class<TObject> objectClass,
@Nullable final Class<TStoreObject> storeObjectClass,
@Nullable final Store<TKey, TStoreObject> store,
@Nullable final Converter<TObject, TStoreObject> converter,
@NonNull final Type objectType,
@NonNull final Type storeObjectType,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter,
@Nullable final ObserveStrategy observeStrategy,
@Nullable final Migration<TKey> migration,
@Nullable final TObject defaultValue,
@Nullable final Scheduler storeScheduler) {
@Nullable final Scheduler storeScheduler,
final long cacheTimeMillis) {
this.key = key;
this.objectClass = objectClass;
this.storeObjectClass = storeObjectClass;
this.objectType = objectType;
this.storeObjectType = storeObjectType;
this.store = store;
this.converter = converter;
this.observeStrategy = observeStrategy;
this.migration = migration;
this.defaultValue = defaultValue;
this.storeScheduler = storeScheduler;
this.cacheTimeMillis = cacheTimeMillis;
}
@SuppressWarnings("CPD-END")
@Nullable
public Class<TStoreObject> getStoreObjectClass() {
return storeObjectClass;
protected void setStoreSchedulerInternal(@Nullable final Scheduler storeScheduler) {
this.storeScheduler = storeScheduler;
}
/**
* Returns {@link Store} where store class representation of object is storing.
*
* @return Store.
*/
@Nullable
public Store<TKey, TStoreObject> getStore() {
return store;
}
protected void setStoreInternal(@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter) {
this.storeObjectClass = storeObjectClass;
this.store = store;
this.converter = converter;
}
/**
* Returns {@link Converter} to convert values from store class to actual and back.
*
* @return Converter.
*/
@Nullable
public Converter<TObject, TStoreObject> getConverter() {
return converter;
}
/**
* Returns {@link Migration} to migrate values from specific version to latest version.
*
* @return Migration.
*/
@Nullable
public Migration<TKey> getMigration() {
return migration;
protected void setObserveStrategyInternal(@Nullable final ObserveStrategy observeStrategy) {
this.observeStrategy = observeStrategy;
}
protected void setMigrationInternal(@NonNull final Migration<TKey> migration) {
this.migration = migration;
}
/**
* Returns value which will be returned instead of null.
*
* @return Default value.
*/
protected void setCacheTimeInternal(final long cacheTime, @NonNull final TimeUnit timeUnit) {
this.cacheTimeMillis = timeUnit.toMillis(cacheTime);
}
@Nullable
public TObject getDefaultValue() {
protected TObject getDefaultValue() {
return defaultValue;
}
@ -502,59 +473,49 @@ public class Storable<TKey, TObject, TStoreObject> {
public static class Builder<TKey, TObject, TStoreObject> extends BuilderCore<TKey, TObject, TStoreObject> {
public Builder(@NonNull final TKey key,
@NonNull final Class<TObject> objectClass) {
super(key, objectClass, null, null);
}
public Builder(@NonNull final TKey key,
@NonNull final Class<TObject> objectClass,
@NonNull final ObserveStrategy observeStrategy) {
super(key, objectClass, observeStrategy, null);
}
public Builder(@NonNull final TKey key,
@NonNull final Class<TObject> objectClass,
@NonNull final Scheduler storeScheduler) {
super(key, objectClass, null, storeScheduler);
}
public Builder(@NonNull final TKey key,
@NonNull final Class<TObject> objectClass,
@NonNull final ObserveStrategy observeStrategy,
@NonNull final Scheduler storeScheduler) {
super(key, objectClass, observeStrategy, storeScheduler);
@NonNull final Type objectType,
@NonNull final Type storeObjectType,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter) {
super(key, objectType, storeObjectType, store, converter);
}
/**
* Sets store and converter.
* Sets specific {@link Scheduler} to store/load/convert values on it.
*
* @param storeObjectClass Class of store object,
* @param store Store to store objects into;
* @param converter Converter to convert values from store class to actual class and back;
* @param storeScheduler Scheduler;
* @return Builder that allows to specify other fields.
*/
@NonNull
public Builder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter) {
setStoreInternal(storeObjectClass, store, converter);
public Builder<TKey, TObject, TStoreObject> setStoreScheduler(@Nullable final Scheduler storeScheduler) {
setStoreSchedulerInternal(storeScheduler);
return this;
}
/**
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
* Sets specific {@link ObserveStrategy} to cache value in memory in specific way.
*
* @param storeObjectClass Class of store object,
* @param store Safe store that is not throwing exceptions;
* @param converter Safe converter that is not throwing exceptions;
* @param observeStrategy ObserveStrategy;
* @return Builder that allows to specify other fields.
*/
@NonNull
public SafeStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final SafeStore<TKey, TStoreObject> store,
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
return new SafeStorableBuilder<>(this, storeObjectClass, store, converter);
public Builder<TKey, TObject, TStoreObject> setObserveStrategy(@Nullable final ObserveStrategy observeStrategy) {
setObserveStrategyInternal(observeStrategy);
return this;
}
/**
* Sets cache time for while value that cached by {@link #setObserveStrategy(ObserveStrategy)} will be in memory after everyone unsubscribe.
* It is important for example for cases when user switches between screens and hide/open app very fast.
*
* @param cacheTime Cache time value;
* @param timeUnit Cache time units.
* @return Builder that allows to specify other fields.
*/
@NonNull
public Builder<TKey, TObject, TStoreObject> setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) {
setCacheTimeInternal(cacheTime, timeUnit);
return this;
}
/**
@ -564,8 +525,9 @@ public class Storable<TKey, TObject, TStoreObject> {
* @return Builder that allows to specify other fields.
*/
@NonNull
public MigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
return new MigratableStorableBuilder<>(this, migration);
public Builder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
setMigrationInternal(migration);
return this;
}
/**

View File

@ -22,6 +22,11 @@ package ru.touchin.roboswag.core.observables.storable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.reflect.Type;
import rx.Completable;
import rx.Single;
/**
* Created by Gavriil Sitnikov on 04/10/2015.
* Interface that is providing access to abstract object which can store (e.g. in file, database, remote store)
@ -37,41 +42,28 @@ public interface Store<TKey, TStoreObject> {
*
* @param key Key which is finding in store;
* @return True if key have found in store.
* @throws StoreException Exception during getting access to store.
*/
boolean contains(@NonNull TKey key) throws StoreException;
@NonNull
Single<Boolean> contains(@NonNull TKey key);
/**
* Stores object to store with related key.
*
* @param storeObjectClass Class of object to store;
* @param key Key related to object;
* @param storeObject Object to store;
* @throws StoreException Exception during getting access to store.
* @param storeObjectType Type of object to store;
* @param key Key related to object;
* @param storeObject Object to store;
*/
void storeObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key, @Nullable TStoreObject storeObject) throws StoreException;
@NonNull
Completable storeObject(@NonNull Type storeObjectType, @NonNull TKey key, @Nullable TStoreObject storeObject);
/**
* Loads object from store by key.
*
* @param storeObjectClass Class of object to store;
* @param key Key related to object;
* @param storeObjectType Type of object to store;
* @param key Key related to object;
* @return Object from store found by key;
* @throws StoreException Exception during getting access to store.
*/
@Nullable
TStoreObject loadObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key) throws StoreException;
class StoreException extends Exception {
public StoreException(@NonNull final String message) {
super(message);
}
public StoreException(@NonNull final String message, @NonNull final Throwable throwable) {
super(message, throwable);
}
}
@NonNull
Single<TStoreObject> loadObject(@NonNull Type storeObjectType, @NonNull TKey key);
}

View File

@ -1,107 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.builders;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.core.observables.storable.Converter;
import ru.touchin.roboswag.core.observables.storable.Migration;
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
import ru.touchin.roboswag.core.observables.storable.SafeStore;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.Store;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 15/05/2016.
* Builder that is already contains not null migration.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class MigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
public MigratableStorableBuilder(@NonNull final Storable.Builder<TKey, TObject, TStoreObject> sourceBuilder,
@NonNull final Migration<TKey> migration) {
super(sourceBuilder);
setMigrationInternal(migration);
}
/**
* Sets store and converter.
*
* @param storeObjectClass Class of store object,
* @param store Store to store objects into;
* @param converter Converter to convert values from store class to actual class and back;
* @return Builder that allows to specify other fields.
*/
@NonNull
public MigratableStorableBuilder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter) {
setStoreInternal(storeObjectClass, store, converter);
return this;
}
/**
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
*
* @param storeObjectClass Class of store object,
* @param store Safe store that is not throwing exceptions;
* @param converter Safe converter that is not throwing exceptions;
* @return Builder that allows to specify other fields.
*/
@NonNull
public SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(
@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final SafeStore<TKey, TStoreObject> store,
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
setStoreInternal(storeObjectClass, store, converter);
return new SafeMigratableStorableBuilder<>(this);
}
/**
* Sets value which will be returned instead of null.
*
* @param defaultValue Default value;
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> setDefaultValue(@NonNull final TObject defaultValue) {
setDefaultValueInternal(defaultValue);
return new NonNullMigratableStorableBuilder<>(this);
}
/**
* Building {@link Storable} object.
*
* @return New {@link Storable}.
*/
@NonNull
public Storable<TKey, TObject, TStoreObject> build() {
if (getMigration() == null) {
throw new ShouldNotHappenException();
}
return new Storable<>(this);
}
}

View File

@ -1,92 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.builders;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.core.observables.storable.Converter;
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
import ru.touchin.roboswag.core.observables.storable.SafeStore;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.Store;
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 15/05/2016.
* Builder that is already contains not null migration and default value.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
public NonNullMigratableStorableBuilder(@NonNull final NonNullStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
public NonNullMigratableStorableBuilder(@NonNull final MigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
@NonNull
public NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter) {
setStoreInternal(storeObjectClass, store, converter);
return this;
}
/**
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
*
* @param storeObjectClass Class of store object,
* @param store Safe store that is not throwing exceptions;
* @param converter Safe converter that is not throwing exceptions;
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(
@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final SafeStore<TKey, TStoreObject> store,
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
setStoreInternal(storeObjectClass, store, converter);
return new NonNullSafeMigratableStorableBuilder<>(this);
}
/**
* Building {@link NonNullStorable} object.
*
* @return New {@link NonNullStorable}.
*/
@NonNull
public NonNullStorable<TKey, TObject, TStoreObject> build() {
if (getDefaultValue() == null) {
throw new ShouldNotHappenException();
}
if (getMigration() == null) {
throw new ShouldNotHappenException();
}
return new NonNullStorable<>(this);
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.builders;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
import ru.touchin.roboswag.core.observables.storable.SafeStore;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullSafeStorable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 15/05/2016.
* Builder with safe store and converter inside that is already contains not null migration and default value.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
public NonNullSafeMigratableStorableBuilder(@NonNull final NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
public NonNullSafeMigratableStorableBuilder(@NonNull final NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
public NonNullSafeMigratableStorableBuilder(@NonNull final SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
/**
* Building {@link NonNullSafeStorable} object.
*
* @return New {@link NonNullSafeStorable}.
*/
@NonNull
public NonNullSafeStorable<TKey, TObject, TStoreObject> build() {
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
throw new ShouldNotHappenException();
}
if (getDefaultValue() == null) {
throw new ShouldNotHappenException();
}
if (getMigration() == null) {
throw new ShouldNotHappenException();
}
return new NonNullSafeStorable<>(this);
}
}

View File

@ -1,77 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.builders;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.core.observables.storable.Migration;
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
import ru.touchin.roboswag.core.observables.storable.SafeStore;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullSafeStorable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 15/05/2016.
* Builder with safe store and converter inside that is already contains not null default value.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
public NonNullSafeStorableBuilder(@NonNull final NonNullStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
public NonNullSafeStorableBuilder(@NonNull final SafeStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
/**
* Sets specific {@link Migration} to migrate values from specific version to latest version.
*
* @param migration Migration;
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
setMigrationInternal(migration);
return new NonNullSafeMigratableStorableBuilder<>(this);
}
/**
* Building {@link NonNullSafeStorable} object.
*
* @return New {@link NonNullSafeStorable}.
*/
@NonNull
public NonNullSafeStorable<TKey, TObject, TStoreObject> build() {
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
throw new ShouldNotHappenException();
}
if (getDefaultValue() == null) {
throw new ShouldNotHappenException();
}
return new NonNullSafeStorable<>(this);
}
}

View File

@ -20,15 +20,15 @@
package ru.touchin.roboswag.core.observables.storable.builders;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.concurrent.TimeUnit;
import ru.touchin.roboswag.core.observables.storable.Converter;
import ru.touchin.roboswag.core.observables.storable.Migration;
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
import ru.touchin.roboswag.core.observables.storable.SafeStore;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.Store;
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import rx.Scheduler;
/**
* Created by Gavriil Sitnikov on 15/05/2016.
@ -47,36 +47,42 @@ public class NonNullStorableBuilder<TKey, TObject, TStoreObject> extends Storabl
}
/**
* Sets store and converter.
* Sets specific {@link Scheduler} to store/load/convert values on it.
*
* @param storeObjectClass Class of store object,
* @param store Store to store objects into;
* @param converter Converter to convert values from store class to actual class and back;
* @param storeScheduler Scheduler;
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final Store<TKey, TStoreObject> store,
@NonNull final Converter<TObject, TStoreObject> converter) {
setStoreInternal(storeObjectClass, store, converter);
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setStoreScheduler(@Nullable final Scheduler storeScheduler) {
setStoreSchedulerInternal(storeScheduler);
return this;
}
/**
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
* Sets specific {@link Storable.ObserveStrategy} to cache value in memory in specific way.
*
* @param storeObjectClass Class of store object,
* @param store Safe store that is not throwing exceptions;
* @param converter Safe converter that is not throwing exceptions;
* @param observeStrategy ObserveStrategy;
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final SafeStore<TKey, TStoreObject> store,
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
setStoreInternal(storeObjectClass, store, converter);
return new NonNullSafeStorableBuilder<>(this);
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setObserveStrategy(@Nullable final Storable.ObserveStrategy observeStrategy) {
setObserveStrategyInternal(observeStrategy);
return this;
}
/**
* Sets cache time for while value that cached by {@link #setObserveStrategy(Storable.ObserveStrategy)}
* will be in memory after everyone unsubscribe.
* It is important for example for cases when user switches between screens and hide/open app very fast.
*
* @param cacheTime Cache time value;
* @param timeUnit Cache time units.
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) {
setCacheTimeInternal(cacheTime, timeUnit);
return this;
}
/**
@ -86,9 +92,9 @@ public class NonNullStorableBuilder<TKey, TObject, TStoreObject> extends Storabl
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
setMigrationInternal(migration);
return new NonNullMigratableStorableBuilder<>(this);
return this;
}
/**

View File

@ -1,76 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.builders;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
import ru.touchin.roboswag.core.observables.storable.SafeStore;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.concrete.SafeStorable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 15/05/2016.
* Builder with safe store and converter inside that is already contains not null migration.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
public SafeMigratableStorableBuilder(@NonNull final MigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
public SafeMigratableStorableBuilder(@NonNull final SafeStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
super(sourceBuilder);
}
/**
* Sets value which will be returned instead of null.
*
* @param defaultValue Default value;
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setDefaultValue(@NonNull final TObject defaultValue) {
setDefaultValueInternal(defaultValue);
return new NonNullSafeMigratableStorableBuilder<>(this);
}
/**
* Building {@link SafeStorable} object.
*
* @return New {@link SafeStorable}.
*/
@NonNull
public SafeStorable<TKey, TObject, TStoreObject> build() {
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
throw new ShouldNotHappenException();
}
if (getMigration() == null) {
throw new ShouldNotHappenException();
}
return new SafeStorable<>(this);
}
}

View File

@ -1,86 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.builders;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.core.observables.storable.Migration;
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
import ru.touchin.roboswag.core.observables.storable.SafeStore;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.concrete.SafeStorable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 15/05/2016.
* Builder with safe store and converter inside.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class SafeStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
public SafeStorableBuilder(@NonNull final Storable.Builder<TKey, TObject, TStoreObject> sourceBuilder,
@NonNull final Class<TStoreObject> storeObjectClass,
@NonNull final SafeStore<TKey, TStoreObject> store,
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
super(sourceBuilder);
setStoreInternal(storeObjectClass, store, converter);
}
/**
* Sets specific {@link Migration} to migrate values from specific version to latest version.
*
* @param migration Migration;
* @return Builder that allows to specify other fields.
*/
@NonNull
public SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
setMigrationInternal(migration);
return new SafeMigratableStorableBuilder<>(this);
}
/**
* Sets value which will be returned instead of null.
*
* @param defaultValue Default value;
* @return Builder that allows to specify other fields.
*/
@NonNull
public NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> setDefaultValue(@NonNull final TObject defaultValue) {
setDefaultValueInternal(defaultValue);
return new NonNullSafeStorableBuilder<>(this);
}
/**
* Building {@link SafeStorable} object.
*
* @return New {@link SafeStorable}.
*/
@NonNull
public SafeStorable<TKey, TObject, TStoreObject> build() {
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
throw new ShouldNotHappenException();
}
return new SafeStorable<>(this);
}
}

View File

@ -1,99 +0,0 @@
/*
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.concrete;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
import ru.touchin.roboswag.core.observables.storable.Converter;
import ru.touchin.roboswag.core.observables.storable.Migration;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.Store;
import rx.Observable;
/**
* Created by Gavriil Sitnikov on 20/12/2016.
* List wrapper of {@link Storable}.
*
* @param <TKey> Type of key to identify object;
* @param <TItemObject> Type of items in actual list object;
* @param <TStoreObject> Type of store object.
*/
@SuppressWarnings({"unchecked", "CPD-START"})
public class ListStorable<TKey, TItemObject, TStoreObject> {
@NonNull
private final Storable<TKey, List, TStoreObject> storable;
public ListStorable(@NonNull final Storable<TKey, List, TStoreObject> storable) {
this.storable = storable;
}
/**
* Wraps {@link Storable#get()}.
*/
@NonNull
public Observable<List<TItemObject>> get() {
return storable.get().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#observe()}.
*/
@NonNull
public Observable<List<TItemObject>> observe() {
return storable.observe().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#set(Object)}.
*/
@NonNull
public Observable<?> set(@Nullable final List<TItemObject> list) {
return storable.set(list);
}
/**
* Wraps {@link Storable#setCalm(Object)}.
*/
public void setCalm(@Nullable final List<TItemObject> list) {
storable.setCalm(list);
}
/**
* Wraps {@link Storable#getSync()}.
*/
@Nullable
public List<TItemObject> getSync()
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
return (List<TItemObject>) storable.getSync();
}
/**
* Wraps {@link Storable#setSync(Object)}.
*/
public void setSync(@Nullable final List<TItemObject> list)
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
storable.setSync(list);
}
}

View File

@ -1,101 +0,0 @@
/*
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.concrete;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
import ru.touchin.roboswag.core.observables.storable.Converter;
import ru.touchin.roboswag.core.observables.storable.Migration;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.Store;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import rx.Observable;
/**
* Created by Gavriil Sitnikov on 20/12/2016.
* List wrapper of {@link Storable} that should return not null value on get.
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
*
* @param <TKey> Type of key to identify object;
* @param <TItemObject> Type of items in actual list object;
* @param <TStoreObject> Type of store object.
*/
@SuppressWarnings({"unchecked", "CPD-START"})
public class NonNullListStorable<TKey, TItemObject, TStoreObject> {
@NonNull
private final NonNullStorable<TKey, List, TStoreObject> storable;
public NonNullListStorable(@NonNull final NonNullStorable<TKey, List, TStoreObject> storable) {
this.storable = storable;
}
/**
* Wraps {@link Storable#get()}.
*/
@NonNull
public Observable<List<TItemObject>> get() {
return storable.get().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#observe()}.
*/
@NonNull
public Observable<List<TItemObject>> observe() {
return storable.observe().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#set(Object)}.
*/
@NonNull
public Observable<?> set(@Nullable final List<TItemObject> list) {
return storable.set(list);
}
/**
* Wraps {@link Storable#setCalm(Object)}.
*/
public void setCalm(@Nullable final List<TItemObject> list) {
storable.setCalm(list);
}
/**
* Wraps {@link Storable#getSync()}.
*/
@NonNull
public List<TItemObject> getSync()
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
return (List<TItemObject>) storable.getSync();
}
/**
* Wraps {@link Storable#setSync(Object)}.
*/
public void setSync(@Nullable final List<TItemObject> list)
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
storable.setSync(list);
}
}

View File

@ -1,96 +0,0 @@
/*
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.concrete;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import rx.Observable;
/**
* Created by Gavriil Sitnikov on 20/12/2016.
* List wrapper of {@link Storable} that should not throw exceptions on set or get and return not null value on get.
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
*
* @param <TKey> Type of key to identify object;
* @param <TItemObject> Type of items in actual list object;
* @param <TStoreObject> Type of store object.
*/
@SuppressWarnings({"unchecked", "CPD-START"})
public class NonNullSafeListStorable<TKey, TItemObject, TStoreObject> {
@NonNull
private final NonNullSafeStorable<TKey, List, TStoreObject> storable;
public NonNullSafeListStorable(@NonNull final NonNullSafeStorable<TKey, List, TStoreObject> storable) {
this.storable = storable;
}
/**
* Wraps {@link Storable#get()}.
*/
@NonNull
public Observable<List<TItemObject>> get() {
return storable.get().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#observe()}.
*/
@NonNull
public Observable<List<TItemObject>> observe() {
return storable.observe().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#set(Object)}.
*/
@NonNull
public Observable<?> set(@Nullable final List<TItemObject> list) {
return storable.set(list);
}
/**
* Wraps {@link Storable#setCalm(Object)}.
*/
public void setCalm(@Nullable final List<TItemObject> list) {
storable.setCalm(list);
}
/**
* Wraps {@link Storable#getSync()}.
*/
@NonNull
public List<TItemObject> getSync() {
return (List<TItemObject>) storable.getSync();
}
/**
* Wraps {@link Storable#setSync(Object)}.
*/
public void setSync(@Nullable final List<TItemObject> list) {
storable.setSync(list);
}
}

View File

@ -1,73 +0,0 @@
/*
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.concrete;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.builders.NonNullSafeMigratableStorableBuilder;
import ru.touchin.roboswag.core.observables.storable.builders.NonNullSafeStorableBuilder;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 04/10/2015.
* {@link Storable} that should not throw exceptions on set or get and return not null value on get.
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class NonNullSafeStorable<TKey, TObject, TStoreObject> extends Storable<TKey, TObject, TStoreObject> {
public NonNullSafeStorable(@NonNull final NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
super(builderCore);
}
public NonNullSafeStorable(@NonNull final NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
super(builderCore);
}
@NonNull
@Override
public TObject getSync() {
final TObject result;
try {
result = super.getSync();
} catch (final Exception exception) {
throw new ShouldNotHappenException(exception);
}
if (result == null) {
throw new ShouldNotHappenException();
}
return result;
}
@Override
public void setSync(@Nullable final TObject newValue) {
try {
super.setSync(newValue);
} catch (final Exception exception) {
throw new ShouldNotHappenException(exception);
}
}
}

View File

@ -21,11 +21,7 @@ package ru.touchin.roboswag.core.observables.storable.concrete;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.core.observables.storable.Converter;
import ru.touchin.roboswag.core.observables.storable.Migration;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.Store;
import ru.touchin.roboswag.core.observables.storable.builders.NonNullMigratableStorableBuilder;
import ru.touchin.roboswag.core.observables.storable.builders.NonNullStorableBuilder;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
@ -44,13 +40,9 @@ public class NonNullStorable<TKey, TObject, TStoreObject> extends Storable<TKey,
super(builderCore);
}
public NonNullStorable(@NonNull final NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
super(builderCore);
}
@NonNull
@Override
public TObject getSync() throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
public TObject getSync() {
final TObject result = super.getSync();
if (result == null) {
throw new ShouldNotHappenException();

View File

@ -1,96 +0,0 @@
/*
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.concrete;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import rx.Observable;
/**
* Created by Gavriil Sitnikov on 20/12/2016.
* List wrapper of {@link Storable} that should not throw exceptions on set or get.
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
*
* @param <TKey> Type of key to identify object;
* @param <TItemObject> Type of items in actual list object;
* @param <TStoreObject> Type of store object.
*/
@SuppressWarnings({"unchecked", "CPD-START"})
public class SafeListStorable<TKey, TItemObject, TStoreObject> {
@NonNull
private final SafeStorable<TKey, List, TStoreObject> storable;
public SafeListStorable(@NonNull final SafeStorable<TKey, List, TStoreObject> storable) {
this.storable = storable;
}
/**
* Wraps {@link Storable#get()}.
*/
@NonNull
public Observable<List<TItemObject>> get() {
return storable.get().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#observe()}.
*/
@NonNull
public Observable<List<TItemObject>> observe() {
return storable.observe().map(list -> (List<TItemObject>) list);
}
/**
* Wraps {@link Storable#set(Object)}.
*/
@NonNull
public Observable<?> set(@Nullable final List<TItemObject> list) {
return storable.set(list);
}
/**
* Wraps {@link Storable#setCalm(Object)}.
*/
public void setCalm(@Nullable final List<TItemObject> list) {
storable.setCalm(list);
}
/**
* Wraps {@link Storable#getSync()}.
*/
@Nullable
public List<TItemObject> getSync() {
return (List<TItemObject>) storable.getSync();
}
/**
* Wraps {@link Storable#setSync(Object)}.
*/
public void setSync(@Nullable final List<TItemObject> list) {
storable.setSync(list);
}
}

View File

@ -1,68 +0,0 @@
/*
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* 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 ru.touchin.roboswag.core.observables.storable.concrete;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import ru.touchin.roboswag.core.observables.storable.Storable;
import ru.touchin.roboswag.core.observables.storable.builders.SafeMigratableStorableBuilder;
import ru.touchin.roboswag.core.observables.storable.builders.SafeStorableBuilder;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 04/10/2015.
* {@link Storable} that should not throw exceptions on set or get.
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
*
* @param <TKey> Type of key to identify object;
* @param <TObject> Type of actual object;
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
*/
public class SafeStorable<TKey, TObject, TStoreObject> extends Storable<TKey, TObject, TStoreObject> {
public SafeStorable(@NonNull final SafeStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
super(builderCore);
}
public SafeStorable(@NonNull final SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
super(builderCore);
}
@Nullable
@Override
public TObject getSync() {
try {
return super.getSync();
} catch (final Exception exception) {
throw new ShouldNotHappenException(exception);
}
}
@Override
public void setSync(@Nullable final TObject newValue) {
try {
super.setSync(newValue);
} catch (final Exception exception) {
throw new ShouldNotHappenException(exception);
}
}
}