ConvertibleValuesMap.java

/*
 * Copyright 2017-2020 original authors
 *
 * Licensed 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
 *
 * https://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 io.micronaut.core.convert.value;

import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.convert.ArgumentConversionContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.ConversionServiceAware;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * An implementation of {@link ConvertibleValues} backed by a map.
 *
 * @author Graeme Rocher
 * @param <V> generic value
 * @since 1.0
 */
public class ConvertibleValuesMap<V> implements ConvertibleValues<V>, ConversionServiceAware {

    protected final Map<? extends CharSequence, V> map;
    private ConversionService conversionService;

    /**
     * Constructor.
     */
    public ConvertibleValuesMap() {
        this(new LinkedHashMap<>(), ConversionService.SHARED);
    }

    /**
     * Constructor.
     * @param map map of values.
     */
    public ConvertibleValuesMap(Map<? extends CharSequence, V> map) {
        this(map, ConversionService.SHARED);
    }

    /**
     * Constructor.
     * @param map map of values.
     * @param conversionService conversionService
     */
    public ConvertibleValuesMap(Map<? extends CharSequence, V> map, ConversionService conversionService) {
        this.map = map;
        this.conversionService = conversionService;
    }

    @Nullable
    @Override
    public V getValue(CharSequence name) {
        return name != null ? map.get(name) : null;
    }

    @Override
    public boolean contains(String name) {
        return name != null && map.containsKey(name);
    }

    @Override
    public <T> Optional<T> get(CharSequence name, ArgumentConversionContext<T> conversionContext) {
        V value = map.get(name);
        if (value != null) {
            return conversionService.convert(value, conversionContext);
        }
        return Optional.empty();
    }

    @Override
    public Set<String> names() {
        return map.keySet().stream().map(CharSequence::toString).collect(Collectors.toSet());
    }

    @Override
    public Collection<V> values() {
        return Collections.unmodifiableCollection(map.values());
    }

    /**
     * An empty {@link ConvertibleValuesMap}.
     *
     * @param <V> The generic type
     * @return The empty {@link ConvertibleValuesMap}
     */
    @SuppressWarnings("unchecked")
    public static <V> ConvertibleValues<V> empty() {
        return EMPTY;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ConvertibleValuesMap<?> that = (ConvertibleValuesMap<?>) o;
        return map.equals(that.map);
    }

    @Override
    public int hashCode() {
        return Objects.hash(map);
    }

    @Override
    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    @Override
    public void forEach(BiConsumer<String, V> action) {
        map.forEach((k, v) -> action.accept(k.toString(), v));
    }
}