From eb357198a29a7d8755adfd5319ae2487ebf4447f Mon Sep 17 00:00:00 2001 From: Gavriil Sitnikov Date: Wed, 1 Mar 2017 22:07:33 +0300 Subject: [PATCH 1/5] Storable simplified a lot --- .../core/observables/ObservableResult.java | 16 +- .../core/observables/storable/Converter.java | 18 +- .../core/observables/storable/Migration.java | 135 ++++-- .../core/observables/storable/Migrator.java | 45 +- .../observables/storable/SafeConverter.java | 43 -- .../core/observables/storable/SafeStore.java | 46 -- .../storable/SameTypesConverter.java | 8 +- .../core/observables/storable/Storable.java | 431 +++++++----------- .../core/observables/storable/Store.java | 40 +- .../builders/MigratableStorableBuilder.java | 107 ----- .../NonNullMigratableStorableBuilder.java | 92 ---- .../NonNullSafeMigratableStorableBuilder.java | 71 --- .../builders/NonNullSafeStorableBuilder.java | 77 ---- .../builders/NonNullStorableBuilder.java | 41 +- .../SafeMigratableStorableBuilder.java | 76 --- .../builders/SafeStorableBuilder.java | 86 ---- .../storable/concrete/ListStorable.java | 99 ---- .../concrete/NonNullListStorable.java | 101 ---- .../concrete/NonNullSafeListStorable.java | 96 ---- .../concrete/NonNullSafeStorable.java | 73 --- .../storable/concrete/NonNullStorable.java | 10 +- .../storable/concrete/SafeListStorable.java | 96 ---- .../storable/concrete/SafeStorable.java | 68 --- 23 files changed, 328 insertions(+), 1547 deletions(-) delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/SafeConverter.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/SafeStore.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/builders/MigratableStorableBuilder.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullMigratableStorableBuilder.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeMigratableStorableBuilder.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeStorableBuilder.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeMigratableStorableBuilder.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeStorableBuilder.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/ListStorable.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullListStorable.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeListStorable.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeStorable.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeListStorable.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeStorable.java diff --git a/src/main/java/ru/touchin/roboswag/core/observables/ObservableResult.java b/src/main/java/ru/touchin/roboswag/core/observables/ObservableResult.java index f02b19b..686fe90 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/ObservableResult.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/ObservableResult.java @@ -15,8 +15,8 @@ public class ObservableResult { @NonNull private final List items = new LinkedList<>(); - @NonNull - private final List errors = new LinkedList<>(); + @Nullable + private Throwable error; /** * Passes item to collect. @@ -33,7 +33,7 @@ public class ObservableResult { * @param error Emitted error. */ public void onError(@NonNull final Throwable error) { - errors.add(error); + this.error = error; } /** @@ -47,13 +47,13 @@ public class ObservableResult { } /** - * Returns list of collected errors. + * Returns collected error. * - * @return Errors. + * @return Error. */ - @NonNull - public List getErrors() { - return new ArrayList<>(errors); + @Nullable + public Throwable getError() { + return error; } } diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java index 26b31f8..29af6ec 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java @@ -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 { /** - * 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 objectClass Type of object; + * @param storeObjectClass 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 objectClass, @NonNull Class storeObjectClass, @Nullable TObject object) + TStoreObject toStoreObject(@NonNull Type objectClass, @NonNull Type storeObjectClass, @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 objectClass Type of object; + * @param storeObjectClass 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 objectClass, @NonNull Class storeObjectClass, @Nullable TStoreObject storeObject) + TObject toObject(@NonNull Type objectClass, @NonNull Type storeObjectClass, @Nullable TStoreObject storeObject) throws ConversionException; class ConversionException extends Exception { diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java index 03cad11..42f6059 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java @@ -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,97 @@ public class Migration { 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; - } - - 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 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))); } /** * 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 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); + Single chain = Single.fromCallable(() -> versionUpdater.initialVersion); + for (final Migrator 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 + .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 { + + @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); diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java index acb0c2d..9517aa7 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java @@ -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 { 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 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. + * Observable 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 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. + * Observable 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 oldStore, - @NonNull Store newStore) throws Migration.MigrationException; + @NonNull + protected abstract Single migrateInternal(@NonNull TKey key, + long version, + @NonNull Store oldStore, + @NonNull Store newStore); } diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/SafeConverter.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/SafeConverter.java deleted file mode 100644 index 6136678..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/SafeConverter.java +++ /dev/null @@ -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 Type of original objects; - * @param Type of objects in store. - */ -public interface SafeConverter extends Converter { - - @Nullable - @Override - TStoreObject toStoreObject(@NonNull Class objectClass, @NonNull Class storeObjectClass, @Nullable TObject object); - - @Nullable - @Override - TObject toObject(@NonNull Class objectClass, @NonNull Class storeObjectClass, @Nullable TStoreObject storeObject); - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/SafeStore.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/SafeStore.java deleted file mode 100644 index af58a78..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/SafeStore.java +++ /dev/null @@ -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 Type of keys for values; - * @param Type of values stored in store. - */ -public interface SafeStore extends Store { - - @Override - boolean contains(@NonNull TKey key); - - @Override - void storeObject(@NonNull Class storeObjectClass, @NonNull TKey key, @Nullable TStoreObject storeObject); - - @Nullable - @Override - TStoreObject loadObject(@NonNull Class storeObjectClass, @NonNull TKey key); - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/SameTypesConverter.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/SameTypesConverter.java index 3479fbc..14a29fb 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/SameTypesConverter.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/SameTypesConverter.java @@ -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 Same type. */ -public class SameTypesConverter implements SafeConverter { +public class SameTypesConverter implements Converter { @Nullable @Override - public T toStoreObject(@NonNull final Class class1, @NonNull final Class 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 class1, @NonNull final Class class2, @Nullable final T object) { + public T toObject(@NonNull final Type type1, @NonNull final Type type2, @Nullable final T object) { return object; } diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java index 5c82a05..dc532ab 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java @@ -22,18 +22,19 @@ 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 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.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 +55,23 @@ public class Storable { public static final LcGroup STORABLE_LC_GROUP = new LcGroup("STORABLE"); + @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 objectClass; + private final Type objectType; @NonNull - private final Class storeObjectClass; + private final Type storeObjectType; @NonNull private final Store store; @NonNull @@ -67,113 +79,97 @@ public class Storable { @NonNull private final PublishSubject newStoreValueEvent = PublishSubject.create(); @NonNull + private final Observable storeValueObservable; + @NonNull private final Observable valueObservable; + @NonNull + private final Scheduler scheduler; public Storable(@NonNull final BuilderCore 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); } public Storable(@NonNull final TKey key, - @NonNull final Class objectClass, - @Nullable final Class storeObjectClass, - @Nullable final Store store, - @Nullable final Converter converter, + @NonNull final Type objectType, + @NonNull final Type storeObjectType, + @NonNull final Store store, + @NonNull final Converter converter, @Nullable final ObserveStrategy observeStrategy, @Nullable final Migration migration, @Nullable final TObject defaultValue, @Nullable final Scheduler storeScheduler) { - if (storeObjectClass == null || store == null || converter == null) { - throw new ShouldNotHappenException(); - } 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 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); + valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy); } @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 createStoreValueObservable(@NonNull final ObserveStrategy observeStrategy, @Nullable final Migration migration, - @Nullable final TObject defaultValue, - @Nullable final Scheduler storeScheduler) { - final Observable result = Observable - .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) { + @Nullable final TObject defaultValue) { + final Observable 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 : result.replay(1).refCount(); + return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE + ? result + : result.replay(1).refCount(); } @NonNull private Observable createValueObservable(@NonNull final Observable storeValueObservable, - @NonNull final ObserveStrategy observeStrategy, - @Nullable final Scheduler storeScheduler) { + @NonNull final ObserveStrategy observeStrategy) { final Observable 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 : result.replay(1).refCount(); + .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 + ? result + : result.replay(1).refCount(); } /** @@ -187,23 +183,23 @@ public class Storable { } /** - * Returns class of actual object. + * Returns type of actual object. * - * @return Class of actual object. + * @return Type of actual object. */ @NonNull - public Class 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 getStoreObjectClass() { - return storeObjectClass; + public Type getStoreObjectType() { + return storeObjectType; } /** @@ -228,66 +224,58 @@ public class Storable { /** * 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}. + * 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 + return storeValueObservable .first() - .switchMap(value -> ObjectUtils.equals(value, newValue) - ? Observable.empty() - : Observable - .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); + .switchMap(oldStoreValue -> Observable + .fromCallable(() -> converter.toStoreObject(objectType, storeObjectType, newValue)) + .subscribeOn(scheduler) + .switchMap(newStoreValue -> { + if (ObjectUtils.equals(newStoreValue, oldStoreValue)) { + return Observable.empty(); } - })); - } - - /** - * 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 store.storeObject(storeObjectType, key, newStoreValue) + .doOnCompleted(() -> { + newStoreValueEvent.onNext(newStoreValue); + STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, oldStoreValue, 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); + } + }); } /** * 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 { + @Deprecated + //deprecation: it should be used for debug only and in very rare cases. + public void setSync(@Nullable final TObject newValue) throws Throwable { final ObservableResult setResult = RxUtils.executeSync(set(newValue)); - checkStorableObservableResult(setResult); + if (setResult.getError() != null) { + throw setResult.getError(); + } } /** * 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 +285,8 @@ public class Storable { } /** - * 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 +299,25 @@ public class Storable { * 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 { + public TObject getSync() throws Throwable { final ObservableResult getResult = RxUtils.executeSync(get()); - checkStorableObservableResult(getResult); + if (getResult.getError() != null) { + throw getResult.getError(); + } 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; - } - } - } - /** - * 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 +332,19 @@ public class Storable { 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,49 +360,48 @@ public class Storable { @NonNull protected final TKey key; @NonNull - protected final Class objectClass; + protected final Type objectType; + @NonNull + private final Type storeObjectType; + @NonNull + private final Store store; + @NonNull + private final Converter converter; @Nullable - private Class storeObjectClass; - @Nullable - private Store store; - @Nullable - private Converter converter; - @Nullable - protected final ObserveStrategy observeStrategy; + private ObserveStrategy observeStrategy; @Nullable private Migration migration; @Nullable private TObject defaultValue; @Nullable - protected final Scheduler storeScheduler; + private Scheduler storeScheduler; protected BuilderCore(@NonNull final TKey key, - @NonNull final Class 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 store, + @NonNull final Converter converter) { + this(key, objectType, storeObjectType, store, converter, null, null, null, null); } protected BuilderCore(@NonNull final BuilderCore 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); } - @SuppressWarnings("CPD-START") - //CPD: it is ok that builder copy-pasted parent constructor parameters private BuilderCore(@NonNull final TKey key, - @NonNull final Class objectClass, - @Nullable final Class storeObjectClass, - @Nullable final Store store, - @Nullable final Converter converter, + @NonNull final Type objectType, + @NonNull final Type storeObjectType, + @NonNull final Store store, + @NonNull final Converter converter, @Nullable final ObserveStrategy observeStrategy, @Nullable final Migration migration, @Nullable final TObject defaultValue, @Nullable final Scheduler storeScheduler) { this.key = key; - this.objectClass = objectClass; - this.storeObjectClass = storeObjectClass; + this.objectType = objectType; + this.storeObjectType = storeObjectType; this.store = store; this.converter = converter; this.observeStrategy = observeStrategy; @@ -428,61 +410,20 @@ public class Storable { this.storeScheduler = storeScheduler; } - @SuppressWarnings("CPD-END") - @Nullable - public Class 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 getStore() { - return store; - } - - protected void setStoreInternal(@NonNull final Class storeObjectClass, - @NonNull final Store store, - @NonNull final Converter 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 getConverter() { - return converter; - } - - /** - * Returns {@link Migration} to migrate values from specific version to latest version. - * - * @return Migration. - */ - @Nullable - public Migration getMigration() { - return migration; + protected void setObserveStrategyInternal(@Nullable final ObserveStrategy observeStrategy) { + this.observeStrategy = observeStrategy; } protected void setMigrationInternal(@NonNull final Migration migration) { this.migration = migration; } - /** - * Returns value which will be returned instead of null. - * - * @return Default value. - */ @Nullable - public TObject getDefaultValue() { + protected TObject getDefaultValue() { return defaultValue; } @@ -502,59 +443,35 @@ public class Storable { public static class Builder extends BuilderCore { public Builder(@NonNull final TKey key, - @NonNull final Class objectClass) { - super(key, objectClass, null, null); - } - - public Builder(@NonNull final TKey key, - @NonNull final Class objectClass, - @NonNull final ObserveStrategy observeStrategy) { - super(key, objectClass, observeStrategy, null); - } - - public Builder(@NonNull final TKey key, - @NonNull final Class objectClass, - @NonNull final Scheduler storeScheduler) { - super(key, objectClass, null, storeScheduler); - } - - public Builder(@NonNull final TKey key, - @NonNull final Class 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 store, + @NonNull final Converter 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 setStore(@NonNull final Class storeObjectClass, - @NonNull final Store store, - @NonNull final Converter converter) { - setStoreInternal(storeObjectClass, store, converter); + public Builder 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 setSafeStore(@NonNull final Class storeObjectClass, - @NonNull final SafeStore store, - @NonNull final SafeConverter converter) { - return new SafeStorableBuilder<>(this, storeObjectClass, store, converter); + public Builder setObserveStrategy(@Nullable final ObserveStrategy observeStrategy) { + setObserveStrategyInternal(observeStrategy); + return this; } /** @@ -564,8 +481,9 @@ public class Storable { * @return Builder that allows to specify other fields. */ @NonNull - public MigratableStorableBuilder setMigration(@NonNull final Migration migration) { - return new MigratableStorableBuilder<>(this, migration); + public Builder setMigration(@NonNull final Migration migration) { + setMigrationInternal(migration); + return this; } /** @@ -589,6 +507,11 @@ public class Storable { return new Storable<>(this); } + @Override + protected void setObserveStrategyInternal(@Nullable final ObserveStrategy observeStrategy) { + super.setObserveStrategyInternal(observeStrategy); + } + } } diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Store.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Store.java index bebc82d..610bb34 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Store.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Store.java @@ -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 { * * @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 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 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 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 loadObject(@NonNull Type storeObjectType, @NonNull TKey key); } diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/MigratableStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/MigratableStorableBuilder.java deleted file mode 100644 index 5addab0..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/MigratableStorableBuilder.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class MigratableStorableBuilder extends Storable.BuilderCore { - - public MigratableStorableBuilder(@NonNull final Storable.Builder sourceBuilder, - @NonNull final Migration 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 setStore(@NonNull final Class storeObjectClass, - @NonNull final Store store, - @NonNull final Converter 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 setSafeStore( - @NonNull final Class storeObjectClass, - @NonNull final SafeStore store, - @NonNull final SafeConverter 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 setDefaultValue(@NonNull final TObject defaultValue) { - setDefaultValueInternal(defaultValue); - return new NonNullMigratableStorableBuilder<>(this); - } - - /** - * Building {@link Storable} object. - * - * @return New {@link Storable}. - */ - @NonNull - public Storable build() { - if (getMigration() == null) { - throw new ShouldNotHappenException(); - } - return new Storable<>(this); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullMigratableStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullMigratableStorableBuilder.java deleted file mode 100644 index e1841f8..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullMigratableStorableBuilder.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class NonNullMigratableStorableBuilder extends Storable.BuilderCore { - - public NonNullMigratableStorableBuilder(@NonNull final NonNullStorableBuilder sourceBuilder) { - super(sourceBuilder); - } - - public NonNullMigratableStorableBuilder(@NonNull final MigratableStorableBuilder sourceBuilder) { - super(sourceBuilder); - } - - @NonNull - public NonNullMigratableStorableBuilder setStore(@NonNull final Class storeObjectClass, - @NonNull final Store store, - @NonNull final Converter 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 setSafeStore( - @NonNull final Class storeObjectClass, - @NonNull final SafeStore store, - @NonNull final SafeConverter converter) { - setStoreInternal(storeObjectClass, store, converter); - return new NonNullSafeMigratableStorableBuilder<>(this); - } - - /** - * Building {@link NonNullStorable} object. - * - * @return New {@link NonNullStorable}. - */ - @NonNull - public NonNullStorable build() { - if (getDefaultValue() == null) { - throw new ShouldNotHappenException(); - } - if (getMigration() == null) { - throw new ShouldNotHappenException(); - } - return new NonNullStorable<>(this); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeMigratableStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeMigratableStorableBuilder.java deleted file mode 100644 index ac72e0d..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeMigratableStorableBuilder.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class NonNullSafeMigratableStorableBuilder extends Storable.BuilderCore { - - public NonNullSafeMigratableStorableBuilder(@NonNull final NonNullMigratableStorableBuilder sourceBuilder) { - super(sourceBuilder); - } - - public NonNullSafeMigratableStorableBuilder(@NonNull final NonNullSafeStorableBuilder sourceBuilder) { - super(sourceBuilder); - } - - public NonNullSafeMigratableStorableBuilder(@NonNull final SafeMigratableStorableBuilder sourceBuilder) { - super(sourceBuilder); - } - - /** - * Building {@link NonNullSafeStorable} object. - * - * @return New {@link NonNullSafeStorable}. - */ - @NonNull - public NonNullSafeStorable 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); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeStorableBuilder.java deleted file mode 100644 index 9b47513..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullSafeStorableBuilder.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class NonNullSafeStorableBuilder extends Storable.BuilderCore { - - public NonNullSafeStorableBuilder(@NonNull final NonNullStorableBuilder sourceBuilder) { - super(sourceBuilder); - } - - public NonNullSafeStorableBuilder(@NonNull final SafeStorableBuilder 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 setMigration(@NonNull final Migration migration) { - setMigrationInternal(migration); - return new NonNullSafeMigratableStorableBuilder<>(this); - } - - /** - * Building {@link NonNullSafeStorable} object. - * - * @return New {@link NonNullSafeStorable}. - */ - @NonNull - public NonNullSafeStorable build() { - if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) { - throw new ShouldNotHappenException(); - } - if (getDefaultValue() == null) { - throw new ShouldNotHappenException(); - } - return new NonNullSafeStorable<>(this); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java index fb488ab..65c4716 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java @@ -21,12 +21,8 @@ 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.observables.storable.concrete.NonNullStorable; import ru.touchin.roboswag.core.utils.ShouldNotHappenException; @@ -46,39 +42,6 @@ public class NonNullStorableBuilder extends Storabl setDefaultValueInternal(defaultValue); } - /** - * 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 NonNullStorableBuilder setStore(@NonNull final Class storeObjectClass, - @NonNull final Store store, - @NonNull final Converter 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 NonNullSafeStorableBuilder setSafeStore(@NonNull final Class storeObjectClass, - @NonNull final SafeStore store, - @NonNull final SafeConverter converter) { - setStoreInternal(storeObjectClass, store, converter); - return new NonNullSafeStorableBuilder<>(this); - } - /** * Sets specific {@link Migration} to migrate values from specific version to latest version. * @@ -86,9 +49,9 @@ public class NonNullStorableBuilder extends Storabl * @return Builder that allows to specify other fields. */ @NonNull - public NonNullMigratableStorableBuilder setMigration(@NonNull final Migration migration) { + public NonNullStorableBuilder setMigration(@NonNull final Migration migration) { setMigrationInternal(migration); - return new NonNullMigratableStorableBuilder<>(this); + return this; } /** diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeMigratableStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeMigratableStorableBuilder.java deleted file mode 100644 index 0760087..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeMigratableStorableBuilder.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class SafeMigratableStorableBuilder extends Storable.BuilderCore { - - public SafeMigratableStorableBuilder(@NonNull final MigratableStorableBuilder sourceBuilder) { - super(sourceBuilder); - } - - public SafeMigratableStorableBuilder(@NonNull final SafeStorableBuilder 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 setDefaultValue(@NonNull final TObject defaultValue) { - setDefaultValueInternal(defaultValue); - return new NonNullSafeMigratableStorableBuilder<>(this); - } - - /** - * Building {@link SafeStorable} object. - * - * @return New {@link SafeStorable}. - */ - @NonNull - public SafeStorable build() { - if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) { - throw new ShouldNotHappenException(); - } - if (getMigration() == null) { - throw new ShouldNotHappenException(); - } - return new SafeStorable<>(this); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeStorableBuilder.java deleted file mode 100644 index 456ae85..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/SafeStorableBuilder.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class SafeStorableBuilder extends Storable.BuilderCore { - - public SafeStorableBuilder(@NonNull final Storable.Builder sourceBuilder, - @NonNull final Class storeObjectClass, - @NonNull final SafeStore store, - @NonNull final SafeConverter 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 setMigration(@NonNull final Migration 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 setDefaultValue(@NonNull final TObject defaultValue) { - setDefaultValueInternal(defaultValue); - return new NonNullSafeStorableBuilder<>(this); - } - - /** - * Building {@link SafeStorable} object. - * - * @return New {@link SafeStorable}. - */ - @NonNull - public SafeStorable build() { - if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) { - throw new ShouldNotHappenException(); - } - return new SafeStorable<>(this); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/ListStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/ListStorable.java deleted file mode 100644 index ad8ad6e..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/ListStorable.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of items in actual list object; - * @param Type of store object. - */ -@SuppressWarnings({"unchecked", "CPD-START"}) -public class ListStorable { - - @NonNull - private final Storable storable; - - public ListStorable(@NonNull final Storable storable) { - this.storable = storable; - } - - /** - * Wraps {@link Storable#get()}. - */ - @NonNull - public Observable> get() { - return storable.get().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#observe()}. - */ - @NonNull - public Observable> observe() { - return storable.observe().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#set(Object)}. - */ - @NonNull - public Observable set(@Nullable final List list) { - return storable.set(list); - } - - /** - * Wraps {@link Storable#setCalm(Object)}. - */ - public void setCalm(@Nullable final List list) { - storable.setCalm(list); - } - - /** - * Wraps {@link Storable#getSync()}. - */ - @Nullable - public List getSync() - throws Store.StoreException, Converter.ConversionException, Migration.MigrationException { - return (List) storable.getSync(); - } - - /** - * Wraps {@link Storable#setSync(Object)}. - */ - public void setSync(@Nullable final List list) - throws Store.StoreException, Converter.ConversionException, Migration.MigrationException { - storable.setSync(list); - } - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullListStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullListStorable.java deleted file mode 100644 index 977a9fc..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullListStorable.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of items in actual list object; - * @param Type of store object. - */ -@SuppressWarnings({"unchecked", "CPD-START"}) -public class NonNullListStorable { - - @NonNull - private final NonNullStorable storable; - - public NonNullListStorable(@NonNull final NonNullStorable storable) { - this.storable = storable; - } - - /** - * Wraps {@link Storable#get()}. - */ - @NonNull - public Observable> get() { - return storable.get().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#observe()}. - */ - @NonNull - public Observable> observe() { - return storable.observe().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#set(Object)}. - */ - @NonNull - public Observable set(@Nullable final List list) { - return storable.set(list); - } - - /** - * Wraps {@link Storable#setCalm(Object)}. - */ - public void setCalm(@Nullable final List list) { - storable.setCalm(list); - } - - /** - * Wraps {@link Storable#getSync()}. - */ - @NonNull - public List getSync() - throws Store.StoreException, Converter.ConversionException, Migration.MigrationException { - return (List) storable.getSync(); - } - - /** - * Wraps {@link Storable#setSync(Object)}. - */ - public void setSync(@Nullable final List list) - throws Store.StoreException, Converter.ConversionException, Migration.MigrationException { - storable.setSync(list); - } - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeListStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeListStorable.java deleted file mode 100644 index 21ac7b3..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeListStorable.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of items in actual list object; - * @param Type of store object. - */ -@SuppressWarnings({"unchecked", "CPD-START"}) -public class NonNullSafeListStorable { - - @NonNull - private final NonNullSafeStorable storable; - - public NonNullSafeListStorable(@NonNull final NonNullSafeStorable storable) { - this.storable = storable; - } - - /** - * Wraps {@link Storable#get()}. - */ - @NonNull - public Observable> get() { - return storable.get().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#observe()}. - */ - @NonNull - public Observable> observe() { - return storable.observe().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#set(Object)}. - */ - @NonNull - public Observable set(@Nullable final List list) { - return storable.set(list); - } - - /** - * Wraps {@link Storable#setCalm(Object)}. - */ - public void setCalm(@Nullable final List list) { - storable.setCalm(list); - } - - /** - * Wraps {@link Storable#getSync()}. - */ - @NonNull - public List getSync() { - return (List) storable.getSync(); - } - - /** - * Wraps {@link Storable#setSync(Object)}. - */ - public void setSync(@Nullable final List list) { - storable.setSync(list); - } - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeStorable.java deleted file mode 100644 index 971716c..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeStorable.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class NonNullSafeStorable extends Storable { - - public NonNullSafeStorable(@NonNull final NonNullSafeStorableBuilder builderCore) { - super(builderCore); - } - - public NonNullSafeStorable(@NonNull final NonNullSafeMigratableStorableBuilder 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); - } - } - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java index 34a7101..16840a9 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java @@ -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 extends Storable 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(); diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeListStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeListStorable.java deleted file mode 100644 index e484442..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeListStorable.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of items in actual list object; - * @param Type of store object. - */ -@SuppressWarnings({"unchecked", "CPD-START"}) -public class SafeListStorable { - - @NonNull - private final SafeStorable storable; - - public SafeListStorable(@NonNull final SafeStorable storable) { - this.storable = storable; - } - - /** - * Wraps {@link Storable#get()}. - */ - @NonNull - public Observable> get() { - return storable.get().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#observe()}. - */ - @NonNull - public Observable> observe() { - return storable.observe().map(list -> (List) list); - } - - /** - * Wraps {@link Storable#set(Object)}. - */ - @NonNull - public Observable set(@Nullable final List list) { - return storable.set(list); - } - - /** - * Wraps {@link Storable#setCalm(Object)}. - */ - public void setCalm(@Nullable final List list) { - storable.setCalm(list); - } - - /** - * Wraps {@link Storable#getSync()}. - */ - @Nullable - public List getSync() { - return (List) storable.getSync(); - } - - /** - * Wraps {@link Storable#setSync(Object)}. - */ - public void setSync(@Nullable final List list) { - storable.setSync(list); - } - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeStorable.java deleted file mode 100644 index 9c88cf9..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/SafeStorable.java +++ /dev/null @@ -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 Type of key to identify object; - * @param Type of actual object; - * @param Type of store object. Could be same as {@link TObject}. - */ -public class SafeStorable extends Storable { - - public SafeStorable(@NonNull final SafeStorableBuilder builderCore) { - super(builderCore); - } - - public SafeStorable(@NonNull final SafeMigratableStorableBuilder 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); - } - } - -} From 4f5353d261a3ecf7036b22a626b767a103e9f2f4 Mon Sep 17 00:00:00 2001 From: Gavriil Sitnikov Date: Wed, 1 Mar 2017 22:28:22 +0300 Subject: [PATCH 2/5] Static analysis fixes --- .../core/observables/storable/Migration.java | 27 +++++++++++-------- .../core/observables/storable/Storable.java | 11 ++++---- .../storable/concrete/NonNullStorable.java | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java index 42f6059..0f36f5e 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java @@ -63,6 +63,21 @@ public class Migration { -> Single.error(new MigrationException(String.format("Can't get version of '%s' from %s", key, versionsStore), throwable))); } + @NonNull + private Single makeMigrationChain(@NonNull final TKey key, @NonNull final VersionUpdater versionUpdater) { + Single chain = Single.fromCallable(() -> versionUpdater.initialVersion); + for (final Migrator 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. * @@ -73,17 +88,7 @@ public class Migration { return loadCurrentVersion(key) .flatMap(currentVersion -> { final VersionUpdater versionUpdater = new VersionUpdater<>(key, versionsStore, currentVersion); - Single chain = Single.fromCallable(() -> versionUpdater.initialVersion); - for (final Migrator 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 + return makeMigrationChain(key, versionUpdater) .doOnSuccess(lastUpdatedVersion -> { if (lastUpdatedVersion < latestVersion) { throw OnErrorThrowable.from(new NextLoopMigrationException()); diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java index dc532ab..1c362e8 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java @@ -105,7 +105,8 @@ public class Storable { this.storeObjectType = storeObjectType; this.store = store; this.converter = converter; - final ObserveStrategy nonNullObserveStrategy = observeStrategy != null ? observeStrategy : getDefaultObserveStrategyFor(objectType, storeObjectType); + final ObserveStrategy nonNullObserveStrategy + = observeStrategy != null ? observeStrategy : getDefaultObserveStrategyFor(objectType, storeObjectType); scheduler = storeScheduler != null ? storeScheduler : Schedulers.from(Executors.newSingleThreadExecutor()); storeValueObservable = createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue); @@ -390,6 +391,8 @@ public class Storable { sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler); } + @SuppressWarnings("CPD-START") + //CPD: it is same code as constructor of Storable private BuilderCore(@NonNull final TKey key, @NonNull final Type objectType, @NonNull final Type storeObjectType, @@ -410,6 +413,7 @@ public class Storable { this.storeScheduler = storeScheduler; } + @SuppressWarnings("CPD-END") protected void setStoreSchedulerInternal(@Nullable final Scheduler storeScheduler) { this.storeScheduler = storeScheduler; } @@ -507,11 +511,6 @@ public class Storable { return new Storable<>(this); } - @Override - protected void setObserveStrategyInternal(@Nullable final ObserveStrategy observeStrategy) { - super.setObserveStrategyInternal(observeStrategy); - } - } } diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java index 16840a9..95dc8ec 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java @@ -42,7 +42,7 @@ public class NonNullStorable extends Storable Date: Fri, 3 Mar 2017 18:11:37 +0300 Subject: [PATCH 3/5] Caching bug fixed --- .../roboswag/core/observables/storable/Storable.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java index 1c362e8..7841f50 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java @@ -149,8 +149,8 @@ public class Storable { .concatWith(newStoreValueEvent) .map(storeObject -> returnDefaultValueIfNull(storeObject, defaultValue)); return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE - ? result - : result.replay(1).refCount(); + ? result.replay(1).refCount() + : result; } @NonNull @@ -169,8 +169,8 @@ public class Storable { } })); return observeStrategy == ObserveStrategy.CACHE_ACTUAL_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE - ? result - : result.replay(1).refCount(); + ? result.replay(1).refCount() + : result; } /** From 631cf157ddbc7751593413fce425152b2c793151 Mon Sep 17 00:00:00 2001 From: Gavriil Sitnikov Date: Thu, 9 Mar 2017 01:55:03 +0300 Subject: [PATCH 4/5] storable bug fix + static fixes --- build.gradle | 6 +- .../core/log/ConsoleLogProcessor.java | 7 +- .../java/ru/touchin/roboswag/core/log/Lc.java | 4 +- .../OnSubscribeRefCountWithCacheTime.java | 212 ++++++++++++++++++ .../roboswag/core/observables/RxUtils.java | 41 ---- .../core/observables/storable/Storable.java | 148 +++++++----- .../builders/NonNullStorableBuilder.java | 43 ++++ .../storable/concrete/NonNullStorable.java | 2 +- 8 files changed, 361 insertions(+), 102 deletions(-) create mode 100644 src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java delete mode 100644 src/main/java/ru/touchin/roboswag/core/observables/RxUtils.java diff --git a/build.gradle b/build.gradle index a014e9a..4f12a5a 100644 --- a/build.gradle +++ b/build.gradle @@ -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' } diff --git a/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java b/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java index 04af630..fd7303d 100644 --- a/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java +++ b/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java @@ -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,8 +52,9 @@ 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)); + if (Log.isLoggable(tag, level.getPriority())) { + Log.println(level.getPriority(), tag, messageToLog.substring(i, end)); + } i = end; } while (i < newline); diff --git a/src/main/java/ru/touchin/roboswag/core/log/Lc.java b/src/main/java/ru/touchin/roboswag/core/log/Lc.java index 8ac2c6c..08f554b 100644 --- a/src/main/java/ru/touchin/roboswag/core/log/Lc.java +++ b/src/main/java/ru/touchin/roboswag/core/log/Lc.java @@ -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() { diff --git a/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java b/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java new file mode 100644 index 0000000..721bb99 --- /dev/null +++ b/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java @@ -0,0 +1,212 @@ +/** + * Copyright 2014 Netflix, Inc. + *

+ * 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; + +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 the value type + */ +@SuppressWarnings({"PMD.AvoidUsingVolatile", "PMD.CompareObjectsWithEquals"}) +//AvoidUsingVolatile,CompareObjectsWithEquals: from OnSubscribeRefCount code +public final class OnSubscribeRefCountWithCacheTime implements OnSubscribe { + + @NonNull + private final ConnectableObservable 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 source, + final long cacheTime, @NonNull final TimeUnit cacheTimeUnit) { + this.source = source; + this.cacheTime = cacheTime; + this.cacheTimeUnit = cacheTimeUnit; + } + + @Override + public void call(@NonNull final Subscriber 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 onSubscribe(@NonNull final Subscriber subscriber, + @NonNull final AtomicBoolean writeLocked) { + return new Action1() { + @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 subscriber, @NonNull final CompositeSubscription currentBase) { + // handle unsubscribing from the base subscription + subscriber.add(disconnect(currentBase)); + + source.unsafeSubscribe(new Subscriber(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) { + 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(); + } + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/observables/RxUtils.java b/src/main/java/ru/touchin/roboswag/core/observables/RxUtils.java deleted file mode 100644 index 5da00f1..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/RxUtils.java +++ /dev/null @@ -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 Type of {@link Observable}'s items; - * @return {@link ObservableResult} which contains all items and errors collected during execution. - */ - @NonNull - public static ObservableResult executeSync(@NonNull final Observable observable) { - final ObservableResult 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() { - } - -} diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java index 7841f50..e6529c2 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java @@ -24,13 +24,12 @@ 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.OnSubscribeRefCountWithCacheTime; import ru.touchin.roboswag.core.observables.storable.builders.NonNullStorableBuilder; import ru.touchin.roboswag.core.utils.ObjectUtils; -import ru.touchin.roboswag.core.utils.ShouldNotHappenException; import rx.Completable; import rx.Observable; import rx.Scheduler; @@ -55,6 +54,8 @@ public class Storable { 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)) { @@ -88,18 +89,21 @@ public class Storable { public Storable(@NonNull final BuilderCore builderCore) { 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 Type objectType, - @NonNull final Type storeObjectType, - @NonNull final Store store, - @NonNull final Converter converter, - @Nullable final ObserveStrategy observeStrategy, - @Nullable final Migration migration, - @Nullable final TObject defaultValue, - @Nullable final Scheduler storeScheduler) { + @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 store, + @NonNull final Converter converter, + @Nullable final ObserveStrategy observeStrategy, + @Nullable final Migration migration, + @Nullable final TObject defaultValue, + @Nullable final Scheduler storeScheduler, + final long cacheTimeMillis) { this.key = key; this.objectType = objectType; this.storeObjectType = storeObjectType; @@ -109,8 +113,8 @@ public class Storable { = observeStrategy != null ? observeStrategy : getDefaultObserveStrategyFor(objectType, storeObjectType); scheduler = storeScheduler != null ? storeScheduler : Schedulers.from(Executors.newSingleThreadExecutor()); storeValueObservable - = createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue); - valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy); + = createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue, cacheTimeMillis); + valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy, cacheTimeMillis); } @Nullable @@ -134,7 +138,8 @@ public class Storable { @NonNull private Observable createStoreValueObservable(@NonNull final ObserveStrategy observeStrategy, @Nullable final Migration migration, - @Nullable final TObject defaultValue) { + @Nullable final TObject defaultValue, + final long cacheTimeMillis) { final Observable result = (migration != null ? migration.migrateToLatestVersion(key).subscribeOn(scheduler) : Completable.complete()) @@ -149,13 +154,14 @@ public class Storable { .concatWith(newStoreValueEvent) .map(storeObject -> returnDefaultValueIfNull(storeObject, defaultValue)); return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE - ? result.replay(1).refCount() + ? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS)) : result; } @NonNull private Observable createValueObservable(@NonNull final Observable storeValueObservable, - @NonNull final ObserveStrategy observeStrategy) { + @NonNull final ObserveStrategy observeStrategy, + final long cacheTimeMillis) { final Observable result = storeValueObservable .switchMap(storeObject -> Observable .fromCallable(() -> converter.toObject(objectType, storeObjectType, storeObject)) @@ -169,7 +175,7 @@ public class Storable { } })); return observeStrategy == ObserveStrategy.CACHE_ACTUAL_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE - ? result.replay(1).refCount() + ? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS)) : result; } @@ -223,28 +229,24 @@ public class Storable { return converter; } - /** - * Creates observable which is async setting value to store. - * 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 storeValueObservable - .first() + 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 (ObjectUtils.equals(newStoreValue, oldStoreValue)) { + if (checkForEqualityBeforeSet && ObjectUtils.equals(newStoreValue, oldStoreValue)) { return Observable.empty(); } return store.storeObject(storeObjectType, key, newStoreValue) .doOnCompleted(() -> { newStoreValueEvent.onNext(newStoreValue); - STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, oldStoreValue, 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(); })) @@ -255,23 +257,48 @@ public class Storable { 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. + * 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 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 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. - public void setSync(@Nullable final TObject newValue) throws Throwable { - final ObservableResult setResult = RxUtils.executeSync(set(newValue)); - if (setResult.getError() != null) { - throw setResult.getError(); - } + public void setSync(@Nullable final TObject newValue) { + set(newValue).toBlocking().subscribe(); } /** @@ -300,21 +327,12 @@ public class Storable { * Gets value synchronously. You should NOT use this method normally. Use {@link #get()} or {@link #observe()} asynchronously instead. * * @return Returns value; - * @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 Throwable { - final ObservableResult getResult = RxUtils.executeSync(get()); - if (getResult.getError() != null) { - throw getResult.getError(); - } - if (getResult.getItems().size() != 1) { - throw new ShouldNotHappenException(); - } - return getResult.getItems().get(0); + public TObject getSync() { + return get().toBlocking().first(); } /** @@ -376,23 +394,25 @@ public class Storable { private TObject defaultValue; @Nullable private Scheduler storeScheduler; + private long cacheTimeMillis; protected BuilderCore(@NonNull final TKey key, @NonNull final Type objectType, @NonNull final Type storeObjectType, @NonNull final Store store, @NonNull final Converter converter) { - this(key, objectType, storeObjectType, store, converter, null, null, null, null); + this(key, objectType, storeObjectType, store, converter, null, null, null, null, DEFAULT_CACHE_TIME_MILLIS); } protected BuilderCore(@NonNull final BuilderCore sourceBuilder) { 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") + @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 Type objectType, @NonNull final Type storeObjectType, @@ -401,7 +421,8 @@ public class Storable { @Nullable final ObserveStrategy observeStrategy, @Nullable final Migration migration, @Nullable final TObject defaultValue, - @Nullable final Scheduler storeScheduler) { + @Nullable final Scheduler storeScheduler, + final long cacheTimeMillis) { this.key = key; this.objectType = objectType; this.storeObjectType = storeObjectType; @@ -411,6 +432,7 @@ public class Storable { this.migration = migration; this.defaultValue = defaultValue; this.storeScheduler = storeScheduler; + this.cacheTimeMillis = cacheTimeMillis; } @SuppressWarnings("CPD-END") @@ -426,6 +448,10 @@ public class Storable { this.migration = migration; } + protected void setCacheTimeInternal(final long cacheTime, @NonNull final TimeUnit timeUnit) { + this.cacheTimeMillis = timeUnit.toMillis(cacheTime); + } + @Nullable protected TObject getDefaultValue() { return defaultValue; @@ -478,6 +504,20 @@ public class Storable { 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 setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) { + setCacheTimeInternal(cacheTime, timeUnit); + return this; + } + /** * Sets specific {@link Migration} to migrate values from specific version to latest version. * diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java index 65c4716..ab5cfba 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/builders/NonNullStorableBuilder.java @@ -20,11 +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.Migration; import ru.touchin.roboswag.core.observables.storable.Storable; 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. @@ -42,6 +46,45 @@ public class NonNullStorableBuilder extends Storabl setDefaultValueInternal(defaultValue); } + /** + * Sets specific {@link Scheduler} to store/load/convert values on it. + * + * @param storeScheduler Scheduler; + * @return Builder that allows to specify other fields. + */ + @NonNull + public NonNullStorableBuilder setStoreScheduler(@Nullable final Scheduler storeScheduler) { + setStoreSchedulerInternal(storeScheduler); + return this; + } + + /** + * Sets specific {@link Storable.ObserveStrategy} to cache value in memory in specific way. + * + * @param observeStrategy ObserveStrategy; + * @return Builder that allows to specify other fields. + */ + @NonNull + public NonNullStorableBuilder 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 setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) { + setCacheTimeInternal(cacheTime, timeUnit); + return this; + } + /** * Sets specific {@link Migration} to migrate values from specific version to latest version. * diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java index 95dc8ec..16840a9 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullStorable.java @@ -42,7 +42,7 @@ public class NonNullStorable extends Storable Date: Thu, 9 Mar 2017 17:23:22 +0300 Subject: [PATCH 5/5] static fixes --- .../roboswag/core/log/ConsoleLogProcessor.java | 4 +--- .../OnSubscribeRefCountWithCacheTime.java | 6 ++++-- .../core/observables/storable/Converter.java | 12 ++++++------ .../roboswag/core/observables/storable/Migrator.java | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java b/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java index fd7303d..0ad69c9 100644 --- a/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java +++ b/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java @@ -52,9 +52,7 @@ public class ConsoleLogProcessor extends LogProcessor { newline = newline != -1 ? newline : length; do { final int end = Math.min(newline, i + MAX_LOG_LENGTH); - if (Log.isLoggable(tag, level.getPriority())) { - Log.println(level.getPriority(), tag, messageToLog.substring(i, end)); - } + Log.println(level.getPriority(), tag, messageToLog.substring(i, end)); i = end; } while (i < newline); diff --git a/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java b/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java index 721bb99..d428aab 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/OnSubscribeRefCountWithCacheTime.java @@ -132,9 +132,7 @@ public final class OnSubscribeRefCountWithCacheTime implements OnSubscribe } private void doSubscribe(@NonNull final Subscriber subscriber, @NonNull final CompositeSubscription currentBase) { - // handle unsubscribing from the base subscription subscriber.add(disconnect(currentBase)); - source.unsafeSubscribe(new Subscriber(subscriber) { @Override public void onError(@NonNull final Throwable throwable) { @@ -159,6 +157,10 @@ public final class OnSubscribeRefCountWithCacheTime implements OnSubscribe lock.lock(); try { if (baseSubscription == currentBase) { + if (worker != null) { + worker.unsubscribe(); + worker = null; + } baseSubscription.unsubscribe(); baseSubscription = new CompositeSubscription(); subscriptionCount.set(0); diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java index 29af6ec..5cd1c5f 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Converter.java @@ -36,27 +36,27 @@ public interface Converter { /** * Converts specific object of objectType to object of storeObjectClass allowed to store. * - * @param objectClass Type of object; - * @param storeObjectClass Type 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 Type objectClass, @NonNull Type 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 objectType. * - * @param objectClass Type of object; - * @param storeObjectClass Type 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 Type objectClass, @NonNull Type storeObjectClass, @Nullable TStoreObject storeObject) + TObject toObject(@NonNull Type objectType, @NonNull Type storeObjectType, @Nullable TStoreObject storeObject) throws ConversionException; class ConversionException extends Exception { diff --git a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java index 9517aa7..7ef8539 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migrator.java @@ -66,7 +66,7 @@ public abstract class Migrator { } /** - * Observable that 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; @@ -80,7 +80,7 @@ public abstract class Migrator { } /** - * Observable that represents 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;