bracket-properties


Improved Java Properties

August 4th, 2015 - Code Hosted on a NEW SITE: https://www.cryptoregistry.com/ Please visit

April 19 2014 - Version 1.3.5 is now available through Maven Central. Use the following coordinates in your pom.xml file:

  • groupId: asia.redact.bracket.properties
  • artifactId: bracket-properties
  • version: 1.3.5

You Tube

Introduction

It does not take much experience with the java.util.Properties class to understand it could be improved.

Bracket Properties is a re-tune of the Properties concept from the ground up, while keeping the ubiquitous .properties file format intact. It offers a number of improvements:

  1. It is not a map (does not implement the Map Interface), but rather it has a Map, so there is no ambiguity about the types of keys and values.
  2. The design is more object-oriented and hopefully clearer and more robust, with a Lexer, Parser, and so on
  3. Order of properties is maintained across serialization
  4. Comments are maintained across serialization
  5. Serialization output can be controlled and customized in user-defined ways
  6. I18n localization is simple and elegant
  7. Multi-line values are handled more easily and maintained across serialization
  8. Handles list properties
  9. Provides a solution for externalized configurations

In addition, Bracket Properties provides innovations which are novel and potentially enlightening:

  1. Provides an arguably better XML serialization format
  2. Provides methods that return a tree-like data structure, for working with properties in a tree-like manner
  3. Provides a way to work with groups of properties (properties with related key structure or roots)

Bracket Properties is compiled for Java version 1.6 (Java 6) and above. It will not work with Java versions 1.5 or below. It has only one dependency, log4j.

Cookbook

Instantiating Properties

Use the factory to get an implementation of the Properties Interface: ``` Properties.Factory.getInstance(); // return an empty properties object

    // Get properties from various input sources

    Reader reader = new FileReader("file.properties");
    Properties props = Properties.Factory.getInstance(reader);

            File file = new File("file.properties");
    Properties props = Properties.Factory.getInstance(file, Charset.forName("UTF-8");


    InputStream in = getClass().getResourceAsStream("/log4j-example.properties");
    Properties props = Properties.Factory.getInstance(in);


    URL url = new URL("http://www.somewhere.com/getProps");
    Properties props = Properties.Factory.getInstance(url);

```

More exotic use cases can be supported by parsing the properties input directly: ``` String input = "# comment\ns1=value1\ns2=value2"; PropertiesLexer lexer = new PropertiesLexer(input); lexer.lex(); List list = lexer.getList(); Properties props = new PropertiesImpl(); new PropertiesParser(list, props).parse(); Assert.assertEqual("value1", props.get("s1"));

```

Convert an existing java.util.Properties object into a Bracket Properties object: java.util.Properties props = new Properties(); props.load(somefile); Properties bracketProps = Properties.Factory.getInstance(props);

Using Properties

Bracket Properties provides what you might expect in the form of get() plus some additional support for accessing properties:

``` Properties props; // ... String value = props.get("key"); //typical invocation List comments = props.getComments("key"); // a (potentially empty) list of associated comments char separator = props.getSeparator("key"); // the separator on this key/value pair, usually "="

```

There are also some convenience conversion methods provided to get numbers out of a property value: Properties props = Properties.Factory.getInstance(); props.put("anInteger", String.valueOf(1000)); int val = props.intValue("anInteger"); long longVal = props.longValue("anInteger");

And a Date extractor as well:

Properties props = Properties.Factory.getInstance(); props.put("aDate", String.valueOf(1323568727353)); Date date = props.dateValue("aDate");

As of Revision 160, accessing lists are supported in a natural way:

Data:

```

list properties

wrapper.java.classpath.1=../lib/wrapper.jar wrapper.java.classpath.2=../lib/myapp.jar wrapper.java.classpath.3=../lib/mysql.jar wrapper.java.classpath.4=../classes wrapper.java.classpath.10=../lib/wrapper2.jar ```

Get the properties as a list:

InputStream in = getClass().getResourceAsStream("/ListResources/list.properties"); Properties props = Properties.Factory.getInstance(in); List<String> list = props.getList("wrapper.java.classpath"); StringBuffer classpath = new StringBuffer(); int i = 0; for(String s: list){ classpath.append(s); if(i<list.size()-1)classpath.append(File.pathSeparator); i++; } System.err.println(classpath.toString());

Which outputs:

../lib/wrapper.jar;../lib/myapp.jar;../lib/mysql.jar;../classes;../lib/wrapper2.jar

As of Revision 175, you can create list properties using putList():

List<String> fruit = new ArrayList<String>(); fruit.add("apple"); fruit.add("orange"); fruit.add("banana"); props.putList(fruit, "fruit");

This will create keys like

fruit.0=apple fruit.1=orange fruit.2=banana

Property Map

To get access to the underlying data structure, including comments, use the underlying properties map: LinkedHashMap<String, ValueModel> map = props.getPropertyMap();

As of version 1.3.2, ValueModel is an interface. The below applies now to BasicValueModel:

BasicValueModel is a Value Object (in the design pattern sense) and has the following structure:

