gt: 			final RowIdsNodeKey rink = (RowIdsNodeKey) nodeKey;
context: package org.allmyinfo.nature.impl.assignment;
  
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  
  import org.allmyinfo.coordinator.Coordinator;
  import org.allmyinfo.coordinator.CoordinatorUtil;
  import org.allmyinfo.coordinator.Reservation;
  import org.allmyinfo.coordinator.nodes.BaseKeyIntent;
  import org.allmyinfo.coordinator.nodes.BaseKeyRule;
  import org.allmyinfo.coordinator.nodes.BaseNodeIntent;
  import org.allmyinfo.coordinator.nodes.BaseNodeRule;
  import org.allmyinfo.cursor.Cursor;
  import org.allmyinfo.nature.BaseNatureImpl;
  import org.allmyinfo.nature.Nature;
  import org.allmyinfo.nature.assignment.Assignment;
  import org.allmyinfo.nature.assignment.AssignmentImpl;
  import org.allmyinfo.nodekey.row.RowIdsNodeKey;
  import org.allmyinfo.nodekey.row.RowIdsNodeKeyType;
  import org.allmyinfo.nodes.Datum;
  import org.allmyinfo.nodes.DuplicateAssignment;
  import org.allmyinfo.nodes.KeyRemovalException;
  import org.allmyinfo.nodes.MissingMeaning;
  import org.allmyinfo.nodes.NodeId;
  import org.allmyinfo.nodes.NodeKey;
  import org.allmyinfo.nodes.database.NodesTransaction;
  import org.allmyinfo.system.cp.SystemContextProvider;
  import org.eclipse.jdt.annotation.NonNull;
  
  public abstract class BaseAssignmentNatureImpl extends BaseNatureImpl implements Nature {
  	private final SystemContextProvider.@NonNull Reactor scpReactor;
  
  	public BaseAssignmentNatureImpl(final @NonNull NodeId nodeId, //
  			final SystemContextProvider.@NonNull Reactor scpReactor, //
  			final @NonNull RowIdsNodeKeyType rinkt, //
  			final Coordinator.@NonNull Reactor coordReactor) {
  		super(nodeId, coordReactor, scpReactor, rinkt);
  		this.scpReactor = scpReactor;
  	}
  
  	@NonNull
  	abstract protected NodeId getSubjectMeaningNodeId();
  
  	@NonNull
  	abstract protected NodeId getObjectMeaningNodeId();
  
  	@Override
  	public final @NonNull Set<NodeId> getRequiredMeanings() {
  		final Set<NodeId> set = new HashSet<NodeId>(1);
  		set.add(getSubjectMeaningNodeId());
  		set.add(getObjectMeaningNodeId());
  		set.addAll(super.getRequiredMeanings());
  		return set;
  	}
  
  	protected @NonNull Assignment getAssignment(final @NonNull NodesTransaction txn,
  			final @NonNull NodeId nodeId) {
  		final NodeId subjectMeaningNodeId = getSubjectMeaningNodeId();
  		final NodeId objectMeaningNodeId = getObjectMeaningNodeId();
  		final NodeId objectNodeId = txn.getTarget(nodeId, objectMeaningNodeId);
  		if (objectNodeId == null)
  			throw new MissingMeaning(nodeId, objectMeaningNodeId, getNodeId());
  		final NodeId subjectNodeId = txn.getTarget(nodeId, subjectMeaningNodeId);
  		if (subjectNodeId == null)
  			throw new MissingMeaning(nodeId, subjectMeaningNodeId, getNodeId());
  		return new AssignmentImpl(nodeId, subjectNodeId, objectNodeId);
  	}
  
  	protected @NonNull Set<Assignment> getAssignments(@NonNull final NodesTransaction txn,
  			@NonNull final NodeId objectNodeId) {
  		final Set<Assignment> set = new HashSet<>(1);
  		final Cursor<NodeId> cursor = txn.fetchSourceNodes(nodeId, getSubjectMeaningNodeId());
  		try {
  			for (final NodeId assignmentNodeId : cursor) {
  				if (assignmentNodeId == null)
  					continue;
  				final NodeId subjectNodeId = txn.getTarget(assignmentNodeId,
  					getSubjectMeaningNodeId());
  				if (subjectNodeId != null) {
  					set.add(new AssignmentImpl(assignmentNodeId, subjectNodeId, objectNodeId));
  				}
  			}
  		}
  		finally {
  			cursor.close();
  		}
  		return set;
  	}
  
  	protected @NonNull Set<@NonNull NodeId> getObjects(@NonNull final NodesTransaction txn,
  			@NonNull final NodeId subjectNodeId) {
  		return txn.getAssignments(getObjectMeaningNodeId(), getSubjectMeaningNodeId(),
  			subjectNodeId);
  	}
  
  	protected @NonNull Set<@NonNull NodeId> getSubjects(@NonNull final NodesTransaction txn,
  			@NonNull final NodeId objectNodeId) {
  		return txn.getAssignments(getSubjectMeaningNodeId(), getObjectMeaningNodeId(),
  			objectNodeId);
  	}
  
  	protected @NonNull NodeId createAssignment(final @NonNull NodesTransaction txn,
  			final @NonNull NodeId subjectNodeId, final @NonNull NodeId objectNodeId,
  			final @NonNull Datum datum) {
  		final NodeId subjectMeaningNodeId = getSubjectMeaningNodeId();
  		final NodeId objectMeaningNodeId = getObjectMeaningNodeId();
  		//
  		// construct key
  		final NodeKey nodeKey = getKey(subjectNodeId, objectNodeId);
  		//
  		final Coordinator coordinator = coordReactor.getInstance();
  		final Reservation reservation = coordinator.reserve( //
  			CoordinatorUtil.makeSetOfRules( //
  				new BaseNodeRule(subjectNodeId), //
  				new BaseNodeRule(objectNodeId), //
  				new BaseKeyRule(nodeKey)), //
  			CoordinatorUtil.makeSetOfIntents( //
  				new BaseNodeIntent(subjectNodeId), //
  				new BaseNodeIntent(objectNodeId), //
  				new BaseKeyIntent(nodeKey)));
  		try {
  			// check for pre-existing assignment node
  			final NodeId existingNodeId = txn.getNode(nodeKey);
  			if (existingNodeId != null) {
  				return existingNodeId;
  			}
  			//
  			// add node
  			final NodeId linkNodeId = txn.insertNode(datum);
  			txn.insertLink(linkNodeId, objectNodeId, objectMeaningNodeId, false);
  			txn.insertLink(linkNodeId, subjectNodeId, subjectMeaningNodeId, false);
  			// note: cannot use index because this nature is not explicitly assigned
  			txn.insertKey(nodeKey, linkNodeId, false);
  			txn.assignNature(linkNodeId, getNodeId());
  			txn.dereference(linkNodeId);
  			return linkNodeId;
  		}
  		finally {
  			reservation.release();
  		}
  	}
  
  	protected boolean deleteAssignment(@NonNull final NodesTransaction txn,
  			@NonNull final NodeId subjectNodeId, final @NonNull NodeId objectNodeId) {
  		final NodeId subjectMeaningNodeId = getSubjectMeaningNodeId();
  		final NodeId objectMeaningNodeId = getObjectMeaningNodeId();
  		final Coordinator coordinator = coordReactor.getInstance();
  		//
  		// construct key
  		final NodeKey nodeKey = getKey(subjectNodeId, objectNodeId);
  		//
  		final Reservation reservation = coordinator.reserve( //
  			CoordinatorUtil.makeSetOfRules( //
  				new BaseNodeRule(subjectNodeId), //
  				new BaseNodeRule(objectNodeId), //
  				new BaseKeyRule(nodeKey)), //
  			CoordinatorUtil.makeSetOfIntents( //
  				new BaseNodeIntent(subjectNodeId), //
  				new BaseNodeIntent(objectNodeId), //
  				new BaseKeyIntent(nodeKey)));
  		try {
  			final NodeId assignmentNodeId = txn.getNode(nodeKey);
  			if (assignmentNodeId == null)
  				return false;
  			// note: cannot use index because this nature is not explicitly assigned
  			txn.deleteKey(nodeKey);
  			txn.deleteLink(assignmentNodeId, objectMeaningNodeId);
  			txn.deleteLink(assignmentNodeId, subjectMeaningNodeId);
  			txn.deleteNodeRecursively(assignmentNodeId);
  			return true;
  		}
  		finally {
  			reservation.release();
  		}
  	}
  
  	@Override
  	public final void validateRequiredMeanings(final @NonNull NodesTransaction txn,
  			final @NonNull NodeId naturedNodeId) {
  		super.validateRequiredMeanings(txn, naturedNodeId);
  		//
  		// the thing that is natured is the link node
  		final NodeId subjectMeaningNodeId = getSubjectMeaningNodeId();
  		final NodeId objectMeaningNodeId = getObjectMeaningNodeId();
  		final NodeId objectNodeId = txn.getTarget(naturedNodeId, objectMeaningNodeId);
  		final NodeId subjectNodeId = txn.getTarget(naturedNodeId, subjectMeaningNodeId);
  		if (objectNodeId != null && subjectNodeId != null) {
  			// construct key
  			final NodeKey nodeKey = getKey(subjectNodeId, objectNodeId);
  			// check for pre-existing assignment node
  			// note: cannot use index because this nature is not explicitly assigned
  			final NodeId existingNodeId = txn.getNode(nodeKey);
  			if (existingNodeId != null)
  				throw new DuplicateAssignment(subjectNodeId, objectNodeId, getNodeId());
  		}
  	}
  
  	@Override
  	public final void validateKeyRemoval(final @NonNull NodesTransaction txn,
  			final @NonNull NodeKey nodeKey) {
  		final NodeId subjectMeaningNodeId = getSubjectMeaningNodeId();
  		final NodeId objectMeaningNodeId = getObjectMeaningNodeId();
  		final NodeId natureMeaningNodeId = scpReactor.getInstance().getNatureMeaningNodeId();
  		if (nodeKey instanceof RowIdsNodeKey) {
  