thoredge
thoredge

Reputation: 12601

Dotted strings to properties

I've got a hash of strings similar to this:

Map map = ['a.b.c': 'Hi']

... that I need to use in gradle to expand an expression like this:

This is a greeting: ${a.b.c}

If I use the gradle copy task with expand I will get an error message 'No such property: a'.

Is there any way to get gradle/groovy to convert that map into the properties I need to resolve?

Upvotes: 3

Views: 1089

Answers (3)

Peter Niederwieser
Peter Niederwieser

Reputation: 123920

I'm not exactly sure what your needs are, but if you just need to replace property-like tokens and don't need the full power of Groovy templates, the filter() method in combination with Ant's ReplaceTokens class is a safer (and faster) bet than expand(). See Filtering files in the Gradle User Guide.

Upvotes: 0

OverZealous
OverZealous

Reputation: 39570

I couldn't find a built-in answer, but it here is a complete, self-contained method that can be used as a meta method on Map to do what you want:

Map map = ['a.b.c': 'Hi', 'a.b.d': 'Yo', 'f.g.h': 'Howdy']

Map.metaClass.expandKeys = { separator = '.' ->
    def mergeMaps = { a, b ->
        b.each{ k, v ->
            if(a[k] && (v instanceof Map)) {
                mergeMaps(a[k], v)
            } else {
                a[k] = v
            }
        }
        a
    }
    delegate.inject([:]){ result, k, v ->
        mergeMaps(result, k.tokenize(separator).reverse().inject(v){last, subkey -> [(subkey):last] })
    }
}

assert map.expandKeys() == [a:[b:[c:"Hi", d:"Yo"]], f:[g:[h:"Howdy"]]]

It also allows for different separators than ., just pass the separator into the expandKeys method

If you want to use it like a normal function, then you can do this instead:

Map map = ['a.b.c': 'Hi', 'a.b.d': 'Yo', 'f.g.h': 'Howdy']

def expandKeys = { Map input, separator = '.' ->
    def mergeMaps = { a, b ->
        b.each{ k, v ->
            if(a[k] && (v instanceof Map)) {
                mergeMaps(a[k], v)
            } else {
                a[k] = v
            }
        }
        a
    }
    input.inject([:]){ result, k, v ->
        mergeMaps(result, k.tokenize(separator).reverse().inject(v){last, subkey -> [(subkey):last] })
    }
}

assert expandKeys(map) == [a:[b:[c:"Hi", d:"Yo"]], f:[g:[h:"Howdy"]]]

The main trick, besides merging the maps, is to split then reverse each key. Then the final hierarchy can be built up backwards. Also, there may be a better way to handle the merge, because I don't like the hanging a at the end.

Upvotes: 2

Dónal
Dónal

Reputation: 187537

I don't know anything about Gradle but maybe this will help....

If you have a Map

Map map = ['a.b.c': 'Hi']

Then you can't retrieve the value 'Hi' using

map.a.b.c

Instead, you must use:

map.'a.b.c'

or

map['a.b.c']

Upvotes: 1

Related Questions