0

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.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
theGamblerRises
  • 686
  • 1
  • 11
  • 27
  • Could you use https://docs.oracle.com/javase/7/docs/api/java/text/MessageFormat.html#parse(java.lang.String)? It gives an Object[] array for params. – Mr R Mar 23 '21 at 09:25
  • Does `String.format()` suites your requirements? – FaltFe Mar 23 '21 at 09:25
  • 2
    @FaltFe the OP is looking for a "reverse" formatter - i.e. parse out the args. – Mr R Mar 23 '21 at 09:26
  • Java Regex / Pattern matching can do that. https://stackoverflow.com/questions/4662215/how-to-extract-a-substring-using-regex – JayC667 Mar 23 '21 at 09:27
  • @JayC667 the OP is looking for a _mature_ solution that handles corner cases. Regex aren't a good fit for that since said corner cases can be hard/impossible to handle. – Thomas Mar 23 '21 at 09:28
  • Can placeholders with the same number appear more than once? Will placeholders with non-consecutive numbers appear? Is there a character to escape the placeholder? –  Mar 23 '21 at 09:33
  • @MrR, I get parse exception for string like `Header '{1}' has a value that ends with '{2}' (ignore case)` – theGamblerRises Mar 23 '21 at 09:36
  • @theGamblerRises - first issue - they are 0 indexed. second issue single quotes must be quoted (it's all in the javadoc)... Do that it does exactly what the OP wanted (wrt to getting the values for a format string - albeit slightly modified). – Mr R Mar 23 '21 at 09:52
  • @saka1029, number will always be different and in increasing order {1} {2} {3}. Didnt understand last quest – theGamblerRises Mar 23 '21 at 11:38
  • @MrR, Will check that. There was an issue with escaping (not related to java, but business logic). Thanks. – theGamblerRises Mar 23 '21 at 11:40
  • @GhostCat, think of it like an internal wrapper where we want to use values entered by user. We give user template/command string and user gives us full statement. We need to fetch value and use those and respond back user with results. We had it in another format earlier, now UX wants everything to be `stringified`. We dont have much choice now but to do reverse engineer. – theGamblerRises Mar 23 '21 at 11:43
  • @GhostCat That ain't my job, mate. This question is closed, anyway. If someone has answer for my query, please help. I am not interested in UX lessons. – theGamblerRises Mar 26 '21 at 09:35
  • Sorry then. If you prefer to spend your time implementing a feature that most likely won't benefit your product, and thus not do anything good for your company, that ain't my problem either. – GhostCat Mar 26 '21 at 10:28

1 Answers1

2

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).

Thomas
  • 87,414
  • 12
  • 119
  • 157