import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableTable;
import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
/**
* Abstract routing table definition for Action and RPC.
+ *
* @param <I> instance type of RPC or Acton
* @param <D> identifier type of RPC or Acton
* @param <M> implementation type of RPC or Acton
// First decompose the identifiers to a multimap
final ListMultimap<K, D> toAdd = decomposeIdentifiers(oprsToAdd);
- // Now iterate over existing entries, modifying them as appropriate...
final Builder<K, E> mb = ImmutableMap.builder();
+
+ // Now iterate over existing entries, modifying them as appropriate...
for (Entry<K, E> re : this.operations.entrySet()) {
List<D> newOperations = new ArrayList<>(toAdd.removeAll(re.getKey()));
if (!newOperations.isEmpty()) {
vb.put(i, v);
}
- final E entry = createOperationEntry(schemaContext, e.getKey(),
- vb.build());
+ final E entry = createOperationEntry(schemaContext, e.getKey(), vb.build());
+ if (entry != null) {
+ mb.put(e.getKey(), entry);
+ }
+ }
+
+ return newInstance(mb.build(), schemaContext);
+ }
+
+ AbstractDOMRoutingTable<I, D, M, L, K, E> addAll(final ImmutableTable<K, D, M> impls) {
+ if (impls.isEmpty()) {
+ return this;
+ }
+
+ // Create a temporary map, which we will mutatate
+ final var toAdd = HashBasedTable.create(impls);
+ final var mb = ImmutableMap.<K, E>builder();
+
+ // Now iterate over existing entries, modifying them as appropriate...
+ for (Entry<K, E> re : this.operations.entrySet()) {
+ final var newImpls = toAdd.rowMap().remove(re.getKey());
+ if (newImpls == null) {
+ mb.put(re);
+ continue;
+ }
+
+ var ne = re;
+ for (var oper : newImpls.entrySet()) {
+ @SuppressWarnings("unchecked")
+ final E newVal = (E) ne.getValue().add(oper.getValue(), Lists.newArrayList(oper.getKey()));
+ ne = Map.entry(ne.getKey(), newVal);
+ }
+ mb.put(ne);
+ }
+
+ // Finally add whatever is left in the decomposed multimap
+ for (Entry<K, Map<D, M>> e : toAdd.rowMap().entrySet()) {
+ final E entry = createOperationEntry(schemaContext, e.getKey(), ImmutableMap.copyOf(
+ Maps.<D, M, List<M>>transformValues(e.getValue(), ImmutableList::of)));
if (entry != null) {
mb.put(e.getKey(), entry);
}
return newInstance(b.build(), schemaContext);
}
+ AbstractDOMRoutingTable<I, D, M, L, K, E> removeAll(final ImmutableTable<K, D, M> impls) {
+ if (impls.isEmpty()) {
+ return this;
+ }
+
+ // Create a temporary map, which we will mutatate
+ final var toRemove = HashBasedTable.create(impls);
+ final var mb = ImmutableMap.<K, E>builder();
+
+ // Now iterate over existing entries, modifying them as appropriate...
+ for (Entry<K, E> re : this.operations.entrySet()) {
+ final var oldImpls = toRemove.rowMap().remove(re.getKey());
+ if (oldImpls == null) {
+ mb.put(re);
+ continue;
+ }
+
+ var ne = re;
+ for (var oper : oldImpls.entrySet()) {
+ if (ne != null) {
+ @SuppressWarnings("unchecked")
+ final E newVal = (E) ne.getValue().remove(oper.getValue(), Lists.newArrayList(oper.getKey()));
+ if (newVal != null) {
+ ne = Map.entry(ne.getKey(), newVal);
+ } else {
+ ne = null;
+ }
+ }
+ }
+ if (ne != null) {
+ mb.put(ne);
+ }
+ }
+
+ // All done, whatever is in toRemove, was not there in the first place
+ return newInstance(mb.build(), schemaContext);
+ }
+
+ static final <K, V> HashMultimap<V, K> invertImplementationsMap(final Map<K, V> map) {
+ return Multimaps.invertFrom(Multimaps.forMap(map), HashMultimap.create());
+ }
+
@VisibleForTesting
Map<K, Set<D>> getOperations() {
return Maps.transformValues(operations, AbstractDOMRoutingTableEntry::registeredIdentifiers);
abstract ListMultimap<K, D> decomposeIdentifiers(Set<I> instances);
- abstract E createOperationEntry(EffectiveModelContext context, K key, Map<D, List<M>> implementations);
+ abstract @Nullable E createOperationEntry(EffectiveModelContext context, K key, Map<D, List<M>> implementations);
}