Thursday, October 25, 2012

Simple Variable Substitution in Java String

When I wrote about how to improve the Java Properties class using Props, I've shown a feature where you can use variable substition such as mypath=${user.home} in your config file. The implementation underneath it uses the Apache Common Lang library with org.apache.commons.lang.text.StrSubstitutor. There is nothing wrong with this, but I was curious how bad would it be to remove such dependency, so the Props can be more standalone.

Here is a quick implementation in Groovy, but you should able to translate to Java easily.

// String variable substitutions
def parseVariableNames(String text) {
    def names = []
    def pos = 0, max = text.length()
    while (pos < max) {
        pos = text.indexOf('${', pos)
        if (pos == -1)
            break
        def end = text.indexOf('}', pos + 2)
        if (end == -1)
            break
        def name = text.substring(pos + 2, end)
        names.add(name)
        pos = end + 1
    }
    return names
}
def replaceVariable(String key, String value, String text) {
    //println "DEBUG: Replacing '${key}'' with '${value}'"
    result = text.replaceAll('\\$\\{' + key + '}', value)
    return result
}

Probably not the most efficient thing, but it should work. Let's have some tests.

// Test
def map = ["name": "Zemian", "id": "1001"]
def inputs  = [
    'Hello ${name}',
    'My id is ${id}',
    '${name} is a good programmer.',
    '${name}\'s id is ${id}.'
]

result = inputs.collect{ line ->
    def names = parseVariableNames(line)
    names.each{ key ->
        line = replaceVariable(key, map.get(key), line) 
    }
    line
}
assert result == [
    'Hello Zemian',
    'My id is 1001',
    'Zemian is a good programmer.',
    'Zemian\'s id is 1001.'
]

The output should print nothing, as it passed the test. What do you think?