Jesse James
Jesse James

Reputation: 1223

Best Way to Extract Values from a String that can take many forms and is Loaded from a .properties file in Java

I have a ws.properties file in which there's a property called success.code that gives me the option the choose which HTTP Status success codes (2xx), aside from the default 200, I want the server to treat as "Success" as well.

The idea is like this:

  1. If I enter the value like this:

success.code=201,202, 203 (values separated by a comma)

The part of the code responsible for extracting this value should immediately understand that these are three separate codes, extract each one of them and store them in a list or set or....

This is actually pretty easy:

private Set<String[]> getSuccessCodes(HashMap<String, String> codes){
    Set<String[]> successCodes = new HashSet<>();
    String value;
    for (Map.Entry<String, String> entry : codes.entrySet()) {
        value = entry.getValue();
        if(!StringUtils.isEmpty(value))
            successCodes.add(value.split("\\s*,\\s*"));
    }
    return successCodes;
}
  1. Here's where it gets a little complicated:

I would also like to add these options as well:

success.code=[202, 220] (two values written between hooks and separated by a comma)

success.code=2**

success.code=[200, 205], 22*

So depending on the value that I enter for the success code, the program should be able to understand what to do and what to return from it.

Is this achievable?

Thanks in advance

Upvotes: 2

Views: 74

Answers (3)

Eritrean
Eritrean

Reputation: 16498

If you don't mind adding three more methods, here is one approach:

  • Split your vlaues using "\\s*,\\s*(?![^\\[]*\\])" to split at each comma unless in brackets
  • Itereate/ stream over the resulting array and map each element checking if it contains [ or *
  • If it contains [ generate an IntStream.rangeClosed(x,y) using the values in [x,y] and map them to their string values
  • If it contains * generate an IntStream.rangeClosed(x00,x99) by replacing * with 0 and 9 and map them to their string values

Something like:

private static Set<String[]> getSuccessCodes(HashMap<String, String> codes){
    Set<String[]> successCodes = new HashSet<>();
    String value;
    for (Map.Entry<String, String> entry : codes.entrySet()) {
        value = entry.getValue();
        if(!StringUtils.isEmpty(value))
            successCodes.add(parse(value));
    }
    return successCodes;
}
public static String[] parse(String val) {
    String[] parts = val.split("\\s*,\\s*(?![^\\[]*\\])");
    return Arrays.stream(parts)
          .map(e -> e.contains("[")? getRangeCodes(e): e.contains("*")? getWildCCodes(e): e)
          .collect(Collectors.joining(",")).split("\\s*,\\s*");
}
private static String getRangeCodes(String e) {
    e = e.substring(1, e.length()-1);
    return IntStream.rangeClosed(Integer.parseInt(e.split("\\s*,\\s*")[0]), Integer.parseInt(e.split("\\s*,\\s*")[1]))
            .mapToObj(i -> String.valueOf(i))
            .collect(Collectors.joining(","));
}

private static String getWildCCodes(String e) {
    return IntStream.rangeClosed(Integer.parseInt(e.replace('*', '0')), Integer.parseInt(e.replace('*', '9')))
            .mapToObj(i -> String.valueOf(i))
            .collect(Collectors.joining(","));
}

Tested with:

public static void main(String[] args) { 
    HashMap<String, String> codes = new HashMap<>();
    codes.put("foo", "201, 202, 203");
    codes.put("bar", "[202, 220]");
    codes.put("baz", "2**");
    codes.put("doo", "[200, 205], 22*");
    codes.put("woo", "200, 201, 202, 203,[209, 214], 29*");

    Set<String[]> set = getSuccessCodes(codes);
    set.forEach(s -> {System.out.println(Arrays.toString(s));});
}

Upvotes: 1

LowKeyEnergy
LowKeyEnergy

Reputation: 652

Instead of requiring your program to accept some elaborate grammar that you defined, why not let the framework do what it does naturally?

success.codes = 201,202,203

success.code.min = 202 success.code.max = 220

success.code.prefix = 22

Then combine these properties in your code as you see fit. This is the easiest way to achieve what you want.

Upvotes: 0

Michael Gantman
Michael Gantman

Reputation: 7808

First of all, the answer is definitely yes it is achievable. The solution would be that you will have to write your own parser or mini-interpreter that would take your String and first "compile" it - i.e. check that it is a valid input (say that number of "[" is equal to number of "]" and that at no point while parsing the number of "]" is not greater then number of "]" i.e. String "[...]][" should be declared invalid). And then it would interpret it. The more flexible you want your language to be the more complex your interpreter would be. Most likely Regular expressions won't be enough. But in general you will need to write a method with signature like this:

Set<Integer> parseHttpCodesParam(String input);

Your implementation will have several rules

  1. How to treat comma-separated integer values
  2. How to treat two comma-separated numbers within []
  3. How to treat special chars like *

etc

Upvotes: 0

Related Questions