/*
* Copyright (c) 2014-2015 the original author or 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
*
* 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 io.werval.util;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
/**
* MultiValueMap MultiValued.
*
* @param <K> Key type
* @param <V> Value type
*/
public class MultiValueMapMultiValued<K, V>
implements MultiValued<K, V>, Serializable
{
private static final long serialVersionUID = 1L;
protected static final Function<String, ? extends RuntimeException> DEFAULT_CONSTRAINT_EX_BUILDER;
static
{
DEFAULT_CONSTRAINT_EX_BUILDER = message -> new MultiValuedConstraintException( message );
}
private final Function<String, ? extends RuntimeException> constraintExBuilder;
protected final MultiValueMap<K, V> mvmap;
public MultiValueMapMultiValued()
{
this( new TreeMultiValueMap<>(), DEFAULT_CONSTRAINT_EX_BUILDER );
}
public MultiValueMapMultiValued( MultiValueMap<K, V> mvmap )
{
this( mvmap, DEFAULT_CONSTRAINT_EX_BUILDER );
}
public MultiValueMapMultiValued(
MultiValueMap<K, V> mvmap,
Function<String, ? extends RuntimeException> constraintExBuilder
)
{
this.mvmap = mvmap;
this.constraintExBuilder = constraintExBuilder;
}
@Override
public boolean isEmpty()
{
return mvmap.isEmpty() || mvmap.values().stream().allMatch( v -> v == null || v.isEmpty() );
}
@Override
public boolean has( K key )
{
return mvmap.containsKey( key ) && mvmap.get( key ) != null && !mvmap.get( key ).isEmpty();
}
@Override
public V singleValue( K key )
throws MultiValuedConstraintException
{
return singleValueOptional( key )
.orElseThrow(
() -> constraintExBuilder.apply(
format( "No or multiple %s(s) for '%s'", valueTypeDisplayName(), key )
)
);
}
@Override
public V firstValue( K key )
throws MultiValuedConstraintException
{
return firstValueOptional( key )
.orElseThrow(
() -> constraintExBuilder.apply(
format( "No %s for '%s', so no first '%s'", valueTypeDisplayName(), key, valueTypeDisplayName() )
)
);
}
@Override
public V lastValue( K key )
throws MultiValuedConstraintException
{
return lastValueOptional( key )
.orElseThrow(
() -> constraintExBuilder.apply(
format( "No %s for '%s', so no last '%s'", valueTypeDisplayName(), key, valueTypeDisplayName() )
)
);
}
@Override
public Optional<V> singleValueOptional( K key )
{
try
{
return Optional.ofNullable( mvmap.getSingle( key ) );
}
catch( IllegalStateException ex )
{
return Optional.empty();
}
}
@Override
public Optional<V> firstValueOptional( K key )
{
return Optional.ofNullable( mvmap.getFirst( key ) );
}
@Override
public Optional<V> lastValueOptional( K key )
{
return Optional.ofNullable( mvmap.getLast( key ) );
}
@Override
public Set<K> keys()
{
return Collections.unmodifiableSet( mvmap.keySet() );
}
@Override
public List<V> values( K key )
{
return Collections.unmodifiableList( mvmap.getOrDefault( key, emptyList() ) );
}
@Override
public Map<K, V> singleValues()
{
try
{
return Collections.unmodifiableMap( mvmap.toMapSingleValues() );
}
catch( IllegalStateException ex )
{
throw constraintExBuilder.apply( ex.getMessage() );
}
}
@Override
public Map<K, V> firstValues()
{
return Collections.unmodifiableMap( mvmap.toMapFirstValues() );
}
@Override
public Map<K, V> lastValues()
{
return Collections.unmodifiableMap( mvmap.toMapLastValues() );
}
@Override
public Map<K, List<V>> allValues()
{
return Maps.unmodifiableMultiValueMap( mvmap );
}
/**
* Key type display name.
* <p>
* Defaults to {@literal key}, override to provide better error messages.
*
* @return the key type display name.
*/
protected String keyTypeDisplayName()
{
return "key";
}
/**
* Value type display name.
* <p>
* Defaults to {@literal value}, override to provide better error messages.
*
* @return the value type display name.
*/
protected String valueTypeDisplayName()
{
return "value";
}
}