diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/ExceptionFilterInstrumentedTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/ExceptionFilterInstrumentedTest.kt index 2dac694c..443af000 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/ExceptionFilterInstrumentedTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/ExceptionFilterInstrumentedTest.kt @@ -2,7 +2,7 @@ package net.pokeranalytics.android.unitTests.filter import androidx.test.ext.junit.runners.AndroidJUnit4 import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest -import net.pokeranalytics.android.exceptions.FilterValueMapException +import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.FilterElement @@ -13,7 +13,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ExceptionFilterInstrumentedTest: BaseFilterInstrumentedUnitTest() { - @Test(expected = FilterValueMapException::class) + @Test(expected = PokerAnalyticsException.FilterElementExpectedValueMissing::class) fun testValueKeyFilterException() { val filter = QueryType.STARTED_FROM_DATE val filterElement = FilterElement() @@ -27,15 +27,8 @@ class ExceptionFilterInstrumentedTest: BaseFilterInstrumentedUnitTest() { ) } - @Test(expected = FilterValueMapException::class) + @Test(expected = PokerAnalyticsException.FilterElementUnknownName::class) fun testFilterException() { - val realm = this.mockRealm - val filter = QueryType.BLINDS - filter.updateValueMap(FilterElement()) - Filter.queryOn( - realm, - Session, - arrayListOf(filter) - ) + FilterElement().queryType } } \ No newline at end of file diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt index 9b421570..0b68f9e2 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt @@ -2,6 +2,7 @@ package net.pokeranalytics.android.unitTests.filter import androidx.test.ext.junit.runners.AndroidJUnit4 import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest +import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Session @@ -37,7 +38,7 @@ class RealmFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() { val filterComponent = filter.filterElements.first() filterComponent?.let { - Assert.assertEquals(QueryType.CASH, QueryType.valueOf(it.filterName)) + Assert.assertEquals(QueryType.CASH, QueryType.valueOf(it.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName)) } ?: run { Assert.fail() } diff --git a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt index 4b572cb1..098dc370 100644 --- a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt +++ b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt @@ -1,25 +1,22 @@ package net.pokeranalytics.android.exceptions -class ModelException(message: String) : Exception(message) { - -} - -class FormattingException(message: String) : Exception(message) { - -} - -class RowRepresentableEditDescriptorException(message: String) : Exception(message) { - -} - -class FilterValueMapException(message: String) : Exception(message) { - init { - println("FilterValueMapException(): $message") - } -} - -class FilterMissingEntityException(message: String) : Exception(message) -class FilterUnhandledEntityException(message : String) : Exception(message) -class ConfigurationException(message: String) : Exception(message) { - -} +import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow + +class ModelException(message: String) : Exception(message) +class FormattingException(message: String) : Exception(message) +class RowRepresentableEditDescriptorException(message: String) : Exception(message) + +class ConfigurationException(message: String) : Exception(message) + +sealed class PokerAnalyticsException(message: String) : Exception(message) { + object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryType") + object FilterElementUnknownSectionName: PokerAnalyticsException(message = "No filterElement section name was found to identify the queryType") + object FilterMissingEntity: PokerAnalyticsException(message = "This filter has no entity initialized") + object FilterUnhandledEntity : PokerAnalyticsException(message = "This entity is not filterable") + object QueryValueMapUnknown: PokerAnalyticsException(message = "fieldName is missing") + object QueryTypeUnhandled: PokerAnalyticsException(message = "filter type not handled") + object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected") + object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "filter is empty or null") + data class QueryValueMapMissingKeys(val missingKeys: List) : PokerAnalyticsException(message = "valueMap does not contain $missingKeys") + data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no filter type for $filterElementRow") +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt index 3625acca..afa66436 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt @@ -1,6 +1,7 @@ package net.pokeranalytics.android.model.filter -import io.realm.RealmObject +import io.realm.RealmModel +import net.pokeranalytics.android.model.realm.Session /** * We want to be able to store filters in the database: @@ -28,15 +29,37 @@ import io.realm.RealmObject * */ +class UnmanagedFilterField(message: String) : Exception(message) { + +} /** * Companion-level Interface to indicate an RealmObject class can be filtered and to provide all the fieldNames (eg: parameter's path) needed to be query on. */ -interface Filterable { +interface Filterable : RealmModel { /** * return the path of the parameter used in the [QueryType] related to this entity */ fun fieldNameForQueryType(queryType: QueryType) : String? + + +} + +class FilterHelper { + + companion object { + + inline fun fieldNameForQueryType(queryType: QueryType) : String? { + when (T::class) { + is Session -> { + Session.fieldNameForQueryType(queryType) + } + } + throw UnmanagedFilterField("Filterable type fields are not defined") + } + + } + } // diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryType.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryType.kt index 2ba40ed6..208760c1 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryType.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryType.kt @@ -1,12 +1,10 @@ package net.pokeranalytics.android.model.filter import io.realm.RealmList -import io.realm.RealmObject import io.realm.RealmQuery -import net.pokeranalytics.android.exceptions.FilterValueMapException -import net.pokeranalytics.android.model.realm.FilterElementBlind +import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.realm.FilterElement -import net.pokeranalytics.android.model.realm.Session +import net.pokeranalytics.android.model.realm.FilterElementBlind import java.util.* @@ -16,7 +14,7 @@ import java.util.* * To handle that, the enum has a public [valueMap] variable * A new type should also set the expected numericValues required in the [filterValuesExpectedKeys] */ -enum class QueryType(private var subType:SubType? = null) { +enum class QueryType(var subType:SubType? = null) { LIVE, CASH, ONLINE, @@ -68,7 +66,7 @@ enum class QueryType(private var subType:SubType? = null) { ; - private enum class SubType { + enum class SubType { BETWEEN, MORE, LESS; @@ -98,18 +96,19 @@ enum class QueryType(private var subType:SubType? = null) { * main method of the enum * providing a base RealmQuery [realmQuery], the method is able to attached the corresponding query and returns the newly formed [RealmQuery] */ - fun filter(realmQuery: RealmQuery, filterable: Filterable): RealmQuery { + inline fun filter(realmQuery: RealmQuery): RealmQuery { when { this == BLINDS -> { - val smallBlindFieldName = filterable.fieldNameForQueryType(SMALL_BLIND) - val bigBlindFieldName = filterable.fieldNameForQueryType(BIG_BLIND) - val currencyCodeFieldName = filterable.fieldNameForQueryType(CURRENCY_CODE) - smallBlindFieldName ?: throw FilterValueMapException("fieldName is missing") - bigBlindFieldName ?: throw FilterValueMapException("fieldName is missing") - currencyCodeFieldName ?: throw FilterValueMapException("fieldName is missing") + + val smallBlindFieldName = FilterHelper.fieldNameForQueryType(SMALL_BLIND) + val bigBlindFieldName = FilterHelper.fieldNameForQueryType(BIG_BLIND) + val currencyCodeFieldName = FilterHelper.fieldNameForQueryType(CURRENCY_CODE) + smallBlindFieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown + bigBlindFieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown + currencyCodeFieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown val blinds: RealmList by valueMap - blinds.forEachIndexed {index, blind -> + blinds.forEachIndexed { index, blind -> realmQuery .beginGroup() @@ -137,12 +136,10 @@ enum class QueryType(private var subType:SubType? = null) { } return realmQuery } - this == ONLINE -> return LIVE.filter(realmQuery.not(), filterable) - this == TOURNAMENT -> return CASH.filter(realmQuery.not(), filterable) - this == WEEK_DAY -> return WEEK_END.filter(realmQuery.not(), filterable) else -> { - val fieldName = filterable.fieldNameForQueryType(this) - fieldName ?: throw FilterValueMapException("fieldName is missing") + + val fieldName = FilterHelper.fieldNameForQueryType(this) + fieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown this.subType?.let { subType -> return when (subType) { @@ -163,8 +160,8 @@ enum class QueryType(private var subType:SubType? = null) { } return when (this) { - LIVE -> realmQuery.equalTo(fieldName, true) - CASH -> realmQuery.equalTo(fieldName, Session.Type.CASH_GAME.ordinal) + LIVE, ONLINE -> realmQuery.equalTo(fieldName, this == LIVE) + CASH, TOURNAMENT -> realmQuery.equalTo(fieldName, this.ordinal) ALL_TOURNAMENT_FEATURES -> { val ids: Array by valueMap ids.forEach { @@ -212,11 +209,13 @@ enum class QueryType(private var subType:SubType? = null) { val year: Int by valueMap realmQuery.equalTo(fieldName, year) } - WEEK_END -> { - realmQuery.`in`(fieldName, arrayOf(Calendar.SATURDAY, Calendar.SUNDAY)) + WEEK_END, WEEK_DAY -> { + var query = realmQuery.`in`(fieldName, arrayOf(Calendar.SATURDAY, Calendar.SUNDAY)) + if (this == WEEK_DAY) { query.not() } + query } else -> { - throw FilterValueMapException("filter type not handled") + throw PokerAnalyticsException.QueryTypeUnhandled } } } @@ -267,7 +266,7 @@ enum class QueryType(private var subType:SubType? = null) { valueMap = mapOf("year" to filterElement.year) } else -> { - throw FilterValueMapException("filter type not handled") + throw PokerAnalyticsException.QueryValueMapUnexpectedValue } } } @@ -278,10 +277,10 @@ enum class QueryType(private var subType:SubType? = null) { field?.let { map -> val missingKeys = map.keys.filter { !valueMapExceptedKeys.contains(it) } if (map.keys.size == valueMapExceptedKeys.size && missingKeys.isNotEmpty()) { - throw FilterValueMapException("valueMap does not contain $missingKeys") + throw PokerAnalyticsException.QueryValueMapMissingKeys(missingKeys) } } ?: run { - throw FilterValueMapException("valueMap null not expected") + throw PokerAnalyticsException.QueryValueMapUnexpectedValue } } return field diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt index 8fb6ad60..07d73637 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt @@ -2,8 +2,9 @@ package net.pokeranalytics.android.model.realm import io.realm.* import io.realm.annotations.PrimaryKey -import net.pokeranalytics.android.exceptions.FilterMissingEntityException -import net.pokeranalytics.android.exceptions.FilterUnhandledEntityException +import net.pokeranalytics.android.exceptions.PokerAnalyticsException + +import io.realm.kotlin.where import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow @@ -38,7 +39,7 @@ open class Filter(entity:Filterable) : RealmObject() { fun filterableClass(entity: Filterable): FilterableClass { return when (entity) { is Session.Companion -> SESSION - else -> throw FilterUnhandledEntityException("this entity is not filterable") + else -> throw PokerAnalyticsException.FilterUnhandledEntity } } } @@ -64,11 +65,10 @@ open class Filter(entity:Filterable) : RealmObject() { companion object { @TestOnly - fun queryOn(realm: Realm, entity: Filterable, queries:List): RealmResults<*> { - val realmEntity : Class < out RealmObject > = FilterableClass.filterableClass(entity).relatedEntity - var realmQuery : RealmQuery = realm.where(realmEntity) + inline fun queryOn(realm: Realm, queries: List): RealmResults { + var realmQuery = realm.where() queries.forEach { - realmQuery = (it.filter(realmQuery, entity)) + realmQuery = (it.filter(realmQuery)) } return realmQuery.findAll() } @@ -116,7 +116,7 @@ open class Filter(entity:Filterable) : RealmObject() { fun countBy(filterCategoryRow: FilterCategoryRow) : Int { val sections = filterCategoryRow.filterSectionRows return filterElements.count { - sections.contains(FilterSectionRow.valueOf(it.sectionName)) + sections.contains(FilterSectionRow.valueOf(it.sectionName ?: throw PokerAnalyticsException.FilterElementUnknownSectionName)) } } @@ -130,16 +130,15 @@ open class Filter(entity:Filterable) : RealmObject() { return filterElementRow.contains(filtered) } - fun results(): RealmResults<*> { - val filterableClass : FilterableClass = this.filterableClass ?: throw FilterMissingEntityException("this filter has no entity initialized") - val realmEntity : Class < out RealmObject > = filterableClass.relatedEntity - var realmQuery : RealmQuery = realm.where(realmEntity) + inline fun results(): RealmResults { + var realmQuery : RealmQuery = realm.where() this.filterElements.map { it.queryType }.forEach { - realmQuery = (it.filter(realmQuery, filterableClass.filterable)) + realmQuery = it.filter(realmQuery) } return realmQuery.findAll() } + } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/FilterElement.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/FilterElement.kt index d6eb5d55..c0d35070 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/FilterElement.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/FilterElement.kt @@ -2,7 +2,7 @@ package net.pokeranalytics.android.model.realm import io.realm.RealmList import io.realm.RealmObject -import net.pokeranalytics.android.exceptions.FilterValueMapException +import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow.* @@ -17,7 +17,8 @@ open class FilterElement() : RealmObject() { } constructor(filterElementRows: ArrayList) : this(filterElementRows.first().filterName, filterElementRows.first().filterSectionRow.name) { - this.stringValues = when (QueryType.valueOf(this.filterName)) { + val filterName : String = this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName + this.stringValues = when (QueryType.valueOf(filterName)) { QueryType.GAME, QueryType.BANKROLL, QueryType.TOURNAMENT_NAME, QueryType.ALL_TOURNAMENT_FEATURES, QueryType.ANY_TOURNAMENT_FEATURES, QueryType.LOCATION -> { RealmList().apply { this.addAll(filterElementRows.map { @@ -86,11 +87,11 @@ open class FilterElement() : RealmObject() { } } - var filterName : String = "" - var sectionName : String = "" + var filterName : String? = null + var sectionName : String? = null val queryType : QueryType - get() = QueryType.valueOf(filterName) + get() = QueryType.valueOf(this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName) .apply { this.updateValueMap(this@FilterElement) } @@ -101,7 +102,7 @@ open class FilterElement() : RealmObject() { private var blindValues : RealmList? = null val ids : Array - get() = stringValues?.toTypedArray()?: throw FilterValueMapException("filter type not handled") + get() = stringValues?.toTypedArray()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val blinds : RealmList get() { @@ -109,44 +110,44 @@ open class FilterElement() : RealmObject() { if (it.isNotEmpty()) { return it } else { - throw FilterValueMapException("filter is empty or null") + throw PokerAnalyticsException.FilterElementExpectedValueMissing } } - throw FilterValueMapException("filter is empty or null") + throw PokerAnalyticsException.FilterElementExpectedValueMissing } val date : Date - get() = dateValue?: throw FilterValueMapException("filter type not handled") + get() = dateValue?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val values : Array get() = numericValues?.map { it.toInt() - }?.toTypedArray()?: throw FilterValueMapException("filter type not handled") + }?.toTypedArray()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val value : Double - get() = numericValues?.first()?: throw FilterValueMapException("filter type not handled") + get() = numericValues?.first()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val leftValue : Double - get() = numericValues?.first()?: throw FilterValueMapException("filter type not handled") + get() = numericValues?.first()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val rightValue : Double - get() = numericValues?.last()?: throw FilterValueMapException("filter type not handled") + get() = numericValues?.last()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val dayOfWeek : Int - get() = numericValues?.first()?.toInt()?: throw FilterValueMapException("filter type not handled") + get() = numericValues?.first()?.toInt()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val month : Int - get() = numericValues?.first()?.toInt()?: throw FilterValueMapException("filter type not handled") + get() = numericValues?.first()?.toInt()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing val year : Int - get() = numericValues?.first()?.toInt()?: throw FilterValueMapException("filter type not handled") + get() = numericValues?.first()?.toInt()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt index 7a899662..5dd5bde3 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt @@ -1,7 +1,7 @@ package net.pokeranalytics.android.ui.view.rowrepresentable import net.pokeranalytics.android.R -import net.pokeranalytics.android.exceptions.FilterValueMapException +import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.interfaces.Manageable import net.pokeranalytics.android.model.realm.FilterElement @@ -88,7 +88,7 @@ sealed class FilterElementRow : RowRepresentable { is AllTournamentFeature -> QueryType.ALL_TOURNAMENT_FEATURES is ResultMoreThan -> QueryType.MORE_THAN_NET_RESULT is ResultLessThan -> QueryType.LESS_THAN_NET_RESULT - else -> throw FilterValueMapException("no filter type for $this") //TODO create exception + else -> throw PokerAnalyticsException.UnknownQueryTypeForRow(this) } }