Merge branch 'master' into feature/tutu
# Conflicts: # build.gradle # src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java # src/main/java/ru/touchin/roboswag/core/observables/storable/Storable.java # src/main/java/ru/touchin/roboswag/core/observables/storable/concrete/NonNullSafeListStorable.java
This commit is contained in:
commit
3fef1bf498
|
|
@ -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'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<T> 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<T> 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<T> 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
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ public class ObservableResult<T> {
|
|||
|
||||
@NonNull
|
||||
private final List<T> items = new LinkedList<>();
|
||||
@NonNull
|
||||
private final List<Throwable> errors = new LinkedList<>();
|
||||
@Nullable
|
||||
private Throwable error;
|
||||
|
||||
/**
|
||||
* Passes item to collect.
|
||||
|
|
@ -33,7 +33,7 @@ public class ObservableResult<T> {
|
|||
* @param error Emitted error.
|
||||
*/
|
||||
public void onError(@NonNull final Throwable error) {
|
||||
errors.add(error);
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -47,13 +47,13 @@ public class ObservableResult<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns list of collected errors.
|
||||
* Returns collected error.
|
||||
*
|
||||
* @return Errors.
|
||||
* @return Error.
|
||||
*/
|
||||
@NonNull
|
||||
public List<Throwable> getErrors() {
|
||||
return new ArrayList<>(errors);
|
||||
@Nullable
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
package ru.touchin.roboswag.core.observables;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 21/05/2016.
|
||||
* Some helper methods to work with JavaRx.
|
||||
*/
|
||||
public final class RxUtils {
|
||||
|
||||
/**
|
||||
* Subscribes to specific {@link Observable} and waits for it's onCompleted event
|
||||
* and then returns {@link ObservableResult} with all collected items and errors during subscription.
|
||||
* You should NOT use such method normally. It is safer than {@link Observable#toBlocking()} but it is also like a hack.
|
||||
*
|
||||
* @param observable {@link Observable} to be executed;
|
||||
* @param <T> Type of {@link Observable}'s items;
|
||||
* @return {@link ObservableResult} which contains all items and errors collected during execution.
|
||||
*/
|
||||
@NonNull
|
||||
public static <T> ObservableResult<T> executeSync(@NonNull final Observable<T> observable) {
|
||||
final ObservableResult<T> result = new ObservableResult<>();
|
||||
final CountDownLatch waiter = new CountDownLatch(1);
|
||||
observable.subscribe(result::onNext, result::onError, waiter::countDown);
|
||||
try {
|
||||
waiter.await();
|
||||
} catch (final InterruptedException exception) {
|
||||
throw new ShouldNotHappenException(exception);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private RxUtils() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -103,6 +103,7 @@ public class Change<TItem> {
|
|||
return count;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return type + " change of " + start + ":" + count;
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ public class MoreLoadRequest<TMoreReference> {
|
|||
}
|
||||
|
||||
@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
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ public class NewerLoadRequest<TNewerReference> {
|
|||
}
|
||||
|
||||
@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
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ package ru.touchin.roboswag.core.observables.storable;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 04/10/2015.
|
||||
* Interface that is providing logic to convert value from specific type to type allowed to store in {@link Store} object and back.
|
||||
|
|
@ -32,29 +34,29 @@ import android.support.annotation.Nullable;
|
|||
public interface Converter<TObject, TStoreObject> {
|
||||
|
||||
/**
|
||||
* Converts specific object of objectClass to object of storeObjectClass allowed to store.
|
||||
* Converts specific object of objectType to object of storeObjectClass allowed to store.
|
||||
*
|
||||
* @param objectClass Class of object;
|
||||
* @param storeObjectClass Class of store object allowed to store;
|
||||
* @param objectType Type of object;
|
||||
* @param storeObjectType Type of store object allowed to store;
|
||||
* @param object Object to be converted to store object;
|
||||
* @return Object that is allowed to store into specific {@link Store};
|
||||
* @throws ConversionException Exception during conversion. Usually it indicates illegal state.
|
||||
*/
|
||||
@Nullable
|
||||
TStoreObject toStoreObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TObject object)
|
||||
TStoreObject toStoreObject(@NonNull Type objectType, @NonNull Type storeObjectType, @Nullable TObject object)
|
||||
throws ConversionException;
|
||||
|
||||
/**
|
||||
* Converts specific store object of storeObjectClass to object of objectClass.
|
||||
* Converts specific store object of storeObjectClass to object of objectType.
|
||||
*
|
||||
* @param objectClass Class of object;
|
||||
* @param storeObjectClass Class of store object allowed to store;
|
||||
* @param objectType Type of object;
|
||||
* @param storeObjectType Type of store object allowed to store;
|
||||
* @param storeObject Object from specific {@link Store};
|
||||
* @return Object converted from store object;
|
||||
* @throws ConversionException Exception during conversion. Usually it indicates illegal state.
|
||||
*/
|
||||
@Nullable
|
||||
TObject toObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TStoreObject storeObject)
|
||||
TObject toObject(@NonNull Type objectType, @NonNull Type storeObjectType, @Nullable TStoreObject storeObject)
|
||||
throws ConversionException;
|
||||
|
||||
class ConversionException extends Exception {
|
||||
|
|
|
|||
|
|
@ -20,11 +20,15 @@
|
|||
package ru.touchin.roboswag.core.observables.storable;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.exceptions.OnErrorThrowable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 06/10/2015.
|
||||
* Object that allows to migrate some store objects from one version to another by migrators passed into constructor.
|
||||
|
|
@ -51,60 +55,102 @@ public class Migration<TKey> {
|
|||
this.migrators = Arrays.asList(migrators);
|
||||
}
|
||||
|
||||
private long loadCurrentVersion(@NonNull final TKey key) throws MigrationException {
|
||||
final Long result;
|
||||
try {
|
||||
result = versionsStore.loadObject(Long.class, key);
|
||||
} catch (final Store.StoreException throwable) {
|
||||
throw new MigrationException(String.format("Can't get version of '%s' from %s", key, versionsStore), throwable);
|
||||
}
|
||||
return result != null ? result : DEFAULT_VERSION;
|
||||
@NonNull
|
||||
private Single<Long> loadCurrentVersion(@NonNull final TKey key) {
|
||||
return versionsStore.loadObject(Long.class, key)
|
||||
.map(version -> version != null ? version : DEFAULT_VERSION)
|
||||
.onErrorResumeNext(throwable
|
||||
-> Single.error(new MigrationException(String.format("Can't get version of '%s' from %s", key, versionsStore), throwable)));
|
||||
}
|
||||
|
||||
private void checkMigrationResult(@NonNull final TKey key, final long oldVersion, final long currentVersion, @Nullable final Migrator migrator)
|
||||
throws MigrationException {
|
||||
if (oldVersion > currentVersion) {
|
||||
throw new MigrationException(String.format("Version of '%s' downgraded from %s to %s [from %s by %s]",
|
||||
key, oldVersion, currentVersion, versionsStore, migrator));
|
||||
}
|
||||
if (currentVersion > latestVersion) {
|
||||
throw new MigrationException(String.format("Version of '%s' is %s and higher than latest version %s [from %s by %s]",
|
||||
key, oldVersion, currentVersion, versionsStore, migrator));
|
||||
}
|
||||
if (oldVersion == currentVersion && migrator != null) {
|
||||
throw new MigrationException(String.format("Version of '%s' is %s and stood same [from %s by %s]",
|
||||
key, currentVersion, versionsStore, migrator));
|
||||
@NonNull
|
||||
private Single<Long> makeMigrationChain(@NonNull final TKey key, @NonNull final VersionUpdater versionUpdater) {
|
||||
Single<Long> chain = Single.fromCallable(() -> versionUpdater.initialVersion);
|
||||
for (final Migrator<TKey, ?, ?> migrator : migrators) {
|
||||
chain = chain.flatMap(updatedVersion ->
|
||||
migrator.canMigrate(key, updatedVersion)
|
||||
.flatMap(canMigrate -> canMigrate
|
||||
? migrator.migrate(key, updatedVersion)
|
||||
.doOnSuccess(newVersion
|
||||
-> versionUpdater.updateVersion(newVersion, latestVersion, migrator))
|
||||
: Single.just(updatedVersion)));
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates some object by key to latest version.
|
||||
*
|
||||
* @param key Key of object to migrate;
|
||||
* @throws MigrationException Exception during object migration. Usually it indicates illegal state.
|
||||
* @param key Key of object to migrate.
|
||||
*/
|
||||
public void migrateToLatestVersion(@NonNull final TKey key) throws MigrationException {
|
||||
long currentVersion = loadCurrentVersion(key);
|
||||
|
||||
while (currentVersion != latestVersion) {
|
||||
final long oldVersion = currentVersion;
|
||||
for (final Migrator<TKey, ?, ?> migrator : migrators) {
|
||||
if (migrator.supportsMigrationFor(currentVersion) && migrator.canMigrate(key, currentVersion)) {
|
||||
currentVersion = migrator.migrate(key, currentVersion);
|
||||
checkMigrationResult(key, oldVersion, currentVersion, migrator);
|
||||
}
|
||||
checkMigrationResult(key, oldVersion, currentVersion, null);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
versionsStore.storeObject(Long.class, key, latestVersion);
|
||||
} catch (final Store.StoreException throwable) {
|
||||
throw new MigrationException(String.format("Can't store version %s of '%s' into %s", key, currentVersion, versionsStore), throwable);
|
||||
}
|
||||
@NonNull
|
||||
public Completable migrateToLatestVersion(@NonNull final TKey key) {
|
||||
return loadCurrentVersion(key)
|
||||
.flatMap(currentVersion -> {
|
||||
final VersionUpdater versionUpdater = new VersionUpdater<>(key, versionsStore, currentVersion);
|
||||
return makeMigrationChain(key, versionUpdater)
|
||||
.doOnSuccess(lastUpdatedVersion -> {
|
||||
if (lastUpdatedVersion < latestVersion) {
|
||||
throw OnErrorThrowable.from(new NextLoopMigrationException());
|
||||
}
|
||||
if (versionUpdater.initialVersion == versionUpdater.oldVersion) {
|
||||
throw new MigrationException(String.format("Version of '%s' not updated from %s",
|
||||
key, versionUpdater.initialVersion));
|
||||
}
|
||||
})
|
||||
.retryWhen(attempts -> attempts.switchMap(throwable -> throwable instanceof NextLoopMigrationException
|
||||
? Observable.just(null) : Observable.error(throwable)));
|
||||
})
|
||||
.toCompletable()
|
||||
.andThen(versionsStore.storeObject(Long.class, key, latestVersion))
|
||||
.onErrorResumeNext(throwable -> {
|
||||
if (throwable instanceof MigrationException) {
|
||||
return Completable.error(throwable);
|
||||
}
|
||||
return Completable.error(new MigrationException(String.format("Can't migrate '%s'", key), throwable));
|
||||
});
|
||||
}
|
||||
|
||||
public static class MigrationException extends Exception {
|
||||
private static class VersionUpdater<TKey> {
|
||||
|
||||
@NonNull
|
||||
private final TKey key;
|
||||
@NonNull
|
||||
private final Store versionsStore;
|
||||
private long oldVersion;
|
||||
private long initialVersion;
|
||||
|
||||
public VersionUpdater(@NonNull final TKey key, @NonNull final Store versionsStore, final long initialVersion) {
|
||||
this.key = key;
|
||||
this.versionsStore = versionsStore;
|
||||
this.oldVersion = initialVersion;
|
||||
this.initialVersion = initialVersion;
|
||||
}
|
||||
|
||||
public void updateVersion(final long updateVersion, final long latestVersion, @NonNull final Migrator migrator) {
|
||||
if (initialVersion > updateVersion) {
|
||||
throw new MigrationException(String.format("Version of '%s' downgraded from %s to %s [from %s by %s]",
|
||||
key, initialVersion, updateVersion, versionsStore, migrator));
|
||||
}
|
||||
if (updateVersion > latestVersion) {
|
||||
throw new MigrationException(
|
||||
String.format("Version of '%s' is %s and higher than latest version %s [from %s by %s]",
|
||||
key, initialVersion, updateVersion, versionsStore, migrator));
|
||||
}
|
||||
if (updateVersion == initialVersion) {
|
||||
throw new MigrationException(String.format("Update version of '%s' equals current version '%s' [from %s by %s]",
|
||||
key, updateVersion, versionsStore, migrator));
|
||||
}
|
||||
oldVersion = initialVersion;
|
||||
initialVersion = updateVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class NextLoopMigrationException extends Exception {
|
||||
}
|
||||
|
||||
public static class MigrationException extends RuntimeException {
|
||||
|
||||
public MigrationException(@NonNull final String message) {
|
||||
super(message);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ package ru.touchin.roboswag.core.observables.storable;
|
|||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import rx.Single;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 05/10/2015.
|
||||
* Abstract class of objects which are able to migrate some values from one version to another.
|
||||
|
|
@ -52,49 +54,44 @@ public abstract class Migrator<TKey, TOldStoreObject, TNewStoreObject> {
|
|||
public abstract boolean supportsMigrationFor(long version);
|
||||
|
||||
/**
|
||||
* Returns if specific object with key of specific version could be migrated by this migrator.
|
||||
* Returns {@link Single} that emits if specific object with key of specific version could be migrated by this migrator.
|
||||
*
|
||||
* @param key Key of object to migrate;
|
||||
* @param version Current version of object;
|
||||
* @return True if object with such key and version could be migrated;
|
||||
* @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state.
|
||||
* @return {@link Single} that emits true if object with such key and version could be migrated.
|
||||
*/
|
||||
public boolean canMigrate(@NonNull final TKey key, final long version) throws Migration.MigrationException {
|
||||
try {
|
||||
return oldStore.contains(key);
|
||||
} catch (final Store.StoreException exception) {
|
||||
throw new Migration.MigrationException("Version " + version + " not supported by " + this, exception);
|
||||
}
|
||||
@NonNull
|
||||
public Single<Boolean> canMigrate(@NonNull final TKey key, final long version) {
|
||||
return supportsMigrationFor(version) ? oldStore.contains(key) : Single.just(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates object with specific key from some version to migrator's version.
|
||||
* Single that migrates object with specific key from some version to migrator's version.
|
||||
*
|
||||
* @param key Key of object to migrate;
|
||||
* @param version Current version of object;
|
||||
* @return New version of object after migration process;
|
||||
* @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state.
|
||||
* @return {@link Single} that emits new version of object after migration process.
|
||||
*/
|
||||
public long migrate(@NonNull final TKey key, final long version) throws Migration.MigrationException {
|
||||
if (!supportsMigrationFor(version)) {
|
||||
throw new Migration.MigrationException(String.format("Version %s of '%s' is not supported by %s", version, key, this));
|
||||
}
|
||||
return migrateInternal(key, version, oldStore, newStore);
|
||||
@NonNull
|
||||
public Single<Long> migrate(@NonNull final TKey key, final long version) {
|
||||
return supportsMigrationFor(version)
|
||||
? migrateInternal(key, version, oldStore, newStore)
|
||||
: Single.error(new Migration.MigrationException(String.format("Version %s of '%s' is not supported by %s", version, key, this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal migration logic specified by implementation.
|
||||
* Single that represents internal migration logic specified by implementation.
|
||||
*
|
||||
* @param key Key of object to migrate;
|
||||
* @param version Current version of object;
|
||||
* @param oldStore Old store of object;
|
||||
* @param newStore new store of object;
|
||||
* @return New version of object after migration process;
|
||||
* @throws Migration.MigrationException Exception during object migration. Usually it indicates illegal state.
|
||||
* @return {@link Single} that emits new version of object after migration process.
|
||||
*/
|
||||
protected abstract long migrateInternal(@NonNull TKey key,
|
||||
final long version,
|
||||
@NonNull Store<TKey, TOldStoreObject> oldStore,
|
||||
@NonNull Store<TKey, TNewStoreObject> newStore) throws Migration.MigrationException;
|
||||
@NonNull
|
||||
protected abstract Single<Long> migrateInternal(@NonNull TKey key,
|
||||
long version,
|
||||
@NonNull Store<TKey, TOldStoreObject> oldStore,
|
||||
@NonNull Store<TKey, TNewStoreObject> newStore);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 04/10/2015.
|
||||
* Interface that is providing logic to convert value from specific type to type allowed to store in {@link Store} object and back.
|
||||
* The only difference is that it is not throwing exceptions during conversion. It is safe to convert objects with it.
|
||||
*
|
||||
* @param <TObject> Type of original objects;
|
||||
* @param <TStoreObject> Type of objects in store.
|
||||
*/
|
||||
public interface SafeConverter<TObject, TStoreObject> extends Converter<TObject, TStoreObject> {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
TStoreObject toStoreObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TObject object);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
TObject toObject(@NonNull Class<TObject> objectClass, @NonNull Class<TStoreObject> storeObjectClass, @Nullable TStoreObject storeObject);
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 04/10/2015.
|
||||
* Interface that is providing access to abstract object which can store (e.g. in file, database, remote store)
|
||||
* some type of objects (e.g. String, byte[], Integer) by key.
|
||||
* The only difference is that it is not throwing exceptions during access to store. It is safe to work with such store.
|
||||
*
|
||||
* @param <TKey> Type of keys for values;
|
||||
* @param <TStoreObject> Type of values stored in store.
|
||||
*/
|
||||
public interface SafeStore<TKey, TStoreObject> extends Store<TKey, TStoreObject> {
|
||||
|
||||
@Override
|
||||
boolean contains(@NonNull TKey key);
|
||||
|
||||
@Override
|
||||
void storeObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key, @Nullable TStoreObject storeObject);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
TStoreObject loadObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key);
|
||||
|
||||
}
|
||||
|
|
@ -3,22 +3,24 @@ package ru.touchin.roboswag.core.observables.storable;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Simple safe converter that is doing nothing on conversion.
|
||||
*
|
||||
* @param <T> Same type.
|
||||
*/
|
||||
public class SameTypesConverter<T> implements SafeConverter<T, T> {
|
||||
public class SameTypesConverter<T> implements Converter<T, T> {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T toStoreObject(@NonNull final Class<T> class1, @NonNull final Class<T> class2, @Nullable final T object) {
|
||||
public T toStoreObject(@NonNull final Type type1, @NonNull final Type type2, @Nullable final T object) {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T toObject(@NonNull final Class<T> class1, @NonNull final Class<T> class2, @Nullable final T object) {
|
||||
public T toObject(@NonNull final Type type1, @NonNull final Type type2, @Nullable final T object) {
|
||||
return object;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TKey, TObject, TStoreObject> {
|
|||
|
||||
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<TObject> objectClass;
|
||||
private final Type objectType;
|
||||
@NonNull
|
||||
private final Class<TStoreObject> storeObjectClass;
|
||||
private final Type storeObjectType;
|
||||
@NonNull
|
||||
private final Store<TKey, TStoreObject> store;
|
||||
@NonNull
|
||||
|
|
@ -73,117 +80,103 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
@NonNull
|
||||
private final PublishSubject<TStoreObject> newStoreValueEvent = PublishSubject.create();
|
||||
@NonNull
|
||||
private final Observable<TStoreObject> storeValueObservable;
|
||||
@NonNull
|
||||
private final Observable<TObject> valueObservable;
|
||||
@NonNull
|
||||
private final Scheduler storeScheduler;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public Storable(@NonNull final BuilderCore<TKey, TObject, TStoreObject> builderCore) {
|
||||
this(builderCore.key, builderCore.objectClass, builderCore.storeObjectClass,
|
||||
this(builderCore.key, builderCore.objectType, builderCore.storeObjectType,
|
||||
builderCore.store, builderCore.converter, builderCore.observeStrategy,
|
||||
builderCore.migration, builderCore.defaultValue, builderCore.storeScheduler);
|
||||
builderCore.migration, builderCore.defaultValue, builderCore.storeScheduler, builderCore.cacheTimeMillis);
|
||||
}
|
||||
|
||||
public Storable(@NonNull final TKey key,
|
||||
@NonNull final Class<TObject> objectClass,
|
||||
@Nullable final Class<TStoreObject> storeObjectClass,
|
||||
@Nullable final Store<TKey, TStoreObject> store,
|
||||
@Nullable final Converter<TObject, TStoreObject> converter,
|
||||
@Nullable final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue,
|
||||
@Nullable final Scheduler storeScheduler) {
|
||||
if (storeObjectClass == null || store == null || converter == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
@SuppressWarnings("PMD.ExcessiveParameterList")
|
||||
//ExcessiveParameterList: that's why we are using builder to create it
|
||||
private Storable(@NonNull final TKey key,
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter,
|
||||
@Nullable final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue,
|
||||
@Nullable final Scheduler storeScheduler,
|
||||
final long cacheTimeMillis) {
|
||||
this.key = key;
|
||||
this.objectClass = objectClass;
|
||||
this.storeObjectClass = storeObjectClass;
|
||||
this.objectType = objectType;
|
||||
this.storeObjectType = storeObjectType;
|
||||
this.store = store;
|
||||
this.converter = converter;
|
||||
final ObserveStrategy nonNullObserveStrategy = observeStrategy != null ? observeStrategy : getDefaultGetStrategy();
|
||||
this.storeScheduler = storeScheduler != null ? storeScheduler : Schedulers.from(Executors.newSingleThreadExecutor());
|
||||
final Observable<TStoreObject> 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<TStoreObject> createStoreValueObservable(@NonNull final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue) {
|
||||
final Observable<TStoreObject> result = Observable
|
||||
.<TStoreObject>create(subscriber -> {
|
||||
try {
|
||||
if (migration != null) {
|
||||
migration.migrateToLatestVersion(key);
|
||||
}
|
||||
subscriber.onNext(store.loadObject(storeObjectClass, key));
|
||||
subscriber.onCompleted();
|
||||
} catch (final Store.StoreException storeException) {
|
||||
STORABLE_LC_GROUP.w(storeException, "Exception while trying to get value of '%s' from store %s", key, store);
|
||||
subscriber.onError(storeException);
|
||||
} catch (final Migration.MigrationException migrationException) {
|
||||
STORABLE_LC_GROUP.assertion(migrationException);
|
||||
subscriber.onError(migrationException);
|
||||
} catch (final RuntimeException throwable) {
|
||||
@Nullable final TObject defaultValue,
|
||||
final long cacheTimeMillis) {
|
||||
final Observable<TStoreObject> result = (migration != null
|
||||
? migration.migrateToLatestVersion(key).subscribeOn(scheduler)
|
||||
: Completable.complete())
|
||||
.andThen(store.loadObject(storeObjectType, key).toObservable().subscribeOn(scheduler))
|
||||
.doOnError(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
STORABLE_LC_GROUP.assertion(throwable);
|
||||
} else {
|
||||
STORABLE_LC_GROUP.w(throwable, "Exception while trying to load value of '%s' from store %s", key, store);
|
||||
}
|
||||
})
|
||||
.subscribeOn(storeScheduler)
|
||||
.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<TObject> createValueObservable(@NonNull final Observable<TStoreObject> storeValueObservable,
|
||||
@NonNull final ObserveStrategy observeStrategy) {
|
||||
@NonNull final ObserveStrategy observeStrategy,
|
||||
final long cacheTimeMillis) {
|
||||
final Observable<TObject> result = storeValueObservable
|
||||
.map(storeObject -> {
|
||||
try {
|
||||
return converter.toObject(objectClass, storeObjectClass, storeObject);
|
||||
} catch (final Converter.ConversionException exception) {
|
||||
STORABLE_LC_GROUP.w(exception, "Exception while converting value of '%s' from '%s' from store %s",
|
||||
key, storeObject, store);
|
||||
throw OnErrorThrowable.from(exception);
|
||||
} catch (final RuntimeException throwable) {
|
||||
STORABLE_LC_GROUP.assertion(throwable);
|
||||
throw OnErrorThrowable.from(throwable);
|
||||
}
|
||||
})
|
||||
.subscribeOn(storeScheduler);
|
||||
|
||||
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<TKey, TObject, TStoreObject> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns class of actual object.
|
||||
* Returns type of actual object.
|
||||
*
|
||||
* @return Class of actual object.
|
||||
* @return Type of actual object.
|
||||
*/
|
||||
@NonNull
|
||||
public Class<TObject> getObjectClass() {
|
||||
return objectClass;
|
||||
public Type getObjectType() {
|
||||
return objectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class of store object.
|
||||
* Returns type of store object.
|
||||
*
|
||||
* @return Class of store object.
|
||||
* @return Type of store object.
|
||||
*/
|
||||
@NonNull
|
||||
public Class<TStoreObject> getStoreObjectClass() {
|
||||
return storeObjectClass;
|
||||
public Type getStoreObjectType() {
|
||||
return storeObjectType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -238,89 +231,80 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
}
|
||||
|
||||
@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
|
||||
.<TStoreObject>create(subscriber -> {
|
||||
try {
|
||||
final TStoreObject storeObject = converter.toStoreObject(objectClass, storeObjectClass, newValue);
|
||||
store.storeObject(storeObjectClass, key, storeObject);
|
||||
newStoreValueEvent.onNext(storeObject);
|
||||
STORABLE_LC_GROUP.i("Value of '%s' changed from '%s' to '%s'", key, value, newValue);
|
||||
subscriber.onCompleted();
|
||||
} catch (final Converter.ConversionException conversionException) {
|
||||
STORABLE_LC_GROUP.w(conversionException, "Exception while converting value of '%s' from '%s' to store object",
|
||||
key, newValue, store);
|
||||
subscriber.onError(conversionException);
|
||||
} catch (final Store.StoreException storeException) {
|
||||
STORABLE_LC_GROUP.w(storeException, "Exception while trying to store value of '%s' to store %s", key, store);
|
||||
subscriber.onError(storeException);
|
||||
} catch (final RuntimeException throwable) {
|
||||
STORABLE_LC_GROUP.assertion(throwable);
|
||||
}
|
||||
})
|
||||
.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<TKey, TObject, TStoreObject> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns Observable which is emitting only one item on subscribe.
|
||||
* Returns Observable which is emitting only one item on subscribe. //TODO: it's Single :(
|
||||
* It could emit next and error events but not completed.
|
||||
* Errors won't be emitted if {@link #getStore()} implements {@link SafeStore} and {@link #getConverter()} implements {@link SafeConverter}.
|
||||
*
|
||||
* @return Returns observable of value.
|
||||
*/
|
||||
|
|
@ -345,38 +328,16 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
* Gets value synchronously. You should NOT use this method normally. Use {@link #get()} or {@link #observe()} asynchronously instead.
|
||||
*
|
||||
* @return Returns value;
|
||||
* @throws Store.StoreException Throws if {@link Store} threw exception during getting from store;
|
||||
* @throws Converter.ConversionException Throws if {@link Converter} threw exception during conversion;
|
||||
* @throws Migration.MigrationException Throws if {@link Migration} threw exception during migration.
|
||||
*/
|
||||
@Deprecated
|
||||
//deprecation: it should be used for debug only and in very rare cases.
|
||||
@Nullable
|
||||
public TObject getSync()
|
||||
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
|
||||
final ObservableResult<TObject> getResult = RxUtils.executeSync(get());
|
||||
checkStorableObservableResult(getResult);
|
||||
if (getResult.getItems().size() != 1) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return getResult.getItems().get(0);
|
||||
}
|
||||
|
||||
private void checkStorableObservableResult(@NonNull final ObservableResult<?> result)
|
||||
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
|
||||
for (final Throwable throwable : result.getErrors()) {
|
||||
if (throwable instanceof Store.StoreException) {
|
||||
throw (Store.StoreException) throwable;
|
||||
}
|
||||
if (throwable instanceof Converter.ConversionException) {
|
||||
throw (Converter.ConversionException) throwable;
|
||||
}
|
||||
if (throwable instanceof Migration.MigrationException) {
|
||||
throw (Migration.MigrationException) throwable;
|
||||
}
|
||||
}
|
||||
public TObject getSync() {
|
||||
return get().toBlocking().first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that is representing strategy of observing item from store.
|
||||
* Enum that is representing strategy of observing item from store.
|
||||
*/
|
||||
public enum ObserveStrategy {
|
||||
|
||||
|
|
@ -391,10 +352,19 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
CACHE_STORE_VALUE,
|
||||
/**
|
||||
* Caching value so it won't spend time for getting value from {@link #getStore()} and converts it by {@link #getConverter()}.
|
||||
* Do not use such strategy if object could be big (like byte-array of file).
|
||||
* But it will take time for getting value from {@link #getStore()} to set value.
|
||||
* Do not use such strategy if object could be big (like Bitmap or long string).
|
||||
* Do not use such strategy if object is mutable because multiple subscribers could then change it's state.
|
||||
*/
|
||||
CACHE_ACTUAL_VALUE
|
||||
CACHE_ACTUAL_VALUE,
|
||||
/**
|
||||
* Caching value so it won't spend time for getting value from {@link #getStore()} and converts it by {@link #getConverter()}.
|
||||
* It won't take time or getting value from {@link #getStore()} to set value.
|
||||
* Do not use such strategy if store object could be big (like byte-array of file).
|
||||
* Do not use such strategy if object could be big (like Bitmap or long string).
|
||||
* Do not use such strategy if object is mutable because multiple subscribers could then change it's state.
|
||||
*/
|
||||
CACHE_STORE_AND_ACTUAL_VALUE
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -410,112 +380,81 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
@NonNull
|
||||
protected final TKey key;
|
||||
@NonNull
|
||||
protected final Class<TObject> objectClass;
|
||||
protected final Type objectType;
|
||||
@NonNull
|
||||
private final Type storeObjectType;
|
||||
@NonNull
|
||||
private final Store<TKey, TStoreObject> store;
|
||||
@NonNull
|
||||
private final Converter<TObject, TStoreObject> converter;
|
||||
@Nullable
|
||||
private Class<TStoreObject> storeObjectClass;
|
||||
@Nullable
|
||||
private Store<TKey, TStoreObject> store;
|
||||
@Nullable
|
||||
private Converter<TObject, TStoreObject> converter;
|
||||
@Nullable
|
||||
protected final ObserveStrategy observeStrategy;
|
||||
private ObserveStrategy observeStrategy;
|
||||
@Nullable
|
||||
private Migration<TKey> migration;
|
||||
@Nullable
|
||||
private TObject defaultValue;
|
||||
@Nullable
|
||||
protected final Scheduler storeScheduler;
|
||||
private Scheduler storeScheduler;
|
||||
private long cacheTimeMillis;
|
||||
|
||||
protected BuilderCore(@NonNull final TKey key,
|
||||
@NonNull final Class<TObject> objectClass,
|
||||
@Nullable final ObserveStrategy observeStrategy,
|
||||
@Nullable final Scheduler storeScheduler) {
|
||||
this(key, objectClass, null, null, null, observeStrategy, null, null, storeScheduler);
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
this(key, objectType, storeObjectType, store, converter, null, null, null, null, DEFAULT_CACHE_TIME_MILLIS);
|
||||
}
|
||||
|
||||
protected BuilderCore(@NonNull final BuilderCore<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
this(sourceBuilder.key, sourceBuilder.objectClass, sourceBuilder.storeObjectClass,
|
||||
this(sourceBuilder.key, sourceBuilder.objectType, sourceBuilder.storeObjectType,
|
||||
sourceBuilder.store, sourceBuilder.converter, sourceBuilder.observeStrategy,
|
||||
sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler);
|
||||
sourceBuilder.migration, sourceBuilder.defaultValue, sourceBuilder.storeScheduler, sourceBuilder.cacheTimeMillis);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-START")
|
||||
//CPD: it is ok that builder copy-pasted parent constructor parameters
|
||||
@SuppressWarnings({"PMD.ExcessiveParameterList", "CPD-START"})
|
||||
//CPD: it is same code as constructor of Storable
|
||||
//ExcessiveParameterList: that's why we are using builder to create it
|
||||
private BuilderCore(@NonNull final TKey key,
|
||||
@NonNull final Class<TObject> objectClass,
|
||||
@Nullable final Class<TStoreObject> storeObjectClass,
|
||||
@Nullable final Store<TKey, TStoreObject> store,
|
||||
@Nullable final Converter<TObject, TStoreObject> converter,
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter,
|
||||
@Nullable final ObserveStrategy observeStrategy,
|
||||
@Nullable final Migration<TKey> migration,
|
||||
@Nullable final TObject defaultValue,
|
||||
@Nullable final Scheduler storeScheduler) {
|
||||
@Nullable final Scheduler storeScheduler,
|
||||
final long cacheTimeMillis) {
|
||||
this.key = key;
|
||||
this.objectClass = objectClass;
|
||||
this.storeObjectClass = storeObjectClass;
|
||||
this.objectType = objectType;
|
||||
this.storeObjectType = storeObjectType;
|
||||
this.store = store;
|
||||
this.converter = converter;
|
||||
this.observeStrategy = observeStrategy;
|
||||
this.migration = migration;
|
||||
this.defaultValue = defaultValue;
|
||||
this.storeScheduler = storeScheduler;
|
||||
this.cacheTimeMillis = cacheTimeMillis;
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-END")
|
||||
@Nullable
|
||||
public Class<TStoreObject> getStoreObjectClass() {
|
||||
return storeObjectClass;
|
||||
protected void setStoreSchedulerInternal(@Nullable final Scheduler storeScheduler) {
|
||||
this.storeScheduler = storeScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Store} where store class representation of object is storing.
|
||||
*
|
||||
* @return Store.
|
||||
*/
|
||||
@Nullable
|
||||
public Store<TKey, TStoreObject> getStore() {
|
||||
return store;
|
||||
}
|
||||
|
||||
protected void setStoreInternal(@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
this.storeObjectClass = storeObjectClass;
|
||||
this.store = store;
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Converter} to convert values from store class to actual and back.
|
||||
*
|
||||
* @return Converter.
|
||||
*/
|
||||
@Nullable
|
||||
public Converter<TObject, TStoreObject> getConverter() {
|
||||
return converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Migration} to migrate values from specific version to latest version.
|
||||
*
|
||||
* @return Migration.
|
||||
*/
|
||||
@Nullable
|
||||
public Migration<TKey> getMigration() {
|
||||
return migration;
|
||||
protected void setObserveStrategyInternal(@Nullable final ObserveStrategy observeStrategy) {
|
||||
this.observeStrategy = observeStrategy;
|
||||
}
|
||||
|
||||
protected void setMigrationInternal(@NonNull final Migration<TKey> migration) {
|
||||
this.migration = migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value which will be returned instead of null.
|
||||
*
|
||||
* @return Default value.
|
||||
*/
|
||||
protected void setCacheTimeInternal(final long cacheTime, @NonNull final TimeUnit timeUnit) {
|
||||
this.cacheTimeMillis = timeUnit.toMillis(cacheTime);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TObject getDefaultValue() {
|
||||
protected TObject getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
|
@ -535,59 +474,49 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
public static class Builder<TKey, TObject, TStoreObject> extends BuilderCore<TKey, TObject, TStoreObject> {
|
||||
|
||||
public Builder(@NonNull final TKey key,
|
||||
@NonNull final Class<TObject> objectClass) {
|
||||
super(key, objectClass, null, null);
|
||||
}
|
||||
|
||||
public Builder(@NonNull final TKey key,
|
||||
@NonNull final Class<TObject> objectClass,
|
||||
@NonNull final ObserveStrategy observeStrategy) {
|
||||
super(key, objectClass, observeStrategy, null);
|
||||
}
|
||||
|
||||
public Builder(@NonNull final TKey key,
|
||||
@NonNull final Class<TObject> objectClass,
|
||||
@NonNull final Scheduler storeScheduler) {
|
||||
super(key, objectClass, null, storeScheduler);
|
||||
}
|
||||
|
||||
public Builder(@NonNull final TKey key,
|
||||
@NonNull final Class<TObject> objectClass,
|
||||
@NonNull final ObserveStrategy observeStrategy,
|
||||
@NonNull final Scheduler storeScheduler) {
|
||||
super(key, objectClass, observeStrategy, storeScheduler);
|
||||
@NonNull final Type objectType,
|
||||
@NonNull final Type storeObjectType,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
super(key, objectType, storeObjectType, store, converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets store and converter.
|
||||
* Sets specific {@link Scheduler} to store/load/convert values on it.
|
||||
*
|
||||
* @param storeObjectClass Class of store object,
|
||||
* @param store Store to store objects into;
|
||||
* @param converter Converter to convert values from store class to actual class and back;
|
||||
* @param storeScheduler Scheduler;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
public Builder<TKey, TObject, TStoreObject> setStoreScheduler(@Nullable final Scheduler storeScheduler) {
|
||||
setStoreSchedulerInternal(storeScheduler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
|
||||
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
|
||||
* Sets specific {@link ObserveStrategy} to cache value in memory in specific way.
|
||||
*
|
||||
* @param storeObjectClass Class of store object,
|
||||
* @param store Safe store that is not throwing exceptions;
|
||||
* @param converter Safe converter that is not throwing exceptions;
|
||||
* @param observeStrategy ObserveStrategy;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public SafeStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final SafeStore<TKey, TStoreObject> store,
|
||||
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
|
||||
return new SafeStorableBuilder<>(this, storeObjectClass, store, converter);
|
||||
public Builder<TKey, TObject, TStoreObject> setObserveStrategy(@Nullable final ObserveStrategy observeStrategy) {
|
||||
setObserveStrategyInternal(observeStrategy);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cache time for while value that cached by {@link #setObserveStrategy(ObserveStrategy)} will be in memory after everyone unsubscribe.
|
||||
* It is important for example for cases when user switches between screens and hide/open app very fast.
|
||||
*
|
||||
* @param cacheTime Cache time value;
|
||||
* @param timeUnit Cache time units.
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder<TKey, TObject, TStoreObject> setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) {
|
||||
setCacheTimeInternal(cacheTime, timeUnit);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -597,8 +526,9 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public MigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
|
||||
return new MigratableStorableBuilder<>(this, migration);
|
||||
public Builder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
|
||||
setMigrationInternal(migration);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ package ru.touchin.roboswag.core.observables.storable;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import rx.Completable;
|
||||
import rx.Single;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 04/10/2015.
|
||||
* Interface that is providing access to abstract object which can store (e.g. in file, database, remote store)
|
||||
|
|
@ -37,41 +42,28 @@ public interface Store<TKey, TStoreObject> {
|
|||
*
|
||||
* @param key Key which is finding in store;
|
||||
* @return True if key have found in store.
|
||||
* @throws StoreException Exception during getting access to store.
|
||||
*/
|
||||
boolean contains(@NonNull TKey key) throws StoreException;
|
||||
@NonNull
|
||||
Single<Boolean> contains(@NonNull TKey key);
|
||||
|
||||
/**
|
||||
* Stores object to store with related key.
|
||||
*
|
||||
* @param storeObjectClass Class of object to store;
|
||||
* @param key Key related to object;
|
||||
* @param storeObject Object to store;
|
||||
* @throws StoreException Exception during getting access to store.
|
||||
* @param storeObjectType Type of object to store;
|
||||
* @param key Key related to object;
|
||||
* @param storeObject Object to store;
|
||||
*/
|
||||
void storeObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key, @Nullable TStoreObject storeObject) throws StoreException;
|
||||
@NonNull
|
||||
Completable storeObject(@NonNull Type storeObjectType, @NonNull TKey key, @Nullable TStoreObject storeObject);
|
||||
|
||||
/**
|
||||
* Loads object from store by key.
|
||||
*
|
||||
* @param storeObjectClass Class of object to store;
|
||||
* @param key Key related to object;
|
||||
* @param storeObjectType Type of object to store;
|
||||
* @param key Key related to object;
|
||||
* @return Object from store found by key;
|
||||
* @throws StoreException Exception during getting access to store.
|
||||
*/
|
||||
@Nullable
|
||||
TStoreObject loadObject(@NonNull Class<TStoreObject> storeObjectClass, @NonNull TKey key) throws StoreException;
|
||||
|
||||
class StoreException extends Exception {
|
||||
|
||||
public StoreException(@NonNull final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public StoreException(@NonNull final String message, @NonNull final Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
}
|
||||
@NonNull
|
||||
Single<TStoreObject> loadObject(@NonNull Type storeObjectType, @NonNull TKey key);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeStore;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.Store;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
* Builder that is already contains not null migration.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class MigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
|
||||
|
||||
public MigratableStorableBuilder(@NonNull final Storable.Builder<TKey, TObject, TStoreObject> sourceBuilder,
|
||||
@NonNull final Migration<TKey> migration) {
|
||||
super(sourceBuilder);
|
||||
setMigrationInternal(migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets store and converter.
|
||||
*
|
||||
* @param storeObjectClass Class of store object,
|
||||
* @param store Store to store objects into;
|
||||
* @param converter Converter to convert values from store class to actual class and back;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public MigratableStorableBuilder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
|
||||
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
|
||||
*
|
||||
* @param storeObjectClass Class of store object,
|
||||
* @param store Safe store that is not throwing exceptions;
|
||||
* @param converter Safe converter that is not throwing exceptions;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(
|
||||
@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final SafeStore<TKey, TStoreObject> store,
|
||||
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
return new SafeMigratableStorableBuilder<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value which will be returned instead of null.
|
||||
*
|
||||
* @param defaultValue Default value;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> setDefaultValue(@NonNull final TObject defaultValue) {
|
||||
setDefaultValueInternal(defaultValue);
|
||||
return new NonNullMigratableStorableBuilder<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Building {@link Storable} object.
|
||||
*
|
||||
* @return New {@link Storable}.
|
||||
*/
|
||||
@NonNull
|
||||
public Storable<TKey, TObject, TStoreObject> build() {
|
||||
if (getMigration() == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return new Storable<>(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeStore;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.Store;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
* Builder that is already contains not null migration and default value.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
|
||||
|
||||
public NonNullMigratableStorableBuilder(@NonNull final NonNullStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
public NonNullMigratableStorableBuilder(@NonNull final MigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
|
||||
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
|
||||
*
|
||||
* @param storeObjectClass Class of store object,
|
||||
* @param store Safe store that is not throwing exceptions;
|
||||
* @param converter Safe converter that is not throwing exceptions;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(
|
||||
@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final SafeStore<TKey, TStoreObject> store,
|
||||
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
return new NonNullSafeMigratableStorableBuilder<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Building {@link NonNullStorable} object.
|
||||
*
|
||||
* @return New {@link NonNullStorable}.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullStorable<TKey, TObject, TStoreObject> build() {
|
||||
if (getDefaultValue() == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
if (getMigration() == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return new NonNullStorable<>(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeStore;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullSafeStorable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
* Builder with safe store and converter inside that is already contains not null migration and default value.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
|
||||
|
||||
public NonNullSafeMigratableStorableBuilder(@NonNull final NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
public NonNullSafeMigratableStorableBuilder(@NonNull final NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
public NonNullSafeMigratableStorableBuilder(@NonNull final SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Building {@link NonNullSafeStorable} object.
|
||||
*
|
||||
* @return New {@link NonNullSafeStorable}.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullSafeStorable<TKey, TObject, TStoreObject> build() {
|
||||
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
if (getDefaultValue() == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
if (getMigration() == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return new NonNullSafeStorable<>(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeStore;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullSafeStorable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
* Builder with safe store and converter inside that is already contains not null default value.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
|
||||
|
||||
public NonNullSafeStorableBuilder(@NonNull final NonNullStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
public NonNullSafeStorableBuilder(@NonNull final SafeStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific {@link Migration} to migrate values from specific version to latest version.
|
||||
*
|
||||
* @param migration Migration;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
|
||||
setMigrationInternal(migration);
|
||||
return new NonNullSafeMigratableStorableBuilder<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Building {@link NonNullSafeStorable} object.
|
||||
*
|
||||
* @return New {@link NonNullSafeStorable}.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullSafeStorable<TKey, TObject, TStoreObject> build() {
|
||||
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
if (getDefaultValue() == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return new NonNullSafeStorable<>(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -20,15 +20,15 @@
|
|||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeStore;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.Store;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Scheduler;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
|
|
@ -47,36 +47,42 @@ public class NonNullStorableBuilder<TKey, TObject, TStoreObject> extends Storabl
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets store and converter.
|
||||
* Sets specific {@link Scheduler} to store/load/convert values on it.
|
||||
*
|
||||
* @param storeObjectClass Class of store object,
|
||||
* @param store Store to store objects into;
|
||||
* @param converter Converter to convert values from store class to actual class and back;
|
||||
* @param storeScheduler Scheduler;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setStore(@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final Store<TKey, TStoreObject> store,
|
||||
@NonNull final Converter<TObject, TStoreObject> converter) {
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setStoreScheduler(@Nullable final Scheduler storeScheduler) {
|
||||
setStoreSchedulerInternal(storeScheduler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets safe store and converter so in such {@link Storable} it is not needed to specify onError action
|
||||
* when subscribing to {@link Storable#set(Object)}, {@link Storable#get()} or {@link Storable#observe()} methods.
|
||||
* Sets specific {@link Storable.ObserveStrategy} to cache value in memory in specific way.
|
||||
*
|
||||
* @param storeObjectClass Class of store object,
|
||||
* @param store Safe store that is not throwing exceptions;
|
||||
* @param converter Safe converter that is not throwing exceptions;
|
||||
* @param observeStrategy ObserveStrategy;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> setSafeStore(@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final SafeStore<TKey, TStoreObject> store,
|
||||
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
return new NonNullSafeStorableBuilder<>(this);
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setObserveStrategy(@Nullable final Storable.ObserveStrategy observeStrategy) {
|
||||
setObserveStrategyInternal(observeStrategy);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cache time for while value that cached by {@link #setObserveStrategy(Storable.ObserveStrategy)}
|
||||
* will be in memory after everyone unsubscribe.
|
||||
* It is important for example for cases when user switches between screens and hide/open app very fast.
|
||||
*
|
||||
* @param cacheTime Cache time value;
|
||||
* @param timeUnit Cache time units.
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setCacheTime(final long cacheTime, @NonNull final TimeUnit timeUnit) {
|
||||
setCacheTimeInternal(cacheTime, timeUnit);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,9 +92,9 @@ public class NonNullStorableBuilder<TKey, TObject, TStoreObject> extends Storabl
|
|||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
|
||||
public NonNullStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
|
||||
setMigrationInternal(migration);
|
||||
return new NonNullMigratableStorableBuilder<>(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeStore;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.SafeStorable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
* Builder with safe store and converter inside that is already contains not null migration.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
|
||||
|
||||
public SafeMigratableStorableBuilder(@NonNull final MigratableStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
public SafeMigratableStorableBuilder(@NonNull final SafeStorableBuilder<TKey, TObject, TStoreObject> sourceBuilder) {
|
||||
super(sourceBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value which will be returned instead of null.
|
||||
*
|
||||
* @param defaultValue Default value;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setDefaultValue(@NonNull final TObject defaultValue) {
|
||||
setDefaultValueInternal(defaultValue);
|
||||
return new NonNullSafeMigratableStorableBuilder<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Building {@link SafeStorable} object.
|
||||
*
|
||||
* @return New {@link SafeStorable}.
|
||||
*/
|
||||
@NonNull
|
||||
public SafeStorable<TKey, TObject, TStoreObject> build() {
|
||||
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
if (getMigration() == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return new SafeStorable<>(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.builders;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SafeStore;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.SafeStorable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 15/05/2016.
|
||||
* Builder with safe store and converter inside.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class SafeStorableBuilder<TKey, TObject, TStoreObject> extends Storable.BuilderCore<TKey, TObject, TStoreObject> {
|
||||
|
||||
public SafeStorableBuilder(@NonNull final Storable.Builder<TKey, TObject, TStoreObject> sourceBuilder,
|
||||
@NonNull final Class<TStoreObject> storeObjectClass,
|
||||
@NonNull final SafeStore<TKey, TStoreObject> store,
|
||||
@NonNull final SafeConverter<TObject, TStoreObject> converter) {
|
||||
super(sourceBuilder);
|
||||
setStoreInternal(storeObjectClass, store, converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific {@link Migration} to migrate values from specific version to latest version.
|
||||
*
|
||||
* @param migration Migration;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> setMigration(@NonNull final Migration<TKey> migration) {
|
||||
setMigrationInternal(migration);
|
||||
return new SafeMigratableStorableBuilder<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value which will be returned instead of null.
|
||||
*
|
||||
* @param defaultValue Default value;
|
||||
* @return Builder that allows to specify other fields.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> setDefaultValue(@NonNull final TObject defaultValue) {
|
||||
setDefaultValueInternal(defaultValue);
|
||||
return new NonNullSafeStorableBuilder<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Building {@link SafeStorable} object.
|
||||
*
|
||||
* @return New {@link SafeStorable}.
|
||||
*/
|
||||
@NonNull
|
||||
public SafeStorable<TKey, TObject, TStoreObject> build() {
|
||||
if (!(getStore() instanceof SafeStore) || !(getConverter() instanceof SafeConverter)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return new SafeStorable<>(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.concrete;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.Store;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 20/12/2016.
|
||||
* List wrapper of {@link Storable}.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TItemObject> Type of items in actual list object;
|
||||
* @param <TStoreObject> Type of store object.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "CPD-START"})
|
||||
public class ListStorable<TKey, TItemObject, TStoreObject> {
|
||||
|
||||
@NonNull
|
||||
private final Storable<TKey, List, TStoreObject> storable;
|
||||
|
||||
public ListStorable(@NonNull final Storable<TKey, List, TStoreObject> storable) {
|
||||
this.storable = storable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#get()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> get() {
|
||||
return storable.get().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#observe()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> observe() {
|
||||
return storable.observe().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#set(Object)}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> set(@Nullable final List<TItemObject> list) {
|
||||
return storable.set(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setCalm(Object)}.
|
||||
*/
|
||||
public void setCalm(@Nullable final List<TItemObject> list) {
|
||||
storable.setCalm(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#getSync()}.
|
||||
*/
|
||||
@Nullable
|
||||
public List<TItemObject> getSync()
|
||||
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
|
||||
return (List<TItemObject>) storable.getSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setSync(Object)}.
|
||||
*/
|
||||
public void setSync(@Nullable final List<TItemObject> list)
|
||||
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
|
||||
storable.setSync(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.concrete;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.Store;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 20/12/2016.
|
||||
* List wrapper of {@link Storable} that should return not null value on get.
|
||||
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TItemObject> Type of items in actual list object;
|
||||
* @param <TStoreObject> Type of store object.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "CPD-START"})
|
||||
public class NonNullListStorable<TKey, TItemObject, TStoreObject> {
|
||||
|
||||
@NonNull
|
||||
private final NonNullStorable<TKey, List, TStoreObject> storable;
|
||||
|
||||
public NonNullListStorable(@NonNull final NonNullStorable<TKey, List, TStoreObject> storable) {
|
||||
this.storable = storable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#get()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> get() {
|
||||
return storable.get().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#observe()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> observe() {
|
||||
return storable.observe().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#set(Object)}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> set(@Nullable final List<TItemObject> list) {
|
||||
return storable.set(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setCalm(Object)}.
|
||||
*/
|
||||
public void setCalm(@Nullable final List<TItemObject> list) {
|
||||
storable.setCalm(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#getSync()}.
|
||||
*/
|
||||
@NonNull
|
||||
public List<TItemObject> getSync()
|
||||
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
|
||||
return (List<TItemObject>) storable.getSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setSync(Object)}.
|
||||
*/
|
||||
public void setSync(@Nullable final List<TItemObject> list)
|
||||
throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
|
||||
storable.setSync(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <TKey> Type of key to identify object;
|
||||
* @param <TItemObject> Type of items in actual list object;
|
||||
* @param <TStoreObject> Type of store object.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "CPD-START"})
|
||||
public class NonNullSafeListStorable<TKey, TItemObject, TStoreObject> {
|
||||
|
||||
@NonNull
|
||||
private final NonNullSafeStorable<TKey, List, TStoreObject> storable;
|
||||
|
||||
public NonNullSafeListStorable(@NonNull final NonNullSafeStorable<TKey, List, TStoreObject> storable) {
|
||||
this.storable = storable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#get()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> get() {
|
||||
return storable.get().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#observe()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> observe() {
|
||||
return storable.observe().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#set(Object)}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> set(@Nullable final List<TItemObject> list) {
|
||||
return storable.set(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#forceSet(Object)} (Object)}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> forseSet(@Nullable final List<TItemObject> list) {
|
||||
return storable.forceSet(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setCalm(Object)}.
|
||||
*/
|
||||
public void setCalm(@Nullable final List<TItemObject> list) {
|
||||
storable.setCalm(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#getSync()}.
|
||||
*/
|
||||
@NonNull
|
||||
public List<TItemObject> getSync() {
|
||||
return (List<TItemObject>) storable.getSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setSync(Object)}.
|
||||
*/
|
||||
public void setSync(@Nullable final List<TItemObject> list) {
|
||||
storable.setSync(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.concrete;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.builders.NonNullSafeMigratableStorableBuilder;
|
||||
import ru.touchin.roboswag.core.observables.storable.builders.NonNullSafeStorableBuilder;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 04/10/2015.
|
||||
* {@link Storable} that should not throw exceptions on set or get and return not null value on get.
|
||||
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class NonNullSafeStorable<TKey, TObject, TStoreObject> extends Storable<TKey, TObject, TStoreObject> {
|
||||
|
||||
public NonNullSafeStorable(@NonNull final NonNullSafeStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
|
||||
super(builderCore);
|
||||
}
|
||||
|
||||
public NonNullSafeStorable(@NonNull final NonNullSafeMigratableStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
|
||||
super(builderCore);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public TObject getSync() {
|
||||
final TObject result;
|
||||
try {
|
||||
result = super.getSync();
|
||||
} catch (final Exception exception) {
|
||||
throw new ShouldNotHappenException(exception);
|
||||
}
|
||||
if (result == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSync(@Nullable final TObject newValue) {
|
||||
try {
|
||||
super.setSync(newValue);
|
||||
} catch (final Exception exception) {
|
||||
throw new ShouldNotHappenException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,11 +21,7 @@ package ru.touchin.roboswag.core.observables.storable.concrete;
|
|||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||
import ru.touchin.roboswag.core.observables.storable.Migration;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.Store;
|
||||
import ru.touchin.roboswag.core.observables.storable.builders.NonNullMigratableStorableBuilder;
|
||||
import ru.touchin.roboswag.core.observables.storable.builders.NonNullStorableBuilder;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
|
|
@ -44,13 +40,9 @@ public class NonNullStorable<TKey, TObject, TStoreObject> extends Storable<TKey,
|
|||
super(builderCore);
|
||||
}
|
||||
|
||||
public NonNullStorable(@NonNull final NonNullMigratableStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
|
||||
super(builderCore);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public TObject getSync() throws Store.StoreException, Converter.ConversionException, Migration.MigrationException {
|
||||
public TObject getSync() {
|
||||
final TObject result = super.getSync();
|
||||
if (result == null) {
|
||||
throw new ShouldNotHappenException();
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.concrete;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 20/12/2016.
|
||||
* List wrapper of {@link Storable} that should not throw exceptions on set or get.
|
||||
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TItemObject> Type of items in actual list object;
|
||||
* @param <TStoreObject> Type of store object.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "CPD-START"})
|
||||
public class SafeListStorable<TKey, TItemObject, TStoreObject> {
|
||||
|
||||
@NonNull
|
||||
private final SafeStorable<TKey, List, TStoreObject> storable;
|
||||
|
||||
public SafeListStorable(@NonNull final SafeStorable<TKey, List, TStoreObject> storable) {
|
||||
this.storable = storable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#get()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> get() {
|
||||
return storable.get().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#observe()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<List<TItemObject>> observe() {
|
||||
return storable.observe().map(list -> (List<TItemObject>) list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#set(Object)}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> set(@Nullable final List<TItemObject> list) {
|
||||
return storable.set(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setCalm(Object)}.
|
||||
*/
|
||||
public void setCalm(@Nullable final List<TItemObject> list) {
|
||||
storable.setCalm(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#getSync()}.
|
||||
*/
|
||||
@Nullable
|
||||
public List<TItemObject> getSync() {
|
||||
return (List<TItemObject>) storable.getSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link Storable#setSync(Object)}.
|
||||
*/
|
||||
public void setSync(@Nullable final List<TItemObject> list) {
|
||||
storable.setSync(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package ru.touchin.roboswag.core.observables.storable.concrete;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.builders.SafeMigratableStorableBuilder;
|
||||
import ru.touchin.roboswag.core.observables.storable.builders.SafeStorableBuilder;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 04/10/2015.
|
||||
* {@link Storable} that should not throw exceptions on set or get.
|
||||
* If this rules are violated then it will throw {@link ShouldNotHappenException}.
|
||||
*
|
||||
* @param <TKey> Type of key to identify object;
|
||||
* @param <TObject> Type of actual object;
|
||||
* @param <TStoreObject> Type of store object. Could be same as {@link TObject}.
|
||||
*/
|
||||
public class SafeStorable<TKey, TObject, TStoreObject> extends Storable<TKey, TObject, TStoreObject> {
|
||||
|
||||
public SafeStorable(@NonNull final SafeStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
|
||||
super(builderCore);
|
||||
}
|
||||
|
||||
public SafeStorable(@NonNull final SafeMigratableStorableBuilder<TKey, TObject, TStoreObject> builderCore) {
|
||||
super(builderCore);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TObject getSync() {
|
||||
try {
|
||||
return super.getSync();
|
||||
} catch (final Exception exception) {
|
||||
throw new ShouldNotHappenException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSync(@Nullable final TObject newValue) {
|
||||
try {
|
||||
super.setSync(newValue);
|
||||
} catch (final Exception exception) {
|
||||
throw new ShouldNotHappenException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,14 +32,23 @@ public class ThreadLocalValue<T> extends ThreadLocal<T> {
|
|||
@NonNull
|
||||
private final Func0<T> creator;
|
||||
|
||||
public ThreadLocalValue(@NonNull final Func0<T> creator) {
|
||||
public ThreadLocalValue(@NonNull final NonNullFunc<T> creator) {
|
||||
super();
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected T initialValue() {
|
||||
return creator.call();
|
||||
}
|
||||
|
||||
public interface NonNullFunc<T> extends Func0<T> {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
T call();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -49,14 +49,6 @@ public class HalfNullablePair<TFirst, TSecond> 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<TFirst, TSecond> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TFirst, TSecond> implements Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object object) {
|
||||
public boolean equals(@Nullable final Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,17 +70,17 @@ public class NullablePair<TFirst, TSecond> 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
|
||||
|
|
|
|||
Loading…
Reference in New Issue