String pattern matching - method needs improving I'm trying to build simple pattern matching in a string.

ie., Given a template of: "Hello ${1}" and an input of "Hello world", I'd like to be able to return a map of 1='world'.

This test demonstrates what I'm trying to acheive:

@Test
public void detectsTokenValues()
{
    Properties properties = formatter.getTokenValues("${1}/andThen/${2}/followedBy/${3}","start/andThen/middle/followedBy/end");
    assertThat((String) properties.get("1"), equalTo("start"));
    assertThat((String) properties.get("2"), equalTo("middle"));
    assertThat((String) properties.get("3"), equalTo("end"));
}


Here's my implementation.  It works, but I feel like it's convoluted, and I suspect there's Regex or other JDK native features I could be using to make it simpler.

// Matches ${0} - ${9999} etc.,  But not ${variblesWithWords}
private static final Pattern NUMERIC_VARIABLES = Pattern.compile("\\$\\{([0-9]+?)\\}");


Properties getTokenValues(String template, String input) {
    Properties properties = new Properties();
    Matcher matcher = NUMERIC_VARIABLES.matcher(sourcestring);
    final int variableWrapperLength = "${}".length();
    int accumulativeOffset = 0;
    while (matcher.find())
    {
        String variableName = matcher.group(1);
        String value = null;
        if (matcher.end() &gt;= template.length())
        {
            value = input.substring(matcher.start() + accumulativeOffset);
        } else {
            // Find the character after the variable in the template.
            // This serves as the end-delimeter in the input string.
            String endDelimiter = template.substring(matcher.end(),matcher.end()+1);
            int variableEndIndex = findEndDelimiterIndex(input, matcher.start() + 1,
                    endDelimiter);
            value = input.substring(matcher.start() + accumulativeOffset, variableEndIndex);

            // Adjust the accumulative offset to account for the difference
            // in lengths between the variableName (eg., ${1}) 
            // and the the actual value (eg., 'foo') 
            accumulativeOffset += value.length() - (variableWrapperLength + variableName.length());
        }

        if (value != null)
            properties.put(variableName, value);
    }
    return properties;
}

private int findEndDelimiterIndex(String input, int fromIndex,
        String endDelimiter) {

    // Find the first instance of the delimiter in the input string
    // that occurs after the start of our  matches variable.
    if (fromIndex &gt; input.length())
    {
        return Integer.MAX_VALUE;
    }
    int variableEndIndex = input.indexOf(endDelimiter,fromIndex);
    return variableEndIndex;
}


How can I improve this method?
The function getTokenValues is actually a pattern matching function. It just uses a different syntax than the pattern format of java.util.regex.

I suggest you to convert your pattern string (the one with $ signs) into a valid Pattern.

${1}/andThen/${2}/followedBy/${3}


becomes

(.+?)/andThen/(.+?)/followedBy/(.+?)


And then you can use Pattern.matcher to find the content of the groups. There is still one thing missing : the fact that the numbering of the original pattern is lost. For that, you can parse the original pattern string to extract those numbers, to create a mapping table.

When you convert your pattern string into a Pattern, be careful to escape the substrings between the $ using Pattern.quote().

I don't know if this solution would be an improvement of yours, but it's an alternative to consider.

Edit

The implementation of my solution :

Map&lt;String, String&gt; getTokenValues(String pattern, String str) {
    Map&lt;String, String&gt; res = new HashMap&lt;String, String&gt;();
    List&lt;String&gt; identifiers = new ArrayList&lt;String&gt;();

    Pattern dollarRgx = Pattern.compile("\\$\\{([0-9]+?)\\}");
    Matcher dollarMatcher = dollarRgx.matcher(pattern);
    StringBuilder convRgxBd = new StringBuilder();
    int lastEnd = 0;
    while(dollarMatcher.find()) {
        String before = pattern.substring(lastEnd, dollarMatcher.start());
        convRgxBd.append(Pattern.quote(before));
        convRgxBd.append("(.+?)");
        lastEnd = dollarMatcher.end();
        identifiers.add(dollarMatcher.group(1));
    }
    String after = pattern.substring(lastEnd, pattern.length());
    convRgxBd.append(Pattern.quote(after));

    Pattern convRgx = Pattern.compile(convRgxBd.toString());
    Matcher convMatcher = convRgx.matcher(str);
    if(convMatcher.matches()) {
        for(int i = 0; i &lt; convMatcher.groupCount(); i++) {
            String id = identifiers.get(i);
            res.put(id, convMatcher.group(i+1));
        }
    }

    return res;
}