Skip to content

Latest commit

 

History

History
402 lines (293 loc) · 13.6 KB

README.adoc

File metadata and controls

402 lines (293 loc) · 13.6 KB

Apron

Apron

Apron - Advanced Properties

Read and write Java .properties files in a more sane manner.

What is Apron

Apron is a small library for reading and writing Java .properties files. The main goal of this library is to be compatible with the java.util.Properties class. Not API-wise (the API is quite different), but being able to read every Java .properties file and getting exactly the same key-value pairs as java.util.Properties does.

However Apron maintains the order of the entries in the properties files and also the comments, blank lines and whitespace before keys and around separators.

This allows writing .properties files back that do not differ from the original ones.

Since version 2.0.0 Apron provides the ability to reformat and reorder the content of .properties files according to different constraints. Refer to Reformatting and Reordering for a more detailled description.

Apron was mainly written to be used in the Kilt toolset, but was intended from the start to be a general purpose library.

What can Apron be used for

Some examples for usage scenarios of Apron are:

  • Using .properties files as config files for an application that may be manually edited by a user as well as modified by the application itself (e.g. via a configuration dialog). The manual modifications (like the order of entries, as well as comments, empty lines and even the formatting of entries) will remain.

  • Exporting and importing Java i18n resource bundles for translation (like Kilt does).

  • Reordering multiple .properties files to contain their entries in the same order.

  • Reformatting .properties files to conform to a specific format.

Prerequisites

Apron has no runtime dependencies on other libraries.

Apron can be used with Java 8 or higher.

Installation

To use Apron in a maven based project use the following maven coordinates:

    <dependency>
      <groupId>de.poiu.apron</groupId>
      <artifactId>apron</artifactId>
      <version>2.1.1</version>
    </dependency>

Otherwise download the jar-file of Apron from the Download page and put it into the classpath of your application.

Usage

The main important class in Apron is de.poiu.apron.PropertyFile. It provides methods to create a new instance by reading a .properties file from File or InputStream as well as methods for populating an instance programmatically.

The main difference to the usual java.util.Properties is that this class does not implement the java.util.Map interface and provides access to the content of the PropertyFile in two different ways:

  • as key-value pairs

  • as Entries

The key-value pairs are the actual interesting content of the .properties files and are the same as when read via java.util.Properties. However, since PropertyFile is able to retain all comments, blank lines and even the formatting of the file it stores all this information in objects of type Entry. There are two implementations of Entry:

BasicEntry

A non-key-value pair like a comment or an empty line

PropertyEntry

An actual key-value pair

The Entry objects store their content in escaped form. That means all whitespaces, linebreaks, escape characters, etc. are contained in exactly the same form as in the written .properties file.

The key-value pairs instead contain the content in unescaped form (as you would expect from java.util.Properties).

To minimize confusion the escaped values are stored as CharSequence whereas the unescaped values are stored as String.

A PropertyFile instance also allows writing its content back to disk. It provides 3 methods (each in two variants) for doing so:

overwrite

Writes the contents of the PropertyFile to a new file or overwrite an existing file.

update

Update an existing .properties file with the values in the written PropertyFile.

save

Use either the above mentioned overwrite method if the given file does not exist or the update method if the file already exists.

The most interesting method is the update method, since this differentiates PropertyFile from java.util.Properties. It actually only updates the values of the key-value pairs without touching any other formatting. Blank lines, comments, whitespaces and even escaping and special formatting of the keys are not altered at all. Also the order of the key-value pairs remains the same.

The behaviour when writing a PropertyFile can be altered by providing it an optional ApronOptions object.

This is an example for a typical usage of PropertyFile as a replacement for java.util.Properties:

// Read the file "application.properties" into a PropertyFile
final PropertyFile propertyFile= PropertyFile.from(
  new File("application.properties"));

// Read the value of the key "someKey"
final String someValue= propertyFile.get("someKey");

// Set the value of "someKey" to a new value
propertyFile.set("someKey", "aNewValue");

// Write the PropertyFile back to file by only updating the modified values
propertyFile.update(new File("application.properties"));

This is an example for a more advanced usage of PropertyFile that allows acessing comment lines and explicitly formatted (escaped) entries:

// Read all Entries (that means BasicEntries as well as PropertyEntries)
final List<Entry> entries= propertyFile.getAllEntries();

// Add a comment line to this PropertyFile
propertyFile.appendEntry(new BasicEntry("# A new key-value pair follows"));

// Add a new key-value pair to this PropertyFile
// Be aware that by using appendEntry() it could be possible to insert
// duplicate keys into this PropertyFile. The behaviour is then undefined.
// It is the responsibility of the user of PropertyFile to avoid this.
// PropertyEntries contain their content in _escaped_ form. Therefore the
// Backslashes and newline character are not really part of the key and value
propertyFile.appendEntry(new PropertyEntry("a new \\\nkey", "a new \\\nvalue"));

// key-value pairs are _unescaped_. Therefore the following method call
// will return the string "a new value"
final String myNewValue= propertyFile.get("a new key");

// Specify an ApronOptions object that writes with ISO-8859-1 encoding
// instead of the default UTF-8.
final ApronOptions apronOptions= ApronOptions.create()
  .with(java.nio.charset.StandardCharsets.ISO_8859_1);

