diff --git a/build.gradle b/build.gradle index bcc85bb..6c048aa 100644 --- a/build.gradle +++ b/build.gradle @@ -17,8 +17,6 @@ android { } dependencies { - //TODO: update when Google will fix restoring fragment state in ViewPager - //noinspection GradleDependency, NewerVersionAvailable - provided 'com.android.support:support-annotations:25.0.1' + provided 'com.android.support:support-annotations:25.3.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 d781300..0c05b84 100644 --- a/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java +++ b/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java @@ -19,7 +19,6 @@ package ru.touchin.roboswag.core.log; -import android.annotation.SuppressLint; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; @@ -36,12 +35,14 @@ public class ConsoleLogProcessor extends LogProcessor { super(lclevel); } + @NonNull private String normalize(@NonNull final String message) { return message.replace("\r\n", "\n").replace("\0", ""); } @Override - @SuppressLint("LogConditional") + @SuppressWarnings({"WrongConstant", "LogConditional"}) + //WrongConstant, LogConditional: 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) : "")); @@ -51,7 +52,6 @@ public class ConsoleLogProcessor extends LogProcessor { newline = newline != -1 ? newline : length; do { final int end = Math.min(newline, i + MAX_LOG_LENGTH); - //noinspection WrongConstant Log.println(level.getPriority(), tag, messageToLog.substring(i, end)); i = end; } 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 1b86817..3d46668 100644 --- a/src/main/java/ru/touchin/roboswag/core/log/Lc.java +++ b/src/main/java/ru/touchin/roboswag/core/log/Lc.java @@ -46,12 +46,12 @@ public final class Lc { public static final LcGroup GENERAL_LC_GROUP = new LcGroup("GENERAL"); + public static final int STACK_TRACE_CODE_DEPTH; + private static boolean crashOnAssertions = true; @NonNull private static LogProcessor logProcessor = new ConsoleLogProcessor(LcLevel.ERROR); - public static final int STACK_TRACE_CODE_DEPTH; - static { final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); int stackDepth; @@ -165,7 +165,7 @@ public final class Lc { * @param message Message or format of message to log; * @param args Arguments of formatted message. */ - public static void e(@NonNull final String message, final Object... args) { + public static void e(@NonNull final String message, @NonNull final Object... args) { GENERAL_LC_GROUP.e(message, args); } @@ -176,7 +176,7 @@ public final class Lc { * @param message Message or format of message to log; * @param args Arguments of formatted message. */ - public static void e(@NonNull final Throwable throwable, @NonNull final String message, final Object... args) { + public static void e(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { GENERAL_LC_GROUP.e(throwable, message, args); } @@ -253,8 +253,8 @@ public final class Lc { @NonNull public static String getCodePoint(@Nullable final Object caller, final int stackShift) { final StackTraceElement traceElement = Thread.currentThread().getStackTrace()[STACK_TRACE_CODE_DEPTH + stackShift]; - return (caller != null ? caller.getClass().getName() + '(' + caller.hashCode() + ") at " : "") - + traceElement.getFileName() + ':' + traceElement.getMethodName() + ':' + traceElement.getLineNumber(); + return traceElement.getMethodName() + '(' + traceElement.getFileName() + ':' + traceElement.getLineNumber() + ')' + + (caller != null ? " of object " + caller.getClass().getSimpleName() + '(' + Integer.toHexString(caller.hashCode()) + ')' : ""); } /** @@ -266,7 +266,9 @@ public final class Lc { @SuppressLint("LogConditional") 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/log/LcGroup.java b/src/main/java/ru/touchin/roboswag/core/log/LcGroup.java index 4ced178..01777f9 100644 --- a/src/main/java/ru/touchin/roboswag/core/log/LcGroup.java +++ b/src/main/java/ru/touchin/roboswag/core/log/LcGroup.java @@ -186,7 +186,7 @@ public class LcGroup { * @param message Message or format of message to log; * @param args Arguments of formatted message. */ - public void e(@NonNull final String message, final Object... args) { + public void e(@NonNull final String message, @NonNull final Object... args) { logMessage(LcLevel.ERROR, message, null, args); } @@ -197,7 +197,7 @@ public class LcGroup { * @param message Message or format of message to log; * @param args Arguments of formatted message. */ - public void e(@NonNull final Throwable throwable, @NonNull final String message, final Object... args) { + public void e(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { logMessage(LcLevel.ERROR, message, throwable, args); } diff --git a/src/main/java/ru/touchin/roboswag/core/observables/Changeable.java b/src/main/java/ru/touchin/roboswag/core/observables/Changeable.java index 40f4f1f..171fc42 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/Changeable.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/Changeable.java @@ -27,6 +27,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import ru.touchin.roboswag.core.utils.ObjectUtils; import rx.Observable; import rx.subjects.BehaviorSubject; @@ -50,7 +51,7 @@ public class Changeable implements Serializable { * * @param value Value to set. */ - public void set(final T value) { + public void set(@Nullable final T value) { subject.onNext(value); } @@ -74,17 +75,17 @@ public class Changeable implements Serializable { return subject.distinctUntilChanged(); } - private void writeObject(final ObjectOutputStream outputStream) throws IOException { + private void writeObject(@NonNull final ObjectOutputStream outputStream) throws IOException { outputStream.writeObject(subject.getValue()); } @SuppressWarnings("unchecked") - private void readObject(final ObjectInputStream inputStream) throws IOException, ClassNotFoundException { + private void readObject(@NonNull final ObjectInputStream inputStream) throws IOException, ClassNotFoundException { subject = BehaviorSubject.create((T) inputStream.readObject()); } @Override - public boolean equals(final Object object) { + public boolean equals(@Nullable final Object object) { if (this == object) { return true; } @@ -93,8 +94,7 @@ public class Changeable implements Serializable { } final Changeable that = (Changeable) object; - return subject.getValue() != null ? subject.getValue().equals(that.subject.getValue()) : that.subject.getValue() == null; - + return ObjectUtils.equals(subject.getValue(), that.subject.getValue()); } @Override 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/RxAndroidUtils.java b/src/main/java/ru/touchin/roboswag/core/observables/RxAndroidUtils.java index 4e4c006..96ff8d6 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/RxAndroidUtils.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/RxAndroidUtils.java @@ -98,7 +98,7 @@ public final class RxAndroidUtils { @SuppressWarnings("unchecked") @Override - public void onServiceConnected(final ComponentName name, final IBinder service) { + public void onServiceConnected(@NonNull final ComponentName name, @Nullable final IBinder service) { if (subscriber == null) { return; } @@ -111,7 +111,7 @@ public final class RxAndroidUtils { } @Override - public void onServiceDisconnected(final ComponentName name) { + public void onServiceDisconnected(@NonNull final ComponentName name) { if (subscriber != null) { subscriber.onNext(null); } @@ -121,8 +121,9 @@ public final class RxAndroidUtils { private static class LooperThread extends Thread { + @NonNull private final CountDownLatch isLooperInitialized = new CountDownLatch(1); - public Looper looper; + private Looper looper; @Override public void run() { 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/collections/Change.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/Change.java index 168c80c..876d25f 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/collections/Change.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/collections/Change.java @@ -103,6 +103,7 @@ public class Change { return count; } + @NonNull @Override public String toString() { return type + " change of " + start + ":" + count; diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreLoadRequest.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreLoadRequest.java index 8e4830f..6cac3fc 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreLoadRequest.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreLoadRequest.java @@ -60,10 +60,10 @@ public class MoreLoadRequest { } @Override - public boolean equals(final Object obj) { - return obj instanceof MoreLoadRequest - && ObjectUtils.equals(((MoreLoadRequest) obj).moreReference, moreReference) - && ((MoreLoadRequest) obj).nextPosition == nextPosition; + public boolean equals(@Nullable final Object object) { + return object instanceof MoreLoadRequest + && ObjectUtils.equals(((MoreLoadRequest) object).moreReference, moreReference) + && ((MoreLoadRequest) object).nextPosition == nextPosition; } @Override diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/NewerLoadRequest.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/NewerLoadRequest.java index d2d3619..6566bd9 100644 --- a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/NewerLoadRequest.java +++ b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/NewerLoadRequest.java @@ -60,10 +60,10 @@ public class NewerLoadRequest { } @Override - public boolean equals(final Object obj) { - return obj instanceof NewerLoadRequest - && ObjectUtils.equals(((NewerLoadRequest) obj).newerReference, newerReference) - && ((NewerLoadRequest) obj).newerItemsCount == newerItemsCount; + public boolean equals(@Nullable final Object object) { + return object instanceof NewerLoadRequest + && ObjectUtils.equals(((NewerLoadRequest) object).newerReference, newerReference) + && ((NewerLoadRequest) object).newerItemsCount == newerItemsCount; } @Override 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..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 @@ -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 objectType Type of object; + * @param storeObjectType Type of store object allowed to store; * @param object Object to be converted to store object; * @return Object that is allowed to store into specific {@link Store}; * @throws ConversionException Exception during conversion. Usually it indicates illegal state. */ @Nullable - TStoreObject toStoreObject(@NonNull Class objectClass, @NonNull Class storeObjectClass, @Nullable TObject object) + TStoreObject toStoreObject(@NonNull Type objectType, @NonNull Type storeObjectType, @Nullable TObject object) throws ConversionException; /** - * Converts specific store object of storeObjectClass to object of objectClass. + * Converts specific store object of storeObjectClass to object of objectType. * - * @param objectClass Class of object; - * @param storeObjectClass Class of store object allowed to store; + * @param objectType Type of object; + * @param storeObjectType Type of store object allowed to store; * @param storeObject Object from specific {@link Store}; * @return Object converted from store object; * @throws ConversionException Exception during conversion. Usually it indicates illegal state. */ @Nullable - TObject toObject(@NonNull Class objectClass, @NonNull Class 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/Migration.java b/src/main/java/ru/touchin/roboswag/core/observables/storable/Migration.java index 03cad11..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 @@ -20,11 +20,15 @@ package ru.touchin.roboswag.core.observables.storable; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import java.util.Arrays; import java.util.List; +import rx.Completable; +import rx.Observable; +import rx.Single; +import rx.exceptions.OnErrorThrowable; + /** * Created by Gavriil Sitnikov on 06/10/2015. * Object that allows to migrate some store objects from one version to another by migrators passed into constructor. @@ -51,60 +55,102 @@ public class Migration { this.migrators = Arrays.asList(migrators); } - private long loadCurrentVersion(@NonNull final TKey key) throws MigrationException { - final Long result; - try { - result = versionsStore.loadObject(Long.class, key); - } catch (final Store.StoreException throwable) { - throw new MigrationException(String.format("Can't get version of '%s' from %s", key, versionsStore), throwable); - } - return result != null ? result : DEFAULT_VERSION; + @NonNull + private Single loadCurrentVersion(@NonNull final TKey key) { + return versionsStore.loadObject(Long.class, key) + .map(version -> version != null ? version : DEFAULT_VERSION) + .onErrorResumeNext(throwable + -> Single.error(new MigrationException(String.format("Can't get version of '%s' from %s", key, versionsStore), throwable))); } - private void checkMigrationResult(@NonNull final TKey key, final long oldVersion, final long currentVersion, @Nullable final Migrator migrator) - throws MigrationException { - if (oldVersion > currentVersion) { - throw new MigrationException(String.format("Version of '%s' downgraded from %s to %s [from %s by %s]", - key, oldVersion, currentVersion, versionsStore, migrator)); - } - if (currentVersion > latestVersion) { - throw new MigrationException(String.format("Version of '%s' is %s and higher than latest version %s [from %s by %s]", - key, oldVersion, currentVersion, versionsStore, migrator)); - } - if (oldVersion == currentVersion && migrator != null) { - throw new MigrationException(String.format("Version of '%s' is %s and stood same [from %s by %s]", - key, currentVersion, versionsStore, migrator)); + @NonNull + private Single 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. * - * @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); + return makeMigrationChain(key, versionUpdater) + .doOnSuccess(lastUpdatedVersion -> { + if (lastUpdatedVersion < latestVersion) { + throw OnErrorThrowable.from(new NextLoopMigrationException()); + } + if (versionUpdater.initialVersion == versionUpdater.oldVersion) { + throw new MigrationException(String.format("Version of '%s' not updated from %s", + key, versionUpdater.initialVersion)); + } + }) + .retryWhen(attempts -> attempts.switchMap(throwable -> throwable instanceof NextLoopMigrationException + ? Observable.just(null) : Observable.error(throwable))); + }) + .toCompletable() + .andThen(versionsStore.storeObject(Long.class, key, latestVersion)) + .onErrorResumeNext(throwable -> { + if (throwable instanceof MigrationException) { + return Completable.error(throwable); + } + return Completable.error(new MigrationException(String.format("Can't migrate '%s'", key), throwable)); + }); } - public static class MigrationException extends Exception { + private static class VersionUpdater { + + @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..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 @@ -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. + * Single that migrates object with specific key from some version to migrator's version. * * @param key Key of object to migrate; * @param version Current version of object; - * @return New version of object after migration process; - * @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state. + * @return {@link Single} that emits new version of object after migration process. */ - public long migrate(@NonNull final TKey key, final long version) throws Migration.MigrationException { - if (!supportsMigrationFor(version)) { - throw new Migration.MigrationException(String.format("Version %s of '%s' is not supported by %s", version, key, this)); - } - return migrateInternal(key, version, oldStore, newStore); + @NonNull + public Single migrate(@NonNull final TKey key, final long version) { + return supportsMigrationFor(version) + ? migrateInternal(key, version, oldStore, newStore) + : Single.error(new Migration.MigrationException(String.format("Version %s of '%s' is not supported by %s", version, key, this))); } /** - * Internal migration logic specified by implementation. + * Single that represents internal migration logic specified by implementation. * * @param key Key of object to migrate; * @param version Current version of object; * @param oldStore Old store of object; * @param newStore new store of object; - * @return New version of object after migration process; - * @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state. + * @return {@link Single} that emits new version of object after migration process. */ - protected abstract long migrateInternal(@NonNull TKey key, - final long version, - @NonNull Store 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 b11b5f7..dea99cd 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,22 +22,18 @@ package ru.touchin.roboswag.core.observables.storable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import java.lang.reflect.Type; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import ru.touchin.roboswag.core.log.LcGroup; -import ru.touchin.roboswag.core.observables.ObservableResult; import ru.touchin.roboswag.core.observables.OnSubscribeRefCountWithCacheTime; -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; @@ -58,14 +54,25 @@ public class Storable { public static final LcGroup STORABLE_LC_GROUP = new LcGroup("STORABLE"); - private static final long CACHE_TIME = TimeUnit.SECONDS.toMillis(5); + private static final long DEFAULT_CACHE_TIME_MILLIS = TimeUnit.SECONDS.toMillis(5); + + @NonNull + private static ObserveStrategy getDefaultObserveStrategyFor(@NonNull final Type objectType, @NonNull final Type storeObjectType) { + if (objectType instanceof Class && ObjectUtils.isSimpleClass((Class) objectType)) { + return ObserveStrategy.CACHE_ACTUAL_VALUE; + } + if (objectType instanceof Class && ObjectUtils.isSimpleClass((Class) storeObjectType)) { + return ObserveStrategy.CACHE_STORE_VALUE; + } + return ObserveStrategy.NO_CACHE; + } @NonNull private final TKey key; @NonNull - private final Class objectClass; + private final Type objectType; @NonNull - private final Class storeObjectClass; + private final Type storeObjectType; @NonNull private final Store store; @NonNull @@ -73,117 +80,103 @@ public class Storable { @NonNull private final PublishSubject newStoreValueEvent = PublishSubject.create(); @NonNull + private final Observable storeValueObservable; + @NonNull private final Observable valueObservable; @NonNull - private final Scheduler storeScheduler; + 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); + builderCore.migration, builderCore.defaultValue, builderCore.storeScheduler, builderCore.cacheTimeMillis); } - public Storable(@NonNull final TKey key, - @NonNull final Class objectClass, - @Nullable final Class storeObjectClass, - @Nullable final Store store, - @Nullable 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(); - } + @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.objectClass = objectClass; - this.storeObjectClass = storeObjectClass; + this.objectType = objectType; + this.storeObjectType = storeObjectType; this.store = store; this.converter = converter; - final ObserveStrategy nonNullObserveStrategy = observeStrategy != null ? observeStrategy : getDefaultGetStrategy(); - this.storeScheduler = storeScheduler != null ? storeScheduler : Schedulers.from(Executors.newSingleThreadExecutor()); - final Observable storeValueObservable - = createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue); - valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy); - } - @NonNull - private ObserveStrategy getDefaultGetStrategy() { - if (ObjectUtils.isSimpleClass(objectClass)) { - return ObserveStrategy.CACHE_ACTUAL_VALUE; - } - if (ObjectUtils.isSimpleClass(storeObjectClass)) { - return ObserveStrategy.CACHE_STORE_VALUE; - } - return ObserveStrategy.NO_CACHE; + final ObserveStrategy nonNullObserveStrategy + = observeStrategy != null ? observeStrategy : getDefaultObserveStrategyFor(objectType, storeObjectType); + scheduler = storeScheduler != null ? storeScheduler : Schedulers.from(Executors.newSingleThreadExecutor()); + storeValueObservable + = createStoreValueObservable(nonNullObserveStrategy, migration, defaultValue, cacheTimeMillis); + valueObservable = createValueObservable(storeValueObservable, nonNullObserveStrategy, cacheTimeMillis); } @Nullable private TStoreObject returnDefaultValueIfNull(@Nullable final TStoreObject storeObject, @Nullable final TObject defaultValue) { - if (storeObject == null && defaultValue != null) { - try { - return converter.toStoreObject(objectClass, storeObjectClass, defaultValue); - } catch (final Converter.ConversionException exception) { - STORABLE_LC_GROUP.w(exception, "Exception while converting default value of '%s' from '%s' from store %s", - key, defaultValue, store); - throw OnErrorThrowable.from(exception); - } catch (final RuntimeException throwable) { - STORABLE_LC_GROUP.assertion(throwable); - } + if (storeObject != null || defaultValue == null) { + return storeObject; + } + + try { + return converter.toStoreObject(objectType, storeObjectType, defaultValue); + } catch (final Converter.ConversionException exception) { + STORABLE_LC_GROUP.w(exception, "Exception while converting default value of '%s' from '%s' from store %s", + key, defaultValue, store); + throw OnErrorThrowable.from(exception); + } catch (final RuntimeException throwable) { + STORABLE_LC_GROUP.assertion(throwable); + throw OnErrorThrowable.from(throwable); } - return storeObject; } @NonNull private Observable createStoreValueObservable(@NonNull final ObserveStrategy observeStrategy, @Nullable final Migration migration, - @Nullable final TObject defaultValue) { - 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 long cacheTimeMillis) { + 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) .concatWith(newStoreValueEvent) .map(storeObject -> returnDefaultValueIfNull(storeObject, defaultValue)); - return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE - ? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), CACHE_TIME, TimeUnit.MILLISECONDS)) + return observeStrategy == ObserveStrategy.CACHE_STORE_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE + ? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS)) : result; } @NonNull private Observable createValueObservable(@NonNull final Observable storeValueObservable, - @NonNull final ObserveStrategy observeStrategy) { + @NonNull final ObserveStrategy observeStrategy, + final long cacheTimeMillis) { 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); - - return observeStrategy == ObserveStrategy.CACHE_ACTUAL_VALUE - ? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), CACHE_TIME, TimeUnit.MILLISECONDS)) + .switchMap(storeObject -> Observable + .fromCallable(() -> converter.toObject(objectType, storeObjectType, storeObject)) + .subscribeOn(scheduler) + .doOnError(throwable -> { + if (throwable instanceof RuntimeException) { + STORABLE_LC_GROUP.assertion(throwable); + } else { + STORABLE_LC_GROUP.w(throwable, "Exception while trying to converting value of '%s' from store %s by %s", + key, storeObject, store, converter); + } + })); + return observeStrategy == ObserveStrategy.CACHE_ACTUAL_VALUE || observeStrategy == ObserveStrategy.CACHE_STORE_AND_ACTUAL_VALUE + ? Observable.create(new OnSubscribeRefCountWithCacheTime<>(result.replay(1), cacheTimeMillis, TimeUnit.MILLISECONDS)) : result; } @@ -198,23 +191,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; } /** @@ -238,89 +231,80 @@ public class Storable { } @NonNull - private Observable internalSet(@Nullable final TObject newValue, final boolean checkForEqualityBeforeSet) { - return (checkForEqualityBeforeSet ? valueObservable.first() : Observable.just(null)) - .switchMap(value -> { - if (checkForEqualityBeforeSet && ObjectUtils.equals(value, newValue)) { - return Observable.empty(); + private Completable internalSet(@Nullable final TObject newValue, final boolean checkForEqualityBeforeSet) { + return (checkForEqualityBeforeSet ? storeValueObservable.first() : Observable.just(null)) + .switchMap(oldStoreValue -> Observable + .fromCallable(() -> converter.toStoreObject(objectType, storeObjectType, newValue)) + .subscribeOn(scheduler) + .switchMap(newStoreValue -> { + if (checkForEqualityBeforeSet && ObjectUtils.equals(newStoreValue, oldStoreValue)) { + return Observable.empty(); + } + return store.storeObject(storeObjectType, key, newStoreValue) + .doOnCompleted(() -> { + newStoreValueEvent.onNext(newStoreValue); + if (checkForEqualityBeforeSet) { + STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, oldStoreValue, newStoreValue); + } else { + STORABLE_LC_GROUP.i("Value of '%s' force changed to '%s'", key, newStoreValue); + } + }) + .toObservable(); + })) + .doOnError(throwable -> { + if (throwable instanceof RuntimeException) { + STORABLE_LC_GROUP.assertion(throwable); + } else { + STORABLE_LC_GROUP.w(throwable, "Exception while trying to store value of '%s' from store %s by %s", + key, newValue, store, converter); } - return 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); - } - }) - .subscribeOn(storeScheduler); - }); + }) + .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! + * 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); + return internalSet(newValue, false).toObservable(); } /** * Creates observable which is async setting value to store. - * NOTE: It could emit ONLY completed and errors events. It is not providing onNext event! - * Errors won't be emitted if {@link #getStore()} implements {@link SafeStore} and {@link #getConverter()} implements {@link SafeConverter}. + * It is 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); - } - - /** - * Setting value in background. If error emits then it will raise assertion. - * - * @param newValue Value to set. - */ - public void setCalm(@Nullable final TObject newValue) { - set(newValue).subscribe(Actions.empty(), STORABLE_LC_GROUP::assertion); + return internalSet(newValue, true).toObservable(); } /** * Sets value synchronously. You should NOT use this method normally. Use {@link #set(Object)} asynchronously instead. * * @param newValue Value to set; - * @throws Store.StoreException Throws if {@link Store} threw exception during storing; - * @throws Converter.ConversionException Throws if {@link Converter} threw exception during conversion; - * @throws Migration.MigrationException Throws if {@link Migration} threw exception during migration. */ - public void setSync(@Nullable final TObject newValue) - throws Store.StoreException, Converter.ConversionException, Migration.MigrationException { - final ObservableResult setResult = RxUtils.executeSync(set(newValue)); - checkStorableObservableResult(setResult); + @Deprecated + //deprecation: it should be used for debug only and in very rare cases. + public void setSync(@Nullable final TObject newValue) { + set(newValue).toBlocking().subscribe(); } /** * Returns Observable which is emitting item on subscribe and every time when someone have changed value. * It could emit next and error events but not completed. - * Errors won't be emitted if {@link #getStore()} implements {@link SafeStore} and {@link #getConverter()} implements {@link SafeConverter}. * * @return Returns observable of value. */ @@ -330,9 +314,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. */ @@ -345,38 +328,16 @@ 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 { - final ObservableResult getResult = RxUtils.executeSync(get()); - checkStorableObservableResult(getResult); - if (getResult.getItems().size() != 1) { - throw new ShouldNotHappenException(); - } - return getResult.getItems().get(0); - } - - private void checkStorableObservableResult(@NonNull final ObservableResult result) - throws Store.StoreException, Converter.ConversionException, Migration.MigrationException { - for (final Throwable throwable : result.getErrors()) { - if (throwable instanceof Store.StoreException) { - throw (Store.StoreException) throwable; - } - if (throwable instanceof Converter.ConversionException) { - throw (Converter.ConversionException) throwable; - } - if (throwable instanceof Migration.MigrationException) { - throw (Migration.MigrationException) throwable; - } - } + public TObject getSync() { + return get().toBlocking().first(); } /** - * Class that is representing strategy of observing item from store. + * Enum that is representing strategy of observing item from store. */ public enum ObserveStrategy { @@ -391,10 +352,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 } @@ -410,112 +380,81 @@ 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; + private long cacheTimeMillis; 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, DEFAULT_CACHE_TIME_MILLIS); } 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); + sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler, sourceBuilder.cacheTimeMillis); } - @SuppressWarnings("CPD-START") - //CPD: it is ok that builder copy-pasted parent constructor parameters + @SuppressWarnings({"PMD.ExcessiveParameterList", "CPD-START"}) + //CPD: it is same code as constructor of Storable + //ExcessiveParameterList: that's why we are using builder to create it private BuilderCore(@NonNull final TKey key, - @NonNull final Class 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) { + @Nullable final Scheduler storeScheduler, + final long cacheTimeMillis) { this.key = key; - this.objectClass = objectClass; - this.storeObjectClass = storeObjectClass; + this.objectType = objectType; + this.storeObjectType = storeObjectType; this.store = store; this.converter = converter; this.observeStrategy = observeStrategy; this.migration = migration; this.defaultValue = defaultValue; this.storeScheduler = storeScheduler; + this.cacheTimeMillis = cacheTimeMillis; } @SuppressWarnings("CPD-END") - @Nullable - public Class 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. - */ + protected void setCacheTimeInternal(final long cacheTime, @NonNull final TimeUnit timeUnit) { + this.cacheTimeMillis = timeUnit.toMillis(cacheTime); + } + @Nullable - public TObject getDefaultValue() { + protected TObject getDefaultValue() { return defaultValue; } @@ -535,59 +474,49 @@ 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; + } + + /** + * 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; } /** @@ -597,8 +526,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; } /** 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..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,15 +20,15 @@ package ru.touchin.roboswag.core.observables.storable.builders; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.concurrent.TimeUnit; -import ru.touchin.roboswag.core.observables.storable.Converter; import ru.touchin.roboswag.core.observables.storable.Migration; -import ru.touchin.roboswag.core.observables.storable.SafeConverter; -import ru.touchin.roboswag.core.observables.storable.SafeStore; import ru.touchin.roboswag.core.observables.storable.Storable; -import ru.touchin.roboswag.core.observables.storable.Store; import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable; import ru.touchin.roboswag.core.utils.ShouldNotHappenException; +import rx.Scheduler; /** * Created by Gavriil Sitnikov on 15/05/2016. @@ -47,36 +47,42 @@ public class NonNullStorableBuilder extends Storabl } /** - * Sets store and converter. + * Sets specific {@link Scheduler} to store/load/convert values on it. * - * @param storeObjectClass Class of store object, - * @param store Store to store objects into; - * @param converter Converter to convert values from store class to actual class and back; + * @param storeScheduler Scheduler; * @return Builder that allows to specify other fields. */ @NonNull - public NonNullStorableBuilder setStore(@NonNull final Class storeObjectClass, - @NonNull final Store store, - @NonNull final Converter converter) { - setStoreInternal(storeObjectClass, store, converter); + public NonNullStorableBuilder setStoreScheduler(@Nullable final Scheduler storeScheduler) { + setStoreSchedulerInternal(storeScheduler); return this; } /** - * Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action - * when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods. + * Sets specific {@link Storable.ObserveStrategy} to cache value in memory in specific way. * - * @param storeObjectClass Class of store object, - * @param store Safe store that is not throwing exceptions; - * @param converter Safe converter that is not throwing exceptions; + * @param observeStrategy ObserveStrategy; * @return Builder that allows to specify other fields. */ @NonNull - public NonNullSafeStorableBuilder setSafeStore(@NonNull final Class storeObjectClass, - @NonNull final SafeStore store, - @NonNull final SafeConverter converter) { - setStoreInternal(storeObjectClass, store, converter); - return new NonNullSafeStorableBuilder<>(this); + 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; } /** @@ -86,9 +92,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 ed4e584..0000000 --- a/src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeListStorable.java +++ /dev/null @@ -1,104 +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#forceSet(Object)} (Object)}. - */ - @NonNull - public Observable forseSet(@Nullable final List list) { - return storable.forceSet(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); - } - } - -} diff --git a/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java b/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java index b7efd9a..cb41c00 100644 --- a/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java +++ b/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java @@ -27,6 +27,8 @@ import java.util.Collection; import java.util.Iterator; import java.util.Map; +import ru.touchin.roboswag.core.observables.collections.ObservableCollection; + /** * Created by Gavriil Sitnikov on 04/10/2015. * Some utilities related to objects. @@ -102,11 +104,12 @@ public final class ObjectUtils { public static boolean isMapsEquals(@Nullable final Map map1, @Nullable final Map map2) { return map1 == map2 || !(map1 == null || map2 == null) && map1.size() == map2.size() - && map1.entrySet().containsAll(map2.entrySet()); + && map1.entrySet().containsAll(map2.entrySet()) + && map2.entrySet().containsAll(map1.entrySet()); } @SuppressWarnings("PMD.AvoidUsingShortType") - private static boolean isArraysEquals(@NonNull final Object object1, @Nullable final Object object2, final Class elementType) { + private static boolean isArraysEquals(@NonNull final Object object1, @Nullable final Object object2, @NonNull final Class elementType) { if (object1 instanceof Object[]) { return Arrays.deepEquals((Object[]) object1, (Object[]) object2); } else if (elementType == int.class) { @@ -150,6 +153,36 @@ public final class ObjectUtils { || objectClass == String.class || objectClass == Object.class; } + /** + * Returns true if collection is null or empty. + * + * @param collection Collection to check; + * @return True if collection is null or empty. + */ + public static boolean isNullOrEmpty(@Nullable final Collection collection) { + return collection == null || collection.isEmpty(); + } + + /** + * Returns true if ObservableCollection is null or empty. + * + * @param observableCollection observableCollection to check; + * @return True if observableCollection is null or empty. + */ + public static boolean isNullOrEmpty(@Nullable final ObservableCollection observableCollection) { + return observableCollection == null || observableCollection.isEmpty(); + } + + /** + * Returns true if map is null or empty. + * + * @param map Map to check; + * @return True if map is null or empty. + */ + public static boolean isNullOrEmpty(@Nullable final Map map) { + return map == null || map.isEmpty(); + } + private ObjectUtils() { } diff --git a/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.java b/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.java index ce8b235..6efbcc7 100644 --- a/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.java +++ b/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.java @@ -32,14 +32,23 @@ public class ThreadLocalValue extends ThreadLocal { @NonNull private final Func0 creator; - public ThreadLocalValue(@NonNull final Func0 creator) { + public ThreadLocalValue(@NonNull final NonNullFunc creator) { super(); this.creator = creator; } + @NonNull @Override protected T initialValue() { return creator.call(); } + public interface NonNullFunc extends Func0 { + + @NonNull + @Override + T call(); + + } + } \ No newline at end of file diff --git a/src/main/java/ru/touchin/roboswag/core/utils/pairs/HalfNullablePair.java b/src/main/java/ru/touchin/roboswag/core/utils/pairs/HalfNullablePair.java index e00d0b4..6e5fb84 100644 --- a/src/main/java/ru/touchin/roboswag/core/utils/pairs/HalfNullablePair.java +++ b/src/main/java/ru/touchin/roboswag/core/utils/pairs/HalfNullablePair.java @@ -49,14 +49,6 @@ public class HalfNullablePair implements Serializable { this.second = second; } - /** - * Get first argument of this pair. It is always not null. - */ - @NonNull - public TFirst getFirst() { - return first; - } - /** * Get second argument of this pair. It may be nullable. */ @@ -65,8 +57,16 @@ public class HalfNullablePair implements Serializable { return second; } + /** + * Get first argument of this pair. It is always not null. + */ + @NonNull + public TFirst getFirst() { + return first; + } + @Override - public boolean equals(final Object object) { + public boolean equals(@Nullable final Object object) { if (this == object) { return true; } diff --git a/src/main/java/ru/touchin/roboswag/core/utils/pairs/NonNullPair.java b/src/main/java/ru/touchin/roboswag/core/utils/pairs/NonNullPair.java index ae6592a..f185c66 100644 --- a/src/main/java/ru/touchin/roboswag/core/utils/pairs/NonNullPair.java +++ b/src/main/java/ru/touchin/roboswag/core/utils/pairs/NonNullPair.java @@ -20,6 +20,7 @@ package ru.touchin.roboswag.core.utils.pairs; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.io.Serializable; @@ -65,7 +66,7 @@ public class NonNullPair implements Serializable { } @Override - public boolean equals(final Object object) { + public boolean equals(@Nullable final Object object) { if (this == object) { return true; } diff --git a/src/main/java/ru/touchin/roboswag/core/utils/pairs/NullablePair.java b/src/main/java/ru/touchin/roboswag/core/utils/pairs/NullablePair.java index 28c4b4b..21f630d 100644 --- a/src/main/java/ru/touchin/roboswag/core/utils/pairs/NullablePair.java +++ b/src/main/java/ru/touchin/roboswag/core/utils/pairs/NullablePair.java @@ -70,17 +70,17 @@ public class NullablePair implements Serializable { //todo: mb } @Override - public boolean equals(final Object obj) { - if (this == obj) { + public boolean equals(@Nullable final Object object) { + if (this == object) { return true; } - if (obj == null || getClass() != obj.getClass()) { + if (object == null || getClass() != object.getClass()) { return false; } - final NullablePair that = (NullablePair) obj; + final NullablePair that = (NullablePair) object; - return ObjectUtils.equals(first, that.getFirst()) && ObjectUtils.equals(second, that.getSecond()); + return ObjectUtils.equals(first, that.first) && ObjectUtils.equals(second, that.second); } @Override