OptionKit.java
package space.sunqian.fs.base.option;
import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;
import space.sunqian.annotation.RetainedParam;
import space.sunqian.fs.Fs;
import space.sunqian.fs.base.string.StringKit;
import space.sunqian.fs.collect.ArrayKit;
import java.util.Arrays;
import java.util.Objects;
/**
* Utilities for option.
*
* @author sunqian
*/
public class OptionKit {
/**
* Finds and returns the first option whose key equals the specified key from the given options, or null if not
* found. This method will cast returned option to the specified type {@code O}.
*
* @param key the specified key
* @param options the given options
* @param <K> the key type
* @param <V> the value type
* @param <O> the returned option type
* @return the first option whose key equals the specified key from the given options, or null if not found
*/
public static <K, V, O extends Option<K, V>> @Nullable O findOption(
@Nonnull K key,
@Nonnull Option<?, ?> @Nonnull ... options
) {
if (ArrayKit.isEmpty(options)) {
return null;
}
for (Option<?, ?> option : options) {
if (Objects.equals(option.key(), key)) {
return Fs.as(option);
}
}
return null;
}
/**
* Finds the first option whose key equals the specified key from the given options. Returns the value of the found
* option, or null if not found. This method will cast returned value to the specified type {@code V}.
*
* @param key the specified key
* @param options the given options
* @param <V> the value type
* @return the value of the found option, or null if not found
*/
public static <V> V findValue(@Nonnull Object key, @Nonnull Option<?, ?> @Nonnull ... options) {
@Nullable Option<?, V> option = OptionKit.findOption(key, options);
return option == null ? null : option.value();
}
/**
* Returns whether the given options contain an option whose key equals the specified key.
*
* @param key the specified key
* @param options the given options
* @return {@code true} if given options contain an option whose key equals the specified key, otherwise
* {@code false}
*/
public static boolean containsKey(@Nonnull Object key, @Nonnull Option<?, ?> @Nonnull ... options) {
return OptionKit.findOption(key, options) != null;
}
/**
* Merges the additional options to the default options.
* <p>
* If the additional options array is empty, the default options array will be returned. Otherwise, a new array will
* be copied from the default options array with the new length and returned. Any additional option whose key equals
* the key of the default option will override the default option in the returned array. Any new additional option
* whose key does not exist in the default options array will be added to the returned array.
*
* @param defaultOptions the default options
* @param additionalOptions the additional options
* @param <K> the key type
* @param <V> the value type
* @return the merged options
*/
public static <K, V> @Nonnull Option<K, V> @Nonnull [] mergeOptions(
@Nonnull Option<?, ?> @Nonnull @RetainedParam [] defaultOptions,
@Nonnull Option<?, ?> @Nonnull ... additionalOptions
) {
if (ArrayKit.isEmpty(additionalOptions)) {
return Fs.as(defaultOptions);
}
int newCount = 0;
ADDITIONAL:
for (Option<?, ?> additionalOption : additionalOptions) {
for (Option<?, ?> defaultOption : defaultOptions) {
if (Objects.equals(defaultOption.key(), additionalOption.key())) {
continue ADDITIONAL;
}
}
newCount++;
}
Option<?, ?>[] result = Arrays.copyOf(defaultOptions, defaultOptions.length + newCount);
int lastIndex = result.length - 1;
ADDITIONAL:
for (Option<?, ?> additionalOption : additionalOptions) {
for (int i = 0; i < defaultOptions.length; i++) {
Option<?, ?> defaultOption = defaultOptions[i];
if (Objects.equals(defaultOption.key(), additionalOption.key())) {
result[i] = additionalOption;
continue ADDITIONAL;
}
}
// new option
result[lastIndex--] = additionalOption;
}
return Fs.as(result);
}
/**
* Merges the additional option to the default options.
* <p>
* This method always returns a new array copied from the default options array. If the key of the additional option
* equals the key of any option in the default options array, the additional option will override the default option
* in the returned array. Otherwise, the additional option will be added to the returned array.
*
* @param defaultOptions the default options
* @param additionalOption the additional option
* @param <K> the key type
* @param <V> the value type
* @return the merged options
*/
public static <K, V> @Nonnull Option<K, V> @Nonnull [] mergeOption(
@Nonnull Option<?, ?> @Nonnull [] defaultOptions,
@Nonnull Option<?, ?> additionalOption
) {
int index = ArrayKit.indexOf(
defaultOptions, (i, o) -> Objects.equals(o.key(), additionalOption.key())
);
if (index == -1) {
Option<?, ?>[] result = Arrays.copyOf(defaultOptions, defaultOptions.length + 1);
result[result.length - 1] = additionalOption;
return Fs.as(result);
}
Option<?, ?>[] result = Arrays.copyOf(defaultOptions, defaultOptions.length);
result[index] = additionalOption;
return Fs.as(result);
}
/**
* Returns {@code true} if the given options contains an option whose key equals the specified key and the value of
* the option is enabled checked by {@link StringKit#isEnabled(CharSequence)}.
*
* @param key the specified key
* @param options the given options
* @return {@code true} if the given options contains an option whose key equals the specified key and the value of
* the option is enabled checked by {@link StringKit#isEnabled(CharSequence)}
*/
public static boolean isEnabled(@Nonnull Object key, @Nonnull Option<?, ?> @Nonnull ... options) {
@Nullable Option<?, ?> option = OptionKit.findOption(key, options);
if (option == null) {
return false;
}
Object value = option.value();
if (value == null) {
return false;
}
if (value instanceof Boolean) {
return (boolean) value;
}
if (value instanceof Number) {
return ((Number) value).intValue() == 1;
}
if (value instanceof String) {
return StringKit.isEnabled((String) value);
}
return false;
}
private OptionKit() {
}
}