1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.text;
/**
* <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);
}
|