-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #31 from ergon/feature/dope-190-any-every-satisfies
Feature/dope 190 any every satisfies
- Loading branch information
Showing
6 changed files
with
664 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
...tlin/ch/ergon/dope/resolvable/expression/unaliased/type/relational/SatisfiesExpression.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package ch.ergon.dope.resolvable.expression.unaliased.type.relational | ||
|
||
import ch.ergon.dope.DopeQuery | ||
import ch.ergon.dope.resolvable.expression.TypeExpression | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.SatisfiesType.ANY | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.SatisfiesType.EVERY | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.toArrayType | ||
import ch.ergon.dope.validtype.ArrayType | ||
import ch.ergon.dope.validtype.BooleanType | ||
import ch.ergon.dope.validtype.ValidType | ||
|
||
object IteratorManager { | ||
var count: Int = 1 | ||
get() = field++ | ||
|
||
fun resetCounter() { | ||
count = 1 | ||
} | ||
} | ||
|
||
const val DEFAULT_ITERATOR_VARIABLE = "iterator" | ||
|
||
enum class SatisfiesType { | ||
ANY, | ||
EVERY, | ||
} | ||
|
||
sealed class SatisfiesExpression<T : ValidType>( | ||
private val satisfiesType: SatisfiesType, | ||
private val list: TypeExpression<ArrayType<T>>, | ||
private val variable: String, | ||
private val predicate: (Iterator<T>) -> TypeExpression<BooleanType>, | ||
) : TypeExpression<BooleanType> { | ||
override fun toDopeQuery(): DopeQuery { | ||
val listDopeQuery = list.toDopeQuery() | ||
val iteratorVariable = if (variable == DEFAULT_ITERATOR_VARIABLE) variable + IteratorManager.count else variable | ||
|
||
val predicateDopeQuery = predicate(Iterator(iteratorVariable)).toDopeQuery() | ||
return DopeQuery( | ||
queryString = "$satisfiesType `$iteratorVariable` IN ${listDopeQuery.queryString} SATISFIES ${predicateDopeQuery.queryString} END", | ||
parameters = listDopeQuery.parameters + predicateDopeQuery.parameters, | ||
) | ||
} | ||
} | ||
|
||
class Iterator<T : ValidType>(private val variable: String) : TypeExpression<T> { | ||
override fun toDopeQuery() = DopeQuery( | ||
queryString = "`$variable`", | ||
parameters = emptyMap(), | ||
) | ||
} | ||
|
||
class AnySatisfiesExpression<T : ValidType>( | ||
list: TypeExpression<ArrayType<T>>, | ||
variable: String = DEFAULT_ITERATOR_VARIABLE, | ||
predicate: (Iterator<T>) -> TypeExpression<BooleanType>, | ||
) : SatisfiesExpression<T>(ANY, list, variable, predicate) | ||
|
||
class EverySatisfiesExpression<T : ValidType>( | ||
list: TypeExpression<ArrayType<T>>, | ||
variable: String = DEFAULT_ITERATOR_VARIABLE, | ||
predicate: (Iterator<T>) -> TypeExpression<BooleanType>, | ||
) : SatisfiesExpression<T>(EVERY, list, variable, predicate) | ||
|
||
fun <T : ValidType> TypeExpression<ArrayType<T>>.any( | ||
variable: String = DEFAULT_ITERATOR_VARIABLE, | ||
predicate: (Iterator<T>) -> TypeExpression<BooleanType>, | ||
): AnySatisfiesExpression<T> = AnySatisfiesExpression(this, variable, predicate) | ||
|
||
fun <T : ValidType> Collection<TypeExpression<T>>.any( | ||
variable: String = DEFAULT_ITERATOR_VARIABLE, | ||
predicate: (Iterator<T>) -> TypeExpression<BooleanType>, | ||
): AnySatisfiesExpression<T> = AnySatisfiesExpression(toArrayType(), variable, predicate) | ||
|
||
fun <T : ValidType> TypeExpression<ArrayType<T>>.every( | ||
variable: String = DEFAULT_ITERATOR_VARIABLE, | ||
predicate: (Iterator<T>) -> TypeExpression<BooleanType>, | ||
): EverySatisfiesExpression<T> = EverySatisfiesExpression(this, variable, predicate) | ||
|
||
fun <T : ValidType> Collection<TypeExpression<T>>.every( | ||
variable: String = DEFAULT_ITERATOR_VARIABLE, | ||
predicate: (Iterator<T>) -> TypeExpression<BooleanType>, | ||
): EverySatisfiesExpression<T> = EverySatisfiesExpression(toArrayType(), variable, predicate) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
package ch.ergon.dope | ||
|
||
import ch.ergon.dope.helper.someBooleanArrayField | ||
import ch.ergon.dope.helper.someNumberArrayField | ||
import ch.ergon.dope.helper.someStringArrayField | ||
import ch.ergon.dope.helper.someStringField | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.ParameterManager | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.arithmetic.mod | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.logical.and | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.AnySatisfiesExpression | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.EverySatisfiesExpression | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.IteratorManager | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.any | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.every | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.isEqualTo | ||
import ch.ergon.dope.resolvable.expression.unaliased.type.stringfunction.upper | ||
import org.junit.jupiter.api.BeforeEach | ||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
|
||
class SatisfiesTest { | ||
|
||
@BeforeEach | ||
fun setup() { | ||
ParameterManager.resetCounter() | ||
IteratorManager.resetCounter() | ||
} | ||
|
||
@Test | ||
fun `should support any satisfies number`() { | ||
val expected = DopeQuery( | ||
queryString = "ANY `iterator1` IN `numberArrayField` SATISFIES (`iterator1` % 2) = 1 END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = AnySatisfiesExpression(someNumberArrayField()) { x -> x.mod(2).isEqualTo(1) }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support any satisfies string`() { | ||
val expected = DopeQuery( | ||
queryString = "ANY `iterator1` IN `stringArrayField` SATISFIES UPPER(`iterator1`) = \"A\" END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = AnySatisfiesExpression(someStringArrayField()) { x -> upper(x).isEqualTo("A") }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support any satisfies boolean`() { | ||
val expected = DopeQuery( | ||
queryString = "ANY `iterator1` IN `booleanArrayField` SATISFIES `iterator1` END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = AnySatisfiesExpression(someBooleanArrayField()) { it }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support query with any satisfies`() { | ||
val expected = DopeQuery( | ||
queryString = "(`firstName` = \"Hans\" AND ANY `iterator1` IN `hobbies` SATISFIES `iterator1` = \"Football\" END)", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringField("firstName").isEqualTo("Hans") | ||
.and(someStringArrayField("hobbies").any { it.isEqualTo("Football") }).toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support query with any satisfies and named iterator`() { | ||
val expected = DopeQuery( | ||
queryString = "ANY `hobby` IN `hobbies` SATISFIES `hobby` = \"Football\" END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringArrayField("hobbies").any("hobby") { it.isEqualTo("Football") }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support any satisfies with collection`() { | ||
val expected = DopeQuery( | ||
queryString = "ANY `iterator1` IN [`stringField`, `stringField`] SATISFIES `iterator1` = \"something\" END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = listOf(someStringField(), someStringField()).any { it.isEqualTo("something") }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support query any satisfies with named iterator`() { | ||
val expected = DopeQuery( | ||
queryString = "(`firstName` = \"Hans\" AND ANY `hobby` IN `hobbies` SATISFIES `hobby` = \"Football\" END)", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringField("firstName").isEqualTo("Hans") | ||
.and(someStringArrayField("hobbies").any("hobby") { it.isEqualTo("Football") }) | ||
.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support nested any satisfies`() { | ||
val expected = DopeQuery( | ||
queryString = "ANY `iterator1` IN `stringArrayField` SATISFIES " + | ||
"ANY `iterator2` IN `stringArrayField` SATISFIES `iterator2` = `iterator1` END END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringArrayField().any { str1 -> someStringArrayField().any { it.isEqualTo(str1) } }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support every satisfies string`() { | ||
val expected = DopeQuery( | ||
queryString = "EVERY `iterator1` IN `stringArrayField` SATISFIES UPPER(`iterator1`) = \"A\" END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = EverySatisfiesExpression(someStringArrayField()) { x -> upper(x).isEqualTo("A") }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support every satisfies number`() { | ||
val expected = DopeQuery( | ||
queryString = "EVERY `iterator1` IN `numberArrayField` SATISFIES (`iterator1` % 2) = 1 END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = EverySatisfiesExpression(someNumberArrayField()) { x -> x.mod(2).isEqualTo(1) }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support every satisfies boolean`() { | ||
val expected = DopeQuery( | ||
queryString = "EVERY `iterator1` IN `booleanArrayField` SATISFIES `iterator1` END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = EverySatisfiesExpression(someBooleanArrayField()) { it }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support query with every satisfies and named iterator`() { | ||
val expected = DopeQuery( | ||
queryString = "EVERY `hobby` IN `hobbies` SATISFIES `hobby` = \"Football\" END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringArrayField("hobbies").every("hobby") { it.isEqualTo("Football") }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support query with every satisfies`() { | ||
val expected = DopeQuery( | ||
queryString = "(`firstName` = \"Hans\" AND EVERY `iterator1` IN `hobbies` " + | ||
"SATISFIES `iterator1` = \"Football\" END)", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = | ||
someStringField("firstName").isEqualTo("Hans").and(someStringArrayField("hobbies").every { it.isEqualTo("Football") }).toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support every satisfies with collection`() { | ||
val expected = DopeQuery( | ||
queryString = "EVERY `iterator1` IN [`stringField`, `stringField`] SATISFIES `iterator1` = \"something\" END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = listOf(someStringField(), someStringField()).every { it.isEqualTo("something") }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support query every satisfies with named iterator`() { | ||
val expected = DopeQuery( | ||
queryString = "(`firstName` = \"Hans\" AND EVERY `hobby` IN `hobbies` SATISFIES `hobby` = \"Football\" END)", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringField("firstName").isEqualTo("Hans") | ||
.and(someStringArrayField("hobbies").every("hobby") { it.isEqualTo("Football") }).toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support nested every satisfies`() { | ||
val expected = DopeQuery( | ||
queryString = "EVERY `iterator1` IN `stringArrayField` SATISFIES " + | ||
"EVERY `iterator2` IN `stringArrayField` SATISFIES `iterator2` = `iterator1` END END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringArrayField().every { str1 -> someStringArrayField().every { it.isEqualTo(str1) } }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `should support mixed every and any satisfies`() { | ||
val expected = DopeQuery( | ||
queryString = "EVERY `iterator1` IN `stringArrayField` SATISFIES " + | ||
"ANY `iterator2` IN `stringArrayField` SATISFIES `iterator2` = `iterator1` END END", | ||
parameters = emptyMap(), | ||
) | ||
|
||
val actual = someStringArrayField().every { str1 -> someStringArrayField().any { it.isEqualTo(str1) } }.toDopeQuery() | ||
|
||
assertEquals(expected, actual) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.