package org.opendaylight.yangtools.util;
import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableMap;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Iterator;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import org.eclipse.jdt.annotation.NonNull;
* using {@link #instantiateWithValues(Object[])} where the argument array has values ordered corresponding to the key
* order defined by {@link #keySet()}.
*
+ * <p>
+ * If the keySet is static known to contain only a single key, consider using {@link SharedSingletonMapTemplate}. If
+ * it is statically known to contain multiple keys, consider using {@link ImmutableOffsetMapTemplate}.
+ *
* @param <K> the type of keys maintained by this template
*/
@Beta
public abstract class ImmutableMapTemplate<K> implements Immutable {
- private abstract static class AbstractMultiple<K> extends ImmutableMapTemplate<K> {
- private final @NonNull ImmutableMap<K, Integer> offsets;
-
- AbstractMultiple(final ImmutableMap<K, Integer> offsets) {
- this.offsets = requireNonNull(offsets);
- }
-
- @Override
- public final Set<K> keySet() {
- return offsets.keySet();
- }
-
- @Override
- public final <T, V> @NonNull ImmutableOffsetMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
- final BiFunction<K, T, V> valueTransformer) {
- final int size = offsets.size();
- checkArgument(fromMap.size() == size);
-
- @SuppressWarnings("unchecked")
- final V[] objects = (V[]) new Object[size];
- for (Entry<K, T> entry : fromMap.entrySet()) {
- final K key = requireNonNull(entry.getKey());
- final Integer offset = offsets.get(key);
- checkArgument(offset != null, "Key %s present in input, but not in offsets %s", key, offsets);
-
- objects[offset.intValue()] = transformValue(key, entry.getValue(), valueTransformer);
- }
-
- return createMap(offsets, objects);
- }
-
- @Override
- @SafeVarargs
- public final <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(final V... values) {
- checkArgument(values.length == offsets.size());
- final V[] copy = values.clone();
- Arrays.stream(copy).forEach(Objects::requireNonNull);
- return createMap(offsets, values);
- }
-
- @Override
- public final String toString() {
- return MoreObjects.toStringHelper(this).add("offsets", offsets).toString();
- }
-
- abstract <V> @NonNull ImmutableOffsetMap<K, V> createMap(ImmutableMap<K, Integer> offsets, V[] objects);
- }
-
- private static final class Ordered<K> extends AbstractMultiple<K> {
- Ordered(final Collection<K> keys) {
- super(OffsetMapCache.orderedOffsets(keys));
- }
-
- @Override
- <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
- return new ImmutableOffsetMap.Ordered<>(offsets, objects);
- }
- }
-
- private static final class Unordered<K> extends AbstractMultiple<K> {
- Unordered(final Collection<K> keys) {
- super(OffsetMapCache.unorderedOffsets(keys));
- }
-
- @Override
- <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
- return new ImmutableOffsetMap.Unordered<>(offsets, objects);
- }
- }
-
- private abstract static class AbstractSingle<K> extends ImmutableMapTemplate<K> {
- private final @NonNull SingletonSet<K> keySet;
-
- AbstractSingle(final K key) {
- this.keySet = SharedSingletonMap.cachedSet(key);
- }
-
- @Override
- public Set<K> keySet() {
- return keySet;
- }
-
- @Override
- public final <T, V> @NonNull SharedSingletonMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
- final BiFunction<K, T, V> valueTransformer) {
- final Iterator<Entry<K, T>> it = fromMap.entrySet().iterator();
- checkArgument(it.hasNext(), "Input is empty while expecting 1 item");
-
- final Entry<K, T> entry = it.next();
- final K expected = keySet.getElement();
- final K actual = entry.getKey();
- checkArgument(expected.equals(actual), "Unexpected key %s, expecting %s", actual, expected);
-
- final V value = transformValue(actual, entry.getValue(), valueTransformer);
- checkArgument(!it.hasNext(), "Input has more than one item");
-
- return createMap(keySet, value);
- }
-
- @Override
- @SafeVarargs
- public final <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(final V... values) {
- checkArgument(values.length == 1);
- return createMap(keySet, values[0]);
- }
-
- @Override
- public final String toString() {
- return MoreObjects.toStringHelper(this).add("keySet", keySet).toString();
- }
-
- abstract <V> @NonNull SharedSingletonMap<K, V> createMap(SingletonSet<K> keySet, V value);
- }
-
- private static final class SingleOrdered<K> extends AbstractSingle<K> {
- SingleOrdered(final K key) {
- super(key);
- }
-
- @Override
- <V> @NonNull SharedSingletonMap<K, V> createMap(final SingletonSet<K> keySet, final V value) {
- return new SharedSingletonMap.Ordered<>(keySet, value);
- }
- }
-
- private static final class SingleUnordered<K> extends AbstractSingle<K> {
- SingleUnordered(final K key) {
- super(key);
- }
-
- @Override
- <V> @NonNull SharedSingletonMap<K, V> createMap(final SingletonSet<K> keySet, final V value) {
- return new SharedSingletonMap.Unordered<>(keySet, value);
- }
- }
-
ImmutableMapTemplate() {
// Hidden on purpose
}
case 0:
throw new IllegalArgumentException("Proposed keyset must not be empty");
case 1:
- return new SingleOrdered<>(keys.iterator().next());
+ return SharedSingletonMapTemplate.ordered(keys.iterator().next());
default:
- return new Ordered<>(keys);
+ return ImmutableOffsetMapTemplate.ordered(keys);
}
}
case 0:
throw new IllegalArgumentException("Proposed keyset must not be empty");
case 1:
- return new SingleUnordered<>(keys.iterator().next());
+ return SharedSingletonMapTemplate.unordered(keys.iterator().next());
default:
- return new Unordered<>(keys);
+ return ImmutableOffsetMapTemplate.unordered(keys);
}
}
* @param <V> the type of mapped values
* @return An immutable map
* @throws NullPointerException if {@code values} or any of its elements is null
- * @throws IllegalArgumentException if {@code values.lenght} does not match the number of keys in this template
+ * @throws IllegalArgumentException if {@code values.length} does not match the number of keys in this template
*/
@SuppressWarnings("unchecked")
public abstract <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(V... values);
* a backing array. This is useful for situations where the same key set is shared across a multitude of maps, as this
* class uses a global cache to share the key-to-offset mapping.
*
+ * <p>
+ * In case the set of keys is statically known, you can use {@link ImmutableOffsetMapTemplate} to efficiently create
+ * {@link ImmutableOffsetMap} instances.
+ *
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*/
--- /dev/null
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiFunction;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Template for instantiating {@link ImmutableOffsetMap} instances with a fixed set of keys. The template can then be
+ * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
+ * using {@link #instantiateWithValues(Object[])} where the argument array has values ordered corresponding to the key
+ * order defined by {@link #keySet()}.
+ *
+ * @param <K> the type of keys maintained by this template
+ */
+public abstract class ImmutableOffsetMapTemplate<K> extends ImmutableMapTemplate<K> {
+ private static final class Ordered<K> extends ImmutableOffsetMapTemplate<K> {
+ Ordered(final Collection<K> keys) {
+ super(OffsetMapCache.orderedOffsets(keys));
+ }
+
+ @Override
+ <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+ return new ImmutableOffsetMap.Ordered<>(offsets, objects);
+ }
+ }
+
+ private static final class Unordered<K> extends ImmutableOffsetMapTemplate<K> {
+ Unordered(final Collection<K> keys) {
+ super(OffsetMapCache.unorderedOffsets(keys));
+ }
+
+ @Override
+ <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+ return new ImmutableOffsetMap.Unordered<>(offsets, objects);
+ }
+ }
+
+ private final @NonNull ImmutableMap<K, Integer> offsets;
+
+ ImmutableOffsetMapTemplate(final ImmutableMap<K, Integer> offsets) {
+ this.offsets = requireNonNull(offsets);
+ }
+
+ /**
+ * Create a template which produces Maps with specified keys, with iteration order matching the iteration order
+ * of {@code keys}. {@link #keySet()} will return these keys in exactly the same order. The resulting map will
+ * retain insertion order through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+ *
+ * @param keys Keys in requested iteration order.
+ * @param <K> the type of keys maintained by resulting template
+ * @return A template object.
+ * @throws NullPointerException if {@code keys} or any of its elements is null
+ * @throws IllegalArgumentException if {@code keys} is does not have at least two keys
+ */
+ public static <K> @NonNull ImmutableOffsetMapTemplate<K> ordered(final Collection<K> keys) {
+ checkArgument(keys.size() > 1);
+ return new Ordered<>(keys);
+ }
+
+ /**
+ * Create a template which produces Maps with specified keys, with unconstrained iteration order. Produced maps
+ * will have the iteration order matching the order returned by {@link #keySet()}. The resulting map will
+ * NOT retain ordering through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+ *
+ * @param keys Keys in any iteration order.
+ * @param <K> the type of keys maintained by resulting template
+ * @return A template object.
+ * @throws NullPointerException if {@code keys} or any of its elements is null
+ * @throws IllegalArgumentException if {@code keys} is does not have at least two keys
+ */
+ public static <K> @NonNull ImmutableOffsetMapTemplate<K> unordered(final Collection<K> keys) {
+ checkArgument(keys.size() > 1);
+ return new Unordered<>(keys);
+ }
+
+ @Override
+ public final Set<K> keySet() {
+ return offsets.keySet();
+ }
+
+ @Override
+ public final <T, V> @NonNull ImmutableOffsetMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
+ final BiFunction<K, T, V> valueTransformer) {
+ final int size = offsets.size();
+ checkArgument(fromMap.size() == size);
+
+ @SuppressWarnings("unchecked")
+ final V[] objects = (V[]) new Object[size];
+ for (Entry<K, T> entry : fromMap.entrySet()) {
+ final K key = requireNonNull(entry.getKey());
+ final Integer offset = offsets.get(key);
+ checkArgument(offset != null, "Key %s present in input, but not in offsets %s", key, offsets);
+
+ objects[offset.intValue()] = transformValue(key, entry.getValue(), valueTransformer);
+ }
+
+ return createMap(offsets, objects);
+ }
+
+ @Override
+ @SafeVarargs
+ public final <V> @NonNull ImmutableOffsetMap<K, V> instantiateWithValues(final V... values) {
+ checkArgument(values.length == offsets.size());
+ final V[] copy = values.clone();
+ Arrays.stream(copy).forEach(Objects::requireNonNull);
+ return createMap(offsets, values);
+ }
+
+ @Override
+ public final String toString() {
+ return MoreObjects.toStringHelper(this).add("offsets", offsets).toString();
+ }
+
+ abstract <V> @NonNull ImmutableOffsetMap<K, V> createMap(ImmutableMap<K, Integer> offsets, V[] objects);
+}
\ No newline at end of file
* Implementation of the {@link Map} interface which stores a single mapping. The key set is shared among all instances
* which contain the same key. This implementation does not support null keys or values.
*
+ * <p>
+ * In case the set of keys is statically known, you can use {@link SharedSingletonMapTemplate} to efficiently create
+ * {@link SharedSingletonMap} instances.
+ *
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*/
--- /dev/null
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.MoreObjects;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.BiFunction;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Template for instantiating {@link SharedSingletonMap} instances with a fixed key. The template can then be
+ * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
+ * using {@link #instantiateWithValue(Object)}.
+ *
+ * @param <K> the type of keys maintained by this template
+ */
+public abstract class SharedSingletonMapTemplate<K> extends ImmutableMapTemplate<K> {
+ private static final class Ordered<K> extends SharedSingletonMapTemplate<K> {
+ Ordered(final K key) {
+ super(key);
+ }
+
+ @Override
+ public <V> @NonNull SharedSingletonMap<K, V> instantiateWithValue(final V value) {
+ return new SharedSingletonMap.Ordered<>(keySet(), value);
+ }
+ }
+
+ private static final class Unordered<K> extends SharedSingletonMapTemplate<K> {
+ Unordered(final K key) {
+ super(key);
+ }
+
+ @Override
+ public <V> @NonNull SharedSingletonMap<K, V> instantiateWithValue(final V value) {
+ return new SharedSingletonMap.Unordered<>(keySet(), value);
+ }
+ }
+
+ private final @NonNull SingletonSet<K> keySet;
+
+ SharedSingletonMapTemplate(final K key) {
+ this.keySet = SharedSingletonMap.cachedSet(key);
+ }
+
+ /**
+ * Create a template which produces Maps with specified key. The resulting map will retain insertion order through
+ * {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+ *
+ * @param key Single key in resulting map
+ * @param <K> the type of keys maintained by resulting template
+ * @return A template object.
+ * @throws NullPointerException if {@code key} is null
+ */
+ public static <K> @NonNull SharedSingletonMapTemplate<K> ordered(final K key) {
+ return new Ordered<>(key);
+ }
+
+ /**
+ * Create a template which produces Maps with specified key. The resulting map will NOT retain ordering through
+ * {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+ *
+ * @param key Single key in resulting map
+ * @param <K> the type of keys maintained by resulting template
+ * @return A template object.
+ * @throws NullPointerException if {@code key} is null
+ */
+ public static <K> @NonNull SharedSingletonMapTemplate<K> unordered(final K key) {
+ return new Unordered<>(key);
+ }
+
+ @Override
+ public final SingletonSet<K> keySet() {
+ return keySet;
+ }
+
+ @Override
+ public final <T, V> @NonNull SharedSingletonMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
+ final BiFunction<K, T, V> valueTransformer) {
+ final Iterator<Entry<K, T>> it = fromMap.entrySet().iterator();
+ checkArgument(it.hasNext(), "Input is empty while expecting 1 item");
+
+ final Entry<K, T> entry = it.next();
+ final K expected = keySet.getElement();
+ final K actual = entry.getKey();
+ checkArgument(expected.equals(actual), "Unexpected key %s, expecting %s", actual, expected);
+
+ final V value = transformValue(actual, entry.getValue(), valueTransformer);
+ checkArgument(!it.hasNext(), "Input has more than one item");
+ return instantiateWithValue(value);
+ }
+
+ @Override
+ @SafeVarargs
+ public final <V> @NonNull SharedSingletonMap<K, V> instantiateWithValues(final V... values) {
+ checkArgument(values.length == 1);
+ return instantiateWithValue(values[0]);
+ }
+
+ /**
+ * Instantiate an immutable map with the value supplied.
+ *
+ * @param value Value to use
+ * @param <V> the type of mapped values
+ * @return An immutable map
+ * @throws NullPointerException if {@code value} is null
+ */
+ public abstract <V> @NonNull SharedSingletonMap<K, V> instantiateWithValue(V value);
+
+ @Override
+ public final String toString() {
+ return MoreObjects.toStringHelper(this).add("keySet", keySet).toString();
+ }
+}
private static void assertOne(final ImmutableMapTemplate<String> template, final Class<?> mapClass) {
assertEquals(ONE_KEYSET, template.keySet());
- assertEquals("Single" + mapClass.getSimpleName() + "{keySet=[foo]}", template.toString());
+ assertEquals(mapClass.getSimpleName() + "{keySet=[foo]}", template.toString());
// Successful instantiation
Map<String, String> map = template.instantiateWithValues(BAR);
this.keyValues = ImmutableOffsetMap.unorderedCopyOf(keyValues);
}
- public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
+ public NodeIdentifierWithPredicates(final QName node, final ImmutableOffsetMap<QName, Object> keyValues) {
+ super(node);
+ this.keyValues = requireNonNull(keyValues);
+ }
+
+ public NodeIdentifierWithPredicates(final QName node, final SharedSingletonMap<QName, Object> keyValues) {
super(node);
- this.keyValues = SharedSingletonMap.unorderedOf(key, value);
+ this.keyValues = requireNonNull(keyValues);
+ }
+
+ public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
+ this(node, SharedSingletonMap.unorderedOf(key, value));
}
public Map<QName, Object> getKeyValues() {