diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 0dfe9c61..84fb5d05 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,17 +1,22 @@
-##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-## (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-## ~
-## Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-## compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-## ~
-## Maintainers: ~
-## Wim Bast, Tom Brus, Ronald Krijgsheld ~
-## Contributors: ~
-## Arjan Kok, Carel Bast ~
-##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+## (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+## ~
+## Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+## compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+## ~
+## Maintainers: ~
+## Wim Bast, Tom Brus ~
+## ~
+## Contributors: ~
+## Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+## --------------------------------------------------------------------------------------------------------------------- ~
+## In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+## Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+## but also our friend. "He will live on in many of the lines of code you see below." ~
+##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
version: 2
updates:
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 8d695821..4622a68e 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -19,22 +19,22 @@ on: [ push, workflow_dispatch ]
jobs:
build:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[no-ci]')"
env:
ALLREP_TOKEN: "${{secrets.ALLREP_TOKEN}}"
TOKEN: "${{secrets.ALLREP_TOKEN}}"
CI: "true"
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
fetch-depth: 0
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v5
with:
- java-version: 17
+ java-version: 21
distribution: zulu
################################################################################
- name: "build"
- run: ./gradlew --info --scan
+ run: ./gradlew --info
diff --git a/.gitignore b/.gitignore
index ef8b1b21..9e7dc34d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,6 +65,9 @@ tmp/
tmp/**
tmp/**/*
+#### vscode
+.vscode/
+
#### java (https://github.com/github/gitignore/blob/master/Java.gitignore)
*.class
*.ear
@@ -90,4 +93,4 @@ gradle-app.setting
/TEST-*.xml
**/*_gen
**/*_gen.caches
-.mps/workspace.xml
+.mps/workspace.xml
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
index b58b603f..7abb13d0 100644
--- a/.idea/.gitignore
+++ b/.idea/.gitignore
@@ -3,3 +3,5 @@
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
+# GitHub Copilot persisted chat sessions
+/copilot/chatSessions
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 4f82f21f..0e6a221d 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -30,7 +30,7 @@
-
+
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 00000000..bbfddcea
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,107 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+**dclare** is a declarative rule engine for Java. Rules are enforced continuously and automatically — no listeners needed. The implementation is heavily multi-threaded while the API exposes no concurrency primitives.
+
+- **Group:** `org.modelingvalue`, **Artifact:** `dclare`, **Version:** 5.1.0
+- **Java:** 21
+- **License:** LGPL-3.0
+- **Dependencies:** `immutable-collections` and `mvg-json` (both from org.modelingvalue)
+
+## Build Commands
+
+```bash
+./gradlew # Runs default tasks: mvgCorrector, test, publish, mvgTagger
+./gradlew test # Run all tests
+./gradlew test --tests "org.modelingvalue.dclare.test.DclareTests.source2target" # Single test method
+./gradlew test --tests "org.modelingvalue.dclare.test.DclareTests" # Single test class
+```
+
+There is no separate lint command; the `mvgCorrector` task handles code corrections (copyright headers, formatting).
+
+## Architecture
+
+### Core Abstractions
+
+The engine is built around a **transaction-based state machine** where immutable `State` snapshots evolve through transactions:
+
+- **Universe** — Root mutable object; entry point for the entire model. Created via `Universe.of(...)`.
+- **Mutable** — Base interface for all model objects. Establishes parent/child containment hierarchy.
+- **MutableClass** — Metadata interface describing the features (observables, observers, derivers) of a Mutable type.
+
+### Property System (Getable → Setable → Observed/Constant)
+
+- **Getable** — Base readable property with ID and default function.
+- **Setable** — Writable property. Supports opposites, scoping, orphan protection.
+- **Observed** — Tracked property that triggers observer re-evaluation on change.
+- **Constant** — Derived/cached value computed from a deriver function. Can be lazy or pushed.
+
+### Rule System (Leaf → Action/Observer)
+
+- **Action** — Imperative operation triggered by state changes. Has priority and direction.
+- **Observer** — Declarative rule that re-executes when its dependencies change. The primary mechanism for maintaining consistency.
+- **Derivation** — Lazy computation that evaluates on demand.
+
+### Transaction Hierarchy
+
+```
+Transaction (abstract)
+├── LeafTransaction (abstract)
+│ ├── ActionTransaction
+│ ├── ObserverTransaction
+│ ├── DerivationTransaction / LazyDerivationTransaction / IdentityDerivationTransaction
+│ ├── ReadOnlyTransaction
+│ └── ImperativeTransaction
+├── MutableTransaction
+└── UniverseTransaction ← orchestrates the overall execution loop
+```
+
+### Priority Scheduling
+
+Actions/Observers have priorities (`Priority.one` through `Priority.six`) controlling execution order. Lower priorities execute first. `Priority.zero` represents the currently executing level.
+
+### Modifiers
+
+- **SetableModifier** / **CoreSetableModifier** — containment, mandatory, preserved, plumbing, durable, doNotMerge, symmetricOpposite
+- **LeafModifier** / **CoreLeafModifier** — preserved, read
+- **Direction** — Push (eager) vs Pull (lazy) evaluation
+
+### Synchronization Module (`sync/`)
+
+Enables multi-instance state synchronization via JSON deltas. Key classes: `DeltaAdaptor`, `SerialisationPool`, `UniverseSynchronizer`, `SocketSyncConnection`.
+
+### OneShot
+
+Abstract class for single-run model initialization. Methods annotated with `@OneShotAction` are discovered and executed in alphabetical order. Supports state caching.
+
+## Naming Conventions
+
+- `D_` prefix — Internal system properties on Mutable (e.g., `D_PARENT_CONTAINING`, `D_OBSERVERS`)
+- `d*()` methods — Dclare-specific operations on Mutable (e.g., `dParent()`, `dChildren()`, `dDelete()`)
+- Factory pattern — Most core types use `of(...)` static factory methods
+- Property access — `.get(object)` to read, `.set(object, value)` to write within transaction context
+
+## Configuration
+
+`DclareConfig` supports system properties for debugging:
+
+- `DEV_MODE` — Enable debug features
+- `RUN_SEQUENTIAL` — Disable parallelism (useful for debugging)
+- `CHECK_ORPHAN_STATE` — Validate orphan handling
+- `TRACE_UNIVERSE`, `TRACE_MUTABLE`, `TRACE_ACTIONS`, `TRACE_MATCHING`, `TRACE_RIPPLE_OUT`, `TRACE_DERIVATION` — Various tracing flags
+- `MAX_TOTAL_NR_OF_CHANGES`, `MAX_NR_OF_CHANGES`, `MAX_NR_OF_OBSERVED`, `MAX_NR_OF_OBSERVERS` — Safety limits
+
+## Testing
+
+Tests use JUnit 5. Test support classes are in `src/test/java/org/modelingvalue/dclare/test/support/` providing `TestUniverse`, `TestMutable`, `TestMutableClass`, and other helpers for constructing test models.
+
+## File Headers
+
+All source files include a copyright header block for Modeling Value Group B.V. The `mvgCorrector` gradle task maintains these automatically.
+
+## Eclipse Integration
+
+When `GRADLE_ECLIPSE=true` environment variable is set, the build uses `includeBuild` to substitute local checkouts of `immutable-collections` and `mvg-json` from sibling directories.
diff --git a/build.gradle.kts b/build.gradle.kts
index dfa54924..503a2cbc 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,30 +1,35 @@
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-// ~
-// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-// ~
-// Maintainers: ~
-// Wim Bast, Tom Brus, Ronald Krijgsheld ~
-// Contributors: ~
-// Arjan Kok, Carel Bast ~
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+// ~
+// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+// ~
+// Maintainers: ~
+// Wim Bast, Tom Brus ~
+// ~
+// Contributors: ~
+// Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+// --------------------------------------------------------------------------------------------------------------------- ~
+// In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+// Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+// but also our friend. "He will live on in many of the lines of code you see below." ~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
defaultTasks("mvgCorrector", "test", "publish", "mvgTagger")
plugins {
`java-library`
`maven-publish`
- id("org.modelingvalue.gradle.mvgplugin") version "1.1.3"
+ id("org.modelingvalue.gradle.mvgplugin") version "2.3.21"
idea
eclipse
}
dependencies {
- implementation("org.modelingvalue:immutable-collections:4.0.0-BRANCHED")
- implementation("org.modelingvalue:mvg-json:4.0.0-BRANCHED")
+ implementation("org.modelingvalue:immutable-collections:6.0.0-BRANCHED")
+ implementation("org.modelingvalue:mvg-json:6.0.0-BRANCHED")
}
publishing {
publications {
diff --git a/gradle.properties b/gradle.properties
index 3f2a56ce..4f6dada8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,20 +1,25 @@
-##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-## (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-## ~
-## Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-## compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-## ~
-## Maintainers: ~
-## Wim Bast, Tom Brus, Ronald Krijgsheld ~
-## Contributors: ~
-## Arjan Kok, Carel Bast ~
-##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+## (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+## ~
+## Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+## compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+## ~
+## Maintainers: ~
+## Wim Bast, Tom Brus ~
+## ~
+## Contributors: ~
+## Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+## --------------------------------------------------------------------------------------------------------------------- ~
+## In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+## Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+## but also our friend. "He will live on in many of the lines of code you see below." ~
+##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# suppress inspection "UnusedProperty" for whole file
group = org.modelingvalue
artifact = dclare
-version = 4.0.0
-version_java = 17
+version = 6.0.0
+version_java = 21
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 249e5832..b1b8ef56 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 070cb702..df6a6ad7 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
+networkTimeout=10000
+retries=0
+retryBackOffMs=500
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index a69d9cb6..b9bb139f 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
#!/bin/sh
#
-# Copyright © 2015-2021 the original authors.
+# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +82,11 @@ do
esac
done
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -133,22 +132,29 @@ location of your Java installation."
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -165,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -193,16 +198,19 @@ if "$cygwin" || "$msys" ; then
done
fi
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
diff --git a/gradlew.bat b/gradlew.bat
index f127cfd4..24c62d56 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -21,11 +23,12 @@
@rem
@rem ##########################################################################
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
+@rem Set local scope for the variables, and ensure extensions are enabled
+setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -42,13 +45,13 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
-goto fail
+"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -56,36 +59,24 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
-goto fail
+"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
+@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
+@rem which allows us to clear the local environment before executing the java command
+endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
-:omega
+:exitWithErrorLevel
+@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
+"%COMSPEC%" /c exit %ERRORLEVEL%
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 8b5c355a..2854cd36 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,26 +1,26 @@
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-// ~
-// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-// ~
-// Maintainers: ~
-// Wim Bast, Tom Brus, Ronald Krijgsheld ~
-// Contributors: ~
-// Arjan Kok, Carel Bast ~
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+// ~
+// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+// ~
+// Maintainers: ~
+// Wim Bast, Tom Brus ~
+// ~
+// Contributors: ~
+// Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+// --------------------------------------------------------------------------------------------------------------------- ~
+// In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+// Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+// but also our friend. "He will live on in many of the lines of code you see below." ~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rootProject.name = "dclare"
include()
-
-plugins {
- id("com.gradle.enterprise") version ("3.5")
-}
-
val inEclipse=System.getenv("GRADLE_ECLIPSE")
println("Gradle: inEclipse="+inEclipse)
if(inEclipse!=null && inEclipse.equals("true")) {
diff --git a/src/main/java/org/modelingvalue/dclare/AbstractDerivationTransaction.java b/src/main/java/org/modelingvalue/dclare/AbstractDerivationTransaction.java
index 29fe4d31..c393445a 100644
--- a/src/main/java/org/modelingvalue/dclare/AbstractDerivationTransaction.java
+++ b/src/main/java/org/modelingvalue/dclare/AbstractDerivationTransaction.java
@@ -1,21 +1,27 @@
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-// ~
-// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-// ~
-// Maintainers: ~
-// Wim Bast, Tom Brus, Ronald Krijgsheld ~
-// Contributors: ~
-// Arjan Kok, Carel Bast ~
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+// ~
+// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+// ~
+// Maintainers: ~
+// Wim Bast, Tom Brus ~
+// ~
+// Contributors: ~
+// Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+// --------------------------------------------------------------------------------------------------------------------- ~
+// In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+// Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+// but also our friend. "He will live on in many of the lines of code you see below." ~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package org.modelingvalue.dclare;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
@@ -30,24 +36,28 @@
public abstract class AbstractDerivationTransaction extends ReadOnlyTransaction {
@SuppressWarnings("rawtypes")
- protected static final Context>> DERIVED = Context.of(Set.of());
+ protected static final Context> ALL_DERIVED = Context.of(Set.of());
@SuppressWarnings("rawtypes")
- protected static final Context> DERIVER = Context.of(null);
- private static final Context DERIVE = Context.of(true);
- private static final Context INDENT = Context.of(0);
+ protected static final Context> DERIVER = Context.of(null);
+ @SuppressWarnings("rawtypes")
+ private static final Context DERIVED = Context.of(null);
+ private static final Context INDENT = Context.of(0);
+ private static final Context DERIVE = Context.of(true);
public static boolean isDeriving() {
- return !DERIVED.get().isEmpty();
+ return !ALL_DERIVED.get().isEmpty();
}
protected AbstractDerivationTransaction(UniverseTransaction universeTransaction) {
super(universeTransaction);
}
- private ConstantState memoization;
+ private ConstantState memoization;
+ private ILeafTransaction iLeafTransaction;
- public R derive(Supplier action, State state, ConstantState memoization) {
+ public R derive(Supplier action, State state, ConstantState memoization, ILeafTransaction iLeafTransaction) {
this.memoization = memoization;
+ this.iLeafTransaction = iLeafTransaction;
try {
return get(action, state);
} catch (Throwable t) {
@@ -55,17 +65,18 @@ public R derive(Supplier action, State state, ConstantState memoization)
return null;
} finally {
this.memoization = null;
+ this.iLeafTransaction = null;
}
}
@SuppressWarnings("rawtypes")
protected boolean doDeriveGet(O object, Getable getable, T nonDerived) {
- return doDeriveSet(object, getable);
+ return object instanceof Mutable && getable instanceof Observed && DERIVE.get();
}
@SuppressWarnings("rawtypes")
- protected boolean doDeriveSet(O object, Getable getable) {
- return object instanceof Mutable && getable instanceof Observed && DERIVE.get();
+ protected boolean doDeriveSet(O object, Getable getable, T nonDerived) {
+ return object instanceof Mutable && getable instanceof Observed && DERIVER.get() != null;
}
protected T getNonDerived(O object, Getable getable) {
@@ -84,48 +95,55 @@ protected T current(O object, Getable getable) {
return derive(object, getable, nonDerived);
}
- @SuppressWarnings("rawtypes")
+ @SuppressWarnings({"rawtypes", "unchecked"})
private T derive(O object, Getable getable, T nonDerived) {
if (doDeriveGet(object, getable, nonDerived)) {
Observed observed = (Observed) getable;
- ConstantState mem = memoization(object);
- Constant constant = observed.constant();
- if (!mem.isSet(this, object, constant)) {
- if (Newable.D_ALL_DERIVATIONS.equals(observed) || Mutable.D_PARENT_CONTAINING.equals(observed)) {
- return nonDerived;
- } else {
- Pair derived = Pair.of((Mutable) object, observed);
- Set> oldDerived = DERIVED.get();
- Set> newDerived = oldDerived.add(derived);
- if (oldDerived == newDerived) {
- if (isTraceDerivation(object, observed)) {
- runNonDeriving(() -> System.err.println(tracePre(object) + "RECU " + object + "." + observed + " => RECURSIVE DERIVATION, result is the non-derived value: " + nonDerived));
- }
+ Derived outerDerived = DERIVED.get();
+ boolean isDerived = outerDerived != null && outerDerived.isDerived(object, observed);
+ if (isDerived && outerDerived.isSet()) {
+ return outerDerived.get();
+ } else {
+ ConstantState mem = memoization(object);
+ Constant constant = observed.constant();
+ if (!mem.isSet(iLeafTransaction, object, constant)) {
+ if (isDerived || Newable.D_ALL_DERIVATIONS.equals(observed) || Mutable.D_PARENT_CONTAINING.equals(observed)) {
return nonDerived;
} else {
- if (isTraceDerivation(object, observed)) {
- runNonDeriving(() -> System.err.println(tracePre(object) + ">>>> " + object + "." + observed));
- }
- INDENT.run(INDENT.get() + 1, () -> DERIVED.run(newDerived, () -> {
- int i = 0;
- Set observers = ((Mutable) object).dAllDerivers(observed).asSet();
- for (Observer observer : observers.filter(Observer::anonymous)) {
- runDeriver((Mutable) object, observed, observer, ++i);
+ Derived innerDerived = new Derived(object, observed, outerDerived);
+ Set oldAllDerived = ALL_DERIVED.get();
+ if (oldAllDerived.contains(innerDerived)) {
+ if (pull() && !observed.synthetic()) {
+ runSilent(() -> System.err.println(tracePre(object, null) + "CYCLE " + innerDerived));
}
- for (Observer observer : observers.exclude(Observer::anonymous)) {
- runDeriver((Mutable) object, observed, observer, ++i);
- }
- }));
- if (!mem.isSet(this, object, constant)) {
if (isTraceDerivation(object, observed)) {
- INDENT.run(INDENT.get() + 1, () -> runNonDeriving(() -> System.err.println(tracePre(object) + "NODR " + object + "." + observed + " => NO DERIVATION, result is the non-derived value: " + nonDerived)));
+ runSilent(() -> System.err.println(tracePre(object, this) + "RECU " + object + "." + observed + " => RECURSIVE DERIVATION, result is the non-derived value: " + nonDerived));
}
return nonDerived;
+ } else {
+ if (isTraceDerivation(object, observed)) {
+ runSilent(() -> System.err.println(tracePre(object, this) + ">>>> " + object + "." + observed));
+ }
+ INDENT.run(INDENT.get() + 1, () -> ALL_DERIVED.run(oldAllDerived.add(innerDerived), () -> DERIVED.run(innerDerived, () -> {
+ int i = 0;
+ for (Observer observer : ((Mutable) object).dAllDerivers(observed)) {
+ runDeriver((Mutable) object, observed, observer, ++i);
+ }
+ if (innerDerived.isSet()) {
+ setInMemoization(mem, object, observed, innerDerived.get(), false);
+ }
+ })));
+ if (!mem.isSet(iLeafTransaction, object, constant)) {
+ if (isTraceDerivation(object, observed)) {
+ INDENT.run(INDENT.get() + 1, () -> runSilent(() -> System.err.println(tracePre(object, this) + "NODR " + object + "." + observed + " => NO DERIVATION, result is the non-derived value: " + nonDerived)));
+ }
+ return nonDerived;
+ }
}
}
}
+ return mem.get(iLeafTransaction, object, constant);
}
- return mem.get(this, object, constant);
} else {
return nonDerived;
}
@@ -133,60 +151,88 @@ private T derive(O object, Getable getable, T nonDerived) {
@SuppressWarnings({"rawtypes", "unchecked"})
protected void runDeriver(Mutable mutable, Observed observed, Observer observer, int i) {
- if (isTraceDerivation(mutable, observed)) {
- runNonDeriving(() -> System.err.println(tracePre(mutable) + String.format(">>%d> ", i) + mutable + "." + observer + "()"));
+ if (isTraceDerivation(mutable, observed) || observer.isTracing()) {
+ runSilent(() -> System.err.println(tracePre(mutable, this) + String.format(">>%d> ", i) + mutable + "." + observer + "()"));
}
INDENT.run(INDENT.get() + 1, () -> DERIVER.run(Pair.of(mutable, observer), () -> {
try {
observer.run(mutable);
} catch (Throwable t) {
if (isTraceDerivation(mutable, observed)) {
- runNonDeriving(() -> System.err.println(tracePre(mutable) + "!!!! " + mutable + "." + observer + "() => THROWS " + t));
+ runSilent(() -> System.err.println(tracePre(mutable, this) + "!!!! " + mutable + "." + observer + "() => THROWS " + t));
}
- universeTransaction().handleException(new TransactionException(mutable, new TransactionException(observer, t)));
+ handleException(mutable, observer, t);
}
}));
}
+ @SuppressWarnings("rawtypes")
+ protected void handleException(Mutable mutable, Observer observer, Throwable t) {
+ universeTransaction().handleException(new TransactionException(mutable, new TransactionException(observer, t)));
+ }
+
@Override
public T set(O object, Setable setable, BiFunction function, E element) {
- return set(object, setable, function.apply(getNonDerived(object, setable), element));
+ T nonDerived = getNonDerived(object, setable);
+ return set(object, setable, function.apply(nonDerived, element), nonDerived);
}
@Override
public T set(O object, Setable setable, UnaryOperator oper) {
- return set(object, setable, oper.apply(getNonDerived(object, setable)));
+ T nonDerived = getNonDerived(object, setable);
+ return set(object, setable, oper.apply(nonDerived), nonDerived);
}
- public void runNonDeriving(Runnable action) {
+ @Override
+ public T set(O object, Setable setable, T post) {
+ T nonDerived = getNonDerived(object, setable);
+ return set(object, setable, post, nonDerived);
+ }
+
+ @Override
+ public void runSilent(Runnable action) {
DERIVE.run(false, action);
}
- @SuppressWarnings({"unchecked", "rawtypes"})
@Override
- public T set(O object, Setable setable, T post) {
- if (doDeriveSet(object, setable)) {
+ public R getSilent(Supplier action) {
+ return DERIVE.get(false, action);
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private T set(O object, Setable setable, T post, T nonDerived) {
+ if (doDeriveSet(object, setable, nonDerived)) {
+ Observed observed = (Observed) setable;
+ Derived derived = DERIVED.get();
+ boolean isDerived = derived != null && derived.isDerived(object, observed);
ConstantState mem = memoization(object);
- Constant constant = setable.constant();
- T pre = mem.isSet(this, object, constant) ? mem.get(this, object, constant) : getNonDerived(object, setable);
- T result = match(mem, setable, pre, post);
- setInMemoization(mem, object, setable, result);
- if (isTraceDerivation(object, setable)) {
- runNonDeriving(() -> {
- Pair deriver = DERIVER.get();
- System.err.println(tracePre(object) + "SET " + deriver.a() + "." + deriver.b() + "(" + object + "." + setable + "=" + pre + "->" + result + ")");
+ Constant constant = observed.constant();
+ T pre = isDerived && derived.isSet() ? derived.get() : mem.isSet(iLeafTransaction, object, constant) ? mem.get(iLeafTransaction, object, constant) : nonDerived;
+ T result = match(mem, observed, pre, post);
+ if (isDerived && derived.isSet() && !Objects.equals(pre, nonDerived) && Objects.equals(result, nonDerived)) {
+ return post;
+ }
+ if (isDerived) {
+ derived.set(result);
+ } else {
+ setInMemoization(mem, object, observed, result, false);
+ }
+ Pair deriver = DERIVER.get();
+ if (isTraceDerivation(object, observed) || deriver.b().isTracing()) {
+ runSilent(() -> {
+ System.err.println(tracePre(object, this) + "SET " + deriver.a() + "." + deriver.b() + "(" + object + "." + observed + "=" + pre + "->" + result + ")");
});
}
- if (setable.containment()) {
+ if (observed.containment()) {
Setable. diff(pre, result, added -> {
- setInMemoization(mem, added, Mutable.D_PARENT_CONTAINING, Pair.of((Mutable) object, (Setable) setable));
+ setInMemoization(mem, added, Mutable.D_PARENT_CONTAINING, Pair.of((Mutable) object, (Setable) observed), true);
}, removed -> {
});
}
return pre;
- } else if (!Objects.equals(getNonDerived(object, setable), post)) {
- return super.set(object, setable, post);
+ } else if (!Objects.equals(nonDerived, post)) {
+ return iLeafTransaction == this ? super.set(object, setable, post) : iLeafTransaction.set(object, setable, post);
} else {
return post;
}
@@ -199,21 +245,21 @@ private T match(ConstantState mem, Setable setable, T pre, T post)
if (!pres.isEmpty()) {
for (Newable po : posts) {
Construction poInit = Mutable.D_INITIAL_CONSTRUCTION.get(po);
- if (poInit.isDerived() && mem.isSet(this, po, Newable.D_ALL_DERIVATIONS.constant())) {
+ if (poInit.isDerived() && mem.isSet(iLeafTransaction, po, Newable.D_ALL_DERIVATIONS.constant())) {
for (Newable pr : pres) {
Construction preInit = Mutable.D_INITIAL_CONSTRUCTION.get(pr);
if (preInit.isDirect() && po.dNewableType().equals(pr.dNewableType()) && Objects.equals(po.dIdentity(), pr.dIdentity())) {
pres = pres.remove(pr);
post = replace(post, po, pr);
- setInMemoization(mem, pr, Mutable.D_ALL_DERIVATIONS, mem.get(this, po, Newable.D_ALL_DERIVATIONS.constant()));
+ setInMemoization(mem, pr, Mutable.D_ALL_DERIVATIONS, mem.get(iLeafTransaction, po, Newable.D_ALL_DERIVATIONS.constant()), true);
}
}
} else if (poInit.isDirect()) {
for (Newable pr : pres) {
Construction preInit = Mutable.D_INITIAL_CONSTRUCTION.get(pr);
- if (preInit.isDerived() && mem.isSet(this, pr, Newable.D_ALL_DERIVATIONS.constant()) && po.dNewableType().equals(pr.dNewableType()) && Objects.equals(po.dIdentity(), pr.dIdentity())) {
+ if (preInit.isDerived() && mem.isSet(iLeafTransaction, pr, Newable.D_ALL_DERIVATIONS.constant()) && po.dNewableType().equals(pr.dNewableType()) && Objects.equals(po.dIdentity(), pr.dIdentity())) {
pres = pres.remove(pr);
- setInMemoization(mem, po, Mutable.D_ALL_DERIVATIONS, mem.get(this, pr, Newable.D_ALL_DERIVATIONS.constant()));
+ setInMemoization(mem, po, Mutable.D_ALL_DERIVATIONS, mem.get(iLeafTransaction, pr, Newable.D_ALL_DERIVATIONS.constant()), true);
}
}
}
@@ -233,6 +279,11 @@ private T replace(T post, Newable po, Newable pr) {
return post;
}
+ @Override
+ public void trigger(O mutable, Action action, Priority priority) {
+ iLeafTransaction.trigger(mutable, action, priority);
+ }
+
@Override
public O directConstruct(Construction.Reason reason, Supplier supplier) {
return super.construct(reason, supplier);
@@ -244,13 +295,18 @@ public O construct(Reason reason, Supplier supplier) {
Pair deriver = DERIVER.get();
O result = supplier.get();
Construction cons = Construction.of(deriver.a(), deriver.b(), reason);
- setInMemoization(memoization(deriver.a()), result, Newable.D_ALL_DERIVATIONS, Newable.D_ALL_DERIVATIONS.getDefault(result).add(cons));
+ setInMemoization(memoization(deriver.a()), result, Newable.D_ALL_DERIVATIONS, Newable.D_ALL_DERIVATIONS.getDefault(result).add(cons), true);
Mutable.D_INITIAL_CONSTRUCTION.force(result, cons);
+ if (isTraceDerivation(deriver.a(), null) || deriver.b().isTracing()) {
+ runSilent(() -> {
+ System.err.println(tracePre(deriver.a(), this) + "CON " + deriver.a() + "." + deriver.b() + "(" + cons.reason() + "->" + result + ")");
+ });
+ }
return result;
}
- protected void setInMemoization(ConstantState mem, O object, Setable setable, T result) {
- mem.set(this, object, setable.constant(), result, true);
+ protected T setInMemoization(ConstantState mem, O object, Setable setable, T result, boolean force) {
+ return force ? mem.set(iLeafTransaction, object, setable.constant(), result, force) : mem.getOrSet(iLeafTransaction, object, setable.constant(), result);
}
protected ConstantState memoization(O object) {
@@ -263,11 +319,11 @@ public ConstantState memoization() {
@SuppressWarnings("rawtypes")
protected boolean isTraceDerivation(O object, Setable setable) {
- return (setable == null || !setable.isPlumbing()) && universeTransaction().getConfig().isTraceDerivation();
+ return (setable == null || (!setable.isPlumbing() && !setable.synthetic())) && universeTransaction().getConfig().isTraceDerivation();
}
- private String tracePre(O object) {
- return DclareTrace.getLineStart(memoization(object).toString(), this);
+ private String tracePre(O object, Transaction transaction) {
+ return DclareTrace.getLineStart(memoization(object).toString(), transaction);
}
@Override
@@ -275,4 +331,59 @@ public int depth() {
return INDENT.get();
}
+ protected static class Derived extends Pair> {
+ private static final long serialVersionUID = -2566539820227398813L;
+
+ @SuppressWarnings("rawtypes")
+ private final Derived, ?> outer;
+ private final AtomicReference value;
+
+ @SuppressWarnings("rawtypes")
+ protected Derived(O a, Observed b, Derived, ?> outer) {
+ super(a, b);
+ this.outer = outer;
+ this.value = new AtomicReference<>();
+ }
+
+ protected T get() {
+ T value = this.value.get();
+ return value == ConstantState.NULL ? null : value;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void set(T value) {
+ this.value.set(value == null ? (T) ConstantState.NULL : value);
+ }
+
+ protected boolean isSet() {
+ return this.value.get() != null;
+ }
+
+ protected boolean isDerived(Object object, Observed, ?> observed) {
+ return a().equals(object) && b().equals(observed);
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected Derived outer() {
+ return outer;
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected Derived outerNotSynthetic() {
+ return outer != null && outer.b().synthetic() ? outer.outerNotSynthetic() : outer;
+ }
+
+ @Override
+ public String toString() {
+ return toString(this);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private String toString(Derived, ?> deepest) {
+ Derived ons = outerNotSynthetic();
+ return (ons != null && !ons.isDerived(deepest.a(), deepest.b()) ? ons.toString(deepest) : "") + "[" + a() + "." + b() + "]";
+ }
+
+ }
+
}
diff --git a/src/main/java/org/modelingvalue/dclare/Action.java b/src/main/java/org/modelingvalue/dclare/Action.java
index e0eaeaa0..7ad5df55 100644
--- a/src/main/java/org/modelingvalue/dclare/Action.java
+++ b/src/main/java/org/modelingvalue/dclare/Action.java
@@ -1,17 +1,22 @@
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-// ~
-// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-// ~
-// Maintainers: ~
-// Wim Bast, Tom Brus, Ronald Krijgsheld ~
-// Contributors: ~
-// Arjan Kok, Carel Bast ~
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+// ~
+// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+// ~
+// Maintainers: ~
+// Wim Bast, Tom Brus ~
+// ~
+// Contributors: ~
+// Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+// --------------------------------------------------------------------------------------------------------------------- ~
+// In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+// Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+// but also our friend. "He will live on in many of the lines of code you see below." ~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package org.modelingvalue.dclare;
@@ -32,14 +37,15 @@ public static Action of(Object id, Consumer action, Le
private final Direction direction;
private final boolean preserved;
private final boolean read;
+ private long durationNano = -1;
protected Action(Object id, Consumer action, LeafModifier>... modifiers) {
super(id, modifiers);
this.action = action;
Direction dir = getModifier(Direction.class);
this.direction = dir == null ? Direction.DEFAULT : dir;
- this.preserved = hasModifier(LeafModifier.preserved);
- this.read = hasModifier(LeafModifier.read);
+ this.preserved = hasModifier(CoreLeafModifier.preserved);
+ this.read = hasModifier(CoreLeafModifier.read);
}
@Override
@@ -53,7 +59,9 @@ public void closeTransaction(Transaction tx) {
}
public void run(O object) {
+ long t0 = System.nanoTime();
action.accept(object);
+ durationNano = System.nanoTime() - t0;
}
public void trigger(O mutable) {
@@ -69,10 +77,14 @@ public ActionTransaction newTransaction(UniverseTransaction universeTransaction)
return new ActionTransaction(universeTransaction);
}
- public Direction direction() {
+ public final Direction direction() {
return direction;
}
+ public final FixpointGroup fixpointGroup() {
+ return direction().fixpointGroup();
+ }
+
@Override
public String toString() {
return (direction != Direction.DEFAULT ? (direction + "::") : "") + super.toString();
@@ -86,4 +98,7 @@ public boolean read() {
return read;
}
+ public long durationNano() {
+ return durationNano;
+ }
}
diff --git a/src/main/java/org/modelingvalue/dclare/ActionInstance.java b/src/main/java/org/modelingvalue/dclare/ActionInstance.java
index d650b18b..15ab8358 100644
--- a/src/main/java/org/modelingvalue/dclare/ActionInstance.java
+++ b/src/main/java/org/modelingvalue/dclare/ActionInstance.java
@@ -1,17 +1,22 @@
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-// ~
-// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-// ~
-// Maintainers: ~
-// Wim Bast, Tom Brus, Ronald Krijgsheld ~
-// Contributors: ~
-// Arjan Kok, Carel Bast ~
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+// ~
+// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+// ~
+// Maintainers: ~
+// Wim Bast, Tom Brus ~
+// ~
+// Contributors: ~
+// Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+// --------------------------------------------------------------------------------------------------------------------- ~
+// In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+// Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+// but also our friend. "He will live on in many of the lines of code you see below." ~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package org.modelingvalue.dclare;
diff --git a/src/main/java/org/modelingvalue/dclare/ActionTransaction.java b/src/main/java/org/modelingvalue/dclare/ActionTransaction.java
index 5c21fdb6..bb055b8b 100644
--- a/src/main/java/org/modelingvalue/dclare/ActionTransaction.java
+++ b/src/main/java/org/modelingvalue/dclare/ActionTransaction.java
@@ -1,23 +1,29 @@
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// (C) Copyright 2018-2023 Modeling Value Group B.V. (http://modelingvalue.org) ~
-// ~
-// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
-// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
-// ~
-// Maintainers: ~
-// Wim Bast, Tom Brus, Ronald Krijgsheld ~
-// Contributors: ~
-// Arjan Kok, Carel Bast ~
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// (C) Copyright 2018-2026 Modeling Value Group B.V. (http://modelingvalue.org) ~
+// ~
+// Licensed under the GNU Lesser General Public License v3.0 (the 'License'). You may not use this file except in ~
+// compliance with the License. You may obtain a copy of the License at: https://choosealicense.com/licenses/lgpl-3.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. ~
+// ~
+// Maintainers: ~
+// Wim Bast, Tom Brus ~
+// ~
+// Contributors: ~
+// Ronald Krijgsheld ✝, Arjan Kok, Carel Bast ~
+// --------------------------------------------------------------------------------------------------------------------- ~
+// In Memory of Ronald Krijgsheld, 1972 - 2023 ~
+// Ronald was suddenly and unexpectedly taken from us. He was not only our long-term colleague and team member ~
+// but also our friend. "He will live on in many of the lines of code you see below." ~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package org.modelingvalue.dclare;
import java.util.ConcurrentModificationException;
import java.util.Objects;
import java.util.function.BiFunction;
+import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import org.modelingvalue.collections.DefaultMap;
@@ -33,9 +39,41 @@
import org.modelingvalue.dclare.ex.TransactionException;
public class ActionTransaction extends LeafTransaction implements StateMergeHandler {
- private final CurrentState currentState = new CurrentState();
- private State preState;
- private State postState;
+ private final CurrentState currentState = new CurrentState();
+ private final ILeafTransaction changeHandler = new ILeafTransaction() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void changed(O object, Setable setable, T preValue, T rawPreValue, T postValue) {
+ ActionTransaction.this.changed(object, setable, preValue, rawPreValue, postValue);
+ if (setable.id() instanceof Observed) {
+ ActionTransaction.this.set(object, (Observed) setable.id(), preValue, postValue);
+ }
+ }
+
+ @Override
+ public State state() {
+ return ActionTransaction.this.state();
+ }
+
+ @Override
+ public T set(O object, Setable property, T post) {
+ // LeafTransaction.getCurrent().runSilent(() -> System.err.println("PUSH " + object + "." + property + "=" + post));
+ return ActionTransaction.this.set(object, property, post);
+ }
+
+ @Override
+ public void trigger(O mutable, Action action, Priority priority) {
+ ActionTransaction.this.trigger(mutable, action, priority);
+ }
+ };
+ @SuppressWarnings("unchecked")
+ private final Supplier