Add condition contributors to DataLoader - #5661
Draft
fractal3000 wants to merge 2 commits into
Draft
fractal3000 wants to merge 2 commits into
fractal3000 wants to merge 2 commits into
Conversation
fractal3000
marked this pull request as draft
September 7, 2026 08:55
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A
DataLoaderhas a single mutableconditionslot that several independent parties compete for: the application (XML orsetCondition), composing filters (GenericFilter, standaloneGroupFilter— capture a baseline and replace the slot) and accumulating filters (standalonePropertyFilter/JpqlFilter,DataGridHeaderFilter— append their node into the slot and edit it in place). Any pair of writers is a potential conflict, and the symptoms group into three families: a contribution lost when another party rebuilds the slot, a contribution duplicated when a party captures another's output as its base, and a load by a stale composition. The identity heuristics and snapshot machinery aroundGenericFilterexist only to work around this.One concrete example: a standalone quick-search
PropertyFilterattached to a loader after a composing filter has already built the slot loses its contribution on the next rebuild (e.g. a configuration switch) — its field still shows the value, the query no longer contains it, and further edits mutate an orphaned node.Change
Commit 1 — additive API, no behavior change.
DataLoadergets a registry of condition contributors:ConditionContributor— a functional interface returning the party's current condition (null= does not participate).DataLoader.addConditionContributor(...)→Subscription; the default implementation throwsUnsupportedOperationExceptionso customDataLoaderimplementations fail loudly at registration, not silently at load.DataLoader.getEffectiveCondition()— the composed read-only view (own condition AND contributions) for anyone who needs to see the whole picture;getCondition()keeps its exact meaning (the slot).createLoadContext(), so the pagination count query and the DISTINCT heuristic are covered automatically. Contributions and the base enter the composed tree as copies — the composition never holds live nodes of another party. With no contributors the very same condition instance is used as before (pinned by a test).Commit 2 — opt-in migration of standalone filters. With
jmix.ui.component.standalone-filter-contributes-condition = truea standalone filter component registers itself as a contributor instead of appending its node into the slot (DataGridHeaderFilter,JpqlFilterandFullTextFilterinherit this throughSingleFilterComponentBase);setConditionModificationDelegated(true)withdraws the contribution. The default isfalseand keeps the old behavior byte for byte, pinned byStandaloneFilterLegacyConditionTest. With the flag on, a standalone filter's contribution survives any rebuild of the slot by a composing filter — the quick-search scenario above becomes correct by construction, and so does theDataGridHeaderFiltercase where Apply used to load by a base the application had already replaced.This also makes application-level scenarios contractual that previously required mutating the shared slot and hoping: a tenant condition, a quick-search field over a grid, a session-attribute restriction — each is a registered contributor polled on every load.
Known problems this addresses
These map onto an inventory of the filter subsystem we maintain while working on #5516, #5623 and related issues. Most entries below have no public issue yet, so they are described here instead of linked.
Open defects - fixed when the property is on (none filed as issues so far):
PropertyFilter/JpqlFilterappends its node into the loader's condition tree once; afterdataLoader.setCondition(...)the node is orphaned - the field keeps showing its value while the query no longer contains it. With a contributor the contribution is composed with the current slot on every load, whatever the application did to the slot.DataGridHeaderFilterapplies by a base the application has already replaced (found while reviewing the One condition is shown but another is applied in GenericFilter #5516 fix in One condition is shown but another is applied in GenericFilter #5634): same mechanics through the header's internalPropertyFilter- the column shows an active filter, the query does not contain it.Open contract gaps - closed:
Already-shipped fixes and documented limitations that stop being relevant (with the property on):
Deliberately not made obsolete: the composing filters' baseline capture, the identity heuristics and the re-navigation snapshot machinery (#5400, #5425, #5488) and the #5516 apply-path fix itself all remain necessary. They exist because a composing filter has to recognize its own output inside a shared mutable tree, and they go away only when
GenericFilter/GroupFilterthemselves become contributors - the major-version follow-up. After that step the "mine vs. foreign" question disappears: each party owns its contribution instead of recognizing it in the shared slot.Out of scope
Composing filters (
GenericFilter,GroupFilter) still own the slot the old way; migrating them (and dismantling the identity/snapshot machinery) is a separate, major-version conversation.Testing
DataLoaderConditionContributorTest— the contributor contract on the loader level (composition, null contribution, per-load polling, copy semantics, unsubscription, ordering, identity without contributors).StandaloneFilterConditionContributorTest— the flag on: the slot stays untouched, the contribution survives a slot rebuild by a composing filter, delegation withdraws it.StandaloneFilterLegacyConditionTest— the flag off: the pre-existing behavior, pinned.:flowui:test: 1263 tests, 0 failures.🤖 Generated with Claude Code