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    */
AbstractMutableOptions(final String[] keys, final String[] values)21   protected AbstractMutableOptions(final String[] keys, final String[] values) {
22     this.keys = keys;
23     this.values = values;
24   }
25 
getKeys()26   String[] getKeys() {
27     return keys;
28   }
29 
getValues()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
toString()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 
self()63     protected abstract U self();
64 
65     /**
66      * Get all of the possible keys
67      *
68      * @return A map of all keys, indexed by name.
69      */
allKeys()70     protected abstract Map<String, K> allKeys();
71 
72     /**
73      * Construct a sub-class instance of {@link AbstractMutableOptions}.
74      *
75      * @param keys the keys
76      * @param values the values
77      *
78      * @return an instance of the options.
79      */
build(final String[] keys, final String[] values)80     protected abstract T build(final String[] keys, final String[] values);
81 
build()82     public T build() {
83       final String keys[] = new String[options.size()];
84       final String values[] = new String[options.size()];
85 
86       int i = 0;
87       for (final Map.Entry<K, MutableOptionValue<?>> option : options.entrySet()) {
88         keys[i] = option.getKey().name();
89         values[i] = option.getValue().asString();
90         i++;
91       }
92 
93       return build(keys, values);
94     }
95 
setDouble( final K key, final double value)96     protected U setDouble(
97        final K key, final double value) {
98       if (key.getValueType() != MutableOptionKey.ValueType.DOUBLE) {
99         throw new IllegalArgumentException(
100             key + " does not accept a double value");
101       }
102       options.put(key, MutableOptionValue.fromDouble(value));
103       return self();
104     }
105 
getDouble(final K key)106     protected double getDouble(final K key)
107         throws NoSuchElementException, NumberFormatException {
108       final MutableOptionValue<?> value = options.get(key);
109       if(value == null) {
110         throw new NoSuchElementException(key.name() + " has not been set");
111       }
112       return value.asDouble();
113     }
114 
setLong( final K key, final long value)115     protected U setLong(
116         final K key, final long value) {
117       if(key.getValueType() != MutableOptionKey.ValueType.LONG) {
118         throw new IllegalArgumentException(
119             key + " does not accept a long value");
120       }
121       options.put(key, MutableOptionValue.fromLong(value));
122       return self();
123     }
124 
getLong(final K key)125     protected long getLong(final K key)
126         throws NoSuchElementException, NumberFormatException {
127       final MutableOptionValue<?> value = options.get(key);
128       if(value == null) {
129         throw new NoSuchElementException(key.name() + " has not been set");
130       }
131       return value.asLong();
132     }
133 
setInt( final K key, final int value)134     protected U setInt(
135         final K key, final int value) {
136       if(key.getValueType() != MutableOptionKey.ValueType.INT) {
137         throw new IllegalArgumentException(
138             key + " does not accept an integer value");
139       }
140       options.put(key, MutableOptionValue.fromInt(value));
141       return self();
142     }
143 
getInt(final K key)144     protected int getInt(final K key)
145         throws NoSuchElementException, NumberFormatException {
146       final MutableOptionValue<?> value = options.get(key);
147       if(value == null) {
148         throw new NoSuchElementException(key.name() + " has not been set");
149       }
150       return value.asInt();
151     }
152 
setBoolean( final K key, final boolean value)153     protected U setBoolean(
154         final K key, final boolean value) {
155       if(key.getValueType() != MutableOptionKey.ValueType.BOOLEAN) {
156         throw new IllegalArgumentException(
157             key + " does not accept a boolean value");
158       }
159       options.put(key, MutableOptionValue.fromBoolean(value));
160       return self();
161     }
162 
getBoolean(final K key)163     protected boolean getBoolean(final K key)
164         throws NoSuchElementException, NumberFormatException {
165       final MutableOptionValue<?> value = options.get(key);
166       if(value == null) {
167         throw new NoSuchElementException(key.name() + " has not been set");
168       }
169       return value.asBoolean();
170     }
171 
setIntArray( final K key, final int[] value)172     protected U setIntArray(
173         final K key, final int[] value) {
174       if(key.getValueType() != MutableOptionKey.ValueType.INT_ARRAY) {
175         throw new IllegalArgumentException(
176             key + " does not accept an int array value");
177       }
178       options.put(key, MutableOptionValue.fromIntArray(value));
179       return self();
180     }
181 
getIntArray(final K key)182     protected int[] getIntArray(final K key)
183         throws NoSuchElementException, NumberFormatException {
184       final MutableOptionValue<?> value = options.get(key);
185       if(value == null) {
186         throw new NoSuchElementException(key.name() + " has not been set");
187       }
188       return value.asIntArray();
189     }
190 
setEnum( final K key, final N value)191     protected <N extends Enum<N>> U setEnum(
192         final K key, final N value) {
193       if(key.getValueType() != MutableOptionKey.ValueType.ENUM) {
194         throw new IllegalArgumentException(
195             key + " does not accept a Enum value");
196       }
197       options.put(key, MutableOptionValue.fromEnum(value));
198       return self();
199     }
200 
201     @SuppressWarnings("unchecked")
getEnum(final K key)202     protected <N extends Enum<N>> N getEnum(final K key)
203         throws NoSuchElementException, NumberFormatException {
204       final MutableOptionValue<?> value = options.get(key);
205       if (value == null) {
206         throw new NoSuchElementException(key.name() + " has not been set");
207       }
208 
209       if (!(value instanceof MutableOptionValue.MutableOptionEnumValue)) {
210         throw new NoSuchElementException(key.name() + " is not of Enum type");
211       }
212 
213       return ((MutableOptionValue.MutableOptionEnumValue<N>) value).asObject();
214     }
215 
fromString( final String keyStr, final String valueStr)216     public U fromString(
217         final String keyStr, final String valueStr)
218         throws IllegalArgumentException {
219       Objects.requireNonNull(keyStr);
220       Objects.requireNonNull(valueStr);
221 
222       final K key = allKeys().get(keyStr);
223       switch(key.getValueType()) {
224         case DOUBLE:
225           return setDouble(key, Double.parseDouble(valueStr));
226 
227         case LONG:
228           return setLong(key, Long.parseLong(valueStr));
229 
230         case INT:
231           return setInt(key, Integer.parseInt(valueStr));
232 
233         case BOOLEAN:
234           return setBoolean(key, Boolean.parseBoolean(valueStr));
235 
236         case INT_ARRAY:
237           final String[] strInts = valueStr
238               .trim().split(INT_ARRAY_INT_SEPARATOR);
239           if(strInts == null || strInts.length == 0) {
240             throw new IllegalArgumentException(
241                 "int array value is not correctly formatted");
242           }
243 
244           final int value[] = new int[strInts.length];
245           int i = 0;
246           for(final String strInt : strInts) {
247             value[i++] = Integer.parseInt(strInt);
248           }
249           return setIntArray(key, value);
250       }
251 
252       throw new IllegalStateException(
253           key + " has unknown value type: " + key.getValueType());
254     }
255   }
256 }
257