Giter VIP home page Giter VIP logo

dazzleconf's Introduction

DazzleConf Maven Central Javadoc discord

Prepare to be dazzled

Introduction

It's here. The moment you've been waiting for.

A type-safe, thread-safe, fail-fast, user-oriented, easy to setup, extensible and flexible configuration library.

Objectives

  • Eliminate stringly-typed spaghetti such as getString("key") or getAsInt("section.subsection"). Use your configuration just as you would any other immutable bean.

  • Write defaults once and never bother with them again.

  • Validate configuration in all manners, and fail-fast with informative messages.

  • Easily update old configurations with the latest keys. No need to version config files.

  • Pave the way for users with incredibly flexible type conversion.

  • Take advantage of enums, collections, and nested configuration sections.

Features

  • Lightweight, simple, well-tested library.
  • Immutable and thread safe by design. Values loaded once and never modified thereafter.
  • Readable for programmers and users. Annotation-driven and supports comments.
  • Configuration objects are format-independent.
  • Support for YAML, HOCON, and JSON out of the box, and allows easy extension with more formats.
  • Identify the precise cause of user errors.
  • Use a decoupled and testable config interface.

Usage

Example

@ConfSerialisers(URLSerialiser.class)
public interface MyConfig {

  @DefaultInteger(3)
  int someInteger();
  
  @ConfKey("display.user-message")
  @ConfComments("The message shown when a certain thing happens")
  @DefaultString("Hello user")
  String userMessage();
  
  @ConfKey("display.enable-user-message")
  @DefaultBoolean(true)
  boolean enableUserMessage();
  
  @IntegerRange(min = 1, max = Integer.MAX_VALUE - 1)
  @DefaultLong(10)
  long boundedNumeric();
  
  @DefaultString("ONE")
  MyEnum enumValue();
  
  @SubSection
  NestedSection nestedConfigSection();

  @DefaultString("https://github.com")
  URL validUrl();

}

public enum MyEnum {
  ONE,
  TWO
}


public interface NestedSection {

  @ConfComments("Every annotation shown above works here too")
  @DefaultString("Also, methods are inherited if this interface extends another, enabling inheritable config interfaces")
  String flexibility();

}

When using the YAML configuration format, the following result can be generated by writing the default configuration:

someInteger: 3
display:
  # The message shown when a certain thing happens
  user-message: 'Hello user'
  enable-user-message: true
boundedNumeric: 10
enumValue: 'ONE'
nestedConfigSection:
  # Every annotation shown above works here too
  flexibility: 'Also, methods are inherited if this interface extends another, enabling inheritable config interfaces'
validUrl: 'https://github.com'

The same document can be reparsed to an instance of the configuration interface. Type and constraint validation is performed when config values are parsed and loaded, not when they are retrieved - having an instance of the config interface is enough to ensure the configuration is valid.

Requirements

  • Java 8

Java 11 is recommended, but not required.

module-info files are also included, and these are backwards compatible with Java 8.

Getting Started

This page will help you out: https://github.com/A248/DazzleConf/wiki/Getting-Started

The wiki also has extra examples such as with setting up a reloadable configuration, automatically updating the configuration with the latest keys, and more.

Documentation

See the docs folder of this repository for a detailed overview.

Additionally, the javadocs are published with the artifact. They can also be browsed here.

License

LGPL. See the license file for more information.

dazzleconf's People

Contributors

a248 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

cdfn

dazzleconf's Issues

How to add a generic type?

Hi, i need to add to the interface a type that represents an item from XSeries.XItemStack

but adding the return type as Object does not work, this is the problematic interface:

public interface EventItens {

    @Order(1)
    Boolean Enabled();

    @Order(2)

    Object Helmet();

    @Order(3)
    Object Chestplate();

    @Order(4)
    Object Leggings();

    @Order(5)
    Object Boots();

