2

I am trying to implement something that will replace ${pattern} in xml files in my build.gradle file:

processResources {
    eachFile { FileCopyDetails fileCopyDetails ->
        if (fileCopyDetails.name.contains("blueprint.xml")) {
            project.logger.quiet "Processing: " + fileCopyDetails.path
            logger.quiet "" + project.ext.properties.entrySet()
            filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [prop1, value, prop2, value])
        }
    }
}

tokens: appears to take a Map. Again, how does this relate to the function signature?

Convert all properties with String values into a Map for input into tokens:

def tokenMap = new LinkedHashMap()
def stringProps = project.ext.properties.entrySet().findAll { entry -> entry.getValue() instanceof String }
stringProps.each { entry -> tokenMap.put(entry.key, entry.value)}

A look at the Gradle Javadoc reveals a filter function whose signature does not seem to match the examples. Particularly, the Map<String,?> and Class<? extends FilterReader> do not match the order in the examples as far as I understand them. Could somebody map the example to the function signature so that I understand what is going on?

CopySpec filter​(Map<String,​?> properties,
            Class<? extends FilterReader> filterType)

Adds a content filter to be used during the copy. Multiple calls to filter, add additional filters to the filter chain. Each filter should implement java.io.FilterReader. Include org.apache.tools.ant.filters.* for access to all the standard Ant filters.

Filter properties may be specified using groovy map syntax.

Examples:

filter(HeadFilter, lines:25, skip:2)
filter(ReplaceTokens, tokens:[copyright:'2009', version:'2.3.1'])  

Specified by:

filter in interface ContentFilterable

Parameters: properties - map of filter properties filterType - Class of filter to add

Returns: this


Related:

How is a token replaced in a file for a Gradle build product?


Note:

What does not work is the SimpleTemplateEngine

processResources {
    filesMatching("**/features.xml") {
        // expand uses Groovy's SimpleTemplateEngine
        project.logger.quiet "Processing: " + file
        expand project.getProperties()
    }
4

1 回答 1

2

This is actually a feature of the underlying Groovy syntax. Groovy allows you to specify method parameters by name (i.e. <name>: <value>) whenever the first parameter to the method is declared as a Map. Crucially, named parameters can appear at any point in the argumemt list, even after so-called positional parameters (i.e. those declared after the Map in the method signature), and they will be put as entries in the initial Map parameter. See the section Mixing named and positional parameters in the Groovy Language Documentation for more details.

So, the Gradle filter method has signature

CopySpec filter​(Map<String,​?> properties, Class<? extends FilterReader> filterType)

The first properties parameter is of type Map, so this method can be called with named parameters. In addition, there is one more positional parameter, filterType. So, to call this method, you must specify one parameter without a name, of type Class<? extends FilterReader>, which will be used for filterType, and zero or more named parameters, which will all be put in the properties Map.

Taking one of the examples from the documentation:

filter(HeadFilter, lines:25, skip:2)

will mean that filter is called with

properties = [
  lines: 25,
  skip: 2
]
filterType = HeadFilter

Any of the following calls would be equivalent:

filter(lines:25, skip:2, HeadFilter)
filter(lines:25, HeadFilter, skip:2)
filter([lines:25, skip:2], HeadFilter)

The last call here passes both parameters positionally (you don't have to use named parameters when the first parameter is declared as a Map).

Aside Note

I'm intrigued as to why using expand doesn't work - it should!

于 2019-10-22T11:32:45.907 回答