Is there a library, which, on given two strings, provide placeholder values?

175 Views Asked by At

Given two strings

String command = "Header '{1}' has a value that ends with '{2}' (ignore case)";
String input = "Header 'some-value' has a value that ends with '123ws' (ignore case)";

I'd like to get value map.

0 -> some-value
1 -> 123ws

I referenced this answer on Java Comparing two strings with placeholder values, tweaked it little for my usage.

private static Map<Integer, Object> getUserInputMap(String command, String input) {

        System.out.println("\t" + command);
        System.out.println("\t" + input);

        command = command.replace("(", "<");
        command = command.replace(")", ">");
        input = input.replace("(", "<");
        input = input.replace(")", ">");

        Map<Integer, Object> userInputMap = new HashMap<>();

        String patternTemplate = command.replace("{0}", "(.*)");
        patternTemplate = patternTemplate.replace("{1}", "(.*)");
        patternTemplate = patternTemplate.replace("{2}", "(.*)");

        Pattern pattern = Pattern.compile(patternTemplate);
        Matcher matcher = pattern.matcher(input);
        if (matcher.matches()) {
            for (int gi = 1; gi <= matcher.groupCount(); gi++) {
                String uin =  matcher.group(gi);
                uin = uin.replace("<", "(");
                uin = uin.replace(">", ")");
                userInputMap.put(gi - 1, uin);
            }

        }
        return userInputMap;
    }

But, there could be many corner cases. My worry with my solution is that I might miss out on a corner case, and then production bug.

Is there any mature library written around this? I am checking MessageFormat/StrSubstitutor but I am not able to get any method which does what I expect.

1

There are 1 best solutions below

0
Thomas On

Getting anything other than strings out of an already formatted string isn't that easy and I won't handle that here.

You basically know the format of your placeholders, i.e. {digits} so you could split your command by that: command.split("\\{0|[1-9][0-9]*\\}" (to not allow {01} etc.).

Then iterate over the elements in the resulting array and look for exact matches in input. While doing this you want to keep track of the ending index to start searching from there and not the start of input again.

Quick and simple example (not tested):

String[] parts = command.split("\\{0|[1-9][0-9]*\\}");
int paramStart = 0;
int index = 0;
for( String part : parts ) {  
  index = input.indexOf(part, index);
  if( index < 0) {
    //problem: command part hasn't been found - you probably want to end parsing here
  } 

  //the first part should be found at index 0 so ignore that
  if( index != 0 )
    //parameter value should be between the last part and the current one
   String parameterValue = input.substring(paramStart, index);
  } 

  //the next parameter starts after the current part
  paramStart= index + part.length();
}

//there seems to be a last placeholder at the end of the command
if(paramStart < input.length() - 1) {
  //get the substring here
}

This should be able to handle most cases except those where parameters look like your command parts or where placeholders are next to each other and can't be distinguished. As an example take "{1} - {2}" and parameters "A - B" and "C - D"- The result would be "A - B - C - D" and in that case you couldn't safely determine the values of both parameters without having more information (which you don't have with just placeholders).