    @Order(6)
    Map<String, Object> Inventory();

}```

Define a structure for configuration definitions

This issue may seem obscure in essence, but it is a backbone for adding more flexibility to DazzleConf in several areas, such as #37, #30, #35.

A configuration definition is the result of reading a config interface and determining its required methods, their return types, default values if present (or if required, see #36); effectively, it is the whole structure of a configuration. It should also include mapped keys (#38).

We can define a configuration definition provider as the interface responsible for creating configuration definitions. When a configuration definition is made, I also propose that the relevant serializer is attached to the definition at the key, rather than retained globally and looked up as required; this would allow wildcard serializers as mentioned in #39.

Here is an example putting this all together:

Configuration<MyConfig> configuration = Configuration
  .builder() // Or builderWithoutDefaultSerializers
  .withSerializers(...) // ValueSerializer... or Collection<ValueSerializer>
  .keyMapping(new DefaultKeyMapper()) // Can be omitted
  .definitionProvider(new DefaultDefinitionProvider<>(MyConfig.class))
  .instantiator(new DefaultInstantiator()) // Not generic over MyConfig. 
  .migrations()
  .backend(new SnakeYamlBackend(
    new ConfigurationFile(path) // Provides methods such as newReader, newWriter, and buffered variants
    // Can also provide options here
  ))
  .build(); // Throws IllDefinedConfigException if developer error

// ConfigResult returns elements such as auxiliary keys, whether defaults were written back, migrations performed, etc.
ConfigResult<MyConfig> myConfigResult = configuration.reload();
MyConfig myConfig = myConfigResult.loadedValue();

// Can build out further abstractions related to reloading
Reloadable<MyConfig> reloadable = Reloadable.create(configuration);
reloadable.setBackingInstance(myConfig);
assert reloadable.getBackingInstance() == myConfig;
MyConfig reloadProofConfig = reloadable.getTransparentProvider();

We can use multiple builder steps so that adding the definition provider and instantiator can be done in an ergonomic generics-friendly fashion. The definition provider should be composable so that the configuration is modular: that is, combining non-overlapping configuration definitions must be possible. Loading by multiple files would also be possible by wrapping the backend. The backend in this design proposal, unlike in the current library, is stateless and decoupled from other aspects of DazzleConf.

Consider deprecating dotted paths in @ConfKey

Dotted paths in @ConfKey tend to be error-prone when the developer mistakenly specifies conflicting keys. For example:

@ConfKey("keyOne")
@ConfDefault.DefaultString("some")
String string();

@ConfKey("keyOne.subKey")
@ConfDefault.DefaultString("other")
String otherString();

Wiki errors

I bug in the wiki.

Error: SnakeYamlConfigurationFactory.create (configClass, ConfigurationOptions.defaults (), yamlOptions)
Fix: new SnakeYamlConfigurationFactory (configClass, ConfigurationOptions.defaults (), yamlOptions)

Further improve error messages

  • For developer-caused exceptions, indicate how the issue could be solved.
  • For user-caused bad-value exceptions, where possible, include examples of what proper input would look like.
  • For user-caused missing key exceptions, more clearly describe what is missing.

Recursive Serializer

Implement a ValueSerializer that deserializes interfaces similar to the core behavior of DazzleConf: Simply provide it an interface class and it will deserialize it using any values annotated on the object.

Bytecode-powered optional enhancements

The following would require an optional bytecode manipulating library:

  • ConfigurationSorter based on source file ordering, requiring no manual order specification
  • Configuration proxy implemented without reflection, improving performance

Map method names to configuration keys automatically (deprecate @ConfKey?)

Using @ConfKey to enforce configuration key conventions is brittle and error-prone. I might want to use this-kind-of-format with YAML, and thus use @ConfKey("my-key") on a method name such as "myKey". However, it can be easy to forget this for a single key, which creates a longlasting ugly-looking spot in the configuration.

An interface should be provided to automatically map method names to configuration keys, therefore.

In the same vein of reasoning, we may want to deprecate @ConfKey entirely. I don't see a reason to keep the annotation. Removing it would make reasoning about a config easier for someone who wants to look at their plugin's source code.

Suggestions for a better DazzleConf

DazzleConf is a very useful library and makes config cooler and easier, but now let's make it even better.

First of all, change how the interface is processed, instead of current way it works it could be where it loads the interface, and looks for method with default which would have a special default, then it would store these extra defaults when generating the config. and also ability for variable methods, which is not related to the config but to make working with dazzleconf's way of configuration easier. It is simple, we can pass variables with identifier, and there would be an annotation like VariableMethod and requires the identifier, the method then is no longer a configuration option and is just a variable used by default methods to know what they should return exactly. and third is Double order because integers between 2 integers are always finite, but decimals are always infinite. And lastly. it is annoying when you have to use DefaultObject so replace/or add a feature where you can add your own default function like when adding config serializable, identified by a string. And most important of all of this, fixing the bug with hashmap. Also, a converter which allows previous options to change location, for example if the type was previously a list.. but now a map, and the map contains the list, it would move the list into its new location. And also make sure all of this doesn't break if their parents are lists.

This will make dazzleconf easier & more beginner friendly. it will reduce the code you have to write, and make it look better, and will also make the library more advanced. So i hope you add this in the next releases, maybe 2.0.

Swappable dynamic proxy factory

Provide a means of using alternative dynamic proxy implementations, such as a proxy with generated byte code.

It remains to be seen whether the proxy factory will be considered standard API or more of an internal interface.

Optionals let you leave out keys

Title: If you have an Optional annotated as a config value, not including a key for the value will simply make the Optional return empty.

A configuration with

@ConfigKey("some.key")
Optional<Integer> lol();

some:
  otherKey: otherValue
  
  # note the lack of some.key

should load lol() as an empty opt

Long Config Value Locked at Lower Value

Very weird problem I'm encountering. I'm using the Gson extension for dazzleconf. I have the following value in my config interface (HexCrawlerConfig):

@ConfKey("channel")
@ConfDefault.DefaultLong(0)
long channel();

In my .json config file it is defined like so:

"channel": 1184252858728726529

Whenever my config is loaded, it changes the value to be 1 less:

"channel": 1184252858728726528

If I leave it at this new value it chose for it, it stays like this every time I load up. When I set it back to the original value, it overrides it back to this value... Something I figured I'd try was just set the value to be 1 greater than what I need it to be, but it still locks the value to the one with the 8 at the end.

Here is how I am loading the config itself:

public final class JsonDazzleConfHandler<C> extends ConfigurationHelper<C>
{
	private volatile C configData;
	
	private JsonDazzleConfHandler(Path configFolder, String fileName, ConfigurationFactory<C> factory)
	{
		super(configFolder, fileName, factory);
	}
	
	public static <C> JsonDazzleConfHandler<C> create(Path configFolder, String fileName, Class<C> configClass)
	{
		GsonOptions jsonOptions = new GsonOptions.Builder()
				.build();
		return new JsonDazzleConfHandler<>(
				configFolder, fileName,
				GsonConfigurationFactory.create(
						configClass,
						new ConfigurationOptions.Builder().sorter(new AnnotationBasedSorter()).build(),
						jsonOptions
				)
		);
	}
	
	public void reload()
	{
		try
		{
			configData = reloadConfigData();
		}
		catch (IOException ex)
		{
			throw new UncheckedIOException(ex);
		}
		catch (ConfigFormatSyntaxException ex)
		{
			configData = getFactory().loadDefaults();
			System.err.println("Uh-oh! The syntax of your configuration are invalid.");
			ex.printStackTrace();
			
		}
		catch (InvalidConfigException ex)
		{
			configData = getFactory().loadDefaults();
			System.err.println("Uh-oh! The values in your configuration are invalid. Check to make sure you have specified the right data types.");
			ex.printStackTrace();
		}
	}
	
	public C get()
	{
		if(configData == null)
		{
			throw new IllegalStateException("Configuration has not been loaded yet");
		}
		return configData;
	}
}
JsonDazzleConfHandler<HexCrawlerConfig> configHandler = JsonDazzleConfHandler.create(
		Path.of("file path here"), "config.json",
		HexCrawlerConfig.class
);
configHandler.reload();
config = configHandler.get();
System.out.println("test: " + config.channel());

Type-Safe Placeholders/Templating with ValueSerialiser integration

My idea is about utilizing method arguments as ValueSerializer parameters. With them, we can pass some context to the serializer when we are getting the value. Ill give an example:
Lets assume that we have a config with Component message that has sender placeholder:
public interface TestConfig { Component message(@Argument("sender") Component sender); }
And in value serializer, we pass arguments in the map where key is arg name/string defined in @argument, and Value is argument value.
public Component deserialise(FlexibleType value, Map<String, Object> args) throws BadValueException { List<Template> templates = args.entrySet().stream() .map(entry -> Template.of(entry.getKey(), (Component) entry.getValue()) ).toList(); return miniMessage.parse(value.getString(), templates); }
I think this addition would make code much cleaner in some cases, especially if you have placeholders in your message.
But, to be honest, im not sure if its the best solution.

Equality of Generated Config Implementations

config.equals(config) returns false because of the way proxies generated with java.lang.reflect.Proxy work. This can be easily corrected.

As a pre-requisite for sub-sections in collections, equality should be well-defined.

Moreover, this is a big enough bug that I would consider back-porting it to 1.1.0.

Please Read: Requiring default values for 2.0 + related changes

Requiring Default Values
On our existing trajectory, DazzleConf 2.0 will be taking a more expansive role with respect to providing default values. We may therefore require the existence of default values when a configuration is defined, e.g. via @DefaultBoolean, @DefaultInteger, @DefaultString and related annotations.

Requiring default values would invalidate the "defaults from a resource file" approach documented in The two ways of using DazzleConf. However, that approach seems to be much rarer (if it exists at all) compared to the mainstream, common style.

Please comment here and describe your use-case if you use DazzleConf 1.x without defaults annotations. We would like to support all library usages when developing DazzleConf 2.x. However, we need your feedback if you use this

Related Plans
Sometimes, software may want to supply a different default value depending on whether a whole fresh configuration is generated versus an individual option was missing due to an upgrade. For example, I might introduce an option whose default setting changes behavior in the new version, but wish to keep backwards compatibility for existing configurations. DazzleConf should address this use-case by defining a default value as well as an "if missing" value. For example, @DefaultString("default for fresh configuration", ifMissing = "default for when the option itself is missing").

Migration should also be supported for configurations, with existing values being taken from other configuration options, sections, and even separate files and sub-sections of other files. We further need to account for the dynamic case of multiple new options inheriting from multiple previous options in an interacting fashion, i.e. a M:N relationship between old and new options. Such feature will require caller hooks to handle the specifics of migration.

Feature: Add some sort of reload helper

Simple - add a "reload helper" object that allows you to return a config value, and also be reloaded. When reloaded the config value returned will change. I may propose an object in a future PR.

Option: Allow default methods to be used to provide default values for the configuration

Currently, the @defaultOption annotation is sloppy at best for providing non-primitive default objects for configuration values. I propose the use (or at least the option to use) default methods to return objects as default values. This is supplemented by the fact that customized serializers already exist in DazzleConf and more importantly already support deserialization of objects. The only issue being the use of default methods as helper methods in configuration classes, for which i propose an annotation that would allow a default method to be ignored, or a boolean that controls configuration reading accepting default methods as default values.

Type-safe configuration migration

We should add easy-to-use migrations to DazzleConf. These allow users to migrate their configuration options from older values as new settings are added.

In particular, migrating one's configuration should be type safe. For example, suppose I have an interface Config. I want to add a new version, so I copy the interface and create two files, ConfigV1 and Config, where Config stands in for the new version of the configuration. DazzleConf should then allow me to write a simple converter:

public class MyMigrator implements ConfigurationMigrator<ConfigV1, Config> {

  public Config fromPrevious(ConfigV1 previous) {
    // Perform migration
  }
}

Detecting the configuration version could be accomplished in a few ways. We could simply attempt to read the configuration according to ConfigV1's definition, and if that fails, move on to the new version, Config. We could perform a differential calculation on the new definition versus the old definition, then use the unique aspects of each version for its detection, starting with newer versions as a fast-path. Or, less appealingly, developers might supply a configuration version as part of the config definition which is supposed to remain untouched by the end user.

This whole framework could even allow us to import from other configuration frameworks. For instance, maybe you wrote an encapsulation-destroying mess of public final fields whose values are loaded via reflection by a different configuration library. You'd still need to depend on the old library for deserializing the old instance, but at least you gain the benefits of DazzleConf going forward, and can stop depending on the old library eventually once end users have migrated.

Handle invalid config values individually with cleanup callback

If the format syntax is invalid, there's nothing we can do. However, if an individual config value is valid, developers might want to handle it in different ways.

The main behavior of DazzleConf is to fail-fast upon reaching the first invalid value. However, it should also be possible to substitute default values and log such substitutions. Moreover, we might benefit from collecting each invalid value, and including them in an exception message or logging about them all at once, so that users don't have to repeatedly reload their configuration and recheck for errors.

We should add an InvalidValueHandler which is allowed to create state per config load, with a cleanup callback after the configuration is finished loading. The default handler should be to fail but to collect all the invalid values first, with a reasonable limit on the number of invalid values to inform the user about in a single error message.

Null collection elements cause NPEs to be thrown

FlexibleType's getCollection, getList, and getSet methods do not handle the edge case where the input collection has a null element.

Per testing, it is possible to specify null collection elements in Yaml, implicitly and literally. Other configuration formats likely have the same "feature."

It is possible, in rare cases, for API users to obtain the null collection element. Moreover, whatever fix settled on will no longer throw NullPointerException. Fixing this bug will therefore cause a minor behavioral change, so it is scheduled for 1.3.0.

Feature: Implement nested map deserialization to configuration calls as a native exposed functionality

Currently, nested modular configuration (considering classes not present at compile-time for an application's primary configuration) is difficult to implement. Exposing a functionality to deserialize maps provided using DazzleConf's ValueSerializer and FlexibleType would allow for enhanced modularity as subclass configurations would be provided the ability to insert new features to a configuration's deserialization by simply providing an instance of their configuration's class object and letting the library handle the heavy lifting.

Support sub-sections in collections

The objective is to support something like this without extra configuration or having to write any ValueSerialisers:

public interface MyConfig {

  Map<String, @SubSection MySubSection> sections();

  interface MySubSection {
  }
}

This takes advantage of the TYPE_USE capability of annotations introduced in Java 8.

Turn all serialization to use the value serializer model

We should take all the default serialization features of DazzleConf and turn them into value serializers. That way, developers can swap out which serialization mechanisms are used. The default serializers, however, should follow existing DazzleConf practices and require no manual configuration.

This will require us to allow some serializers to match multiple kinds of types. For example, serializing enums must check whether the type is an enum.

ConfigurationOptions#equals ignores createSingleElementCollections

The equals implementation should check createSingleElementCollections, but presently it does not.

Fix timeline:

  • This bug will be fixed in the next release.

Risks and assumptions:

  • Anyone relying on bugged behaviour of .equals. No one ought to be relying on such a bug, as it is not part of the method's contract.

Add @DefaultObject to return complex default objects

The current @Defaultxxx annotations are limited to the "literal objects" - literal primitive values, literal lists of such values, and values parseable from strings.

This works for simple values of inbuilt types, as well as collections thereof. It works for custom objects where the custom object can be represented as a string, and also collections of these.

However, there is a limitation when using a custom object, which spans configuration options and therefore cannot be a simple string, is specified.

Consider this example, where each KeyModel is represented by a configuration section:
https://hasteb.in/unebetod.js
How would the default annotation be specified? @defaultmap fails because, barring much pain and format-specific coupling, the values of the default-specified map cannot be written as configuration sections.

To solve this, @defaultobject, which specifies the fully qualified name of a static method, can be used. The method @defaultobject points to would be invoked, and its result used to provide the default object.

TOML Support

Implement Toml support using tomlj as well as a custom toml writer.

It should not be too difficult to create a toml writer.

See #2

Map Key Requirement for Serializer (Hocon)

Hello.

This is similar to an issue discussed some time ago for YAML, except now it is for Hocon.

java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
        at libs.space.arim.dazzleconf.ext.hocon.HoconConfigurationFactoryImpl.convertMapToHocon(HoconConfigurationFactoryImpl.java:77) ~[system.jar:?]
        at libs.space.arim.dazzleconf.ext.hocon.HoconConfigurationFactoryImpl.convertValueToHocon(HoconConfigurationFactoryImpl.java:88) ~[system.jar:?]
        at libs.space.arim.dazzleconf.ext.hocon.HoconConfigurationFactoryImpl.convertValueToHocon(HoconConfigurationFactoryImpl.java:92) ~[system.jar:?]
        at libs.space.arim.dazzleconf.ext.hocon.HoconConfigurationFactoryImpl.convertMapToHocon(HoconConfigurationFactoryImpl.java:77) ~[system.jar:?]
        at libs.space.arim.dazzleconf.ext.hocon.HoconConfigurationFactoryImpl.writeMap(HoconConfigurationFactoryImpl.java:65) ~[system.jar:?]
        at libs.space.arim.dazzleconf.factory.HumanReadableConfigurationFactory.bufferedWriteMap(HumanReadableConfigurationFactory.java:125) ~[system.jar:?]
        at libs.space.arim.dazzleconf.factory.HumanReadableConfigurationFactory.writeMap(HumanReadableConfigurationFactory.java:114) ~[system.jar:?]
        at libs.space.arim.dazzleconf.factory.ConfigurationFormatFactory.write(ConfigurationFormatFactory.java:184) ~[system.jar:?]
        at libs.space.arim.dazzleconf.helper.ConfigurationHelper.reloadConfigData(ConfigurationHelper.java:102) ~[system.jar:?]

Can this be improved? For better experience. ๐Ÿ˜Š

Consider deprecating @ConfSerialisers?

This way to declare serialisers forces you to use singletones. Those ones are not the best practice in the object oriented programming.
Since there's already a better solution, using ConfigurationOptions, i suggest deprecating @ConfSerialisers annotation, or, at least, showing another way in documentation.

Slightly improve null return values

There are only a couple API methods which return null. These are methods highly unlikely to be used by most API users, so I do not expect any confusion arising from them.

However, it would be better for overall API consistency and clarity to simply declare all return values non-null, rather than have these few exceptions.

  • ConfigurationOptions.getSorter
  • ValueSerialiserMap.getSerialiser

Save Comments Directly Using SnakeYaml

Snakeyaml 1.28 adds a way to write comments directly:

https://bitbucket.org/asomov/snakeyaml/wiki/Changes

Presently, it is already possible to write comments using SnakeYamlConfigurationFactory, but this is done through an alternative yaml writer, specifically CommentedWriter.

Some considerations:

  • Incrementing the version of the snakeyaml dependency is not itself a breaking change. However, existing client code which relies on an older version of snakeyaml cannot be broken without a major release. With this in mind, it is not possible for this library to leverage new snakeyaml features, without those features being enabled explicitly.
  • Once snakeyaml has comment support, DazzleConf's alternative yaml writer may be considered outmoded. Should useCommentingWriter therefore be deprecated, with snakeyaml-written comments recommended as a replacement?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.