From eeb9a8483a0d2dfe5673efd03c7405456ccde4cd Mon Sep 17 00:00:00 2001 From: Gavriil Sitnikov Date: Thu, 17 Mar 2016 11:17:09 +0300 Subject: [PATCH] storable classsesadded for a while --- .../org/storable/ApplicationTest.java | 13 + .../storable/src/main/AndroidManifest.xml | 9 + .../java/roboswag/org/core/ObjectUtils.java | 150 +++++++++ .../exceptions/ObjectIsMutableException.java | 15 + .../java/roboswag/org/storable/Converter.java | 24 ++ .../java/roboswag/org/storable/Migration.java | 73 +++++ .../java/roboswag/org/storable/Migrator.java | 47 +++ .../java/roboswag/org/storable/Storable.java | 295 ++++++++++++++++++ .../java/roboswag/org/storable/Store.java | 23 ++ .../java/roboswag/org/storable/Validator.java | 15 + .../exceptions/ConversionException.java | 19 ++ .../exceptions/MigrationException.java | 19 ++ .../storable/exceptions/StoreException.java | 19 ++ .../exceptions/ValidationException.java | 19 ++ .../storable/src/main/res/values/strings.xml | 3 + .../org/storable/ExampleUnitTest.java | 15 + 16 files changed, 758 insertions(+) create mode 100644 Storable/storable/src/androidTest/java/roboswag/org/storable/ApplicationTest.java create mode 100644 Storable/storable/src/main/AndroidManifest.xml create mode 100644 Storable/storable/src/main/java/roboswag/org/core/ObjectUtils.java create mode 100644 Storable/storable/src/main/java/roboswag/org/core/exceptions/ObjectIsMutableException.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/Converter.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/Migration.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/Migrator.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/Storable.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/Store.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/Validator.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/exceptions/ConversionException.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/exceptions/MigrationException.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/exceptions/StoreException.java create mode 100644 Storable/storable/src/main/java/roboswag/org/storable/exceptions/ValidationException.java create mode 100644 Storable/storable/src/main/res/values/strings.xml create mode 100644 Storable/storable/src/test/java/roboswag/org/storable/ExampleUnitTest.java diff --git a/Storable/storable/src/androidTest/java/roboswag/org/storable/ApplicationTest.java b/Storable/storable/src/androidTest/java/roboswag/org/storable/ApplicationTest.java new file mode 100644 index 0000000..9c2fefd --- /dev/null +++ b/Storable/storable/src/androidTest/java/roboswag/org/storable/ApplicationTest.java @@ -0,0 +1,13 @@ +package roboswag.org.storable; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/Storable/storable/src/main/AndroidManifest.xml b/Storable/storable/src/main/AndroidManifest.xml new file mode 100644 index 0000000..867823c --- /dev/null +++ b/Storable/storable/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/Storable/storable/src/main/java/roboswag/org/core/ObjectUtils.java b/Storable/storable/src/main/java/roboswag/org/core/ObjectUtils.java new file mode 100644 index 0000000..7c6b75f --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/core/ObjectUtils.java @@ -0,0 +1,150 @@ +package roboswag.org.core; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.AbstractList; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import roboswag.org.core.exceptions.ObjectIsMutableException; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public final class ObjectUtils { + + private static final List IMMUTABLE_COLLECTIONS_TYPES + = Arrays.asList(AbstractList.class, AbstractMap.class, AbstractSet.class); + + public static boolean equals(@Nullable Object object1, @Nullable Object object2) { + // copy of Arrays.deepEqualsElements + + if (object1 == object2) { + return true; + } + + if (object1 == null || object2 == null) { + return false; + } + + Class cl1 = object1.getClass().getComponentType(); + Class cl2 = object2.getClass().getComponentType(); + + if (cl1 != cl2) { + return false; + } + + if (cl1 == null) { + return object1.equals(object2); + } + + /* + * compare as arrays + */ + if (object1 instanceof Object[]) { + return Arrays.deepEquals((Object[]) object1, (Object[]) object2); + } else if (cl1 == int.class) { + return Arrays.equals((int[]) object1, (int[]) object2); + } else if (cl1 == char.class) { + return Arrays.equals((char[]) object1, (char[]) object2); + } else if (cl1 == boolean.class) { + return Arrays.equals((boolean[]) object1, (boolean[]) object2); + } else if (cl1 == byte.class) { + return Arrays.equals((byte[]) object1, (byte[]) object2); + } else if (cl1 == long.class) { + return Arrays.equals((long[]) object1, (long[]) object2); + } else if (cl1 == float.class) { + return Arrays.equals((float[]) object1, (float[]) object2); + } else if (cl1 == double.class) { + return Arrays.equals((double[]) object1, (double[]) object2); + } else { + return Arrays.equals((short[]) object1, (short[]) object2); + } + } + + public static void checkIfIsImmutable(@NonNull Class objectClass) throws ObjectIsMutableException { + checkIfIsImmutable(objectClass, false, new HashSet<>()); + } + + private static void checkIfIsImmutable(@NonNull Class objectClass, + boolean isSuperclass, + @NonNull Set checkedClasses) + throws ObjectIsMutableException { + + if (checkedClasses.contains(objectClass)) { + return; + } + checkedClasses.add(objectClass); + + if (objectClass.isArray()) { + throw new ObjectIsMutableException(objectClass + " is array which is mutable"); + } + + if (objectClass.isPrimitive() || objectClass.getSuperclass() == Number.class + || objectClass.isEnum() || objectClass == Boolean.class + || objectClass == String.class || objectClass == Object.class) { + return; + } + + if (isImmutableCollection(objectClass, objectClass.getGenericSuperclass(), checkedClasses)) { + return; + } + + if (!isSuperclass + && (!Modifier.isFinal(objectClass.getModifiers()) + || (objectClass.isMemberClass() && !Modifier.isStatic(objectClass.getModifiers())))) { + throw new ObjectIsMutableException(objectClass + " is not final and static"); + } + + for (Field field : objectClass.getDeclaredFields()) { + if (!Modifier.isFinal(field.getModifiers())) { + throw new ObjectIsMutableException("Field " + field.getName() + " of class " + objectClass + " is not final"); + } + if (isImmutableCollection(field.getType(), field.getGenericType(), checkedClasses)) { + continue; + } + checkIfIsImmutable(field.getType(), false, checkedClasses); + } + + if (objectClass.getSuperclass() != null) { + checkIfIsImmutable(objectClass.getSuperclass(), true, checkedClasses); + } + } + + private static boolean isImmutableCollection(@NonNull Class objectClass, + @Nullable Type genericType, + @NonNull Set checkedClasses) + throws ObjectIsMutableException { + for (Class collectionClass : IMMUTABLE_COLLECTIONS_TYPES) { + if (collectionClass != objectClass && collectionClass != objectClass.getSuperclass()) { + continue; + } + + if (!(genericType instanceof ParameterizedType)) { + throw new ObjectIsMutableException(objectClass + " is immutable collection but generic type " + genericType + " is not ParameterizedType"); + } + for (Type parameterType : ((ParameterizedType) genericType).getActualTypeArguments()) { + if (!(parameterType instanceof Class)) { + throw new ObjectIsMutableException(objectClass + " is immutable collection but generic parameterType " + parameterType + "is not ParameterizedType"); + } + checkIfIsImmutable((Class) parameterType, false, checkedClasses); + } + return true; + } + return false; + } + + private ObjectUtils() { + } + +} diff --git a/Storable/storable/src/main/java/roboswag/org/core/exceptions/ObjectIsMutableException.java b/Storable/storable/src/main/java/roboswag/org/core/exceptions/ObjectIsMutableException.java new file mode 100644 index 0000000..3a310f5 --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/core/exceptions/ObjectIsMutableException.java @@ -0,0 +1,15 @@ +package roboswag.org.core.exceptions; + +import android.support.annotation.NonNull; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public class ObjectIsMutableException extends Exception { + + public ObjectIsMutableException(@NonNull String message){ + super(message); + } + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/Converter.java b/Storable/storable/src/main/java/roboswag/org/storable/Converter.java new file mode 100644 index 0000000..fbf20d5 --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/Converter.java @@ -0,0 +1,24 @@ +package roboswag.org.storable; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import roboswag.org.storable.exceptions.ConversionException; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public interface Converter { + + @Nullable + TStoreObject toStoreObject(@NonNull Class objectClass, + @NonNull Class storeObjectClass, + @Nullable TObject object) throws ConversionException; + + @Nullable + TObject toObject(@NonNull Class objectClass, + @NonNull Class storeObjectClass, + @Nullable TStoreObject storeObject) throws ConversionException; + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/Migration.java b/Storable/storable/src/main/java/roboswag/org/storable/Migration.java new file mode 100644 index 0000000..8a1064f --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/Migration.java @@ -0,0 +1,73 @@ +package roboswag.org.storable; + +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +import roboswag.org.storable.exceptions.MigrationException; +import roboswag.org.storable.exceptions.StoreException; + +/** + * Created by Gavriil Sitnikov on 06/10/2015. + * TODO: fill description + */ +public class Migration { + + private final long latestVersion; + @NonNull + private final Store versionsStore; + @NonNull + private final List> migrators = new ArrayList<>(); + + public Migration(@NonNull Store versionsStore, long latestVersion) { + this.versionsStore = versionsStore; + this.latestVersion = latestVersion; + } + + public void addMigrator(@NonNull Migrator migrator) { + migrators.add(migrator); + } + + public void migrateToLatestVersion(@NonNull TKey key) throws MigrationException { + Long version; + try { + version = versionsStore.loadObject(Long.class, key); + } catch (StoreException throwable) { + throw new MigrationException("Version for key " + key + " is null", throwable); + } + + if (version == null) { + version = Migrator.DEFAULT_VERSION; + } + + while (!version.equals(latestVersion)) { + long oldVersion = version; + boolean migrationTriggered = false; + for (Migrator migrator : migrators) { + if (migrator.supportMigrationFor(version) && migrator.getOldStore().contains(key)) { + version = migrator.migrate(key, version); + migrationTriggered = true; + } + } + if (oldVersion > version) { + throw new MigrationException("Version downgraded from [" + oldVersion + "] to [" + version + "]"); + } else if (oldVersion == version) { + if (migrationTriggered) { + throw new MigrationException("Migration not changed version [" + version + "]"); + } else { + break; + } + } else if (version > latestVersion) { + throw new MigrationException("Version [" + version + "] is higher than latest version [" + latestVersion + "]"); + } + } + + try { + versionsStore.storeObject(Long.class, key, latestVersion); + } catch (StoreException throwable) { + throw new MigrationException("Storing version failed for " + key, throwable); + } + } + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/Migrator.java b/Storable/storable/src/main/java/roboswag/org/storable/Migrator.java new file mode 100644 index 0000000..6d22799 --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/Migrator.java @@ -0,0 +1,47 @@ +package roboswag.org.storable; + +import android.support.annotation.NonNull; + +import roboswag.org.storable.exceptions.MigrationException; + +/** + * Created by Gavriil Sitnikov on 05/10/2015. + * TODO: fill description + */ +public abstract class Migrator { + + public static final long DEFAULT_VERSION = -1L; + + @NonNull + private final Store oldStore; + @NonNull + private final Store newStore; + + public Migrator(@NonNull Store oldStore, + @NonNull Store newStore) { + this.oldStore = oldStore; + this.newStore = newStore; + } + + @NonNull + public Store getOldStore() { + return oldStore; + } + + @NonNull + public Store getNewStore() { + return newStore; + } + + public abstract boolean supportMigrationFor(long version); + + public long migrate(TKey oldKey, long version) throws MigrationException { + if (!supportMigrationFor(version)) { + throw new MigrationException("Version " + version + " not supported by " + this); + } + return migrateInternal(oldKey); + } + + protected abstract long migrateInternal(TKey key) throws MigrationException; + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/Storable.java b/Storable/storable/src/main/java/roboswag/org/storable/Storable.java new file mode 100644 index 0000000..b2afd7d --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/Storable.java @@ -0,0 +1,295 @@ +package roboswag.org.storable; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import roboswag.org.core.ObjectUtils; +import roboswag.org.core.exceptions.ObjectIsMutableException; +import roboswag.org.storable.exceptions.ConversionException; +import roboswag.org.storable.exceptions.MigrationException; +import roboswag.org.storable.exceptions.StoreException; +import roboswag.org.storable.exceptions.ValidationException; +import rx.Observable; +import rx.functions.Actions; +import rx.schedulers.Schedulers; +import rx.subjects.PublishSubject; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO + */ +public abstract class Storable { + + private static final String LOG_TAG = "Storable"; + + private static int globalLogLevel = Log.ERROR; + private static boolean isInDebugMode = false; + + public static void setLogLevel(int logLevel) { + globalLogLevel = logLevel; + } + + public static void setDebugMode(boolean debugMode) { + isInDebugMode = debugMode; + } + + @NonNull + private final String name; + @NonNull + private final TKey key; + @NonNull + private final Class objectClass; + @NonNull + private final Class storeObjectClass; + @NonNull + private final Store store; + @NonNull + private final Converter converter; + private final boolean cloneOnGet; + @Nullable + private final Migration migration; + @Nullable + private final Validator validator; + @Nullable + private final TObject defaultValue; + + @NonNull + private final PublishSubject valueSubject = PublishSubject.create(); + @NonNull + private final Observable valueObservable = Observable.create(subscriber -> { + try { + subscriber.onNext(get()); + } catch (Exception throwable) { + if (globalLogLevel <= Log.ERROR) { + Log.e(LOG_TAG, "Error during get: " + Log.getStackTraceString(throwable)); + } + subscriber.onError(throwable); + } + }).subscribeOn(Schedulers.io()) + .concatWith(valueSubject) + .replay(1).autoConnect(); + + @Nullable + private CachedValue cachedStoreDefaultValue; + @Nullable + private CachedValue cachedStoreValue; + @Nullable + private CachedValue cachedValue; + + protected Storable(@NonNull String name, + @NonNull TKey key, + @NonNull Class objectClass, + @NonNull Class storeObjectClass, + @NonNull Store store, + @NonNull Converter converter, + boolean cloneOnGet, + @Nullable Migration migration, + @Nullable Validator validator, + @Nullable TObject defaultValue) { + this.name = name; + this.key = key; + this.objectClass = objectClass; + this.storeObjectClass = storeObjectClass; + this.store = store; + this.converter = converter; + this.cloneOnGet = cloneOnGet; + this.migration = migration; + this.validator = validator; + this.defaultValue = defaultValue; + + if (isInDebugMode && !cloneOnGet) { + try { + ObjectUtils.checkIfIsImmutable(objectClass); + } catch (ObjectIsMutableException throwable) { + Log.w(LOG_TAG, Log.getStackTraceString(throwable)); + } + } + } + + @NonNull + public String getName() { + return name; + } + + @NonNull + public TKey getKey() { + return key; + } + + @NonNull + public Store getStore() { + return store; + } + + @NonNull + public Converter getConverter() { + return converter; + } + + @Nullable + public TObject getDefaultValue() { + return defaultValue; + } + + @Nullable + public Validator getValidator() { + return validator; + } + + @NonNull + private CachedValue getCachedStoreDefaultValue() throws ConversionException { + if (cachedStoreDefaultValue == null) { + cachedStoreDefaultValue = new CachedValue<>(converter.toStoreObject(objectClass, storeObjectClass, defaultValue)); + } + return cachedStoreDefaultValue; + } + + @Nullable + private TStoreObject getStoreValue() throws StoreException, ConversionException, MigrationException { + synchronized (this) { + if (cachedStoreValue == null) { + if (migration != null) { + try { + migration.migrateToLatestVersion(key); + } catch (MigrationException throwable) { + if (isInDebugMode) { + throw throwable; + } else if (globalLogLevel <= Log.ERROR) { + Log.e(LOG_TAG, "Error during migration: " + Log.getStackTraceString(throwable)); + } + } + } + TStoreObject storeObject = store.loadObject(storeObjectClass, key); + cachedStoreValue = storeObject == null && defaultValue != null + ? getCachedStoreDefaultValue() + : new CachedValue<>(storeObject); + } + + return cachedStoreValue.value; + } + } + + @Nullable + private TObject getDirectValue() throws StoreException, ConversionException { + synchronized (this) { + if (cachedValue == null) { + TStoreObject storeObject = store.loadObject(storeObjectClass, key); + cachedValue = storeObject == null && defaultValue != null + ? new CachedValue<>(defaultValue) + : new CachedValue<>(converter.toObject(objectClass, storeObjectClass, storeObject)); + } + return cachedValue.value; + } + } + + @Nullable + public TObject get() throws StoreException, ConversionException, MigrationException { + synchronized (this) { + if (cloneOnGet) { + TStoreObject storeValue = getStoreValue(); + return storeValue != null ? converter.toObject(objectClass, storeObjectClass, storeValue) : null; + } else { + return getDirectValue(); + } + } + } + + private void updateCachedValue(@Nullable TObject value, @Nullable TStoreObject storeObject) throws ConversionException { + cachedValue = null; + cachedStoreValue = null; + if (cloneOnGet) { + cachedStoreValue = storeObject == null && defaultValue != null + ? getCachedStoreDefaultValue() + : new CachedValue<>(storeObject); + } else { + cachedValue = storeObject == null && defaultValue != null + ? new CachedValue<>(defaultValue) + : new CachedValue<>(value); + } + } + + public void set(@Nullable TObject value) + throws ValidationException, ConversionException, StoreException, MigrationException { + synchronized (this) { + if (validator != null) { + validator.validate(value); + } + + TObject oldValue = null; + if (!cloneOnGet && cachedValue != null) { + oldValue = cachedValue.value; + if (ObjectUtils.equals(oldValue, value)) { + return; + } + } + + TStoreObject valueToStore = converter.toStoreObject(objectClass, storeObjectClass, value); + try { + TStoreObject storedValue = getStoreValue(); + if (ObjectUtils.equals(storedValue, valueToStore)) { + return; + } + if (oldValue == null) { + oldValue = converter.toObject(objectClass, storeObjectClass, storedValue); + } + } catch (Exception throwable) { + // some invalid value in store + if (globalLogLevel <= Log.WARN) { + Log.w(LOG_TAG, "Can't get current store value: " + Log.getStackTraceString(throwable)); + } + } + + store.storeObject(storeObjectClass, key, valueToStore); + updateCachedValue(value, valueToStore); + onValueChanged(get(), oldValue); + } + } + + public void setAsync(@Nullable TObject value) { + setObservable(value).subscribe(Actions.empty(), this::onSetError); + } + + private void onSetError(Throwable throwable) { + if (globalLogLevel <= Log.ERROR) { + Log.e(LOG_TAG, "Error during set: " + Log.getStackTraceString(throwable)); + } + } + + public Observable setObservable(@Nullable TObject value) { + return Observable.create(subscriber -> { + try { + set(value); + } catch (Exception throwable) { + if (globalLogLevel <= Log.ERROR) { + Log.e(LOG_TAG, "Error during set: " + Log.getStackTraceString(throwable)); + } + subscriber.onError(throwable); + } + subscriber.onCompleted(); + }).subscribeOn(Schedulers.io()); + } + + public Observable observe() { + return valueObservable; + } + + protected void onValueChanged(@Nullable TObject newValue, TObject oldValue) { + valueSubject.onNext(newValue); + if (globalLogLevel <= Log.INFO) { + Log.w(LOG_TAG, "Value changed from '" + oldValue + "' to '" + newValue + "'"); + } + } + + private class CachedValue { + + @Nullable + private final T value; + + private CachedValue(@Nullable T value) { + this.value = value; + } + + } + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/Store.java b/Storable/storable/src/main/java/roboswag/org/storable/Store.java new file mode 100644 index 0000000..2608820 --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/Store.java @@ -0,0 +1,23 @@ +package roboswag.org.storable; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import roboswag.org.storable.exceptions.StoreException; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public interface Store { + + boolean contains(@NonNull TKey key); + + void storeObject(@NonNull Class storeObjectClass, + @NonNull TKey key, + @Nullable TStoreObject storeObject) throws StoreException; + + @Nullable + TStoreObject loadObject(@NonNull Class storeObjectClass, @NonNull TKey key) throws StoreException; + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/Validator.java b/Storable/storable/src/main/java/roboswag/org/storable/Validator.java new file mode 100644 index 0000000..9cd9d23 --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/Validator.java @@ -0,0 +1,15 @@ +package roboswag.org.storable; + +import android.support.annotation.Nullable; + +import roboswag.org.storable.exceptions.ValidationException; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public interface Validator { + + void validate(@Nullable T value) throws ValidationException; + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/exceptions/ConversionException.java b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/ConversionException.java new file mode 100644 index 0000000..dcd513b --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/ConversionException.java @@ -0,0 +1,19 @@ +package roboswag.org.storable.exceptions; + +import android.support.annotation.NonNull; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public class ConversionException extends Exception { + + public ConversionException(@NonNull String message) { + super(message); + } + + public ConversionException(@NonNull String message, @NonNull Throwable throwable) { + super(message, throwable); + } + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/exceptions/MigrationException.java b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/MigrationException.java new file mode 100644 index 0000000..d42ff3c --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/MigrationException.java @@ -0,0 +1,19 @@ +package roboswag.org.storable.exceptions; + +import android.support.annotation.NonNull; + +/** + * Created by Gavriil Sitnikov on 05/10/2015. + * TODO: fill description + */ +public class MigrationException extends Exception { + + public MigrationException(@NonNull String message) { + super(message); + } + + public MigrationException(@NonNull String message, @NonNull Throwable throwable) { + super(message, throwable); + } + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/exceptions/StoreException.java b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/StoreException.java new file mode 100644 index 0000000..c4f74c3 --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/StoreException.java @@ -0,0 +1,19 @@ +package roboswag.org.storable.exceptions; + +import android.support.annotation.NonNull; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public class StoreException extends Exception { + + public StoreException(@NonNull String message) { + super(message); + } + + public StoreException(@NonNull String message, @NonNull Throwable throwable) { + super(message, throwable); + } + +} diff --git a/Storable/storable/src/main/java/roboswag/org/storable/exceptions/ValidationException.java b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/ValidationException.java new file mode 100644 index 0000000..3b3cafd --- /dev/null +++ b/Storable/storable/src/main/java/roboswag/org/storable/exceptions/ValidationException.java @@ -0,0 +1,19 @@ +package roboswag.org.storable.exceptions; + +import android.support.annotation.NonNull; + +/** + * Created by Gavriil Sitnikov on 04/10/2015. + * TODO: fill description + */ +public class ValidationException extends Exception { + + public ValidationException(@NonNull String message) { + super(message); + } + + public ValidationException(@NonNull String message, @NonNull Throwable throwable) { + super(message, throwable); + } + +} diff --git a/Storable/storable/src/main/res/values/strings.xml b/Storable/storable/src/main/res/values/strings.xml new file mode 100644 index 0000000..3c697bf --- /dev/null +++ b/Storable/storable/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Storable + diff --git a/Storable/storable/src/test/java/roboswag/org/storable/ExampleUnitTest.java b/Storable/storable/src/test/java/roboswag/org/storable/ExampleUnitTest.java new file mode 100644 index 0000000..6726abd --- /dev/null +++ b/Storable/storable/src/test/java/roboswag/org/storable/ExampleUnitTest.java @@ -0,0 +1,15 @@ +package roboswag.org.storable; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * To work on unit tests, switch the Test Artifact in the Build Variants view. + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file