Skip to content

Commit

Permalink
enable forking of Modes and add ImmutableMode
Browse files Browse the repository at this point in the history
 - make sure the default empty Mode is immutable
  • Loading branch information
robinfriedli committed Apr 16, 2022
1 parent dfa7dcd commit f12d68d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 17 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repositories {
}
dependencies {
implementation "com.github.robinfriedli:exec:1.3"
implementation "com.github.robinfriedli:exec:1.4"
}
```

Expand All @@ -22,7 +22,7 @@ dependencies {
<dependency>
<groupId>com.github.robinfriedli</groupId>
<artifactId>exec</artifactId>
<version>1.3</version>
<version>1.4</version>
<type>pom</type>
</dependency>
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

group = "net.robinfriedli"
version = "1.3"
version = "1.4"
sourceCompatibility = "8"
targetCompatibility = "8"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ abstract class AbstractNestedModeWrapper : ModeWrapper {

private var outer: ModeWrapper? = null

override fun combine(mode: ModeWrapper): ModeWrapper {
final override fun combine(mode: ModeWrapper): ModeWrapper {
mode.setOuter(this)
return mode
}

override fun getOuter(): ModeWrapper? {
final override fun getOuter(): ModeWrapper? {
return outer
}

override fun setOuter(mode: ModeWrapper?) {
final override fun setOuter(mode: ModeWrapper?) {
this.outer = mode
}

}
override fun fork(): ModeWrapper {
// since this combine implementation does not modify this ModeWrapper instance it is inherently immutable
return this
}

}
52 changes: 43 additions & 9 deletions src/main/kotlin/net/robinfriedli/exec/Mode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package net.robinfriedli.exec
/**
* Helper class to build a mode with the given [ModeWrapper] applied
*/
class Mode {
open class Mode() {

private constructor(currentWrapper: ModeWrapper?) : this() {
this.currentWrapper = currentWrapper
}

private var currentWrapper: ModeWrapper? = null

companion object {
@JvmStatic
val empty = create()
val empty = create().immutable()

@JvmStatic
fun create(): Mode {
Expand All @@ -23,19 +27,49 @@ class Mode {
* inside the task of the current wrapper.
*
* @param wrapper the new wrapper to add, normally wrapping it inside the current wrapper
* @return this mode with the new wrapper applied
* @return this mode with the new wrapper applied, or a new Mode instance if this Mode was immutable
*/
fun with(wrapper: ModeWrapper): Mode {
if (currentWrapper != null) {
currentWrapper = currentWrapper!!.combine(wrapper)
open fun with(wrapper: ModeWrapper): Mode {
currentWrapper = if (currentWrapper != null) {
currentWrapper!!.combine(wrapper)
} else {
currentWrapper = wrapper
wrapper
}
return this
}

fun getWrapper(): ModeWrapper? {
open fun getWrapper(): ModeWrapper? {
return currentWrapper
}

}
/**
* Clone this Mode so that changes made to the returned Mode are not reflected by this instance.
*/
open fun fork(): Mode {
return Mode(currentWrapper?.fork())
}

/**
* Wraps this Mode into a [ImmutableMode], prohibiting modifications to this instance by making calls to [Mode.with]
* fork the Mode by calling [Mode.fork] and operating on the cloned instance. Note that instances returned by
* [ImmutableMode.with] are not immutable.
*/
fun immutable(): ImmutableMode {
return ImmutableMode(this)
}

class ImmutableMode(private val mode: Mode) : Mode() {
override fun with(wrapper: ModeWrapper): Mode {
return mode.fork().with(wrapper)
}

override fun getWrapper(): ModeWrapper? {
return mode.getWrapper()
}

override fun fork(): Mode {
return mode.fork()
}
}

}
10 changes: 9 additions & 1 deletion src/main/kotlin/net/robinfriedli/exec/ModeWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ interface ModeWrapper : Iterable<ModeWrapper> {

fun setOuter(mode: ModeWrapper?)

/**
* Clones this ModeWrapper for use in a new [Mode]. Changes to the returned ModeWrapper are not reflected by this instance.
*
* If the [ModeWrapper.combine] implementation does not modify this instance, meaning this implementation is practically
* immutable, this method does not have to do anything and can return the current instance.
*/
fun fork(): ModeWrapper

override fun iterator(): Iterator<ModeWrapper> {
return object : Iterator<ModeWrapper> {

Expand All @@ -59,4 +67,4 @@ interface ModeWrapper : Iterable<ModeWrapper> {

}
}
}
}
37 changes: 37 additions & 0 deletions src/test/kotlin/net/robinfriedli/exec/modes/ModesTest.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,52 @@
package net.robinfriedli.exec.modes

import net.robinfriedli.exec.AbstractNestedModeWrapper
import net.robinfriedli.exec.Invoker
import net.robinfriedli.exec.Mode
import net.robinfriedli.exec.MutexSync
import org.testng.Assert.*
import org.testng.annotations.DataProvider
import org.testng.annotations.Test
import java.util.concurrent.Callable
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger

class ModesTest {

@DataProvider
fun boolDataProvider(): Array<Any> {
return arrayOf(true, false)
}

@Test(dataProvider = "boolDataProvider")
fun testForkMode(immutableMode: Boolean) {
val mode1 = Mode
.create()
.with(AdditionModeWrapper(2))
.with(AdditionModeWrapper(7))
val mode2 = if (immutableMode) {
mode1.immutable()
} else {
mode1.fork()
}.with(AdditionModeWrapper(4))

val invoker = Invoker.defaultInstance
val res1 = invoker.invoke<Int>(mode1) { 3 }
val res2 = invoker.invoke<Int>(mode2) { 3 }

assertEquals(res1, 12)
assertEquals(res2, 16)
}

class AdditionModeWrapper(private val summand: Int) : AbstractNestedModeWrapper() {
override fun <T> wrap(callable: Callable<T>): Callable<T> {
return Callable {
(callable.call() as Int + summand) as T
}
}

}

@Test
fun testExceptionWrappingMode() {
val invoker = Invoker.newInstance()
Expand Down

0 comments on commit f12d68d

Please sign in to comment.