/* * 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 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 the type of keys maintained by this template */ public abstract class ImmutableOffsetMapTemplate extends ImmutableMapTemplate { private static final class Ordered extends ImmutableOffsetMapTemplate { Ordered(final Collection keys) { super(OffsetMapCache.orderedOffsets(keys)); } @Override @NonNull ImmutableOffsetMap createMap(final ImmutableMap offsets, final V[] objects) { return new ImmutableOffsetMap.Ordered<>(offsets, objects); } } private static final class Unordered extends ImmutableOffsetMapTemplate { Unordered(final Collection keys) { super(OffsetMapCache.unorderedOffsets(keys)); } @Override @NonNull ImmutableOffsetMap createMap(final ImmutableMap offsets, final V[] objects) { return new ImmutableOffsetMap.Unordered<>(offsets, objects); } } private final @NonNull ImmutableMap offsets; ImmutableOffsetMapTemplate(final ImmutableMap 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 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 @NonNull ImmutableOffsetMapTemplate ordered(final Collection 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 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 @NonNull ImmutableOffsetMapTemplate unordered(final Collection keys) { checkArgument(keys.size() > 1); return new Unordered<>(keys); } @Override public final Set keySet() { return offsets.keySet(); } @Override public final @NonNull ImmutableOffsetMap instantiateTransformed(final Map fromMap, final BiFunction valueTransformer) { final int size = offsets.size(); checkArgument(fromMap.size() == size); @SuppressWarnings("unchecked") final V[] objects = (V[]) new Object[size]; for (Entry entry : fromMap.entrySet()) { final K key = requireNonNull(entry.getKey()); objects[offsetOf(key)] = transformValue(key, entry.getValue(), valueTransformer); } return createMap(offsets, objects); } @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "SpotBugs does not grok checkArgument()") private int offsetOf(final K key) { final Integer offset = offsets.get(key); checkArgument(offset != null, "Key %s present in input, but not in offsets %s", key, offsets); return offset; } @Override @SafeVarargs public final @NonNull ImmutableOffsetMap 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 @NonNull ImmutableOffsetMap createMap(ImmutableMap offsets, V[] objects); }