// Write the PropertyFile back to file by only updating the modified values
propertyFile.update(new File("application.properties"), apronOptions);

See the Javadoc API for more details.

Reformatting and Reordering

Since version 2.0.0 Apron provides a de.poiu.apron.reformatting.Reformatter class that allows reformatting and reordering the content of .properties files.

The specific behaviour when reformatting and reordering can be specified via a de.poiu.apron.reformatting.ReformatOptions object.

For convenience the de.poiu.apron.PropertyFile class provides some methods to reformat or reorder the entries in that PropertyFile.

Reformatting

When reformatting a format string can be given to specify how to format leading whitespace, separators and line endings. The default format string is <key> = <value>\n for

  • no leading whitespace

  • an equals sign surrounded by a single whitespace on each side as separator

  • a \n (line feed) character as new line character

By default the keys and values of the reformatted files are not modified. That means any special formatting (like insignificant whitespace, newlines and escape characters) remain after reformatting.

This can be changed via the reformatKeyAndValue option in which case these will be modified as well.

This is an example for reformatting a PropertyFile:

// Create the ReformatOptions to use to read and write with UTF-8 (which is the default anyway),
// reformat via a custom format string and also reformat the keys and values.
final ReformatOptions reformatOptions= ReformatOptions.create()
	.with(UTF_8)
	.withFormat("<key>: <value>\r\n")
	.withReformatKeyAndValue(true)
	;

// Create a Reformatter with the specified ReformatOptions
final Reformatter reformatter= new Reformatter(reformatOptions);

// Reformat a single .properties file according to the specified ReformatOptions
reformatter.reformat(new File("myproperties.properties"));

Reordering

Reordering the content of .properties files can be done either by alphabetically sorting the keys of the key-value pairs or by referring to a template file in which case the keys are ordered in the same order as in the template file.

Apron allows specifying how to handle non-property lines (comments and empty lines) when reordering. It is possible to move them along with the key-value pair that follows them or the key-value pair that precedes them or be just left at the same position as they are.

This is an example for reordering a PropertyFile:

// Create the ReformatOptions to use that does not reorder empty lines and comments
final ReformatOptions reorderOptions= ReformatOptions.create()
  .with(AttachCommentsTo.ORIG_LINE)
  ;

// Create a Reformatter with the specified ReformatOptions
final Reformatter reformatter= new Reformatter(reorderOptions);

// Reorder a single .properties file alphabetically according to the specified ReformatOptions
reformatter.reorderByKey(new File("myproperties.properties"));

// Reorder a single .properties file according to the order in another .properties file.
// This time we want to reorder comments and empty lines along with the key-value pair that
// follows them. This is possible by specifying a ReformatOptions object when calling the
// corresponding reorder method.
reformatter.reorderByTemplate(
  new File("template.properties"),
  new File("someOther.properties"),
  reorderOptions.with(AttachCommentsTo.NEXT_PROPERTY)
);

java.util.Properties wrapper

Since version 2.1.0 Apron provides a de.poiu.apron.java.util.Properties class as a wrapper to be used as a drop-in replacement where a java.util.Properties object is required.

This wrapper derives from java.util.Properties, but uses an Apron PropertyFile as the actual implementation.

Example

To use it create it either via

de.poiu.apron.PropertyFile propertyFile= …
de.poiu.apron.java.util.Properties properties=
  new de.poiu.apron.java.util.Properties(propertyFile);

or via

de.poiu.apron.PropertyFile propertyFile= …
de.poiu.apron.java.util.Properties properties= propertyFile.asProperties();

All access via the properties object will then access to the propertyFile object. Both objects can be used interchangebly to access the actual contents.

Differences to java.util.Properties

The wrapper tries to fulfil the java.util.Properties API as good as possible. However there are a few differences:

  • java.util.Properties is derived from Hashtable and therefore non-String keys and values can be stored in it (although that is highly discouraged). As Aprons PropertyFile is not derived from Hashtable it doesn’t share this flaw. Therefore trying to use any other objects than Strings as keys or values will fail.

  • Aprons PropertyFile only supports key-value-based .properties files. As java.util.Properties also provides methods to read and write to XML files and those formats are not supported by Apron, the corresponding methods will always throw an UnsupportedOperationException.

  • java.util.Properties being derived from Hashtable is thread-safe. However Aprons PropertyFile is not thread-safe and therefore this wrapper is also not thread-safe.

Logging

There are a few cases this library issues some logging statements (when closing a writer didn’t succeed and if an invalid unicode sequence was found that will be left as is). Those few logging statements don’t justify a dependency on a logging framework. Therefore we just use java.util.logging for that purpose.

When using Apron in an application that uses another logging framework please use those logging frameworks ability to bridge java.util.logging to their actual implementation.

For log4j2 this can be done by including the log4j2-jul and log4j2-api jar (and some implemention, e.g. log4j2-core) and setting the system property java.util.logging.manager to org.apache.logging.log4j.jul.LogManager. See https://logging.apache.org/log4j/2.x/log4j-jul/index.html for more information.

For slf4j this can be done by including the jul-to-slf4j jar (and some implementation, e.g. logback) and programmatically calling

SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

or setting the handler in the logging.properties:

handlers = org.slf4j.bridge.SLF4JBridgeHandler

License

Apron is licensed under the terms of the Apache license 2.0.