org.forstdb.AbstractMutableOptions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of forstjni Show documentation
Show all versions of forstjni Show documentation
ForSt fat jar with modifications specific for Apache Flink that contains .so files for linux32 and linux64 (glibc and musl-libc), jnilib files
for Mac OSX, and a .dll for Windows x64.
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
package org.forstdb;
import java.util.*;
/**
* This class is not strictly abstract in Java language terms, so we do not declare it as such.
* The name remains {@code AbstractMutableOptions} to reflect the underlying C++ name.
* The constructor is protected, so it will always be used as a base class.
*/
public class AbstractMutableOptions {
protected static final String KEY_VALUE_PAIR_SEPARATOR = ";";
protected static final char KEY_VALUE_SEPARATOR = '=';
static final String INT_ARRAY_INT_SEPARATOR = ":";
private static final String HAS_NOT_BEEN_SET = " has not been set";
protected final String[] keys;
private final String[] values;
/**
* User must use builder pattern, or parser.
*
* @param keys the keys
* @param values the values
*/
@SuppressWarnings("PMD.ArrayIsStoredDirectly")
protected AbstractMutableOptions(final String[] keys, final String[] values) {
this.keys = keys;
this.values = values;
}
@SuppressWarnings("PMD.MethodReturnsInternalArray")
String[] getKeys() {
return keys;
}
@SuppressWarnings("PMD.MethodReturnsInternalArray")
String[] getValues() {
return values;
}
/**
* Returns a string representation of MutableOptions which
* is suitable for consumption by {@code #parse(String)}.
*
* @return String representation of MutableOptions
*/
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
for(int i = 0; i < keys.length; i++) {
buffer
.append(keys[i])
.append(KEY_VALUE_SEPARATOR)
.append(values[i]);
if(i + 1 < keys.length) {
buffer.append(KEY_VALUE_PAIR_SEPARATOR);
}
}
return buffer.toString();
}
public abstract static class AbstractMutableOptionsBuilder<
T extends AbstractMutableOptions, U extends AbstractMutableOptionsBuilder, K
extends MutableOptionKey> {
private final Map> options = new LinkedHashMap<>();
private final List unknown = new ArrayList<>();
protected abstract U self();
/**
* Get all the possible keys
*
* @return A map of all keys, indexed by name.
*/
protected abstract Map allKeys();
/**
* Construct a subclass instance of {@link AbstractMutableOptions}.
*
* @param keys the keys
* @param values the values
*
* @return an instance of the options.
*/
protected abstract T build(final String[] keys, final String[] values);
public T build() {
final String[] keys = new String[options.size()];
final String[] values = new String[options.size()];
int i = 0;
for (final Map.Entry> option : options.entrySet()) {
keys[i] = option.getKey().name();
values[i] = option.getValue().asString();
i++;
}
return build(keys, values);
}
protected U setDouble(
final K key, final double value) {
if (key.getValueType() != MutableOptionKey.ValueType.DOUBLE) {
throw new IllegalArgumentException(
key + " does not accept a double value");
}
options.put(key, MutableOptionValue.fromDouble(value));
return self();
}
protected double getDouble(final K key)
throws NoSuchElementException, NumberFormatException {
final MutableOptionValue> value = options.get(key);
if(value == null) {
throw new NoSuchElementException(key.name() + HAS_NOT_BEEN_SET);
}
return value.asDouble();
}
protected U setLong(
final K key, final long value) {
if(key.getValueType() != MutableOptionKey.ValueType.LONG) {
throw new IllegalArgumentException(
key + " does not accept a long value");
}
options.put(key, MutableOptionValue.fromLong(value));
return self();
}
protected long getLong(final K key)
throws NoSuchElementException, NumberFormatException {
final MutableOptionValue> value = options.get(key);
if(value == null) {
throw new NoSuchElementException(key.name() + HAS_NOT_BEEN_SET);
}
return value.asLong();
}
protected U setInt(
final K key, final int value) {
if(key.getValueType() != MutableOptionKey.ValueType.INT) {
throw new IllegalArgumentException(
key + " does not accept an integer value");
}
options.put(key, MutableOptionValue.fromInt(value));
return self();
}
protected int getInt(final K key)
throws NoSuchElementException, NumberFormatException {
final MutableOptionValue> value = options.get(key);
if(value == null) {
throw new NoSuchElementException(key.name() + HAS_NOT_BEEN_SET);
}
return value.asInt();
}
protected U setBoolean(
final K key, final boolean value) {
if(key.getValueType() != MutableOptionKey.ValueType.BOOLEAN) {
throw new IllegalArgumentException(
key + " does not accept a boolean value");
}
options.put(key, MutableOptionValue.fromBoolean(value));
return self();
}
protected boolean getBoolean(final K key)
throws NoSuchElementException, NumberFormatException {
final MutableOptionValue> value = options.get(key);
if(value == null) {
throw new NoSuchElementException(key.name() + HAS_NOT_BEEN_SET);
}
return value.asBoolean();
}
protected U setIntArray(
final K key, final int[] value) {
if(key.getValueType() != MutableOptionKey.ValueType.INT_ARRAY) {
throw new IllegalArgumentException(
key + " does not accept an int array value");
}
options.put(key, MutableOptionValue.fromIntArray(value));
return self();
}
protected int[] getIntArray(final K key)
throws NoSuchElementException, NumberFormatException {
final MutableOptionValue> value = options.get(key);
if(value == null) {
throw new NoSuchElementException(key.name() + HAS_NOT_BEEN_SET);
}
return value.asIntArray();
}
protected > U setEnum(
final K key, final N value) {
if(key.getValueType() != MutableOptionKey.ValueType.ENUM) {
throw new IllegalArgumentException(
key + " does not accept a Enum value");
}
options.put(key, MutableOptionValue.fromEnum(value));
return self();
}
@SuppressWarnings("unchecked")
protected > N getEnum(final K key)
throws NoSuchElementException, NumberFormatException {
final MutableOptionValue> value = options.get(key);
if (value == null) {
throw new NoSuchElementException(key.name() + HAS_NOT_BEEN_SET);
}
if (!(value instanceof MutableOptionValue.MutableOptionEnumValue)) {
throw new NoSuchElementException(key.name() + " is not of Enum type");
}
return ((MutableOptionValue.MutableOptionEnumValue) value).asObject();
}
/**
* Parse a string into a long value, accepting values expressed as a double (such as 9.00) which
* are meant to be a long, not a double
*
* @param value the string containing a value which represents a long
* @return the long value of the parsed string
*/
private long parseAsLong(final String value) {
try {
return Long.parseLong(value);
} catch (final NumberFormatException nfe) {
final double doubleValue = Double.parseDouble(value);
if (doubleValue != Math.round(doubleValue))
throw new IllegalArgumentException("Unable to parse or round " + value + " to long", nfe);
return Math.round(doubleValue);
}
}
/**
* Parse a string into an int value, accepting values expressed as a double (such as 9.00) which
* are meant to be an int, not a double
*
* @param value the string containing a value which represents an int
* @return the int value of the parsed string
*/
private int parseAsInt(final String value) {
try {
return Integer.parseInt(value);
} catch (final NumberFormatException nfe) {
final double doubleValue = Double.parseDouble(value);
if (doubleValue != Math.round(doubleValue))
throw new IllegalArgumentException("Unable to parse or round " + value + " to int", nfe);
return (int) Math.round(doubleValue);
}
}
/**
* Constructs a builder for mutable column family options from a hierarchical parsed options
* string representation. The {@link OptionString.Parser} class output has been used to create a
* (name,value)-list; each value may be either a simple string or a (name, value)-list in turn.
*
* @param options a list of parsed option string objects
* @param ignoreUnknown what to do if the key is not one of the keys we expect
*
* @return a builder with the values from the parsed input set
*
* @throws IllegalArgumentException if an option value is of the wrong type, or a key is empty
*/
protected U fromParsed(final List options, final boolean ignoreUnknown) {
Objects.requireNonNull(options);
for (final OptionString.Entry option : options) {
try {
if (option.key.isEmpty()) {
throw new IllegalArgumentException("options string is invalid: " + option);
}
fromOptionString(option, ignoreUnknown);
} catch (final NumberFormatException nfe) {
throw new IllegalArgumentException(
"" + option.key + "=" + option.value + " - not a valid value for its type", nfe);
}
}
return self();
}
/**
* Set a value in the builder from the supplied option string
*
* @param option the option key/value to add to this builder
* @param ignoreUnknown if this is not set, throw an exception when a key is not in the known
* set
* @return the same object, after adding options
* @throws IllegalArgumentException if the key is unknown, or a value has the wrong type/form
*/
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
private U fromOptionString(final OptionString.Entry option, final boolean ignoreUnknown)
throws IllegalArgumentException {
Objects.requireNonNull(option.key);
Objects.requireNonNull(option.value);
final K key = allKeys().get(option.key);
if (key == null && ignoreUnknown) {
unknown.add(option);
return self();
} else if (key == null) {
throw new IllegalArgumentException("Key: " + null + " is not a known option key");
}
if (!option.value.isList()) {
throw new IllegalArgumentException(
"Option: " + key + " is not a simple value or list, don't know how to parse it");
}
// Check that simple values are the single item in the array
if (key.getValueType() != MutableOptionKey.ValueType.INT_ARRAY) {
{
if (option.value.list.size() != 1) {
throw new IllegalArgumentException(
"Simple value does not have exactly 1 item: " + option.value.list);
}
}
}
final List valueStrs = option.value.list;
final String valueStr = valueStrs.get(0);
switch (key.getValueType()) {
case DOUBLE:
return setDouble(key, Double.parseDouble(valueStr));
case LONG:
return setLong(key, parseAsLong(valueStr));
case INT:
return setInt(key, parseAsInt(valueStr));
case BOOLEAN:
return setBoolean(key, Boolean.parseBoolean(valueStr));
case INT_ARRAY:
final int[] value = new int[valueStrs.size()];
for (int i = 0; i < valueStrs.size(); i++) {
value[i] = Integer.parseInt(valueStrs.get(i));
}
return setIntArray(key, value);
case ENUM:
final String optionName = key.name();
if ("prepopulate_blob_cache".equals(optionName)) {
final PrepopulateBlobCache prepopulateBlobCache =
PrepopulateBlobCache.getFromInternal(valueStr);
return setEnum(key, prepopulateBlobCache);
} else if ("compression".equals(optionName)
|| "blob_compression_type".equals(optionName)) {
final CompressionType compressionType = CompressionType.getFromInternal(valueStr);
return setEnum(key, compressionType);
} else {
throw new IllegalArgumentException("Unknown enum type: " + key.name());
}
default:
throw new IllegalStateException(key + " has unknown value type: " + key.getValueType());
}
}
/**
*
* @return the list of keys encountered which were not known to the type being generated
*/
public List getUnknown() {
return new ArrayList<>(unknown);
}
}
}