blob: 8629e96ee91a3938c6d48fb1b1e7663e30686e6c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
* 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
*
* Contributors:
* Gabor Bergmann - initial API and implementation
*******************************************************************************/
package org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.boundary;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.construction.RetePatternBuildException;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.construction.Stub;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.index.Indexer;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.index.IterableIndexer;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.index.JoinNode;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.matcher.IPatternMatcherContext;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.matcher.IPatternMatcherContext.GeneralizationQueryDirection;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.matcher.IPatternMatcherRuntimeContext;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.matcher.ReteEngine;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Direction;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Network;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Production;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Receiver;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.ReteContainer;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Supplier;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Tunnel;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.remote.Address;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.single.TrimmerNode;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.FlatTuple;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.Tuple;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.TupleMask;
/**
* Responsible for the storage, maintenance and communication of the nodes of the network
* that are accessible form the outside for various reasons.
*
* @author Bergmann Gábor
*
* @param <PatternDescription>
*/
public class ReteBoundary<PatternDescription> {
protected ReteEngine<PatternDescription> engine;
protected Network network;
protected ReteContainer headContainer;
protected IPatternMatcherRuntimeContext<PatternDescription> context;
IPatternMatcherContext.GeneralizationQueryDirection generalizationQueryDirection;
/*
* arity:1
* used as simple entity constraints
* label is the object representing the type
* null label means all entities regardless of type (global supertype), if allowed
*/
protected Map<Object, Address<? extends Tunnel>> unaryRoots;
/*
* arity:3 (rel, from, to)
* used as VPM relation constraints
* null label means all relations regardless of type (global supertype)
*/
protected Map<Object, Address<? extends Tunnel>> ternaryEdgeRoots;
/*
* arity:2 (from, to)
* not used over VPM; can be used as EMF references for instance
* label is the object representing the type
* null label means all entities regardless of type if allowed (global supertype), if allowed
*/
protected Map<Object, Address<? extends Tunnel>> binaryEdgeRoots;
protected Map<PatternDescription, Address<? extends Production>> productions;
//protected Map<PatternDescription, Map<Map<Integer, Scope>, Address<? extends Production>>> productionsScoped; // (pattern, scopemap) -> production
protected Address<? extends Tunnel> containmentRoot;
protected Address<? extends Supplier> containmentTransitiveRoot;
protected Address<? extends Tunnel> instantiationRoot;
protected Address<? extends Supplier> instantiationTransitiveRoot;
protected Address<? extends Tunnel> generalizationRoot;
protected Address<? extends Supplier> generalizationTransitiveRoot;
/**
* Stubs of parent nodes that have the key node as their child.
* For RETE --> Stub traceability, mainly at production nodes.
*/
protected Map<Address<? extends Receiver>, Set<Stub<Address<? extends Supplier>>>> parentStubsOfReceiver;
/**
* Prerequisite: engine has its network and framework fields initialized
*
* @param headContainer
*/
public ReteBoundary(ReteEngine<PatternDescription> engine) {
super();
this.engine = engine;
this.network = engine.getReteNet();
this.headContainer = network.getHeadContainer();
this.context = engine.getContext();
this.generalizationQueryDirection = this.context.allowedGeneralizationQueryDirection();
this.parentStubsOfReceiver = new HashMap<Address<? extends Receiver>, Set<Stub<Address<? extends Supplier>>>>();
unaryRoots = new HashMap<Object, Address<? extends Tunnel>>();
ternaryEdgeRoots = new HashMap<Object, Address<? extends Tunnel>>();
binaryEdgeRoots = new HashMap<Object, Address<? extends Tunnel>>();
productions = new HashMap<PatternDescription, Address<? extends Production>>();
//productionsScoped = new HashMap<GTPattern, Map<Map<Integer,Scope>,Address<? extends Production>>>();
containmentRoot = null;
containmentTransitiveRoot = null;
instantiationRoot = null;
generalizationRoot = null;
generalizationTransitiveRoot = null;
}
/**
* Wraps the element into a form suitable for entering the network
* model element -> internal object
*/
public Object wrapElement(Object element) {
return element;// .getID();
}
/**
* Unwraps the element into its original form
* internal object -> model element
*/
public Object unwrapElement(Object wrapper) {
return wrapper;// modelManager.getElementByID((String)
// wrapper);
}
/**
* Unwraps the tuple of elements into a form suitable for entering the
* network
*/
public Tuple wrapTuple(Tuple unwrapped) {
// int size = unwrapped.getSize();
// Object[] elements = new Object[size];
// for (int i=0; i<size; ++i) elements[i] =
// wrapElement(unwrapped.get(i));
// return new FlatTuple(elements);
return unwrapped;
}
/**
* Unwraps the tuple of elements into their original form
*/
public Tuple unwrapTuple(Tuple wrappers) {
// int size = wrappers.getSize();
// Object[] elements = new Object[size];
// for (int i=0; i<size; ++i) elements[i] =
// unwrapElement(wrappers.get(i));
// return new FlatTuple(elements);
return wrappers;
}
/**
* fetches the entity Root node under specified label; returns null if it
* doesn't exist yet
*/
public Address<? extends Tunnel> getUnaryRoot(Object label) {
return unaryRoots.get(label);
}
/**
* fetches the relation Root node under specified label; returns null if it
* doesn't exist yet
*/
public Address<? extends Tunnel> getTernaryEdgeRoot(Object label) {
return ternaryEdgeRoots.get(label);
}
/**
* accesses the entity Root node under specified label; creates the node if
* it doesn't exist yet
*/
public Address<? extends Tunnel> accessUnaryRoot(Object typeObject) {
Address<? extends Tunnel> tn;
tn = unaryRoots.get(typeObject);
if (tn == null) {
tn = headContainer.getLibrary().newUniquenessEnforcerNode(1, typeObject);
unaryRoots.put(typeObject, tn);
new EntityFeeder(tn, context, network, this, typeObject).feed();
if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
Collection<? extends Object> subTypes = context.enumerateDirectUnarySubtypes(typeObject);
for (Object subType : subTypes) {
Address<? extends Tunnel> subRoot = accessUnaryRoot(subType);
network.connectRemoteNodes(subRoot, tn, true);
}
}
}
return tn;
}
/**
* accesses the relation Root node under specified label; creates the node
* if it doesn't exist yet
*/
public Address<? extends Tunnel> accessTernaryEdgeRoot(Object typeObject) {
Address<? extends Tunnel> tn;
tn = ternaryEdgeRoots.get(typeObject);
if (tn == null) {
tn = headContainer.getLibrary().newUniquenessEnforcerNode(3, typeObject);
ternaryEdgeRoots.put(typeObject, tn);
new RelationFeeder(tn, context, network, this, typeObject).feed();
if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
Collection<? extends Object> subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject);
for (Object subType : subTypes) {
Address<? extends Tunnel> subRoot = accessTernaryEdgeRoot(subType);
network.connectRemoteNodes(subRoot, tn, true);
}
}
}
return tn;
}
/**
* accesses the reference Root node under specified label; creates the node
* if it doesn't exist yet
*/
public Address<? extends Tunnel> accessBinaryEdgeRoot(Object typeObject) {
Address<? extends Tunnel> tn;
tn = binaryEdgeRoots.get(typeObject);
if (tn == null) {
tn = headContainer.getLibrary().newUniquenessEnforcerNode(2, typeObject);
binaryEdgeRoots.put(typeObject, tn);
new ReferenceFeeder(tn, context, network, this, typeObject).feed();
if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
Collection<? extends Object> subTypes = context.enumerateDirectBinaryEdgeSubtypes(typeObject);
for (Object subType : subTypes) {
Address<? extends Tunnel> subRoot = accessBinaryEdgeRoot(subType);
network.connectRemoteNodes(subRoot, tn, true);
}
}
}
return tn;
}
/**
* accesses the special direct containment relation Root node; creates the
* node if it doesn't exist yet
*/
public Address<? extends Tunnel> accessContainmentRoot() {
if (containmentRoot == null) {
// containment: relation quasi-type
containmentRoot = headContainer.getLibrary()
.newUniquenessEnforcerNode(2, "$containment");
new ContainmentFeeder(containmentRoot, context, network, this).feed();
}
return containmentRoot;
}
/**
* accesses the special transitive containment relation Root node; creates
* the node if it doesn't exist yet
*/
public Address<? extends Supplier> accessContainmentTransitiveRoot() {
if (containmentTransitiveRoot == null) {
// transitive containment: derived
Address<? extends Tunnel> containmentTransitiveRoot = headContainer
.getLibrary().newUniquenessEnforcerNode(2, "$containmentTransitive");
network.connectRemoteNodes(accessContainmentRoot(),
containmentTransitiveRoot, true);
final int[] actLI = { 1 };
final int arcLIw = 2;
final int[] actRI = { 0 };
final int arcRIw = 2;
Address<? extends IterableIndexer> jPrimarySlot = headContainer.getLibrary()
.accessProjectionIndexer(accessContainmentRoot(),
new TupleMask(actLI, arcLIw));
Address<? extends IterableIndexer> jSecondarySlot = headContainer.getLibrary()
.accessProjectionIndexer(containmentTransitiveRoot,
new TupleMask(actRI, arcRIw));
final int[] actRIcomp = { 1 };
final int arcRIwcomp = 2;
TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
Address<JoinNode> andCT = headContainer.getLibrary()
.accessJoinNode(jPrimarySlot, jSecondarySlot,
complementerMask);
final int[] mask = { 0, 2 };
final int maskw = 3;
Address<TrimmerNode> tr = headContainer.getLibrary()
.accessTrimmerNode(andCT, new TupleMask(mask, maskw));
network.connectRemoteNodes(tr, containmentTransitiveRoot, true);
this.containmentTransitiveRoot = containmentTransitiveRoot; // cast
// back
// to
// org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.
// Supplier
}
return containmentTransitiveRoot;
}
/**
* accesses the special instantiation relation Root node; creates the node
* if it doesn't exist yet
*/
public Address<? extends Tunnel> accessInstantiationRoot() {
if (instantiationRoot == null) {
// instantiation: relation quasi-type
instantiationRoot = headContainer.getLibrary()
.newUniquenessEnforcerNode(2, "$instantiation");
new InstantiationFeeder(instantiationRoot, context, network, this).feed();
}
return instantiationRoot;
}
/**
* accesses the special transitive instantiation relation Root node; creates
* the node if it doesn't exist yet InstantiationTransitive = Instantiation
* o (Generalization)^*
*/
public Address<? extends Supplier> accessInstantiationTransitiveRoot() {
if (instantiationTransitiveRoot == null) {
// transitive instantiation: derived
Address<? extends Tunnel> instantiationTransitiveRoot = headContainer
.getLibrary().newUniquenessEnforcerNode(2, "$instantiationTransitive");
network.connectRemoteNodes(accessInstantiationRoot(),
instantiationTransitiveRoot, true);
final int[] actLI = { 1 };
final int arcLIw = 2;
final int[] actRI = { 0 };
final int arcRIw = 2;
Address<? extends IterableIndexer> jPrimarySlot = headContainer.getLibrary()
.accessProjectionIndexer(accessGeneralizationRoot(),
new TupleMask(actLI, arcLIw));
Address<? extends Indexer> jSecondarySlot = headContainer.getLibrary()
.accessProjectionIndexer(instantiationTransitiveRoot,
new TupleMask(actRI, arcRIw));
final int[] actRIcomp = { 1 };
final int arcRIwcomp = 2;
TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
Address<JoinNode> andCT = headContainer.getLibrary()
.accessJoinNode(jPrimarySlot, jSecondarySlot,
complementerMask);
final int[] mask = { 0, 2 };
final int maskw = 3;
Address<? extends TrimmerNode> tr = headContainer.getLibrary()
.accessTrimmerNode(andCT, new TupleMask(mask, maskw));
network.connectRemoteNodes(tr, instantiationTransitiveRoot, true);
this.instantiationTransitiveRoot = instantiationTransitiveRoot; // cast
// back
// to
// org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network
// .
// Supplier
}
return instantiationTransitiveRoot;
}
/**
* accesses the special generalization relation Root node; creates the node
* if it doesn't exist yet
*/
public Address<? extends Tunnel> accessGeneralizationRoot() {
if (generalizationRoot == null) {
// generalization: relation quasi-type
generalizationRoot = headContainer.getLibrary()
.newUniquenessEnforcerNode(2, "$generalization");
new GeneralizationFeeder(generalizationRoot, context, network, this).feed();
}
return generalizationRoot;
}
/**
* accesses the special transitive containment relation Root node; creates
* the node if it doesn't exist yet
*/
public Address<? extends Supplier> accessGeneralizationTransitiveRoot() {
if (generalizationTransitiveRoot == null) {
// transitive generalization: derived
Address<? extends Tunnel> generalizationTransitiveRoot = headContainer
.getLibrary().newUniquenessEnforcerNode(2, "$generalizationTransitive");
network.connectRemoteNodes(accessGeneralizationRoot(),
generalizationTransitiveRoot, true);
final int[] actLI = { 1 };
final int arcLIw = 2;
final int[] actRI = { 0 };
final int arcRIw = 2;
Address<? extends IterableIndexer> jPrimarySlot = headContainer.getLibrary()
.accessProjectionIndexer(accessGeneralizationRoot(),
new TupleMask(actLI, arcLIw));
Address<? extends Indexer> jSecondarySlot = headContainer.getLibrary()
.accessProjectionIndexer(generalizationTransitiveRoot,
new TupleMask(actRI, arcRIw));
final int[] actRIcomp = { 1 };
final int arcRIwcomp = 2;
TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
Address<JoinNode> andCT = headContainer.getLibrary()
.accessJoinNode(jPrimarySlot, jSecondarySlot,
complementerMask);
final int[] mask = { 0, 2 };
final int maskw = 3;
Address<TrimmerNode> tr = headContainer.getLibrary()
.accessTrimmerNode(andCT, new TupleMask(mask, maskw));
network.connectRemoteNodes(tr, generalizationTransitiveRoot, true);
this.generalizationTransitiveRoot = generalizationTransitiveRoot; // cast
// back
// to
// org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network
// .
// Supplier
}
return generalizationTransitiveRoot;
}
// /**
// * Registers and publishes a supplier under specified label.
// */
// public void publishSupplier(Supplier s, Object label)
// {
// publishedSuppliers.put(label, s);
// }
//
// /**
// * fetches the production node under specified label;
// * returns null if it doesn't exist yet
// */
// public Production getProductionNode(Object label)
// {
// return productions.get(label);
// }
//
// /**
// * fetches the published supplier under specified label;
// * returns null if it doesn't exist yet
// */
// public Supplier getPublishedSupplier(Object label)
// {
// return publishedSuppliers.get(label);
// }
/**
* accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet
*/
public synchronized Address<? extends Production> accessProduction(PatternDescription gtPattern) throws RetePatternBuildException {
Address<? extends Production> pn;
pn = productions.get(gtPattern);
if (pn == null) {
construct(gtPattern);
pn = productions.get(gtPattern);
if (pn == null) {
String[] args = {gtPattern.toString()};
throw new RetePatternBuildException("Unsuccessful creation of production node for pattern {1}", args, gtPattern);
}
}
return pn;
}
/**
* creates the production node for the specified pattern
* Contract: only call from the builder (through Buildable) responsible for building this pattern
* @throws PatternMatcherCompileTimeException if production node is already created
*/
public synchronized Address<? extends Production> createProductionInternal(PatternDescription gtPattern)
throws RetePatternBuildException {
if (productions.containsKey(gtPattern)) {
String[] args = {gtPattern.toString()};
throw new RetePatternBuildException("Multiple creation attempts of production node for {1}", args, gtPattern);
}
HashMap<Object, Integer> posMapping = engine.getBuilder().getPosMapping(gtPattern);
Address<? extends Production> pn = headContainer.getLibrary().newProductionNode(posMapping, gtPattern);
productions.put(gtPattern, pn);
context.reportPatternDependency(gtPattern);
return pn;
}
// /**
// * accesses the production node for specified pattern and scope map; creates the node if
// * it doesn't exist yet
// */
// public synchronized Address<? extends Production> accessProductionScoped(
// GTPattern gtPattern, Map<Integer, Scope> additionalScopeMap) throws PatternMatcherCompileTimeException {
// if (additionalScopeMap.isEmpty()) return accessProduction(gtPattern);
//
// Address<? extends Production> pn;
//
// Map<Map<Integer, Scope>, Address<? extends Production>> scopes = productionsScoped.get(gtPattern);
// if (scopes == null) {
// scopes = new HashMap<Map<Integer, Scope>, Address<? extends Production>>();
// productionsScoped.put(gtPattern, scopes);
// }
//
// pn = scopes.get(additionalScopeMap);
// if (pn == null) {
// Address<? extends Production> unscopedProduction = accessProduction(gtPattern);
//
// HashMap<Object, Integer> posMapping = headContainer.resolveLocal(unscopedProduction).getPosMapping();
// pn = headContainer.getLibrary().newProductionNode(posMapping);
// scopes.put(additionalScopeMap, pn);
//
// constructScoper(unscopedProduction, additionalScopeMap, pn);
// }
// return pn;
// }
/**
* @pre: builder is set
*/
protected void construct(PatternDescription gtPattern)
throws RetePatternBuildException {
engine.getReteNet().waitForReteTermination();
engine.getBuilder().construct(gtPattern);
// production.setDirty(false);
}
// protected void constructScoper(
// Address<? extends Production> unscopedProduction,
// Map<Integer, Scope> additionalScopeMap,
// Address<? extends Production> production)
// throws PatternMatcherCompileTimeException {
// engine.reteNet.waitForReteTermination();
// engine.builder.constructScoper(unscopedProduction, additionalScopeMap, production);
// }
// /**
// * Invalidates the subnet constructed for the recognition of a given
// pattern.
// * The pattern matcher will have to be rebuilt.
// * @param gtPattern the pattern whose matcher subnet should be invalidated
// */
// public void invalidatePattern(GTPattern gtPattern) {
// Production production = null;
// try {
// production = accessProduction(gtPattern);
// } catch (PatternMatcherCompileTimeException e) {
// // this should not occur here, since we already have a production node
// e.printStackTrace();
// }
//
// production.tearOff();
// //production.setDirty(true);
// }
// updaters for change notification
// if the corresponding rete input isn't created yet, call is ignored
public void updateUnary(Direction direction, Object entity, Object typeObject) {
Address<? extends Tunnel> root = unaryRoots.get(typeObject);
if (root != null) {
network.sendExternalUpdate(root, direction, new FlatTuple(wrapElement(entity)));
if (!engine.isParallelExecutionEnabled())
network.waitForReteTermination();
}
if (typeObject!=null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
for (Object superType: context.enumerateDirectUnarySupertypes(typeObject)) {
updateUnary(direction, entity, superType);
}
}
}
public void updateTernaryEdge(Direction direction, Object relation,
Object from, Object to, Object typeObject) {
Address<? extends Tunnel> root = ternaryEdgeRoots.get(typeObject);
if (root != null) {
network.sendExternalUpdate(root, direction, new FlatTuple(
wrapElement(relation), wrapElement(from), wrapElement(to)));
if (!engine.isParallelExecutionEnabled())
network.waitForReteTermination();
}
if (typeObject!=null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
for (Object superType: context.enumerateDirectTernaryEdgeSupertypes(typeObject)) {
updateTernaryEdge(direction, relation, from, to, superType);
}
}
}
public void updateBinaryEdge(Direction direction,
Object from, Object to, Object typeObject) {
Address<? extends Tunnel> root = binaryEdgeRoots.get(typeObject);
if (root != null) {
network.sendExternalUpdate(root, direction, new FlatTuple(
wrapElement(from), wrapElement(to)));
if (!engine.isParallelExecutionEnabled())
network.waitForReteTermination();
}
if (typeObject!=null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
for (Object superType: context.enumerateDirectBinaryEdgeSupertypes(typeObject)) {
updateBinaryEdge(direction, from, to, superType);
}
}
}
public void updateContainment(Direction direction, Object container,
Object element) {
if (containmentRoot != null) {
network.sendExternalUpdate(containmentRoot, direction,
new FlatTuple(wrapElement(container),
wrapElement(element)));
if (!engine.isParallelExecutionEnabled())
network.waitForReteTermination();
}
}
public void updateInstantiation(Direction direction, Object parent,
Object child) {
if (instantiationRoot != null) {
network.sendExternalUpdate(instantiationRoot, direction,
new FlatTuple(wrapElement(parent), wrapElement(child)));
if (!engine.isParallelExecutionEnabled())
network.waitForReteTermination();
}
}
public void updateGeneralization(Direction direction, Object parent,
Object child) {
if (generalizationRoot != null) {
network.sendExternalUpdate(generalizationRoot, direction,
new FlatTuple(wrapElement(parent), wrapElement(child)));
if (!engine.isParallelExecutionEnabled())
network.waitForReteTermination();
}
}
// no wrapping needed!
public void notifyEvaluator(Address<? extends Receiver> receiver,
Tuple tuple) {
network.sendExternalUpdate(receiver, Direction.INSERT, tuple);
if (!engine.isParallelExecutionEnabled())
network.waitForReteTermination();
}
public void registerParentStubForReceiver(Address<? extends Receiver> receiver, Stub<Address<? extends Supplier>> parentStub) {
Set<Stub<Address<? extends Supplier>>> parents = parentStubsOfReceiver.get(receiver);
if (parents == null) {
parents = new HashSet<Stub<Address<? extends Supplier>>>();
parentStubsOfReceiver.put(receiver, parents);
}
parents.add(parentStub);
}
public Set<Stub<Address<? extends Supplier>>> getParentStubsOfReceiver(Address<? extends Receiver> receiver) {
Set<Stub<Address<? extends Supplier>>> parents = parentStubsOfReceiver.get(receiver);
if (parents == null) parents = Collections.emptySet();
return parents;
}
}