-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
EveryConfig is a thin set of layers over one canonical Jackson tree.
text (yaml / json / toml / jsonc)
│ ▲
readTree │ │ writeWithComments / writeTreePlain
▼ │
┌─────────────────────────────┐ ┌──────────────────────────┐
│ Codec (codec.jackson.*) │ │ CommentTree (overlay) │
│ text ⇄ ObjectNode + mapper │ │ block/side/header/footer │
└─────────────────────────────┘ └──────────────────────────┘
│ ▲ ▲
▼ │ │
┌──────────────────────────────────────────────────────────────┐
│ Config — the canonical ObjectNode + dynamic path API │
│ (setValue / getValue / typed getters / getOrSetValueIfAbsent) │
└──────────────────────────────────────────────────────────────┘
│ │
EntityBinder (derived, merges) BackStore / io (atomic file I/O)
| Layer | Package | Role |
|---|---|---|
| Config |
config, config.section
|
The handle: the canonical ObjectNode, the dynamic path API, the comment API, and the file lifecycle. |
| Core model |
core.tree, core.coerce, core.comment, core
|
DPath/PathOptions (dotted-path utils — the separator is fixed at ., a dot inside a key is escaped a\.b), NodeCoercion (the single Java ⇄ Jackson seam), TypeFamily (shared type classification), CommentTree + CommentType, KeyOrder. |
| Codec |
codec, codec.jackson
|
The Codec SPI + CommentFidelity + registry + mapper profiles; the four format codecs plus the no-file InMemoryCodec. |
| I/O | io |
BackStore SPI + AtomicFileBackStore: atomic write, .bak rescue, reload, poll watcher, async executor. |
| Binding |
binding, binding.schema, binding.merge, binding.introspect
|
The typed view: EntityBinder, the schema model, smart-merge + @KeyIndex indexing (KeyIndexer), the Jackson annotation bridge. |
| Annotations | annotation |
@Key, @Comment, @Section, @KeyIndex, @PostLoad (+ KeyTransformCase, CommentMode). |
These are the invariants every layer obeys.
-
The tree is canonical. The dynamic API operates on the
ObjectNode. Typed binding is a derived view; on conflict the tree wins (unknown file keys survive,FAIL_ON_UNKNOWN_PROPERTIESis off), and a binding save merges into the tree, never replaces it. -
Comments = seed / override.
@CommentandgetOrSetValueIfAbsent(path, def, comment)write comments in two explicit modes — rewrite-every-save (OVERRIDE) or write-once (SET_IF_ABSENT). See Default Values & Comments. -
Comment fidelity is a codec capability. Each codec declares
LOSSLESS/LOSSY/NONE. JSON isNONE. See Codecs & Formats. -
Save = reconciliation. Load captures the (data tree,
CommentTree,KeyOrder); the emitter reconciles per path and emits in file order, appending new keys. -
The emitter renders structure itself (keys, indent, sections, comment lines) and delegates only
leaf-value serialization to the
ObjectMapper— the mapper's output is never re-parsed, so a custom mapper can restyle a value but cannot break the layout. The Codec (text⇄tree⇄entity) is separate from the I/O layer (atomic file writes).
A Codec holds a single, thread-safe Jackson ObjectMapper, shared across every live Config of that
format. There is no per-file engine, no pool, no ThreadLocal — a config's footprint is just its tree.
Because the tree is canonical and the codec is just the text⇄tree⇄entity seam, a Config need not be
backed by a file at all, and its format can be swapped at runtime.
-
Config.inMemory()binds anInMemoryCodec: the full typed/POJO API works (setValue/mergeValue,getValue(path, type),@Key/@Section/@Comment, enum-by-name,java.time,Optional) but there is no text format, sosave()throws. (A barenew Config()has no codec at all and accepts only native scalar/Map/list/JsonNodevalues.) -
save(Codec)persists the current tree once through a different codec, leaving the live codec untouched — e.g. open a.ymland dump a JSON snapshot. -
changeCodec(Codec)switches the format every latersave()uses, rebinding the POJO coercion to the new mapper.
Config cfg = Config.open("server.yml"); // YAML on disk
cfg.save(new JsonCodec()); // one-shot JSON snapshot, codec still YAML
cfg.changeCodec(new TomlCodec()); // every future save() now emits TOMLDot-in-key. The path separator is fixed at
.; a key that itself contains a dot is escaped:cfg.getInt("rates.usd\\.brl")reads the single key"usd.brl"under"rates".\\is a literal backslash, and the escaping is a no-op for an ordinary dotted path.
→ See also Project Layout · The Dynamic API · Entity Binding · @KeyIndex Collections
EveryConfig · br.com.finalcraft:EveryConfig · One config API, every format, comments included · made by Petrus Pradella
Getting Started
Core Concepts
Typed Binding
Operations
Reference
Contributing