1

sI use a simple text-file like this

BMG-P   (someLongComplicatedExpression)(.*P)
BMG T   (someLongComplicatedExpression)(.*[Tt])
BMG MPA (someLongComplicatedExpression)(.*MPA)

to configure my application (Simple import with bufferedReader.readLine().split("\t")). What is bugging me is the redundance.

I am thinking about a solution like this:

%s=(someLongComplicatedExpression)
BMG-P   %s(.*P)
BMG T   %s(.*[Tt])
BMG MPA %s(.*MPA)

where I read the value of my variables (like %s), then replace their occurrences in the Strings after the import.

My questions are:

  • What alternative approaches do you know?
  • What is an easy way to implement the replacement of my variables in my code?
  • Can you point me to any frameworks that support property-files like that?
Jonas Eicher
  • 1,413
  • 12
  • 18

1 Answers1

1

I wrote this simple extension to the Java Properties class:

import java.io.Serializable;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Allows properties to contain expansions of the form ${propertyName}. This
 * class makes no attempt to detect circular references, so be careful.
 */
public class ExpandingProperties extends Properties implements PropertySource {

    private static final long serialVersionUID = 259782782423517925L;
    private final Expander expander = new Expander();

    @Override
    public String getProperty(String key) {
        return expander.expand(super.getProperty(key), this);
    }
}

class Expander implements Serializable {

    private static final long serialVersionUID = -2229337918353092460L;
    private final Pattern pattern = Pattern.compile("\\$\\{([^}]+)\\}");

    /**
     * Expands variables of the form "${variableName}" within the
     * specified string, using the property source to lookup the
     * relevant value.
     */
    public String expand(final String s, final PropertySource propertySource) {
        if (s == null) {
            return null;
        }
        final StringBuffer sb = new StringBuffer();
        final Matcher matcher = pattern.matcher(s);
        while (matcher.find()) {
            final String variableName = matcher.group(1);
            final String value = propertySource.getProperty(variableName);
            if (value == null) {
                throw new RuntimeException("No property found for: " + variableName);
            }
            matcher.appendReplacement(sb, value.replace("$", "\\$"));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}

interface PropertySource {

    String getProperty(String key);
}

Example usage:

public static void main(String[] args) {
    Properties properties = new ExpandingProperties();
    properties.put("myVar", "myLongExpression");
    properties.put("foo", "${myVar}_1");
    properties.put("bar", "${foo}_abc");

    System.out.println(properties.getProperty("bar"));
}

Prints:

myLongExpression_1_abc

As ExpandingProperties is an extension of Properties it inherits all the load...() methods for loading values from property files.

An alternative is EProperties which does a similar thing to the above code, but goes even further and allows you to nest property files etc. I found it overkill for what I needed.

ᴇʟᴇvᴀтᴇ
  • 12,285
  • 4
  • 43
  • 66
  • On first look, I like it. The lazy expanding is a nice idea. I think I can use it in my scenario. – Jonas Eicher Aug 10 '12 at 13:11
  • I don't undestand the PropertySource Interface. Don't you inherit that from Properties? – Jonas Eicher Aug 10 '12 at 13:13
  • The interface helps to make to make it testable (I can mock an interface). It also means the Expander doesn't depend on Properties and could be reused elsewhere with some other object acting as a PropertySource. – ᴇʟᴇvᴀтᴇ Aug 10 '12 at 13:17
  • Makes sense. You couldn't pass in java.util.Properties though. – Jonas Eicher Aug 10 '12 at 13:47
  • Right, you'd have to adapt it. I'm a ports-and-adapters freak. That is, I try to write code to use and depend on "ideal" interfaces and then adapt other objects as necessary so they present the required interface. In this case, `PropertySource` is the "ideal" interface for getting a single String property with a String key, so that's what `Expander` asks for. – ᴇʟᴇvᴀтᴇ Aug 10 '12 at 13:48
  • I should have you review my ItemProvider-framework (which I stole mostly from emf) then ;) It is basically java beans wrapped in almighty adapters who inherit dozens of tiny interfaces. Not all of them ideal though.. – Jonas Eicher Aug 10 '12 at 14:27
  • Hmm, I think I'll pass on that. :) – ᴇʟᴇvᴀтᴇ Aug 10 '12 at 15:50