tags:

views:

100

answers:

3

I have a large string, similar to this one:

BREW pot HTCPCP/1.0

Accept-Additions: #milk;3#whiskey;splash

Content-Length: 5

Content-Type: message/coffeepot

I also have an array with several additions (#whiskey, #espresso, etc). What I need to do is send an error if this large string contains an addition that is NOT in the array of available additions. For example, if the "Accept-Additions" part of the string contained "#bricks;3" an error was produced as it is not in the array.

How would I go about this in Java? I'm having trouble implementing this part, although I've coded the rest of the program (which many of you may recognise). How would I code the following problem, with emphasis on the addition not being available?

+1  A: 

You'd parse the string. Looking at it, you one set of options per line, so you can look for all the lines that start with ACCEPT-ADDITIONS. Then you have to extract the additions, which appear to be separate with semi-colons, indicating String.split(). Then iterate over the resuling array to find additions.

Or you could create a grammar, and use a tool such as ANTLR to generate your parser.

kdgregory
And a quick way to test it is to use the Arrays class to check (rather than iterating): like Arrays.binarySearch(additionsArray, parsedString) which will return <0 if the addition is not in your array.
mattandrews
Not sure how binary search helps you here. You're basically looking for the members of one set that don't appear in another. So I'd create a HashSet with the valid additions, and iterate over the values specified in the file.
kdgregory
+1  A: 

This code makes a few assumptions about the input. It does look like you could split each token up even further into #; components. Using a List for your acceptable liquids parameter would clean up the code a bit (just use liquids.contains(String s))

  static String[] liquids = {"#milk;3", "#whiskey;splash"};

  public static void parseString(String input)
  {
    // Break the String down into line-by-line.
    String[] lines = input.split("" + '\n');
    for (int line_index = 0; line_index < lines.length; line_index++)
    {
      if (lines[line_index].length() > 16)
      {
        // Assume you're delimiting by '#'
        String[] tokens = lines[line_index].split("#");
        if (tokens.length > 1)
        {
          // Start at index = 1 to kill "Accept-Additions:"
          for (int token_index = 1; token_index < tokens.length; token_index++)
          {
            boolean valid = false;
            for (int liquids_index = 0; liquids_index < liquids.length; liquids_index++)
            {
              if (liquids[liquids_index].equals("#" + tokens[token_index]))
              {
                valid = true;
                // break to save some time if liquids is very long
                break;
              }
            }
            if (!valid)
            {
              throwError("#" + tokens[token_index]);
            }
          }
        }
      }
    }
  }

  public static void throwError(String error)
  {
    System.out.println(error + " is not in the Array!");
  }
Catchwa
This is very close to what I want, although the array does not contain the amount, so the values in the array are along the lines of "#whole-milk".
EnderMB
I guess the OP wanted to be spoonfed ... Rather than this mess of nested loops, I'd start with wrapping the String in a StringReader, and that in a LineNumberReader, then use a readLine() loop. String.startsWith() is a clean way of identifying the lines you care about (although you should trim() first), and there's no good reason to check the line length. Refactor the tokenization into its own method for potential reusability, and as I noted below, probe a HashSet to find valid/invalid values.
kdgregory
A: 

Here is a possible solution using regular expressions. It extracts the 'Accept-Additions' line from the payload, then checks each key-value pair of the form #foo;bar.

final String[] VALID_ADDITIONS = { 
    "milk", "whiskey"
};

final Pattern LINE = Pattern.compile("Accept-Additions:(.+)$", Pattern.MULTILINE);
final Pattern ADDITIONS = Pattern.compile("#(.+?);([^#]+)");

void checkValidAdditions(String request) {
    Matcher lineMatcher = LINE.matcher(request);
    if (!lineMatcher.find()) {
        // no additions - do whatever is appropriate here
        throw new IllegalArgumentException("Additions line not found");
    }
    String line = lineMatcher.group(1);
    Matcher additions = ADDITIONS.matcher(line);
    while (additions.find()) {
        String key = additions.group(1);
        //String value = additions.group(2);

        boolean validKey = false;
        for (String validAddition : VALID_ADDITIONS) {
            if (key.equals(validAddition)) {
                validKey = true;
            }
        }
        if (!validKey) {
            // ...
        }
    }
}

The first regexp extracts the relevant line from the request. The second one extracts the key-value pairs. Note the following caveats:

  • This method won't pick up malformed requests properly - only invalid 'keys'.

  • If you're allowing different capitalisation (e.g. 'ACCEPT-ADDITIONS', 'accept-additions'), add the Pattern.CASE_INSENSITIVE flag, i.e. Pattern.MULTILINE & Pattern.CASE_INSENSITIVE.

harto
I've tried out your code, but I receive an IllegalStateException when I try to send the string from the client.
EnderMB
What is the exception message?
harto
I assume the first regexp wasn't matching. I've relaxed it slightly - and added a list of 'caveats'...
harto