```

    public class BasicValueModel implements ValueModel {

        protected final List<String> comments;
        protected final List<String> values;
        protected char separator = '=';

        // ...accessors
    }

```

It has addValue(), addComment(), and clearComments() methods in addition to the usual accessors.

Serialization

Bracket Properties takes the position that the Properties class itself should not be in the business of serialization; other classes should perform this role. An OutputAdapter is provided for this purpose:

``` OutputAdapter out = new OutputAdapter(props); Writer w = new StringWriter(); try { out.writeTo(w); } catch (IOException e) { e.printStackTrace(); }

```

This will produce useful default output including a timestamped header and end footer using BasicOutputFormat.

Besides a good default serializer, Bracket Properties provides opportunities to customize the basic .properties file output. The OutputAdapter has writeTo(File) and writeTo(Writer). writeTo(OutputStream) is not provided, as properties files by definition are always text files.

To custom format the output, an interface called OutputFormat is provided:

```

public interface OutputFormat {

    public String formatHeader();
    public String format(String key, char sep, List<String> values, List<String> comments);
    public String formatFooter();

}

```

For example,

``` MyOutputFormat format = new MyOutputFormat(); OutputAdapter out = new OutputAdapter(props); Writer w = new StringWriter(); try { out.writeTo(w, format); } catch (IOException e) { e.printStackTrace(); }

```

For the common case of java.util.Properties compatibility in US-ASCII encoding with embedded unicode escapes, AsciiOutputFormat is provided:

AsciiOutputFormat format = new AsciiOutputFormat(); OutputAdapter out = new OutputAdapter(props); File file = new File("my.properties"); try { out.writeAsciiTo(file,format); } catch (IOException e) { e.printStackTrace(); }

or just use

out.writeAsciiTo(file);

Groups of Properties

It is common to organize property keys using dot notation, such as

``` // ... a.b.c.s1=this a.b.c.s2=that a.b.c.s3=another // ...

```

An easy way of working with a group of related properties is to pull them out as a group:

Properties abcs = props.getGroup(new GroupParams("a.b.c")); // abcs has only the above three items Assert.assertEquals(3,abcs.size());

Tree-like Operations

Very often there is a tree-like structure present in the keys because it is there in the program. We can use this observation to work with properties in a more tree-like way:

InputStream in = getClass().getResourceAsStream("/log4j.properties"); Properties props = Properties.Factory.getInstance(in); Node node = props.getTree().getDescendant("log4j.appender.FILE"); if(node.hasValue()){ Assert.assertEquals("org.apache.log4j.FileAppender", node.value.getValue()); }

XML Format

The bracket XML format is a bit different then the default Sun DTD. Suppose you have a Properties file called input.properties containing:

a.b.c=value a.b.c.d=subvalue1 a.b.c.e=subvalue2

This could be serialized to XML using the following:

InputStream in = getClass().getResourceAsStream("/xml/input0.properties"); Properties props = Properties.Factory.getInstance(in); OutputAdapter outAdapter = new OutputAdapter(props); File temp; try { temp = File.createTempFile("test", "xml"); FileWriter writer = new FileWriter(temp); outAdapter.writeAsXml(writer); writer.close(); } catch (IOException e) {}

The output looks like this:

<?xml version="1.0" encoding="ISO-8859-1"?> <nproperties xmlns="http://code.google.com/p/bracket-properties"> <na> <nb> <nc> <s>=</s> <v><![CDATA[value]]></v> <nd> <s>=</s> <v><![CDATA[subvalue1]]></v> </nd> <ne> <s>=</s> <v><![CDATA[subvalue2]]></v> </ne> </nc> </nb> </na> </nproperties>

More details about this serialization can be found in the wiki under XMLFormat.

Easy I18N

To use Bracket Properties with java Resources (like a Resource Bundle) use the Factory method with the base name and Locale:

``` Properties.Factory.mode = Mode.Compatibility; Properties props = Properties.Factory.getInstance("TestLocaleProps", Locale.ITALY); Assert.assertEquals("Albero", props.get("s1")); props = Properties.Factory.getInstance("TestLocaleProps", Locale.ITALIAN); Assert.assertEquals("Albero", props.get("s1")); props = Properties.Factory.getInstance("TestLocaleProps", Locale.getDefault()); Assert.assertEquals("Tree", props.get("s1"));

```

Easy Externalization of Configurations

``` // some externalized properties in user.home String home = System.getProperty("user.home"); String adminExternalProps = home+File.separator+"administrator.properties";

  // some properties loaded from an embedded config file in the app
  String templateProps = "/template.properties";


   List<PropertiesReference> refs = new ArrayList<PropertiesReference>();
   refs.add(new PropertiesReference(ReferenceType.CLASSLOADED,templateProps));
   refs.add(new PropertiesReference(ReferenceType.EXTERNAL,adminExternalProps));


   Properties props = Properties.Factory.loadReferences(refs);

```

See ExternalizingConfigurations for the rationale.

Project Information

The project was created on Nov 7, 2011.

Labels:
Java Properties improved comments-retained Middleware Maven UTF-8 internationalization i18n