Using ReplaceTokens providing tokens from a file

Hello all, I’m trying to do a token replacement in a copy task but I’m failing at something basic

https://docs.gradle.org/current/userguide/working_with_files.html
https://docs.gradle.org/current/userguide/working_with_files.html#sec:filtering_files

I have my files that are like a template and I want to say that for example all my tokens will come from a file.
Is this possible?

It would be something like this:

import org.apache.tools.ant.filters.ReplaceTokens

tasks.register('demoPlaceholders', Copy)  {
    from 'config-sets'
    into 'config-sets'
    include '**/*.template'
    rename '(.+).template', '$1'
    filter(ReplaceTokens, tokens: "placeholders.txt")
    filteringCharset = 'UTF-8'
}

This seems to not work but my idea is that I expect my tokens to come from a file because I have over 400 tokens
in the ant replace this would be the same as using the replacefilterfile
https://ant.apache.org/manual/Tasks/replace.html

I also tried to create my own filter type but was a massive failure so I think I may be missing something very basic.
any ideas how I could do this? my google skills did not let me find an answer

I managed to make something myself but I honestly don’t like it that much, there must be a better way:

ext {
    placeholderFile = "dummy/placeholders.txt"
    filterTokens = getFilterTokens()
}

Map<String, String> getFilterTokens () {
    Map<String, String> tokens = new HashMap<>()
    new File(project.ext.placeholderFile.toString()).eachLine {
        if (it.contains("@")) {
            String[] split = it.split("=")
            String key = split[0].replaceAll("@","")
            String value = split[1]
            tokens.put(key, value)
        }
    }
    return tokens
}

import org.apache.tools.ant.filters.ReplaceTokens

tasks.register('demoPlaceholders', Copy)  {
    from 'config-sets'
    into 'config-sets'
    include '**/*.template'
    rename '(.+).template', '$1'
    filter(ReplaceTokens, tokens:project.ext.filterTokens)
    filteringCharset = 'UTF-8'
}

Actually in 98% of the cases when you use ext you are doing something wrong.
At least just use local properties with def, not Gradle extra properties.
But even then, you are doing this calculation for every Gradle invocation, whether that task is executed or not.
If you make your tokens file a properties file, you can easily do something like

tasks.register('demoPlaceholders', Copy)  {
    from 'config-sets'
    into 'config-sets'
    include '**/*.template'
    rename '(.+).template', '$1'
    def myTokens = new Properties()
    file("my.properties").withInputStream{
        myTokens.load(it);   
    }
    inputs.properties(myTokens)
    filter(ReplaceTokens, tokens: myTokens)
    filteringCharset = 'UTF-8'
}