/*
* Copyright 2015 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.common.util;
import static java.util.Objects.requireNonNull;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import com.google.common.collect.Streams;
/**
* A set of configuration options and their respective values.
*
* @see AbstractOption
* @see AbstractOptionValue
*/
public abstract class AbstractOptions {
private final Map<AbstractOption<Object>, AbstractOptionValue<AbstractOption<Object>, Object>> valueMap;
/**
* Creates a new instance.
*
* @param <T> the type of the {@link AbstractOptionValue}
* @param valueFilter the {@link Function} to apply to the elements of the specified {@code values}
* @param values the option values
*/
@SafeVarargs
protected <T extends AbstractOptionValue<?, ?>> AbstractOptions(Function<T, T> valueFilter, T... values) {
requireNonNull(valueFilter, "valueFilter");
requireNonNull(values, "values");
valueMap = new IdentityHashMap<>();
putAll(valueFilter, Stream.of(values));
}
/**
* Creates a new instance.
*
* @param <T> the type of the {@link AbstractOptionValue}
* @param valueFilter the {@link Function} to apply to the elements of the specified {@code values}
* @param values the option values
*/
protected <T extends AbstractOptionValue<?, ?>> AbstractOptions(Function<T, T> valueFilter,
Iterable<T> values) {
requireNonNull(valueFilter, "valueFilter");
requireNonNull(values, "values");
valueMap = new IdentityHashMap<>();
putAll(valueFilter, Streams.stream(values));
}
/**
* Creates a new instance.
*
* @param <T> the type of the {@link AbstractOptionValue}
* @param valueFilter the {@link Function} to apply to the elements of the specified {@code values}
* @param baseOptions the base options to merge
* @param values the option values
*/
@SafeVarargs
protected <T extends AbstractOptionValue<?, ?>> AbstractOptions(Function<T, T> valueFilter,
AbstractOptions baseOptions, T... values) {
requireNonNull(baseOptions, "baseOptions");
requireNonNull(valueFilter, "valueFilter");
requireNonNull(values, "values");
valueMap = new IdentityHashMap<>(baseOptions.valueMap);
putAll(valueFilter, Stream.of(values));
}
/**
* Creates a new instance.
*
* @param <T> the type of the {@link AbstractOptionValue}
* @param valueFilter the {@link Function} to apply to the elements of the specified {@code values}
* @param baseOptions the base options to merge
* @param values the option values
*/
protected <T extends AbstractOptionValue<?, ?>> AbstractOptions(Function<T, T> valueFilter,
AbstractOptions baseOptions,
Iterable<T> values) {
requireNonNull(baseOptions, "baseOptions");
requireNonNull(valueFilter, "valueFilter");
requireNonNull(values, "values");
valueMap = new IdentityHashMap<>(baseOptions.valueMap);
putAll(valueFilter, Streams.stream(values));
}
/**
* Creates a new instance by merging two options.
*
* @param baseOptions the base options to merge
* @param options the additional options to merge
*/
protected AbstractOptions(AbstractOptions baseOptions, AbstractOptions options) {
requireNonNull(baseOptions, "baseOptions");
requireNonNull(options, "options");
valueMap = new IdentityHashMap<>(baseOptions.valueMap);
valueMap.putAll(options.valueMap);
}
@SuppressWarnings("unchecked")
private <T extends AbstractOptionValue<?, ?>> void putAll(Function<T, T> valueFilter, Stream<T> values) {
values.map(valueFilter)
.forEach(v -> valueMap.put((AbstractOption<Object>) v.option(),
(AbstractOptionValue<AbstractOption<Object>, Object>) v));
}
/**
* Returns the value of the specified {@code option}.
*
* @param <O> the type of the option
* @param <V> the type of the value
*/
@SuppressWarnings("unchecked")
protected final <O extends AbstractOption<V>, V> Optional<V> get0(AbstractOption<V> option) {
@SuppressWarnings("rawtypes")
AbstractOptionValue<O, V> optionValue =
(AbstractOptionValue<O, V>) (AbstractOptionValue) valueMap.get(option);
return optionValue == null ? Optional.empty() : Optional.of(optionValue.value());
}
/**
* Returns the value of the specified {@code option}.
*
* @param <O> the type of the option
* @param <V> the type of the value
* @return the value of the specified {@code option}. {@code defaultValue} if there's no such option.
*/
@SuppressWarnings("unchecked")
protected final <O extends AbstractOption<V>, V> V getOrElse0(O option, V defaultValue) {
return get0(option).orElse(defaultValue);
}
/**
* Returns the {@link Map} whose key is {@link AbstractOption} and value is {@link AbstractOptionValue}.
*
* @param <K> the type of the options
* @param <V> the type of the option values
*/
@SuppressWarnings("unchecked")
protected final <K extends AbstractOption<?>, V extends AbstractOptionValue<K, ?>> Map<K, V> asMap0() {
return Collections.unmodifiableMap((Map<? extends K, ? extends V>) valueMap);
}
@Override
public String toString() {
return toString(asMap0().values());
}
static String toString(Collection<?> values) {
return "OptionValues{" + values + '}';
}
}