Skip to content

Commit

Permalink
Introduce keyTransformer property for PropertiesFileTransformer
Browse files Browse the repository at this point in the history
The new keyTransformer property can be used to transform keys in
properties files, e.g. because they contain class names about to be
relocated. Its value can be set to a closure that receives the original
key and returns the key name to be used in the resulting properties
file.
  • Loading branch information
marcphilipp committed Sep 11, 2016
1 parent 60355cb commit 4fcfddb
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/docs/asciidoc/90-changes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

* Don't resolve dependency configurations during config phase (https://github.com/johnrengelman/shadow/issues/129[Issue #128])
* Build plugin with Gradle 2.14
* https://github.com/marcphilipp[Marc Philipp] - Add `keyTransformer` property to `PropertiesFileTransformer`

[discrete]
=== v1.2.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement
import org.codehaus.plexus.util.IOUtil

import static groovy.lang.Closure.IDENTITY

/**
* Resources transformer that merges Properties files.
*
Expand Down Expand Up @@ -78,7 +80,8 @@ import org.codehaus.plexus.util.IOUtil
* <li>key3 = value3</li>
* </ul>
*
* <p>There are two additional properties that can be set: <tt>paths</tt> and <tt>mappings</tt>.
* <p>There are three additional properties that can be set: <tt>paths</tt>, <tt>mappings</tt>,
* and <tt>keyTransformer</tt>.
* The first contains a list of strings or regexes that will be used to determine if
* a path should be transformed or not. The merge strategy and merge separator are
* taken from the global settings.</p>
Expand All @@ -88,6 +91,10 @@ import org.codehaus.plexus.util.IOUtil
* entries will be merged. <tt>mappings</tt> has precedence over <tt>paths</tt> if both
* are defined.</p>
*
* <p>If you need to transform keys in properties files, e.g. because they contain class
* names about to be relocated, you can set the <tt>keyTransformer</tt> property to a
* closure that receives the original key and returns the key name to be used.</p>
*
* <p>Example:</p>
* <pre>
* import org.codehaus.griffon.gradle.shadow.transformers.*
Expand All @@ -96,11 +103,15 @@ import org.codehaus.plexus.util.IOUtil
* paths = [
* 'META-INF/editors/java.beans.PropertyEditor'
* ]
* keyTransformer = { key ->
* key.replaceAll('^(orig\.package\..*)$', 'new.prefix.$1')
* }
* }
* }
* </pre>
*
* @author Andres Almiray
* @author Marc Philipp
*/
class PropertiesFileTransformer implements Transformer {
private static final String PROPERTIES_SUFFIX = '.properties'
Expand All @@ -113,6 +124,7 @@ class PropertiesFileTransformer implements Transformer {
Map<String, Map<String, String>> mappings = [:]
String mergeStrategy = 'first' // latest, append
String mergeSeparator = ','
Closure<String> keyTransformer = IDENTITY

@Override
boolean canTransformResource(FileTreeElement element) {
Expand All @@ -133,13 +145,10 @@ class PropertiesFileTransformer implements Transformer {
@Override
void transform(String path, InputStream is, List<Relocator> relocators) {
Properties props = propertiesEntries[path]
Properties incoming = loadAndTransformKeys(is)
if (props == null) {
props = new Properties()
props.load(is)
propertiesEntries[path] = props
propertiesEntries[path] = incoming
} else {
Properties incoming = new Properties()
incoming.load(is)
incoming.each { key, value ->
if (props.containsKey(key)) {
switch (mergeStrategyFor(path).toLowerCase()) {
Expand All @@ -161,6 +170,22 @@ class PropertiesFileTransformer implements Transformer {
}
}

private Properties loadAndTransformKeys(InputStream is) {
Properties props = new Properties()
props.load(is)
return transformKeys(props)
}

private Properties transformKeys(Properties properties) {
if (keyTransformer == IDENTITY)
return properties
def result = new Properties()
properties.each { key, value ->
result.put(keyTransformer.call(key), value)
}
return result
}

private String mergeStrategyFor(String path) {
if (mappings.containsKey(path)) {
return mappings.get(path).mergeStrategy ?: mergeStrategy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers

import spock.lang.Unroll

import static groovy.lang.Closure.IDENTITY

@Unroll
class PropertiesFileTransformerSpec extends TransformerSpecSupport {

Expand Down Expand Up @@ -117,6 +119,30 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport {
'foo.properties' | ['.*bar': [mergeStrategy: 'first']] | ['foo': 'foo'] | ['foo': 'bar'] || [:]
}

void appliesKeyTransformer() {
given:
def element = getFileElement(path)
Transformer transformer = new PropertiesFileTransformer()
transformer.keyTransformer = keyTransformer
transformer.mergeStrategy = 'append'

when:
if (transformer.canTransformResource(element)) {
transformer.transform(path, toInputStream(toProperties(input1)), [])
transformer.transform(path, toInputStream(toProperties(input2)), [])
}

then:
output == toMap(transformer.propertiesEntries[path])

where:
path | keyTransformer | input1 | input2 || output
'foo.properties' | IDENTITY | ['foo': 'bar'] | ['FOO': 'baz'] || ['foo': 'bar', 'FOO': 'baz']
'foo.properties' | { key -> key.toUpperCase() } | ['foo': 'bar'] | ['FOO': 'baz'] || ['FOO': 'bar,baz']
'foo.properties' | { key -> 'bar.' + key.toLowerCase() } | ['foo': 'bar'] | ['FOO': 'baz'] || ['bar.foo': 'bar,baz']
'foo.properties' | { key -> key.replaceAll('^(foo)', 'bar.$1') } | ['foo': 'bar'] | ['FOO': 'baz'] || ['bar.foo': 'bar', 'FOO': 'baz']
}

private static InputStream toInputStream(Properties props) {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
props.store(baos, '')
Expand Down

0 comments on commit 4fcfddb

Please sign in to comment.