de.scravy.bedrock.Mapping Maven / Gradle / Ivy
Show all versions of bedrock Show documentation
package de.scravy.bedrock;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* A Mapping from keys to values. A Mapping is very much like {@link java.util.Map}, but it precludes mutable operations.
*
* Every mapping is a function from keys to values that does know about the keys it maps. Hence a minimal
* complete definition of a Mapping consists of {@link #get(Object)} and {@link #keys()}.
*
* @param The type of the keys.
* @param The type of the values.
*/
public interface Mapping extends Function1, ExtendedIterable> {
/**
* Retrieves the value associated with the given key or Optional.empty() if the key is not mapped to any value.
*
* @param key The key.
* @return The value which the given key is mapped to.
*/
@Nonnull
Optional get(From key);
Seq keys();
default To getOrElse(final From key, final To fallback) {
return get(key).orElse(fallback);
}
@Nullable
default To getOrNull(final From key) {
return get(key).orElse(null);
}
@Override
default To apply(final From key) {
final Optional result = get(key);
if (result.isPresent()) {
return result.get();
} else {
throw new NoSuchElementException(Objects.toString(key));
}
}
default int size() {
return keys().length();
}
default boolean isEmpty() {
return keys().isEmpty();
}
default Seq values() {
final SeqBuilder builder = Seq.builder();
for (final From key : keys()) {
builder.add(apply(key));
}
return builder.result();
}
@Override
@Nonnull
default Iterator> iterator() {
return new Iterator>() {
private final Iterator underlying = keys().iterator();
@Override
public boolean hasNext() {
return underlying.hasNext();
}
@Override
public Pair next() {
final From nextKey = underlying.next();
return Pair.of(nextKey, apply(nextKey));
}
};
}
default Stream> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Map toMap() {
return new AbstractMap() {
private SoftReference>> entrySet = new SoftReference<>(null);
@Override
@Nonnull
public java.util.Set> entrySet() {
final java.util.Set> entrySet = this.entrySet.get();
if (entrySet == null) {
final HashSet> set = new HashSet<>();
for (final From key : keys()) {
set.add(Pair.of(key, apply(key)));
}
this.entrySet = new SoftReference<>(set);
return set;
}
return entrySet;
}
};
}
default void forEach(final BiConsumer super From, ? super To> action) {
Objects.requireNonNull(action, "'action' must not be null");
for (final Pair t : this) {
action.accept(t.fst(), t.snd());
}
}
static Mapping wrap(final Map map) {
return new Mapping() {
@Override
@Nonnull
public Map toMap() {
return map;
}
@Override
@Nonnull
public Iterator> iterator() {
return new Iterator>() {
private final Iterator> underlying = map.entrySet().iterator();
@Override
public boolean hasNext() {
return underlying.hasNext();
}
@Override
public Pair next() {
return Pair.of(underlying.next());
}
};
}
private SoftReference> keys = new SoftReference<>(null);
private SoftReference> values = new SoftReference<>(null);
@Nonnull
@Override
public Optional get(final From key) {
if (map.containsKey(key)) {
return Optional.ofNullable(map.get(key));
}
return Optional.empty();
}
@Override
public Seq keys() {
final Seq keys = this.keys.get();
if (keys == null) {
final Seq newKeys = Seq.ofCollection(map.keySet());
this.keys = new SoftReference<>(newKeys);
return newKeys;
}
return keys;
}
@Override
public Seq values() {
final Seq values = this.values.get();
if (values == null) {
final Seq newValues = Seq.ofCollection(map.values());
this.values = new SoftReference<>(newValues);
return newValues;
}
return values;
}
};
}
@SuppressWarnings("unchecked")
static Mapping empty() {
return (Mapping) EmptyMapping.EMPTY;
}
class EmptyMapping implements Mapping {
private static EmptyMapping EMPTY = new EmptyMapping();
@Nonnull
@Override
public Optional get(final K key) {
return Optional.empty();
}
@Override
public Seq keys() {
return Seq.empty();
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
return obj instanceof Mapping && ((Mapping) obj).isEmpty();
}
@Override
public int hashCode() {
return 1;
}
}
}