package org.nightlabs.jfire.personrelation.trade.ui.tucked;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.nightlabs.jdo.ObjectID;
import org.nightlabs.jfire.base.ui.jdo.tree.lazy.JDOLazyTreeNodesChangedEvent;
import org.nightlabs.jfire.base.ui.jdo.tree.lazy.JDOObjectLazyTreeNode;
import org.nightlabs.jfire.personrelation.PersonRelationManagerRemote.TuckedQueryCount;
import org.nightlabs.jfire.personrelation.dao.PersonRelationDAO;
import org.nightlabs.jfire.personrelation.id.PersonRelationID;
import org.nightlabs.jfire.personrelation.ui.tree.PersonRelationTreeController;
import org.nightlabs.jfire.personrelation.ui.tree.PersonRelationTreeNode;
import org.nightlabs.jfire.personrelation.ui.tree.PersonRelationTreeUtil;
import org.nightlabs.jfire.prop.id.PropertySetID;
import org.nightlabs.progress.NullProgressMonitor;
import org.nightlabs.progress.ProgressMonitor;
import org.nightlabs.progress.SubProgressMonitor;
import org.nightlabs.util.CollectionUtil;
/**
* A specialised {@link PersonRelationTreeController} to handle {@link TuckedPersonRelationTreeNode}.
*
* @author khaireel (at) nightlabs (dot) de
*/
public class TuckedPersonRelationTreeController extends PersonRelationTreeController<TuckedPersonRelationTreeNode> {
private static final Logger logger = Logger.getLogger(TuckedPersonRelationTreeController.class);
// Maintain some sort of synchronicity between the two paths; and one that will provide constant time reference in
// retrieving relatable information between a PRID and its corresponding PSID (and vice-versa).
private Map<Integer, Deque<ObjectID>> tuckedPRIDPathMaps = null; // <-- mixed PropertySetID & PersonRelationID.
private Map<Integer, Deque<ObjectID>> tuckedPSIDPathMaps = null; // <-- PropertySetID only.
/**
* Set the controller's internal information manipulator for the original tuckedPaths.
* @see PersonRelationDAO.getRelationRootNodes().
*/
public void setTuckedPaths(Map<Class<? extends ObjectID>, List<Deque<ObjectID>>> tuckedPaths) {
List<Deque<ObjectID>> pathsToRoot_PRID = tuckedPaths.get(PersonRelationID.class); // <-- mixed PropertySetID & PersonRelationID.
List<Deque<ObjectID>> pathsToRoot_PSID = tuckedPaths.get(PropertySetID.class); // <-- PropertySetID only.
// Initialise the path-expansion trackers.
tuckedPRIDPathMaps = new HashMap<Integer, Deque<ObjectID>>(pathsToRoot_PRID.size());
tuckedPSIDPathMaps = new HashMap<Integer, Deque<ObjectID>>(pathsToRoot_PRID.size());
Iterator<Deque<ObjectID>> iterPaths_PRID = pathsToRoot_PRID.iterator();
Iterator<Deque<ObjectID>> iterPaths_PSID = pathsToRoot_PSID.iterator();
int index = 0;
while (iterPaths_PSID.hasNext()) {
tuckedPRIDPathMaps.put(index, iterPaths_PRID.next());
tuckedPSIDPathMaps.put(index, iterPaths_PSID.next());
index++;
}
if (logger.isDebugEnabled()) {
logger.debug("******--******--******--******--******--******--******--******--******--******--******--******");
logger.debug(" @setTuckedPaths :: paths received = " + index);
logger.debug("******--******--******--******--******--******--******--******--******--******--******--******");
for(int i=0; i<index; i++)
logger.debug(PersonRelationTreeUtil.showObjectIDs("PSID-path @ref=" + i, tuckedPSIDPathMaps.get(i), 10));
logger.debug("..............................................................................................");
for(int i=0; i<index; i++)
logger.debug(PersonRelationTreeUtil.showObjectIDs("PRID-path @ref=" + i, tuckedPRIDPathMaps.get(i), 10));
logger.debug("******--******--******--******--******--******--******--******--******--******--******--******");
}
}
@Override
protected TuckedPersonRelationTreeNode createNode() {
return new TuckedPersonRelationTreeNode();
}
// -------------------------------------------------------------------------------------------------- ++ ------>>
// Handles the interaction to change a node's status from TUCKED to UNTUCKED, and vice-versa.
// -------------------------------------------------------------------------------------------------- ++ ------>>
/**
* Given a set of parentsToRefresh in the changedEvent, we specifically look into the node's getStatusToChangeTo() method
* and work to properly change it's status.
*/
protected void fireTuckChangedEvent(JDOLazyTreeNodesChangedEvent<ObjectID, TuckedPersonRelationTreeNode> changedEvent, ProgressMonitor monitor) {
Set<TuckedPersonRelationTreeNode> parentsToRefresh = changedEvent.getParentsToRefresh();
for (TuckedPersonRelationTreeNode parentNode : parentsToRefresh) {
// Status change: From TUCKED to UNTUCKED.
if (parentNode.getStatusToChangeTo().equals(TuckedNodeStatus.UNTUCKED)) {
// 1. Retrieve the children.
// 2. Create (a new node) + add only those children that are not already loaded.
Collection<ObjectID> childObjectIDs = retrieveChildObjectIDs(parentNode, new SubProgressMonitor(monitor, 80));
Set<ObjectID> nextObjectIDsOnPath = getNextObjectIDOnPath(parentNode); // <-- The node representing this ObjectID is ALWAYS loaded, whether the node is TUCKED or UNTUCKED.
for (ObjectID objectID : childObjectIDs)
if (!nextObjectIDsOnPath.contains(objectID))
parentNode.addChildNode(createOrRetrieveChildNodeByObjectID(parentNode, objectID));
parentNode.setTuckedStatus(TuckedNodeStatus.UNTUCKED);
parentNode.setStatusToChangeTo(TuckedNodeStatus.UNSET);
}
// Status change: From UNTUCKED back to TUCKED.
else if (parentNode.getStatusToChangeTo().equals(TuckedNodeStatus.TUCKED)) {
// The set of TUCKED nodes are exactly from the same set of UNTUCKED nodes, which we have already created when
// dealing with the status change form TUCKED to UNTUCKED. And also, we have kept a reference in the node's very
// own loadedTuckedChildren, for use in future references.
Collection<ObjectID> childObjectIDs = retrieveChildObjectIDs(parentNode, new SubProgressMonitor(monitor, 80));
List<JDOObjectLazyTreeNode<ObjectID, Object, PersonRelationTreeController<? extends PersonRelationTreeNode>>> subListOfTuckedNodes = new ArrayList<JDOObjectLazyTreeNode<ObjectID,Object,PersonRelationTreeController<? extends PersonRelationTreeNode>>>(childObjectIDs.size());
for (ObjectID objectID : childObjectIDs)
subListOfTuckedNodes.add(createOrRetrieveChildNodeByObjectID(parentNode, objectID));
parentNode.setChildNodes(subListOfTuckedNodes);
parentNode.setTuckedStatus(TuckedNodeStatus.TUCKED);
parentNode.setStatusToChangeTo(TuckedNodeStatus.UNSET);
}
}
// Done. Now react to the new changes.
onJDOObjectsChanged(new JDOLazyTreeNodesChangedEvent<ObjectID, TuckedPersonRelationTreeNode>(changedEvent.getSource(), parentsToRefresh));
}
/**
* Consults the parentNode's previously loaded child nodes on UNTUCKing (but which are not present due to a subsequent TUCK).
* @return the childNode representing the given objectID, if one is already existing. Otherwise, we create a new node, and
* have it initialised accordingly.
*/
private TuckedPersonRelationTreeNode createOrRetrieveChildNodeByObjectID(TuckedPersonRelationTreeNode parentNode, ObjectID objectID) {
TuckedPersonRelationTreeNode childNode = parentNode.getLoadedTuckedChildByObjectID(objectID);
if (childNode == null) {
childNode = createNode();
childNode.setActiveJDOObjectLazyTreeController(TuckedPersonRelationTreeController.this);
childNode.setParent(parentNode);
childNode.setJdoObjectID(objectID);
addTreeNode(childNode); // <-- For the controller's internal map: objectID2TreeNodeList
}
return childNode;
}
@Override
protected void onJDOObjectsChanged(JDOLazyTreeNodesChangedEvent<ObjectID, TuckedPersonRelationTreeNode> changedEvent) {
// Tucked information (basically the child counting) may have changed and needs to be refreshed.
Set<TuckedPersonRelationTreeNode> parentsToRefresh = changedEvent.getParentsToRefresh();
if (!(changedEvent.getSource() instanceof TuckedPersonRelationTree) && parentsToRefresh != null && !parentsToRefresh.isEmpty()) {
for (TuckedPersonRelationTreeNode parentNode : parentsToRefresh)
if (parentNode != null)
updateTuckedNodeChildCounts(parentNode, new NullProgressMonitor()); // FIXME Put a proper monitor here?
}
}
// -------------------------------------------------------------------------------------------------- ++ ------>>
// Retrieving COUNTs of the children of a given SET of personIDs.
// -------------------------------------------------------------------------------------------------- ++ ------>>
/**
* Upon consolidation, we have made it possible to handle the 'general' case of retrieving {@link TuckedPersonRelationTreeNode}'s childCounts.
* The only exception here is that if we find cases where the objectIDs (or the nodes containing the objectIDs) are not in our tuckedPath,
* then we need to refer them to the super class's methods which should then handle the separate cases between {@link PropertySetID}s and
* {@link PersonRelationID}s. These 'unhandled' objectIDs are placed back in the parameter unhandledObjectIDs, and the method calling this
* general method should handle this case independently.
*/
protected <T extends ObjectID> Map<ObjectID, Long> retrieveTuckedNodeChildCount
(Map<ObjectID, Long> result, Set<TuckedPersonRelationTreeNode> parentNodes, Set<T> objectIDs, Set<T> unhandledObjectIDs, ProgressMonitor monitor, int tix) {
// [Note. A.] When our tuckedPath ends, we should get nextIDOnPath == null. In this case, personRelationIDs we come across should not be affect
// with our tucked-node methodologies. We gather these IDs and then later on relegate them back to the super class's method.
for (T objectID : objectIDs) {
// ----------------------------------------------------------------------------------->> CONSOLIDATED for codes-optimality ------------>>---||
// [I] Fetch our related tuckedNode.
TuckedPersonRelationTreeNode tuckedNode = getTuckedNodeFromObjectID(parentNodes, objectID);
if (tuckedNode == null) { // <-- When we dont recognise a tuckedNode from our tuckedPath, we simply relegate the ID back to the super class's method.
unhandledObjectIDs.add(objectID);
monitor.worked(1);
continue;
}
// [II] Update its childCounts.
boolean isChildCountsUpdated = updateTuckedNodeChildCounts(tuckedNode, new SubProgressMonitor(monitor, 10));
if (!isChildCountsUpdated) { // <-- See [Note. A.]
unhandledObjectIDs.add(objectID);
monitor.worked(1);
continue;
}
// [III] If we got all the way up to this point, then we have successfully gotten our tuckedNode 'set'.
result.put(objectID, tuckedNode.getChildNodeCount());
monitor.worked(1);
// ----------------------------------------------------------------------------------->> CONSOLIDATED for codes-optimality ------------>>---||
if (logger.isDebugEnabled())
logger.debug("*** " + tuckedNode.toDebugString());
}
return result;
}
@Override
protected Map<ObjectID, Long> retrieveChildCountByPersonRelationIDs
(Map<ObjectID, Long> result, Set<TuckedPersonRelationTreeNode> parentNodes, Set<PersonRelationID> personRelationIDs, ProgressMonitor monitor, int tix) {
ProgressMonitor subMonitor = new SubProgressMonitor(monitor, tix);
subMonitor.beginTask("Retrieving child count...", personRelationIDs.size());
if (logger.isDebugEnabled())
logger.debug("~~~~~~~~~~~~~~ I'm here: @retrieve--[[ChildCount]]--ByPersonRelationIDs.");
Set<PersonRelationID> unhandledPersonRelationIDs = new HashSet<PersonRelationID>();
result = retrieveTuckedNodeChildCount(result, parentNodes, personRelationIDs, unhandledPersonRelationIDs, subMonitor, tix); // <-- Factorised. Since 2010.03.31.
// If there exists any personRelationIDs that has not been handled for the tucked situation, we let the super class handle them.
if (!unhandledPersonRelationIDs.isEmpty())
result = super.retrieveChildCountByPersonRelationIDs(result, parentNodes, unhandledPersonRelationIDs, subMonitor, tix);
subMonitor.done();
return result;
}
@Override
protected Map<ObjectID, Long> retrieveChildCountByPropertySetIDs
(Map<ObjectID, Long> result, Set<TuckedPersonRelationTreeNode> parentNodes, Set<PropertySetID> personIDs, ProgressMonitor monitor, int tix) {
// This should be about as similar as the previous method: retrieveChildCountByPersonRelationIDs().
ProgressMonitor subMonitor = new SubProgressMonitor(monitor, tix);
subMonitor.beginTask("Retrieving child count...", personIDs.size());
if (logger.isDebugEnabled())
logger.debug("@retrieve..ChildCount..By<<PropertySetID>>s");
Set<PropertySetID> unhandledPersonIDs = new HashSet<PropertySetID>();
result = retrieveTuckedNodeChildCount(result, parentNodes, personIDs, unhandledPersonIDs, subMonitor, tix); // <-- Factorised. Since 2010.03.31.
// If there exists any personRelationIDs that has not been handled for the tucked situation, we let
// the super class handle them.
if (!unhandledPersonIDs.isEmpty())
result = super.retrieveChildCountByPropertySetIDs(result, parentNodes, unhandledPersonIDs, subMonitor, tix);
subMonitor.done();
return result;
}
// ---------------------------------------------------------------------------------------------------------------------------------- ++ ------>>
/**
* Given a proper {@link TuckedPersonRelationTreeNode}, we check to see if the {@link ObjectID} it carries lie on the
* known tuckedPath. If so, we retrieve the childCounts for both actual and tucked. This, however, shall NOT set the
* status of the tuckedNode.
* @return true if the given tuckedNode has had both its childCounts updated.
*/
protected boolean updateTuckedNodeChildCounts(TuckedPersonRelationTreeNode tuckedNode, ProgressMonitor monitor) {
Set<PropertySetID> nextIDsOnPath = getNextRelatedPropertySetIDOnPath(tuckedNode);
// [Note. A.] When our tuckedPath ends, we should get nextIDOnPath == null. In this case, the personIDs we come across should not be affect
// with our tucked-node methodologies. We gather these IDs and then later on relegate them back to the super class's method.
if (nextIDsOnPath == null)
return false;
Set<PropertySetID> propertySetIDsToRoot = CollectionUtil.createHashSetFromCollection(tuckedNode.getPropertySetIDsToRoot()); // FIXME This is dangerous! Kai.
PropertySetID nodePropertySetID = tuckedNode.getPropertySetID();
if (nodePropertySetID == null) // <-- It is possible that parentNode.getPropertySetID() returns null. But at this point, we know that the next element on the tuckedPath exists!
nodePropertySetID = getCorrespondingPSID(tuckedNode);
TuckedQueryCount tqCount = PersonRelationDAO.sharedInstance().getTuckedPersonRelationCount(
null, nodePropertySetID, propertySetIDsToRoot, nextIDsOnPath,
new SubProgressMonitor(monitor, 20));
tuckedNode.setActualChildCount(tqCount.actualChildCount);
tuckedNode.setTuckedChildCount(tqCount.tuckedChildCount);
return true;
}
/**
* @return the {@link TuckedPersonRelationTreeNode} containing within it's JDOObjectID, the given objectID.
* Returns null if the node cannot be found.
*/
private TuckedPersonRelationTreeNode getTuckedNodeFromObjectID(Set<TuckedPersonRelationTreeNode> parentNodes, ObjectID objectID) {
if (parentNodes == null)
return null;
for (TuckedPersonRelationTreeNode tuckedNode : parentNodes)
if (tuckedNode != null) {
ObjectID jdoObjectID = tuckedNode.getJdoObjectID();
if (jdoObjectID != null && jdoObjectID.equals(objectID))
return tuckedNode;
}
return null;
}
// -------------------------------------------------------------------------------------------------- ++ ------>>
// Retrieving ObjectIDs of the children of a given parentNode.
// -------------------------------------------------------------------------------------------------- ++ ------>>
@Override
protected Collection<ObjectID> retrieveChildObjectIDsByPersonRelationIDs(TuckedPersonRelationTreeNode parentNode, ProgressMonitor monitor) {
// We need some kind of coordination, in order to know what to load here. Done.
// This depends on the information we can gather from the parentNode: check the status indicated by getStatusToChangeTo().
if (logger.isDebugEnabled()) {
logger.debug("~~~~~~~~~~~~~~ I'm here: @retrieveChild--((ObjectID))--sByPersonRelationIDs");
logger.debug(" ::: parentNode: " + PersonRelationTreeUtil.showObjectID(parentNode.getPropertySetID()) + ", statusToChangeTo: " + parentNode.getStatusToChangeTo());
}
// Our tucked-situational environment is rather unique:
// The tucked-path is ALWAYs known before hand; i.e. upon initiation, we have the required tucked path, which is the basis for the entire TuckedNode concept.
// Thus, at any node we encounter, we already know what the next PropertySetID is going to be, and so we don't need to fetch that ID again. This was
// previously done by first fetching the PersonRelation and then through it we access the getToID(). Note that it is possible to have null, which simply
// carries two meanings: 1. we have reached the end of the line, or 2. ALL child nodes need to be fetched, since they don't belong to the tuckedPath.
Collection<ObjectID> result = new ArrayList<ObjectID>();
ObjectID parentID = parentNode.getJdoObjectID(); // <-- This parentID is a 'PersonRelationID'.
Set<PropertySetID> nextIDsOnPath = getNextRelatedPropertySetIDOnPath(parentNode);
PropertySetID correspondingPSID = getCorrespondingPSID(parentNode); // <-- This is the same as the original getToID(). And, only if the parentNode has been instantiated, then this is the same as parentNode.getPropertySetID().
if (logger.isDebugEnabled()) {
logger.debug("~~ CHECK I: correspondingPSID = " + PersonRelationTreeUtil.showObjectID(correspondingPSID));
logger.debug(PersonRelationTreeUtil.showObjectIDs(" nextIDsOnPath ", nextIDsOnPath, 10));
}
// [Guard check 1] Here, if our nextIDOnPath is null, then this node should be treated as a normal node; i.e. and if we check its status, we should also see 'TuckedNodeStatus.NORMAL'.
if (nextIDsOnPath == null)
return parentID instanceof PersonRelationID ? super.retrieveChildObjectIDsByPersonRelationIDs(parentNode, monitor) : super.retrieveChildObjectIDsByPropertySetIDs(parentNode, monitor);
// [Guard check 2] No need to handle a node that has already been handled.
PropertySetID parentPSID = parentNode.getPropertySetID();
TuckedNodeStatus statusToChangeTo = parentNode.getStatusToChangeTo();
if (parentPSID != null && statusToChangeTo.equals(TuckedNodeStatus.UNSET))
return result;
// Load the ID's accordingly. These are the cases that we need to adhere to. (since 2010.03.28)
// [Case 1]:: Upon first load. Condition: parentNode.getPropertySetID() == null. Reaction: Default --> treat parentNode as a TuckedNode.
// [Case 2]:: Change of status from TUCKED to UNTUCKED. Condition: parentNode.getStatusToChangeTo() == UNTUCKED. Reaction: Untuck parentNode, and retrieve all childObjectIDs as per normal.
// [Case 3]:: Change of status from UNTUCKED to TUCKED. Condition: parentNode.getStatusToChangeTo() == TUCKED. Reaction: Default --> treat parentNode as a TuckedNode.
// [General case]: Ensure that the TuckedNode internal information is current.
Set<PropertySetID> propertySetIDsToRoot = CollectionUtil.createHashSetFromCollection(parentNode.getPropertySetIDsToRoot());
TuckedQueryCount tqCount = PersonRelationDAO.sharedInstance().getTuckedPersonRelationCount(
null, correspondingPSID, propertySetIDsToRoot, nextIDsOnPath, //CollectionUtil.createHashSet(nextIDOnPath),
new SubProgressMonitor(monitor, 80));
parentNode.setActualChildCount(tqCount.actualChildCount);
parentNode.setTuckedChildCount(tqCount.tuckedChildCount);
// The filtered IDs that we want.
Collection<PersonRelationID> filteredPersonRelationIDs = null;
// These two conditions are NOT mutually exclusive.
boolean isHandleTuckedRetrieval = parentPSID == null || statusToChangeTo.equals(TuckedNodeStatus.TUCKED);
boolean isHandleUnTuckedRetrieval = statusToChangeTo.equals(TuckedNodeStatus.UNTUCKED);
// Now we handle [Case 1] and [Case 3].
if (isHandleTuckedRetrieval) {
if (logger.isDebugEnabled())
logger.debug(":::: Handling [Case 1] and [Case 3] :::: ::::::::::::::::::::");
filteredPersonRelationIDs = PersonRelationDAO.sharedInstance().getInclusiveFilteredPersonRelationIDs(
null, correspondingPSID, null,
null, nextIDsOnPath,
getPersonRelationComparator(),
new SubProgressMonitor(monitor, 80));
parentNode.setTuckedStatus(TuckedNodeStatus.TUCKED);
}
// Handle [Case 2].
else if (isHandleUnTuckedRetrieval) {
if (logger.isDebugEnabled())
logger.debug(":::: Handling [Case 2] :::: :::::::::::::::::::::::::::::::::");
filteredPersonRelationIDs = PersonRelationDAO.sharedInstance().getFilteredPersonRelationIDs(
null, correspondingPSID, null,
null, propertySetIDsToRoot,
getPersonRelationComparator(),
new SubProgressMonitor(monitor, 80));
parentNode.setTuckedStatus(TuckedNodeStatus.UNTUCKED);
}
if (logger.isDebugEnabled())
logger.debug("++++++++++++++++++++++++ -------------->>> " + parentNode.toDebugString());
// Tidy up, and we're done.
if (filteredPersonRelationIDs != null) {
parentNode.setChildNodeCount(filteredPersonRelationIDs.size());
result.addAll(filteredPersonRelationIDs);
}
return result;
}
// ---------------------------------------------------------------------------------------------------------------------------------- ++ ------>>
@Override
protected Collection<ObjectID> retrieveChildObjectIDsByPropertySetIDs(TuckedPersonRelationTreeNode parentNode, ProgressMonitor monitor) {
// Similar to the method retrieveChildObjectIDsByPersonRelationIDs, we need some kind of coordination.
// And theoretically, we can handle them collectively! ... and so far, the theory is holding :)
if (!parentNode.isNodeSet())
parentNode.setStatusToChangeTo(TuckedNodeStatus.TUCKED); // <-- We just need to ensure that the node is TUCKED upon initialisation.
if (logger.isDebugEnabled())
logger.debug("~~~~~~~~~~~~~~ I'm here: @retrieveChild--((ObjectID))--sByPropertySetIDs");
return retrieveChildObjectIDsByPersonRelationIDs(parentNode, monitor); // <-- Factorised since 2010.03.30.
}
// -------------------------------------------------------------------------------------------------- ++ ------>>
// These manage the the correlated correspondence between the tuckedPSIDPaths and tuckedPRIDPaths.
// -------------------------------------------------------------------------------------------------- ++ ------>>
// Notes on the multiple forked paths:
// Let P1, P2, ..., Pn be the n unique paths in PSIDPaths.
// Let P' \subset PSIDPaths so that P' is the set of unique paths containing an element e.
// Then the following is true:
// (i) Each subPath from the element e to the root, for all paths in P', is unique; and
// (ii) Each subPath from the element e to the final-child, for all paths in P', is the same.
//
// This means that we can correctly identify the correct path P \elementOf PSIDPaths, given an element e, if and only if we
// we have the subPath from the element e to the root.
/**
* @return the reference indexes to all the paths containing the mathing subPath-to-the-root based on the given tuckedNode.
* Returns an empty list if no reference(s) to the path can be found.
*/
private List<Integer> getPathIndexReferences(TuckedPersonRelationTreeNode tuckedNode) {
LinkedList<Integer> indexRefs = new LinkedList<Integer>();
// For the correct identification of the path's index, the given tuckedNode must have already been duly and correctly initialised.
LinkedList<ObjectID> objectIDsToRoot = (LinkedList<ObjectID>) tuckedNode.getJDOObjectIDsToRoot(); // The root reference is at the beginning of this List.
for (int index = 0; index < tuckedPRIDPathMaps.size(); index++) {
Deque<ObjectID> tuckedPRIDPath = tuckedPRIDPathMaps.get(index); // [Note. B.] The root reference is at the END of this Deque. Yeah, I know, how stupidly annoying... Kai :/
Iterator<ObjectID> iterPRIDPath = tuckedPRIDPath.iterator();
// This loop terminates on three conditions:
// 1. CLEANLY. When both paths we are comparing are the same length, and have exactly the same elements.
// 2. CLEANLY. When objectIDsToRoot is exactly a subPath of iterPRIDPath. --> In which case, we simply keep the index value at where we stopped.
// 3. When we find disequality between elements.
boolean isSubPath = true;
for(Iterator<ObjectID> iterObjectID = objectIDsToRoot.descendingIterator(); iterObjectID.hasNext(); ) { // See reason in [Note. B.] above.
ObjectID objectID = iterObjectID.next();
if (!iterPRIDPath.hasNext()) // [Case 2.] If we survive up till here, which means we have exhausted the elements in objectIDsToRoot, which means we have found the correct index reference.
break;
if (!iterPRIDPath.next().equals(objectID)) { // [Case 3.]
isSubPath = false;
break;
}
}
if (isSubPath) // [Case 1.]
indexRefs.add(index);
}
return indexRefs;
}
/**
* @return the next {@link PropertySetID}s on the related tuckedPSIDpath, based on the {@link ObjectID} from the given tuckedNode.
* Returns null, if no match is found.
*/
private Set<PropertySetID> getNextRelatedPropertySetIDOnPath(TuckedPersonRelationTreeNode tuckedNode) {
List<Integer> indexes = getPathIndexReferences(tuckedNode);
if (indexes.isEmpty())
return null;
Set<PropertySetID> propertySetIDs = null;
for (Integer index : indexes) {
Deque<ObjectID> tuckedPRIDPath = tuckedPRIDPathMaps.get(index);
Deque<ObjectID> tuckedPSIDPath = tuckedPSIDPathMaps.get(index);
Iterator<ObjectID> iterPRID = tuckedPRIDPath.iterator();
Iterator<ObjectID> iterPSID = tuckedPSIDPath.iterator();
while (iterPRID.hasNext()) {
iterPSID.next();
if (iterPRID.next().equals(tuckedNode.getJdoObjectID()) && iterPSID.hasNext()) {
// return (PropertySetID) (iterPSID.hasNext() ? iterPSID.next() : null); // <-- Original: Handles the singular form.
if (propertySetIDs == null)
propertySetIDs = new HashSet<PropertySetID>();
propertySetIDs.add((PropertySetID) iterPSID.next());
break;
}
}
}
return propertySetIDs;
}
/**
* @return the next {@link ObjectID}s on the related tuckedPRIDPath, based on the {@link ObjectID} from the given tuckedNode.
* Returns null, if not match is found.
*/
private Set<ObjectID> getNextObjectIDOnPath(TuckedPersonRelationTreeNode tuckedNode) {
List<Integer> indexes = getPathIndexReferences(tuckedNode);
if (indexes.isEmpty())
return null;
Set<ObjectID> objectIDs = null;
for (Integer index : indexes) {
Deque<ObjectID> tuckedPRIDPath = tuckedPRIDPathMaps.get(index);
Iterator<ObjectID> iterPRID = tuckedPRIDPath.iterator();
while (iterPRID.hasNext()) {
if (iterPRID.next().equals(tuckedNode.getJdoObjectID()) && iterPRID.hasNext()) {
// return iterPRID.hasNext() ? iterPRID.next() : null; // <-- Original: Handles the 'singular' form.
if (objectIDs == null)
objectIDs = new HashSet<ObjectID>();
objectIDs.add(iterPRID.next());
break;
}
}
}
return objectIDs;
}
/**
* @return the corresponding {@link PropertySetID}, based on the {@link ObjectID} from the given tuckedNode.
* Returns null, if no match is found.
*/
private PropertySetID getCorrespondingPSID(TuckedPersonRelationTreeNode tuckedNode) {
List<Integer> indexes = getPathIndexReferences(tuckedNode);
if (indexes.isEmpty())
return null;
Deque<ObjectID> tuckedPRIDPath = tuckedPRIDPathMaps.get(indexes.get(0));
Deque<ObjectID> tuckedPSIDPath = tuckedPSIDPathMaps.get(indexes.get(0));
Iterator<ObjectID> iterPRID = tuckedPRIDPath.iterator();
Iterator<ObjectID> iterPSID = tuckedPSIDPath.iterator();
while (iterPRID.hasNext()) {
ObjectID psID = iterPSID.next();
if (iterPRID.next().equals(tuckedNode.getJdoObjectID()))
return (PropertySetID) psID;
}
return null;
}
/**
* @return the sub-path of the tuckedPRIDpath, up to and inclusive of the currentID in the given tuckedNode.
* Returns null if the given currentID is not found in any of the known tuckedPRIDpaths.
*/
protected Deque<ObjectID> getSubPathUpUntilCurrentID(TuckedPersonRelationTreeNode tuckedNode) {
List<Integer> indexes = getPathIndexReferences(tuckedNode);
if (indexes.isEmpty())
return null;
Deque<ObjectID> tuckedPRIDPath = tuckedPRIDPathMaps.get(indexes.get(0));
Deque<ObjectID> subPath = new LinkedList<ObjectID>();
for (ObjectID objectID : tuckedPRIDPath) {
subPath.add(objectID);
if (objectID.equals(tuckedNode.getJdoObjectID()))
return subPath;
}
return null;
}
}