4.0.0 (unreleased)
This is a breaking release. See the Builder-first construction migration for required client and extension changes.
Documentation infrastructure
- Clarified when to choose
@Required, a field@Validateclosure, or an@Validatemethod for a validation rule. - Made the initial Schema onboarding examples directly copyable by showing the canonical
com.blackbuild.klum.astimports for@DSL,@Key, and@Validate(#742). - Corrected 4.0 Fundamentals terminology for validation, lifecycle examples, Builder-first ownership timing, rooted composition, Owner backlinks, and
LINKside connections. Gradle Onboarding now documents optional local source-mirror automation while retaining explicit portable refresh tasks. Exact versioned documentation now exposes the Changelog and a square Season 4 favicon (#724). - Current 4.x user documentation is authored in
docs/user/and rendered locally from an explicit Git revision into an immutable exact-version static HTML tree. The renderer captures a deterministic site manifest and versioned Season/logo input, while the former mutable wiki publisher fails closed (#456). - Exact 4.x documentation renders six isolated module-Javadoc trees below
/<version>/api/forklum-ast, runtime, annotations, Jackson, Bean Validation, and the Gradle plugin. The BOM and IDE-only source mirrors are not public API inputs (#456). - Protected release orchestration now requires #456's separately permissioned, immutable, unlisted pending documentation/Javadoc stage on
gh-pagesbefore artifact publication. It validates the exact stage/version/master SHA and returns only a manifest-bound evidence handoff; it neither publishes artifacts nor advances aliases (#456). - Protected publication now proves Plugin Portal markers once from an isolated fixture, then polls the isolated Maven Central product fixture for at most two hours. A maintainer explicitly creates the annotated tag and matching release after that proof; REL-2: Verify public release rejects any lightweight or mismatched record before it re-proves the product and promotes documentation. RCs can advance
/preview/, while only finals can advance/stable/and a maintained-line alias such as/4.0/(#488, #456). - Public documentation now has a styled root landing and styled release aliases using the selected immutable version's local CSS and approved branding logo; a tightly scoped Pages-writer repair path backfills an earlier promotion without changing its immutable version tree (#456).
- Current user-documentation examples use the portable
<klum-version>token. Exact rendered snapshots replace it with their own version so KlumAST artifact and plugin coordinates are copy-ready without making source documentation version-specific (#456).
Dependency compatibility
- Promoted to final AnnoDocimal 1.0.0, released from
2780ab1b4e919d40ec19476eac1e3288378fda0b. KlumAST uses its supported documentation-authoring and source-projection APIs. The schema plugin retains its IDEA-only mirror policy while using the configuration-cache-safe projection task; property documentation is projected verbatim to generated Model and Builder accessors unless an accessor supplies its own documentation (#461). Source-mirror projection now resolves public generated-interface references from the Schema compile classpath without making mirrors compilation inputs (#700, #523). - Promoted to final KlumCast 0.4.0, released from
770b1ad2d6b4c109b4e5131b294fa2dafdef09b4:klum-cast-annotations,klum-cast-spi, andklum-cast-compile. The artifacts have stable automatic module names (com.blackbuild.klum.cast.annotations,.spi, and.compiler) and no split KlumCast packages. Recompile schemas and custom checks for 4.0 (#524). - Migrated KlumAST's eight name-bound compiler checks to KlumCast's stateless
CheckSPI. Their expected violations now emit source-positioned structured diagnostics; diagnostic codes are the check implementation names, while unexpected failures remain technical errors with their causes. Invalid@Overwrite.Single(MERGE)strategies on non-DSL fields are rejected during compilation (#460).
Java modules
- Named schema modules are supported with Groovy 4 and 5; Groovy 3 remains supported on the ordinary classpath. The Schema plugin validates the user-owned descriptor as part of
checkand Maven publication, reporting copyable missing directives without editing it. A named schema requires the documentedorg.apache.groovydependency and a qualifiedopensdirective to KlumAST runtime, Jackson when used, and Hibernate Validator when Bean Validation is used. No JVM module-path workaround flags are required (#391). - Fixed Groovy 4/5 named-schema materialization for owned direct, collection, and keyed relationships without changing the approved module descriptors or requiring consumer flags (#622).
Validation
- Default-value annotations marked with
@DefaultValuesnow reject compilation when used outside a@DSLclass or a field declared by one. The diagnostic names the misplaced annotation and the supported targets (#697). KlumValidationExceptionis now solelycom.blackbuild.klum.ast.runtime.validation.KlumValidationException; update imports and caught types. The former runtime-package class has been removed as an intentional 4.0 source and binary compatibility break (#657).- Added the supported
KlumSchemaSupport/KlumValidationReporterfacade for custom lifecycle diagnostics, including explicit target reporting, suppression, and configured fail-level access. The preliminaryValidatorandValidatorBasetypes are removed with direct 4.0 migration guidance; #406 remains the separate compile-time placement rule (#626).
Builder-first construction
-
Template definition now forms a separate nested Builder-composition scope. Generated converter twins create Template-owned children while defining
Create.Template.WithorFrom, including inside an active root Construction session; no nested lifecycle or session attachment occurs, and ordinaryCreate.AsBuilderbehavior resumes when the Template scope exits (#731). -
Replaced the active-session JavaBean accessor and Groovy-property spelling with the one explicit Java/Groovy operation
Foo.Create.AsBuilder(). Update nested Builder creation, importer calls, and static consumers to call the operation;getAsBuilder()andCreate.AsBuilderare intentionally not retained. The change corrects IntelliJ completion before the 4.0 API freeze without changing construction-session or lifecycle behavior. Its generated public factory contract consistently names the declared model and Builder even when@DSL(defaultImpl = ...)selects the runtime implementation (#729). -
Public
Foo_DSL.Buildercontracts and their IDEA-only AnnoDocimal mirrors now explicitly expose supported generated relationship-creator overloads that omit the optional trailing configuration closure. This is an additive 4.0 API correction; existing lifecycle, relationship, Factory, and source-DSL behavior is unchanged (#719). -
Template root creation now lives below
Foo.Create.Template.WithandFrom;Foo.Template.WithandWithAllare reserved for scoped application. The conflictingFoo.Create.Template(...)andTemplateFrom(...)methods are removed. The documentedFoo.Template.Create(...)andCreateFrom(...)calls remain deprecated forwarding aliases throughout 4.x. The Builder-first migration helper provides only direct, type-qualified starting rewrites; review its diff and complete the migration checklist (#710). -
The scoped Template-application generated type is now
Foo_DSL.TemplateScope;Foo.Templatecarries that descriptor.Foo_DSL.Factory.Templateremains the distinct type for theFoo.Create.Templateroot-creation field. Explicitly typed RC clients must rename and recompile; the intentionally removedFoo_DSL.Templatehas no compatibility alias (#737). -
IntelliJ support now materializes the packaged GDSL contributors once in a root-owned, IDE-only directory and registers that single physical resource root with every Schema module. After a mirror refresh, it contributes static
Foo.CreateandFoo.Templateproperties as the publicFoo_DSL.FactoryandFoo_DSL.TemplateScopecontracts. Because IntelliJ's documented GDSL property helper normalizes those names to lowercase JavaBean properties, 4.0 uses a temporary, version-sensitive internal raw-member hook, validated with IntelliJ IDEA 2026.2; IDEA-392559 tracks the supported IntelliJ API needed to replace it. The bridge changes neither bytecode nor read-only runtime semantics. Ordinary classes and unresolved support namespaces receive no synthetic property, and the retained polymorphic closure contributor delegates through the publicFoo_DSL.Buildercontract. Neither the GDSL root nor the mirrors are compilation, publication, or downstream inputs (#703). -
Fixed cross-source
@Owner(root = true)public Builder accessors to expose the target model'sRoot_DSL.Builder<Root>contract instead of the hiddenRoot$Builderimplementation. Generated AnnoDocimal source mirrors now preserve that same public type (#702). -
Fixed same-source forward relationship declarations so public Builder accessors, creators, and AnnoDocimal source mirrors use the target's
Child_DSL.Builder<Child>contract rather than the hiddenChild$Builderimplementation (#728). -
Public static methods declared on custom
Factoryclasses now fail compilation instead of being silently omitted fromCreate. Make these public factory operations instance methods; private, protected, and package-private static helpers and model-level static converters remain supported (#706). -
Qualified same-source static model converters now project to active-session Builders in relocated lifecycle and mutator code, while ordinary root converter calls retain their completed-model result (#662).
-
Fixed declared DSL relationship fields, including direct, collection, map, and
@Ownerfields, to retain their declared public Builder type when adefaultImplexists; ordinary relationship creation continues to select the default implementation (#666). -
Uninitialized
SortedSet/NavigableSetandSortedMap/NavigableMapfields now receive their natural-order sorted defaults before Builder-first materialization. Their completed views remain sorted and immutable, while explicitTreeSet/TreeMapcomparators continue to be preserved (#664). -
Factory maps continue to prefer an explicit same-named Builder mutator over direct field storage. KlumAST now warns when an exact single-argument
@Mutatoroverride returns the field value or compatible Builder, points direct assignment tosetX, and rejects other non-void return types as likely helper-method collisions.voidoverrides and no-field map-method fallback remain valid (#661). -
Statically checked Builder-phase code now identifies source-visible
Child.Create.With,One, andFromroot factories, explains that they return a completed model, and directs nested composition toChild.Create.AsBuilder().*attached to an owned relationship. Completed-model validation, ordinary static source factories, non-DSLCreate, and valid Builder composition remain valid (#656). -
Statically checked Builder-phase code now rejects
instanceof SomeDslModelwhen a relationship value is known to be a Builder before materialization. The diagnostic reports the inferred Builder type and points to the Builder-first migration guide; completed-model validation, ordinary non-model checks, and operands known only asObjectremain valid (#654). -
Added a deliberately incomplete, best-effort 3.x-to-4.0 migration starter script for known annotation,
KlumModelException, and copy-annotation import moves plus current-target validation-reporter edits. It runs from a schema module in a clean disposable Git worktree and requires diff review, normal compilation, and the Builder-first migration checklist; it is not an automatic migration tool (#652). -
Added the portable
build-domain-first-schemaskill, task-oriented domain-first guidance, and an executable Layer 3 smart-home journey. The fixture separates generic API, fixed floorplan Schema, registered Model script, and API-onlyclient-demo; it covers Cluster projection, provider-polymorphic Builder calls,@DefaultValueslabels, and a bounded field-test artifact (#471). -
Added a task-oriented Gradle onboarding preview, portable
start-klum-project,author-klum-model, andfeature-advisorAgent Skills, plus an executable minimal fixture.feature-advisoralso assesses whether KlumAST or its skill distribution needs an update (#470). -
Added the portable
build-target-contract-schemaskill and an executable direct-schema Helm journey. It renders two validated service models as human-readable values files with semantic golden-contract evidence, makes the Layer 3 decision explicit, and keeps resource-backed defaults and ordered configuration composition with #79 and #304 (#472). -
Replaced mutable generated RW objects with generated Builders inheriting from
KlumBuilder, while preserving DSL inheritance. Builders own field initializers, relationship state, mutators, and lifecycle work throughPOST_TREE(#416, #266). -
Fixed generated Builder fields and accessors to retain declared simple collection and map generic types for lifecycle static checking, including inherited fields, while relationship collections retain their generated Builder element types (#646).
-
Added the
INSTANTIATEphase at ordinal 40. It materializes the complete graph before validation, including cycles and self-links, then runs validation against completed DSL Objects. -
Completed DSL Objects no longer expose generated
applyor construction-path members directly. Non-transient simple fields are final;FieldType.TRANSIENTremains mutable (#323). -
Relationship fields hold Builders during construction.
OPTIONAL_LINKpreserves Layer 3@LinkTooverrides per single, List, and Map entry: a fresh same-session Builder is owned composition, while an already claimed Builder or completed model is aggregation.LINKremains aggregation-only and rejects fresh Builders;KlumBuilder.link(fieldName, target)provides non-destructive custom Auto-Link fallback (#474). -
Completed collections are independent read-only snapshots. Supported declarations are
List,Set,SortedSet/NavigableSet,Map,SortedMap/NavigableMap, andEnumSet; unsupported concrete or custom declarations now fail schema compilation. -
Split construction and completed-model state between
KlumBuilderandKlumModelProxy.KlumInstanceProxyis now a deprecated Builder-only compatibility adapter, andVisitingPhaseActionis replaced by state-specific Builder and Model variants. -
Replaced the legacy
$_RW/KlumRwObjectBuilder implementation contract with generated, self-typedFoo_DSL.Builder<SELF extends Foo> extends KlumBuilder<SELF>capabilities. Inherited Builder interfaces retain their parent interface and thread the same leafSELF; factories expose the concreteFoo_DSL.Builder<Foo>. Runtime operations are internal; use generated Builder/factory interfaces and@DelegatesToBuilder.@DelegatesToRWremains a deprecated source alias (#394). -
Added
createKlumDslSourceMirrorsto the schema Gradle plugin. Run it after schema changes to compile the realFoo_DSLinterfaces and refresh their AnnoDocimal IDE source mirrors without compiling, packaging, publishing, or propagating the mirrors themselves (#434). -
Renamed the root multi-project IDE-mirror aggregate to
generateAllKlumDslSourceMirrors, making its all-Schema-project scope explicit. It lazily refreshes each participating Schema project's IDE-only mirror task, including Layer 3api/schemalayouts, without generating or exposing a root payload (#559). -
Generated completed-model and Builder getters now carry field-derived AnnoDoc documentation, including deprecation reasons (#383).
-
Provisional Builder validation issues transfer to the completed-model companion, and each
InstanceValidatoris memoized once per completed model. -
Added active-session
Create.AsBuilder().With,One,FromMap, andFrom(DelegatingScript)operations. They create an unsealed owned Builder in the current root Construction session, apply active Templates, and runPostCreate, explicit configuration, andPostApplyonce without starting a nested lifecycle. Calls outside the session, across root sessions, or after lifecycle completion fail with migration guidance; ordinary materializing Scripts remain root-only (#436). -
Added statically precise polymorphic relationship selection with
child(ConcreteChild.Create) { ... }. Generated factories expose the exact publicConcreteChild_DSL.Builder<ConcreteChild>through a public provider contract, while single, collection, map, named-parameter, and keyed child creation stays in the current parent session. The dynamicchild(ConcreteChild) { ... }Class selector remains supported and unchanged (#620). -
Restored Builder-first collection, map, Cluster, direct
DelegatingScript, converter, alternative, and custom-factory composition. Source-visible model-producing methods receive synthetic$klum$asBuilder$...twins linked through AST metadata; generated public contracts expose concreteFoo_DSL.Builderresults while direct root factory behavior remains completed-model-oriented. Multi-result projections retain their original container, iteration order, comparator, duplicates, and map keys. Opaque or precompiled model producers are omitted from generated APIs and matching dynamic calls fail with targeted migration guidance (#437, ADR 0004). -
Fixed Builder-producing projection for source-visible recursive unqualified static factory and converter calls, including overload selection. The generated relationship APIs and IDE mirrors now expose the matching public Builder overloads for single, List, and Map relationships without changing direct root-factory behavior (#642).
-
Added the Java-first
KlumObjectSupport.of(completedObject)facade for a completed DSL Object root or subtree. Its construction-path getter and composition-onlyStructurehelper expose paths, direct ownership, relative paths, and cycle-safe typed traversal. ItsValidationhelper exposesgetResult()for the target andgetSubtreeResults()for the target plus owned subtree, and verifies stored results without rerunning validators. Subtree result lists include every stored result, including results without issues; the deprecatedValidator.getValidationResultsFromStructureandverifyStructureadapters now inherit that broader list contract. The Model companion and generic metadata access are now internal (#435, #390, ADR 0006). Its sole public construction-string getter isgetConstructionPath(); nogetBreadcrumbPath()facade alias remains (#390, #549). -
Corrected the public
KlumObjectSupport.Structuretraversal boundary:ModelVisitorremains runtime-internal, while typedvisit(Class<R>, BiConsumer<String, R>)andfindAll(Class<R>)remain the completed-model client API (#693). -
Split the generated internal companion into sealed Model and Template variants. Ordinary models retain no deferred actions; every owned Template node carries persistent recipe identity and paths, while pre-existing ordinary
LINKtargets retain their identity. Direct Template relationship assignment, includingLINK, is rejected with rehydration guidance (#438). -
Defined copy-source behavior: ordinary completed models and Maps are value-only; marked Templates add immutable recipe replay; same-session unsealed Builders add an ephemeral dehydrated snapshot of pending actions without identity conversion. Sealed and cross-session Builders are rejected.
-
Generated public Builder contracts now expose the same-model active-Builder
copyFromoverload, so Builder lifecycle merges are statically checked without exposing internal Builder implementations (#644). -
applyLater/scheduleApplyLaternow reject every phase at or afterINSTANTIATE(40) immediately and direct completed-model work toModelVisitingPhaseAction.
Templates, serialization, and Jackson
- Templates remain DSL Object recipes and rehydrate into fresh Builder graphs on every application. Template
applyLaterrecipes are stored in immutable serializable recipe state, cloned on replay, and validated when the Template materializes. Captured values must be serializable and captured Builders are rejected. Java serialization preserves graph-wide Template identity without serializing Builders, Construction sessions, scopes, or mutable recipe collections. - Completed-model companion state is serializable. Technical metadata rejects non-serializable values immediately.
- Defined Jackson as asymmetric external-format interoperability rather than Klum persistence: managed import binds foreign data through Builders, completed models export through ordinary Jackson APIs, and KlumAST adds no wire-format or producer metadata. External version fields and migration adapters remain Schema-owned (#447, ADR 0009).
- Jackson serialization rejects marked Templates, including nested values, so JSON cannot silently discard recipe actions.
- Jackson import now binds resolved public configuration properties into root and owned child Builders between
PostCreateandPostApply, then runs one normal lifecycle, materialization, validation, and verification pipeline. Missing input keeps initializer/default behavior; present values,null, and containers replace authoritatively. Resolved@JsonProperty,@JsonAlias, naming strategies, mixins, access/ignore rules, and unknown-property policy are honored without ambient Templates or copy/overwrite semantics (#439, #251, ADR 0009). Explicit type-level custom deserializers remain the opt-out. - Added
KlumJacksonImporterandKlumJacksonInputas the explicit Jackson 2 import seam. Caller-owned mapper/reader configuration is captured without mutation; root, value-only Template, active-session Builder, and existing-Builder application modes each consume one parser/tree/Map input (#463). - Added executable asymmetric YAML interoperability evidence: one foreign input is imported through one Builder lifecycle
and ordinary Jackson export emits an intentionally enriched, separately owned projection. Import/export fixtures cover
foreign aliases, composition, polymorphism, null and unknown-field policy,
LINK, Templates, diagnostics, custom serializers, and the absence of Klum wire metadata; no round-trip or Jackson-owned layering contract is introduced (#464). - Jackson
LINKimport is reference-only. Explicit Jackson identities withalwaysAsId, custom property codecs, and customObjectIdResolvers preserve completed targets and same-session Builder identity across backward and forward references, including Collection and MapLINKs. Inline input fails with focused mapping errors. Export requires an explicit projection and may use identity, omission, scalar/custom structure, or a deliberate inline serializer; Owner and Role remain framework-managed (#440). - Jackson views, inclusion, formats, Simple Value codecs, mixins, and polymorphic owned DSL subtypes work without replacing
Klum construction.
@JsonCreator, direct model mutators, foreign Jackson Builders, completed-model owned deserializers, and managed/back references cannot take over Builder allocation. The former publicKlumValueInstantiatorandSettableKlumBeanPropertyextension classes remain removed.
3.0.1
- New annodocimal version, ignores irrelevant inner class entries in class files
3.0.0
- Support for Groovy 2 has been dropped, support for Groovy 5 has been added
- Minimum Java version is now 17
- Only include base groovy module instead of groovy-all in gradle plugins
- Gradle plugins now support versions as strings ("5", "5.0" or "5.0.5") or int (5) instead of the GroovyVersion enum, which is deprecated.
2.2.0
- Breaking change: Dropped manualValidation() support
- Minor Breaking change:
toString()methods are not created anymore. If needed, they can still be generated using the default Groovy@ToStringannotation. - Validation improvements (see Validation #395)
- Validation-phase is split into validation and verify phases.
- Provided new (preliminary) methods to explicitly create validation issues. This allows validation issues to be created in earlier phases, as well as multiple issues in a single validation/lifecycle method.
- further validation issues can explicitly be suppressed for specific fields and specific maximum levels
- New annotation
@Optionalas alias for@Validate(Validate.Ignore) - VerifyPhase can now be skipped using system property
klum.validation.skipVerify - Results of a complete structure can be retrieved using
Validator.getValidationResultsFromStructure(Object)or verified later usingValidator.verifyStructure(Object) - Deprecation checks now run in the new early validation phase and check only for manually set values, not values created by later phases
- new annotation
@Notifyto raise issues if a field is set or unset in the early validation phase. - new module: klum-ast-bean-validation to provide Bean Validation integration (JSR380). (see Validation #395)
- Validation methods can be collected into inner classes annotated with
@Validate(see Validation and #415)
- Added a bom with all module versions (
com.blackbuild.klum.ast:klum-ast-bom) for easier dependency management. Note that this BOM is already applied by the gradle plugins
Bugfixes
StructureUtil.getPathOfFieldContaining()and therefore@Rolefields ignored fields where the value was actually a subclass of the field type.- CopyFrom and templating failed on primitive values. See #400
- modelPath / Validation path was off if using various complex inner factories or AutoCreates
2.1.4/2.1.5
- Prevent ConcurrentModificationExceptions when calling
applyLaterfrom a lifecycle method
2.1.3
StructureUtil.visit()andStructureUtil.deepFind()should ignoreOwnerandLinkfields (see #396)- internal:
StructureUtil.deepFind()should internally use a visitor instead of duplicating logic (see #397)
2.1.2
- Validate problems should include the structure path along with the breadcrumb path
2.1.1
- KlumValidationException should omit empty KlumValidationResult instances.
2.1.0
No new features, but all deprecated features from 2.0.0 have been removed. All dropped methods are correctly declared in 2.0.0, along with their migration paths.
Dropped methods and features (see Migration):
- The
@Validationannotation. Use@Validateon class level instead. - Creator methods on the model class have been dropped
X.create*()->X.Create.*()
- The generated
validate()method. UseValidator.validate()instead. This means that creating ownvalidate()methods is legal again. - Template-specific methods are now pooled in a new
BoundTemplateHandlerclass, which is accessible for as static Field Template.X.withTemplate()->X.Template.With()X.withTemplates()->X.Template.WithAll()X.Create.TemplateFrom()->X.Template.CreateFrom()X.Create.AsTemplate()->X.Template.Create()withTemplate()andwithTemplates()are now deprecated, use the new methods instead.
2.0.0
New Features
- New Field Type: BUILDER: Getters are protected or private in model, but dsl methods are public
- Compatibility with Groovy 3 and 4. KlumAST is currently still built with Groovy 2.4 (for compatibility with Jenkins). Tests are run with Groovy 3 and 4 as well.
- Replace basic jackson transformation with a dedicated (beta) JacksonModule (see Jackson Integration)).
- First steps for Layer3 models. (see Layer3)
- Split model creation into distinct phases (see #156, #155,#187 and Model Phases)
- New Phases:
- In addition to lifecycle methods, fields of type
Closurecan now be used to define model provided (instead of schema provided) lifecycle methods. These closures will be executed in their respective Lifecycle phases. - default implementation: by providing the attribute
defaultImplon either@DSLor@Field, one can allow the creation of non-polymorphic field methods even for interfaces and abstract types. (see Default Implementations) - Creator methods have been moved to a separate creator class (see #76), creator methods on the model class have been deprecated (see Migration).
- Custom creator classes can be provided (see Factory Classes)
- Methods of creator classes (including custom creators) are included in collection factories (see #300 and Factory Classes)
- Creator class also supports creating templates from scripts (files or URLS) (see Templates and #322)
- Switch annotation validation to KlumCast Framework (see #312))
- Generate Documentation for almost all generated methods via AnnoDocimal (see #197))
- Gradle Plugin for easier project setup
- Various owner improvements:
- Owner targets now can be transitive, i.e. be filled with the value of an ancestor of the specified type (instead of the direct owner) (see Transitive Owners and #49)
- Ower fields can be filled with the actual root of the model. This works even if no explicit owner field is present (see Root Owner)
- Owner objects can be converted before handing them to owner fields or methods (see Owner Converters and #189)
- New
@Roleannotation to infer the name of the owner field containing an object (see Role fields and #86)
- Overwrite strategies for
copyFromand templates (see Copy Strategies, #309, #348) - Multiple calls to a single object closure now configure the same object instead of completely overriding the previous field, the same for map entries using the same key. (see #325). While this is a more natural behaviour, it might break existing code in some corner cases, see Migration).
- Breadcrumbs: each Method or closures encountered while creating model is used to setup a breadcrumb path. This path is used in exceptions to identify the location of the problem in the scripts, which is especially handy when the model is split over various scripts. (see Exception Handling and #264)
- Rework exception handling as a whole, this includes a new hierarchy of exceptions (see Exception Handling) which contain relevant information about the phase in which the exception occured as well as the object which caused the exception. This is especially useful for validation exceptions. (see #149 and #288)
- Validations are now all executed, even if exceptions are encountered. All violations are aggregated into a single
KlumValidationExceptionwhich is thrown at the end of the phase (see #146) - New
FromMapfactory to allow a "poor man's deserialization" (see Convenience Factories and #359) - DefaultValues annotations provide an annotation based way to set default values in Layer3 scenarios (see Default Values and #361)
@Cluster-Fields create now a factory closure for that field, containing only the cluster members (see #365)applyLatermethods for all objects that can be used to hook closures to be applied in later phases (see Model Phases)- Validations have additional levels (WARNING, DEPRECATION and INFO) that can be set for each individual validation. Non error validations to lead to failure in the model (see #145 and Validation)
Improvements
- Creator classes also support methods creating multiple instances at once (see #319)
- CopyFrom now creates deep clones (see #36)
booleanfields are never validated (makes no sense),Booleanfields are evaluated against not null, not against Groovy Truth (i.e. the field must have an explicit value assigned) (see #223)- Provide
@Requiredas an alternative to an empty@Validateannotation (see #221) EnumSetfields are now supported. Note that for enum sets a copy of the underlying set is returned as opposed to a readonly instance. (see #249)- Converter methods are now honored for Alternatives methods as well. (see #270)
@Validatenow can be placed on classes. This effectively replaces@Validate(option=Validation.Option.VALIDATE_UNMARKED), which is internally converted to the new format (see #276). The@Validationannotation is deprecated.- Sanity check: Key Fields must not have
@Owneror@Fieldannotations. - Selector members for
@LinkToannotations allows to determine the link source from the provider based on the value of another field (see #302) - @LinkTo now correctly handles empty collections/maps as target
- Allow a custom key-provider function for
createFrom(URL)andcreateFrom(File) @Clustercan also be placed on fields, which will be converted into a getter method (see #366)@Clustercan be combined with@AutoCreateto auto create all members of the annotated cluster (see #363)- Templates that where active during an object's creation are now stored in the proxy of that object and will be applied to any object created by that object in later phases (usually AutoCreate) (see #377)
@DefaultValueshas avalueTargetmember that is used to remap thevaluemember of the targeted annotation to a specific field (see #370)- new Layer3
@DefaultApplyannotation that can be used for complex, schema-controlled default values (see #370 and Default Values)
Deprecations (see Migration):
- The
@Validationannotation is deprecated. Use@Validateon class level instead. - creator methods on the model class have been deprecated.
- The generated
validate()method is now deprecated, useKlumInstanceProxy.validate()instead. This means that creating ownvalidate()methods is legal again. - Template-specific methods are now pooled in a new
BoundTemplateHandlerclass, which is accessible for as static Field Template.X.withTemplate()->X.Template.With()X.withTemplates()->X.Template.WithAll()X.Create.TemplateFrom()->X.Template.CreateFrom()X.Create.AsTemplate()->X.Template.Create()withTemplate()andwithTemplates()are now deprecated, use the new methods instead.
Breaking changes (see Migration)
- it is a compile error to place the
@Validateannotation on a boolean field. - KlumAST is split into different modules, klum-ast-compile is compile-time only, klum-ast-runtime is needed for runtime as well. This completes the changes started in 1.2.0
- In order for the serialization in jackson to work, the new klum-ast-jackson module needs to be included in the project (see Jackson Integration)
- The naming of virtual fields is changed, now the virtual field is identical to the method name (previously, the first element of the camel cased method name was removed).
- methods named
doValidateare no longer considered Validate methods by default. - Static Type Checking for Configuration Scripts does not (yet) work under Groovy 3
- Previously, only public methods were checked for illegal write access. This has been changed to include all visibilities. Protected methods that are conceptually write access methods must now also be annotated with @Mutator, otherwise a compile error is thrown.
- Owner fields are now set in a later phase, meaning that they are not yet set when apply closures are resolved. This logic must be moved to a later phase (postTree), for example using lifecycle closures.
- Default values are no longer a modification of the getter but rather explicitly set during the 'default' phase. This might result in subtle differences in the behavior, especially when using a non-template as template / target for
copyFrom. Make sure to create template instances withCreate.Templateif you want to use them as templates. withTemplates(Map, Closure)now only accepts anonymous templates, i.e. the signature changed fromwithTemplates(Map<Class, Object>, Closure)towithTemplates(Map<Class, Map<String, Object>, Closure). Calls using concrete templates now must usewithTemplates(List<Object>, Closure)instead.
Fixes
Required.value()is correctly translated toValidate.message()(see #373).CopyHandlerignores theIGNOREDfield type (see #374).- Root objects of the wrong type are ignored, allowing partial Models for testing.
- Unqualified links in
KlumFactoryJavadoc no longer cause Javadoc failures. - The Gradle model plugin uses the script class rather than the model class in its model descriptor.
ClusterModelannotation filtering retrieves the actualFieldobject.- Script names are determined correctly when a filename contains multiple dots (see #328).
- Generated RW classes are public, so static type checking works when owner and child are in different packages.
- The AnnoDocimal inner-enum final-modifier fix is included.
@Overridesis not copied to RW delegation methods (see #340).- Polymorphic virtual setters work correctly (see #250).
- Converter methods honor default values (see #268).
- Nested generic types work correctly (see #248).
- Converter methods work for maps of DSL Objects (see #242).
- Generated classes with generic field types and generic factories such as
List.ofare valid (see #243). - A default delegate may be a getter rather than a field (see #244).
applyaccepts a Map-only call (see #241).- Key fields work correctly in hierarchies (see #238).
- Creator-method visibility is correct (see #232).