blob: 98a4768c0921256a1b7ba9b8eaa02bf6ff0e0518 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 org.apache.sshd.common;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import org.apache.sshd.common.util.ValidateUtils;
/**
* Property definition.
*
* @param <T> The generic property type
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public interface Property<T> extends NamedResource {
static Property<String> string(String name) {
return string(name, null);
}
static Property<String> string(String name, String def) {
return new StringProperty(name, def);
}
static Property<Boolean> bool(String name) {
return new BooleanProperty(name);
}
static Property<Boolean> bool(String name, boolean def) {
return new BooleanProperty(name, def);
}
static Property<Integer> integer(String name) {
return new IntegerProperty(name);
}
static Property<Integer> integer(String name, int def) {
return new IntegerProperty(name, def);
}
// CHECKSTYLE:OFF
static Property<Long> long_(String name) {
return new LongProperty(name);
}
static Property<Long> long_(String name, long def) {
return new LongProperty(name, def);
}
static <T extends Enum<T>> Property<T> enum_(String name, Class<T> type) {
return enum_(name, type, null);
}
static <T extends Enum<T>> Property<T> enum_(String name, Class<T> type, T def) {
return new EnumProperty<>(name, type, def);
}
// CHECKSTYLE:ON
static Property<Duration> duration(String name) {
return duration(name, null);
}
static Property<Duration> duration(String name, Duration def) {
return new DurationProperty(name, def);
}
static Property<Duration> duration(String name, Duration def, Duration min) {
return new DurationProperty(name, def, min);
}
static Property<Duration> durationSec(String name) {
return durationSec(name, null);
}
static Property<Duration> durationSec(String name, Duration def) {
return new DurationInSecondsProperty(name, def);
}
static Property<Duration> durationSec(String name, Duration def, Duration min) {
return new DurationInSecondsProperty(name, def, min);
}
static Property<Charset> charset(String name) {
return charset(name, null);
}
static Property<Charset> charset(String name, Charset def) {
return new CharsetProperty(name, def);
}
static Property<Object> object(String name) {
return object(name, null);
}
static Property<Object> object(String name, Object def) {
return new ObjectProperty(name, def);
}
static <T> Property<T> validating(Property<T> prop, Consumer<? super T> validator) {
return new Validating<>(prop, validator);
}
abstract class BaseProperty<T> implements Property<T> {
private final String name;
private final Class<T> type;
private final Optional<T> defaultValue;
protected BaseProperty(String name, Class<T> type) {
this(name, type, null);
}
protected BaseProperty(String name, Class<T> type, T defaultValue) {
this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
this.type = Objects.requireNonNull(type, "Type must be provided");
this.defaultValue = Optional.ofNullable(defaultValue);
}
@Override
public String getName() {
return name;
}
@Override
public Class<T> getType() {
return type;
}
@Override
public Optional<T> getDefault() {
return defaultValue;
}
@Override
public Optional<T> get(PropertyResolver resolver) {
Object propValue = PropertyResolverUtils.resolvePropertyValue(resolver, getName());
return (propValue != null) ? Optional.of(fromStorage(propValue)) : getDefault();
}
@Override
public T getOrCustomDefault(PropertyResolver resolver, T defaultValue) {
Object propValue = PropertyResolverUtils.resolvePropertyValue(resolver, getName());
return (propValue != null) ? fromStorage(propValue) : defaultValue;
}
@Override
public void set(PropertyResolver resolver, T value) {
PropertyResolverUtils.updateProperty(resolver, getName(), toStorage(value));
}
protected Object toStorage(T value) {
return value;
}
protected abstract T fromStorage(Object value);
@Override
public String toString() {
return "Property[" + getName() + "](" + getType().getSimpleName() + "]";
}
}
class DurationProperty extends BaseProperty<Duration> {
protected final Duration min;
public DurationProperty(String name) {
this(name, null);
}
public DurationProperty(String name, Duration def) {
super(name, Duration.class, def);
min = null;
}
public DurationProperty(String name, Duration def, Duration min) {
super(name, Duration.class, atLeast(name, def, min));
this.min = min;
}
@Override
protected Object toStorage(Duration value) {
atLeast(getName(), value, min);
return (value != null) ? value.toMillis() : null;
}
@Override
protected Duration fromStorage(Object value) {
Long val = PropertyResolverUtils.toLong(value);
return (val != null) ? Duration.ofMillis(val) : null;
}
private static Long toMillis(Duration value) {
return value == null ? null : value.toMillis();
}
protected static Duration atLeast(String name, Duration value, Duration min) {
if (min != null) {
ValidateUtils.checkTrue(value != null && min.compareTo(value) <= 0,
"Property %s must be at least %d ms; actual value: %d ms", name, toMillis(min), toMillis(value));
}
return value;
}
}
class DurationInSecondsProperty extends DurationProperty {
public DurationInSecondsProperty(String name) {
this(name, null);
}
public DurationInSecondsProperty(String name, Duration def) {
super(name, def);
}
public DurationInSecondsProperty(String name, Duration def, Duration min) {
super(name, def, min);
}
@Override
protected Object toStorage(Duration value) {
atLeast(getName(), value, min);
return (value != null) ? value.getSeconds() : null;
}
@Override
protected Duration fromStorage(Object value) {
Long val = PropertyResolverUtils.toLong(value);
return val != null ? Duration.ofSeconds(val) : null;
}
}
class StringProperty extends BaseProperty<String> {
public StringProperty(String name) {
this(name, null);
}
public StringProperty(String name, String def) {
super(name, String.class, def);
}
@Override
protected String fromStorage(Object value) {
return (value != null) ? value.toString() : null;
}
}
class BooleanProperty extends BaseProperty<Boolean> {
public BooleanProperty(String name) {
this(name, null);
}
public BooleanProperty(String name, Boolean defaultValue) {
super(name, Boolean.class, defaultValue);
}
@Override
protected Boolean fromStorage(Object value) {
return PropertyResolverUtils.toBoolean(value);
}
}
class LongProperty extends BaseProperty<Long> {
public LongProperty(String name) {
this(name, null);
}
public LongProperty(String name, Long defaultValue) {
super(name, Long.class, defaultValue);
}
@Override
protected Long fromStorage(Object value) {
return PropertyResolverUtils.toLong(value);
}
}
class IntegerProperty extends BaseProperty<Integer> {
public IntegerProperty(String name) {
this(name, null);
}
public IntegerProperty(String name, Integer defaultValue) {
super(name, Integer.class, defaultValue);
}
@Override
protected Integer fromStorage(Object value) {
return PropertyResolverUtils.toInteger(value);
}
}
class CharsetProperty extends BaseProperty<Charset> {
public CharsetProperty(String name) {
this(name, null);
}
public CharsetProperty(String name, Charset defaultValue) {
super(name, Charset.class, defaultValue);
}
@Override
protected Charset fromStorage(Object value) {
return PropertyResolverUtils.toCharset(value);
}
}
class ObjectProperty extends BaseProperty<Object> {
public ObjectProperty(String name) {
this(name, null);
}
public ObjectProperty(String name, Object defaultValue) {
super(name, Object.class, defaultValue);
}
@Override
protected Object fromStorage(Object value) {
return value;
}
}
class EnumProperty<T extends Enum<T>> extends BaseProperty<T> {
protected final Collection<T> values;
public EnumProperty(String name, Class<T> type) {
this(name, type, null);
}
public EnumProperty(String name, Class<T> type, T def) {
super(name, type, def);
values = Collections.unmodifiableSet(EnumSet.allOf(type));
}
@Override
protected T fromStorage(Object value) {
Class<T> type = getType();
return PropertyResolverUtils.toEnum(type, value, false, values);
}
}
class Validating<T> implements Property<T> {
protected final Property<T> delegate;
protected final Consumer<? super T> validator;
public Validating(Property<T> delegate, Consumer<? super T> validator) {
this.delegate = delegate;
this.validator = validator;
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public Class<T> getType() {
return delegate.getType();
}
@Override
public Optional<T> getDefault() {
return delegate.getDefault();
}
@Override
public T getRequiredDefault() {
return delegate.getRequiredDefault();
}
@Override
public Optional<T> get(PropertyResolver resolver) {
Optional<T> t = delegate.get(resolver);
t.ifPresent(validator);
return t;
}
@Override
public T getOrCustomDefault(PropertyResolver resolver, T defaultValue) {
T value = delegate.getOrCustomDefault(resolver, defaultValue);
validator.accept(value);
return value;
}
@Override
public void set(PropertyResolver resolver, T value) {
validator.accept(value);
delegate.set(resolver, value);
}
@Override
public void remove(PropertyResolver resolver) {
delegate.remove(resolver);
}
}
/**
* @return Property type - <B>Note:</B> for primitive types the wrapper equivalent is returned
*/
Class<T> getType();
/**
* @return The {@link Optional} pre-defined default value
*/
Optional<T> getDefault();
default T getRequiredDefault() {
return getDefault().get();
}
/**
* @param resolver The {@link PropertyResolver} to query for the property value.
* @return The {@link Optional} result - if resolver contains a value then the resolver's value, otherwise
* the pre-defined {@link #getDefault() default}
*/
Optional<T> get(PropertyResolver resolver);
/**
* @param resolver The {@link PropertyResolver} to query for the property value.
* @return The resolved value
* @throws NoSuchElementException if resolver contains no value and no {@link #getDefault()} defined
*/
default T getRequired(PropertyResolver resolver) {
return get(resolver).get();
}
/**
* @param resolver The {@link PropertyResolver} to query for the property value.
* @return The resolver's value or {@code null} if no specific value found in the resolver - regardless of
* whether there is a default value
*/
default T getOrNull(PropertyResolver resolver) {
return getOrCustomDefault(resolver, null);
}
/**
* @param resolver The {@link PropertyResolver} to query for the property value.
* @param defaultValue The default value to return if no specific value found in resolver
* @return The resolver's value or specified default if no specific value found in the resolver -
* regardless of whether there is a default value
*/
T getOrCustomDefault(PropertyResolver resolver, T defaultValue);
/**
* @param resolver The {@link PropertyResolver} to update with the property value.
* @param value The value to set
*/
void set(PropertyResolver resolver, T value);
/**
* @param resolver The {@link PropertyResolver} to remove the property from
*/
default void remove(PropertyResolver resolver) {
PropertyResolverUtils.updateProperty(resolver, getName(), null);
}
}