]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/java/src/main/java/org/rocksdb/AbstractMutableOptions.java
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / java / src / main / java / org / rocksdb / AbstractMutableOptions.java
1 // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2 package org.rocksdb;
3
4 import java.util.*;
5
6 public abstract class AbstractMutableOptions {
7
8 protected static final String KEY_VALUE_PAIR_SEPARATOR = ";";
9 protected static final char KEY_VALUE_SEPARATOR = '=';
10 static final String INT_ARRAY_INT_SEPARATOR = ":";
11
12 protected final String[] keys;
13 private final String[] values;
14
15 /**
16 * User must use builder pattern, or parser.
17 *
18 * @param keys the keys
19 * @param values the values
20 */
21 protected AbstractMutableOptions(final String[] keys, final String[] values) {
22 this.keys = keys;
23 this.values = values;
24 }
25
26 String[] getKeys() {
27 return keys;
28 }
29
30 String[] getValues() {
31 return values;
32 }
33
34 /**
35 * Returns a string representation of MutableOptions which
36 * is suitable for consumption by {@code #parse(String)}.
37 *
38 * @return String representation of MutableOptions
39 */
40 @Override
41 public String toString() {
42 final StringBuilder buffer = new StringBuilder();
43 for(int i = 0; i < keys.length; i++) {
44 buffer
45 .append(keys[i])
46 .append(KEY_VALUE_SEPARATOR)
47 .append(values[i]);
48
49 if(i + 1 < keys.length) {
50 buffer.append(KEY_VALUE_PAIR_SEPARATOR);
51 }
52 }
53 return buffer.toString();
54 }
55
56 public static abstract class AbstractMutableOptionsBuilder<
57 T extends AbstractMutableOptions,
58 U extends AbstractMutableOptionsBuilder<T, U, K>,
59 K extends MutableOptionKey> {
60
61 private final Map<K, MutableOptionValue<?>> options = new LinkedHashMap<>();
62 private final List<OptionString.Entry> unknown = new ArrayList<>();
63
64 protected abstract U self();
65
66 /**
67 * Get all of the possible keys
68 *
69 * @return A map of all keys, indexed by name.
70 */
71 protected abstract Map<String, K> allKeys();
72
73 /**
74 * Construct a sub-class instance of {@link AbstractMutableOptions}.
75 *
76 * @param keys the keys
77 * @param values the values
78 *
79 * @return an instance of the options.
80 */
81 protected abstract T build(final String[] keys, final String[] values);
82
83 public T build() {
84 final String[] keys = new String[options.size()];
85 final String[] values = new String[options.size()];
86
87 int i = 0;
88 for (final Map.Entry<K, MutableOptionValue<?>> option : options.entrySet()) {
89 keys[i] = option.getKey().name();
90 values[i] = option.getValue().asString();
91 i++;
92 }
93
94 return build(keys, values);
95 }
96
97 protected U setDouble(
98 final K key, final double value) {
99 if (key.getValueType() != MutableOptionKey.ValueType.DOUBLE) {
100 throw new IllegalArgumentException(
101 key + " does not accept a double value");
102 }
103 options.put(key, MutableOptionValue.fromDouble(value));
104 return self();
105 }
106
107 protected double getDouble(final K key)
108 throws NoSuchElementException, NumberFormatException {
109 final MutableOptionValue<?> value = options.get(key);
110 if(value == null) {
111 throw new NoSuchElementException(key.name() + " has not been set");
112 }
113 return value.asDouble();
114 }
115
116 protected U setLong(
117 final K key, final long value) {
118 if(key.getValueType() != MutableOptionKey.ValueType.LONG) {
119 throw new IllegalArgumentException(
120 key + " does not accept a long value");
121 }
122 options.put(key, MutableOptionValue.fromLong(value));
123 return self();
124 }
125
126 protected long getLong(final K key)
127 throws NoSuchElementException, NumberFormatException {
128 final MutableOptionValue<?> value = options.get(key);
129 if(value == null) {
130 throw new NoSuchElementException(key.name() + " has not been set");
131 }
132 return value.asLong();
133 }
134
135 protected U setInt(
136 final K key, final int value) {
137 if(key.getValueType() != MutableOptionKey.ValueType.INT) {
138 throw new IllegalArgumentException(
139 key + " does not accept an integer value");
140 }
141 options.put(key, MutableOptionValue.fromInt(value));
142 return self();
143 }
144
145 protected int getInt(final K key)
146 throws NoSuchElementException, NumberFormatException {
147 final MutableOptionValue<?> value = options.get(key);
148 if(value == null) {
149 throw new NoSuchElementException(key.name() + " has not been set");
150 }
151 return value.asInt();
152 }
153
154 protected U setBoolean(
155 final K key, final boolean value) {
156 if(key.getValueType() != MutableOptionKey.ValueType.BOOLEAN) {
157 throw new IllegalArgumentException(
158 key + " does not accept a boolean value");
159 }
160 options.put(key, MutableOptionValue.fromBoolean(value));
161 return self();
162 }
163
164 protected boolean getBoolean(final K key)
165 throws NoSuchElementException, NumberFormatException {
166 final MutableOptionValue<?> value = options.get(key);
167 if(value == null) {
168 throw new NoSuchElementException(key.name() + " has not been set");
169 }
170 return value.asBoolean();
171 }
172
173 protected U setIntArray(
174 final K key, final int[] value) {
175 if(key.getValueType() != MutableOptionKey.ValueType.INT_ARRAY) {
176 throw new IllegalArgumentException(
177 key + " does not accept an int array value");
178 }
179 options.put(key, MutableOptionValue.fromIntArray(value));
180 return self();
181 }
182
183 protected int[] getIntArray(final K key)
184 throws NoSuchElementException, NumberFormatException {
185 final MutableOptionValue<?> value = options.get(key);
186 if(value == null) {
187 throw new NoSuchElementException(key.name() + " has not been set");
188 }
189 return value.asIntArray();
190 }
191
192 protected <N extends Enum<N>> U setEnum(
193 final K key, final N value) {
194 if(key.getValueType() != MutableOptionKey.ValueType.ENUM) {
195 throw new IllegalArgumentException(
196 key + " does not accept a Enum value");
197 }
198 options.put(key, MutableOptionValue.fromEnum(value));
199 return self();
200 }
201
202 @SuppressWarnings("unchecked")
203 protected <N extends Enum<N>> N getEnum(final K key)
204 throws NoSuchElementException, NumberFormatException {
205 final MutableOptionValue<?> value = options.get(key);
206 if (value == null) {
207 throw new NoSuchElementException(key.name() + " has not been set");
208 }
209
210 if (!(value instanceof MutableOptionValue.MutableOptionEnumValue)) {
211 throw new NoSuchElementException(key.name() + " is not of Enum type");
212 }
213
214 return ((MutableOptionValue.MutableOptionEnumValue<N>) value).asObject();
215 }
216
217 /**
218 * Parse a string into a long value, accepting values expressed as a double (such as 9.00) which
219 * are meant to be a long, not a double
220 *
221 * @param value the string containing a value which represents a long
222 * @return the long value of the parsed string
223 */
224 private long parseAsLong(final String value) {
225 try {
226 return Long.parseLong(value);
227 } catch (NumberFormatException nfe) {
228 final double doubleValue = Double.parseDouble(value);
229 if (doubleValue != Math.round(doubleValue))
230 throw new IllegalArgumentException("Unable to parse or round " + value + " to long");
231 return Math.round(doubleValue);
232 }
233 }
234
235 /**
236 * Parse a string into an int value, accepting values expressed as a double (such as 9.00) which
237 * are meant to be an int, not a double
238 *
239 * @param value the string containing a value which represents an int
240 * @return the int value of the parsed string
241 */
242 private int parseAsInt(final String value) {
243 try {
244 return Integer.parseInt(value);
245 } catch (NumberFormatException nfe) {
246 final double doubleValue = Double.parseDouble(value);
247 if (doubleValue != Math.round(doubleValue))
248 throw new IllegalArgumentException("Unable to parse or round " + value + " to int");
249 return (int) Math.round(doubleValue);
250 }
251 }
252
253 /**
254 * Constructs a builder for mutable column family options from a hierarchical parsed options
255 * string representation. The {@link OptionString.Parser} class output has been used to create a
256 * (name,value)-list; each value may be either a simple string or a (name, value)-list in turn.
257 *
258 * @param options a list of parsed option string objects
259 * @param ignoreUnknown what to do if the key is not one of the keys we expect
260 *
261 * @return a builder with the values from the parsed input set
262 *
263 * @throws IllegalArgumentException if an option value is of the wrong type, or a key is empty
264 */
265 protected U fromParsed(final List<OptionString.Entry> options, final boolean ignoreUnknown) {
266 Objects.requireNonNull(options);
267
268 for (final OptionString.Entry option : options) {
269 try {
270 if (option.key.isEmpty()) {
271 throw new IllegalArgumentException("options string is invalid: " + option);
272 }
273 fromOptionString(option, ignoreUnknown);
274 } catch (NumberFormatException nfe) {
275 throw new IllegalArgumentException(
276 "" + option.key + "=" + option.value + " - not a valid value for its type", nfe);
277 }
278 }
279
280 return self();
281 }
282
283 /**
284 * Set a value in the builder from the supplied option string
285 *
286 * @param option the option key/value to add to this builder
287 * @param ignoreUnknown if this is not set, throw an exception when a key is not in the known
288 * set
289 * @return the same object, after adding options
290 * @throws IllegalArgumentException if the key is unkown, or a value has the wrong type/form
291 */
292 private U fromOptionString(final OptionString.Entry option, final boolean ignoreUnknown)
293 throws IllegalArgumentException {
294 Objects.requireNonNull(option.key);
295 Objects.requireNonNull(option.value);
296
297 final K key = allKeys().get(option.key);
298 if (key == null && ignoreUnknown) {
299 unknown.add(option);
300 return self();
301 } else if (key == null) {
302 throw new IllegalArgumentException("Key: " + key + " is not a known option key");
303 }
304
305 if (!option.value.isList()) {
306 throw new IllegalArgumentException(
307 "Option: " + key + " is not a simple value or list, don't know how to parse it");
308 }
309
310 // Check that simple values are the single item in the array
311 if (key.getValueType() != MutableOptionKey.ValueType.INT_ARRAY) {
312 {
313 if (option.value.list.size() != 1) {
314 throw new IllegalArgumentException(
315 "Simple value does not have exactly 1 item: " + option.value.list);
316 }
317 }
318 }
319
320 final List<String> valueStrs = option.value.list;
321 final String valueStr = valueStrs.get(0);
322
323 switch (key.getValueType()) {
324 case DOUBLE:
325 return setDouble(key, Double.parseDouble(valueStr));
326
327 case LONG:
328 return setLong(key, parseAsLong(valueStr));
329
330 case INT:
331 return setInt(key, parseAsInt(valueStr));
332
333 case BOOLEAN:
334 return setBoolean(key, Boolean.parseBoolean(valueStr));
335
336 case INT_ARRAY:
337 final int[] value = new int[valueStrs.size()];
338 for (int i = 0; i < valueStrs.size(); i++) {
339 value[i] = Integer.parseInt(valueStrs.get(i));
340 }
341 return setIntArray(key, value);
342
343 case ENUM:
344 String optionName = key.name();
345 if (optionName.equals("prepopulate_blob_cache")) {
346 final PrepopulateBlobCache prepopulateBlobCache =
347 PrepopulateBlobCache.getFromInternal(valueStr);
348 return setEnum(key, prepopulateBlobCache);
349 } else if (optionName.equals("compression")
350 || optionName.equals("blob_compression_type")) {
351 final CompressionType compressionType = CompressionType.getFromInternal(valueStr);
352 return setEnum(key, compressionType);
353 } else {
354 throw new IllegalArgumentException("Unknown enum type: " + key.name());
355 }
356
357 default:
358 throw new IllegalStateException(key + " has unknown value type: " + key.getValueType());
359 }
360 }
361
362 /**
363 *
364 * @return the list of keys encountered which were not known to the type being generated
365 */
366 public List<OptionString.Entry> getUnknown() {
367 return new ArrayList<>(unknown);
368 }
369 }
370 }