diff options
Diffstat (limited to 'vespajlib/src/main/java/com/yahoo/text/SimpleMapParser.java')
-rw-r--r-- | vespajlib/src/main/java/com/yahoo/text/SimpleMapParser.java | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/vespajlib/src/main/java/com/yahoo/text/SimpleMapParser.java b/vespajlib/src/main/java/com/yahoo/text/SimpleMapParser.java new file mode 100644 index 00000000000..a27563ebea1 --- /dev/null +++ b/vespajlib/src/main/java/com/yahoo/text/SimpleMapParser.java @@ -0,0 +1,119 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.text; + +import java.util.HashMap; +import java.util.Map; + +/** + * <p>Superclasses of parsers of a map represented textually as + * <code>{key1:value1,"anystringkey":value2,'anystringkey2':value3 ...}</code>. + * This parser must be extended to specify how to handle the key/value pairs.</p> + * + * <p>Example: To create a Double map parser:</p> + * <pre> + * public static final class DoubleMapParser extends MapParser<Double> { + * private Map<String, Double> map; + * + * ... + * + * @Override + * protected Double handleKeyValue(String key, String value) { + * map.put(key, Double.parseDouble(value)); + * } + * + * } + * </pre> + * + * <p>Map parsers are NOT multithread safe, but are cheap to construct.</p> + * + * @author bratseth + * @since 5.1.15 + */ +public abstract class SimpleMapParser { + + private PositionedString s; + + /** + * Parses a map on the form <code>{key1:value1,key2:value2 ...}</code> + * + * @param string the textual representation of the map + */ + public void parse(String string) { + try { + this.s=new PositionedString(string); + + s.consumeSpaces(); + s.consume('{'); + while ( ! s.peek('}')) { + s.consumeSpaces(); + String key=consumeKey(); + s.consume(':'); + s.consumeSpaces(); + consumeValue(key); + s.consumeOptional(','); + s.consumeSpaces(); + } + s.consume('}'); + } + catch (IllegalArgumentException e) { + throw new IllegalArgumentException("'" + s + "' is not a legal sparse vector string",e); + } + } + + private String consumeKey() { + if (s.consumeOptional('"')) { + String key=s.consumeTo('"'); + s.consume('"'); + return key; + } + else if (s.consumeOptional('\'')) { + String key=s.consumeTo('\''); + s.consume('\''); + return key; + } + else { + int keyEnd=findEndOfKey(); + if (keyEnd<0) + throw new IllegalArgumentException("Expected a key followed by ':' " + s.at()); + return s.consumeToPosition(keyEnd); + } + } + + protected int findEndOfKey() { + for (int peekI=s.position(); peekI<s.string().length(); peekI++) { + if (s.string().charAt(peekI)==':' || s.string().charAt(peekI)==',') + return peekI; + } + return -1; + } + + protected int findEndOfValue() { + for (int peekI=s.position(); peekI<s.string().length(); peekI++) { + if (s.string().charAt(peekI)==',' || s.string().charAt(peekI)=='}') + return peekI; + } + return -1; + } + + protected void consumeValue(String key) { + // find the next comma or bracket, whichever is next + int endOfValue=findEndOfValue(); + if (endOfValue<0) { + throw new IllegalArgumentException("Expected a value followed by ',' or '}' " + s.at()); + } + try { + handleKeyValue(key, s.substring(endOfValue)); + s.setPosition(endOfValue); + } + catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Expected a legal value from position " + s.position() + " to " + endOfValue + + " but was '" + s.substring(endOfValue) + "'", e); + } + } + + /** Returns the string being parsed along with its current position */ + public PositionedString string() { return s; } + + protected abstract void handleKeyValue(String key, String value); + +} |