package org.gbif.checklistbank.nub;
import org.gbif.api.vocabulary.Kingdom;
import org.gbif.checklistbank.nub.model.NubUsage;
import org.gbif.checklistbank.nub.model.SrcUsage;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ParentStack {
private static final Logger LOG = LoggerFactory.getLogger(ParentStack.class);
private Map<Integer, NubUsage> nubMap = Maps.newHashMap();
private LinkedList<SrcUsage> parents = Lists.newLinkedList();
private NubUsage currParent;
private Kingdom currKingdom;
private final NubUsage unknownKingdom;
public ParentStack(NubUsage unknownKingdom) {
this.unknownKingdom = unknownKingdom;
currKingdom = Kingdom.INCERTAE_SEDIS;
}
/**
* Returns the current lowest nub parent node. If no parent was set the unknown kingdom is returned, never null.
*/
public NubUsage nubParent() {
return currParent == null ? unknownKingdom : currParent;
}
/**
* @return the nub kingdom the current classification is rooted in
*/
public Kingdom nubKingdom() {
return currKingdom;
}
public int size() {
return parents.size();
}
/**
* Puts a new source usage onto the stack.
* If the parentKey of the new usage matches the currently last source usage on the stack it is simply added.
* Otherwise the current stack is reduced as long until the parentKey matches the key of the last usage on the stack
* @param src
* @return the matching nub node of the source parentKey
*/
public void add(SrcUsage src) {
if (src.parentKey == null) {
// this is a new root source usage, clear stack
clear();
} else {
while (!parents.isEmpty()) {
if (parents.getLast().key.equals(src.parentKey)) {
// the last src usage on the parent stack represents the current parentKey, we are in good state!
break;
} else {
// remove last parent until we find the real one
SrcUsage p = parents.removeLast();
nubMap.remove(p.key);
}
}
if (parents.isEmpty()) {
throw new IllegalStateException("Source parent node " + src.parentKey + " not found for " + src.scientificName);
}
// set current nub parent
currParent = null;
Iterator<SrcUsage> iter = parents.descendingIterator();
while (iter.hasNext()) {
SrcUsage u = iter.next();
if (u.key != null && nubMap.containsKey(u.key)) {
currParent = nubMap.get(u.key);
break;
}
}
}
parents.add(src);
}
/**
* @return true if the current sources immediate parent is part of the nub
*/
public boolean parentInNub() {
SrcUsage curr = parents.getLast();
if (curr != null) {
return nubMap.containsKey(curr.parentKey);
}
return false;
}
public LinkedList<SrcUsage> getParents() {
return parents;
}
/**
* Assigns the matching nub node to the currently last source usage on the stack, replacing whatever was there.
*/
public void put(NubUsage nub) {
nubMap.put(parents.getLast().key, nub);
currKingdom = nub.kingdom;
}
public void clear() {
nubMap.clear();
parents.clear();
currParent = null;
currKingdom = Kingdom.INCERTAE_SEDIS;
}
}