Package space.arim.dazzleconf.backend
The classes in this package each correspond to steps in the access and mutation of data, its in-memory representation, and its usage by other parts of the library:
- Data comes from a data root as raw bytes or characters
- It is then parsed into a
DataTreeby the configuration format. - A configuration format is represented by
Backendwhich handles reading and writing data trees by interfacing with the data root. - Some configuration formats will provide a
KeyMapper, an interface for making sure that Java method names (as used with this library) map to strings in accordance with the configuration format's recommended practices.
Roots and Backends
Data comes from a root source. We call this root source a 'data root.' The data root is usually a file, but it can
also be a String or raw bytes. It is represented abstractly by DataRoot.
Root data is just bytes or string data; it is unparsed and could be full of a bajillion errors.
The Backend represents the configuration format itself. An implementation
might exist for YAML, HOCON, TOML, .ini, .properties, or any other configuration formats that are to be supported. A
backend takes a data root, and it allows reading or writing to that root in the form of a data tree.
Some backends require specific roots. For example, a backend for a human-readable configuration format might
expect a ReadableRoot. A backend for byte serialization, in contrast, might need to use BinaryRoot
if the deserialized bytes do not comprise a valid string. Backend implementations can declare which root is
necessary in their constructor.
Data trees
Data is held in memory using a DataTree. A data tree is essentially a map of
keys to values, where the values themselves can be other DataTrees. Additionally, each value can have
metadata like comments or line number attached, see DataEntry.
Mutability models
Both DataTree, DataList and KeyPath rely on a type-level mutability model that is designed
for efficient data access, guarded mutation, and firm control over ownership.
This model makes use of subclasses which denote the mutability of the data they contain. Because mutation methods are only available in the mutable variant, modifying an immutable data tree becomes statically impossible. This is implemented through a triangle relationship: an umbrella parent type, a mutable variant, and an immutable variant.
At runtime, a value of the parent type can be one of either sub-type. For example, a DataTree could be either
DataTree.Mut or a DataTree.Immut. The API, however, is intended to elide instanceof checks by making
conversions between these types logical and efficient. Let's use DataTree as an example.
- An existing
DataTreecan be made mutable withDataTree.intoMut(). If the receiver data tree is not already mutable, a new object is created and the data is copied to it. For efficiency, the data is actually copied lazily upon first mutation. If already mutable, the data tree yields itself. - An existing
DataTreecan be made immutable withDataTree.intoImmut(). This function moves the data into a new immutable container and poisons the oldDataTreeinstance. Thus, it mimics a Rust-like ownership model where the caller is expected to have exclusive access to the mutable data tree, and therefore the ability to move that data to a new place. If already immutable, the data tree yields itself. - Thanks to lazy copying, repeated conversion using the aforementioned methods has low performance impact. Copying is performed upon first mutation of a mutable instance that was created from an immutable one.
As expected, the Mut variant is not thread safe without the presence of external synchronization.
-
ClassDescriptionConfiguration backend for reading and writing data trees.A document loaded from, or writable to, a backend.Defines metadata about a backend.Data roots which are capable of working with raw bytes.The comments on a data entry or configuration class.A data value, with associated metadata as interoperable with backend formats.A list of in-memory configuration data.A data list which is immutable.A data list which can be modified.The root source of configuration data.DataRoot.Operation<R,
U> IO proof, functional interface for use with reader/writer methodsA tree of in-memory configuration data.A data tree which is immutable.A data tree which can be modified.Default, "no-op" mapper for keys and method namesA base class for data root that makes use ofInputStreamandOutputStream.A key mapper which converts lower camel case patterns to kebab case.Simple interface for mapping method names into backend configuration keys, and vice versa.A key path consists of an ordered sequence of strings.A key path which is unmistakably immutable.Specifies either the start or the end of the key path sequence.A key mapper which converts lower camel case patterns to snake case.A data root from a file path.A displayable item, printed in one of multiple ways.A base implementation forPrintable.Unifying interface for string based data roots.Implementation of data root for a string