/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.runtime.value;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiConsumer;
/**
* An abstract implementation of map of references to other properties.
* @param <K> the key type
* @param <V> the value type
* @since 0.9.2
*/
public abstract class ValueOptionMap<K, V extends ValueOption<V>> extends AbstractMap<K, V> {
private EntrySet<K, V> entries;
/**
* Builds a key set.
* @param <T> the key type
* @param keys the sequence of keys
* @return the built key set
*/
@SafeVarargs
public static <T> Set<T> keys(T... keys) {
if (keys.length == 0) {
return Collections.emptySet();
} else if (keys.length == 1) {
return Collections.singleton(keys[0]);
} else {
Set<T> results = new LinkedHashSet<>(keys.length * 2);
Collections.addAll(results, keys);
return Collections.unmodifiableSet(results);
}
}
@Override
public abstract V get(Object key);
/**
* {@inheritDoc}
* @throws IllegalArgumentException if the given key is not in this map
*/
@Override
public V put(K key, V value) {
V property = get(key);
if (property == null) {
throw new IllegalArgumentException(String.valueOf(key));
}
return update(property, value);
}
@Override
public abstract Set<K> keySet();
@Override
public boolean containsKey(Object key) {
return get(key) != null;
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
return entrySet0();
}
private EntrySet<K, V> entrySet0() {
if (entries == null) {
Set<K> keys = keySet();
@SuppressWarnings("unchecked")
Map.Entry<K, V>[] array = (Map.Entry<K, V>[]) new Map.Entry<?, ?>[keys.size()];
int index = 0;
for (K key : keys) {
V value = get(key);
assert value != null;
array[index++] = new Entry<>(key, value);
}
entries = new EntrySet<>(array);
}
return entries;
}
@Override
public int size() {
return keySet().size();
}
@SuppressWarnings("deprecation")
static <V extends ValueOption<V>> V update(V property, V newValue) {
property.copyFrom(newValue);
return property;
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
entrySet0().forEach(action);
}
private static final class EntrySet<K, V> extends AbstractSet<Map.Entry<K, V>> {
private final Map.Entry<K, V>[] entries;
EntrySet(Map.Entry<K, V>[] entries) {
this.entries = entries;
}
void forEach(BiConsumer<? super K, ? super V> action) {
for (Map.Entry<K, V> e : entries) {
action.accept(e.getKey(), e.getValue());
}
}
@Override
public Iterator<Map.Entry<K, V>> iterator() {
Map.Entry<K, V>[] es = entries;
return new Iterator<Map.Entry<K, V>>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < es.length;
}
@Override
public Map.Entry<K, V> next() {
if (index >= es.length) {
throw new NoSuchElementException();
}
return es[index++];
}
};
}
@Override
public int size() {
return entries.length;
}
}
private static final class Entry<K, V extends ValueOption<V>> extends SimpleEntry<K, V> {
private static final long serialVersionUID = 1L;
Entry(K key, V value) {
super(key, value);
}
@Override
public V setValue(V newValue) {
return ValueOptionMap.update(getValue(), newValue);
}
}
}