All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.carleton.gcrc.couch.date.cluster.Tree Maven / Gradle / Ivy

There is a newer version: 2.2.7
Show newest version
package ca.carleton.gcrc.couch.date.cluster;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ca.carleton.gcrc.couch.date.impl.NowReference;
import ca.carleton.gcrc.couch.date.impl.TimeInterval;

/**
 * This is the implementation of a cluster tree. It accepts a number of elements
 * and builds an index. It creates a number of nodes to manage the various clusters
 * which ultimately are the keys to the index.
 * 
 * A tree is made of nodes in a tree arrangement. The tree holds to the root node.
 * 
 * There is also a list of legacy nodes. The legacy nodes represents clusters from a
 * previous tree. These clusters are still referenced in the database by documents. However,
 * there is no need for these nodes to be arranged in a tree since no more elements are
 * added to those nodes.
 */
public class Tree implements IntervalClusterTree {
	
	static public Tree restoreTree(JSONObject jsonTree, TreeOperations operations) throws Exception {

		int nextId = jsonTree.getInt("nextId");

		// Regular root node
		TreeNodeRegular regularNode = null;
		JSONObject regularRoot = jsonTree.optJSONObject("regularRoot");
		if( null == regularRoot ){
			if( null != jsonTree.opt("min") 
			 && null != jsonTree.opt("max")
			 && null != jsonTree.opt("mid") ){
				// Legacy image of tree.
				TreeNode node = restoreNode(jsonTree);
				if( node instanceof TreeNodeRegular ){
					regularNode = (TreeNodeRegular)node;
				} else {
					throw new Exception("Unexpected class for regular root node: "+node.getClass().getName());
				}
			}
		} else {
			TreeNode node = restoreNode(regularRoot);
			if( node instanceof TreeNodeRegular ){
				regularNode = (TreeNodeRegular)node;
			} else {
				throw new Exception("Unexpected class for regular root node: "+node.getClass().getName());
			}
		}
		if( null == regularNode ){
			TimeInterval interval = new TimeInterval(0, 1500000000000L);
			regularNode = new TreeNodeRegular(nextId, interval);
			++nextId;
		}
		
		// Ongoing root node
		TreeNodeOngoing ongoingNode = null;
		JSONObject nowRoot = jsonTree.optJSONObject("nowRoot");
		if( null != nowRoot ){
			TreeNode node = restoreNode(nowRoot);
			if( node instanceof TreeNodeOngoing ){
				ongoingNode = (TreeNodeOngoing)node;
			} else {
				throw new Exception("Unexpected class for now root node: "+node.getClass().getName());
			}
		}
		if( null == ongoingNode ){
			TimeInterval interval = new TimeInterval(0, (NowReference)null);
			ongoingNode = new TreeNodeOngoing(nextId, interval);
			++nextId;
		}
		
		Tree tree = new Tree(regularNode, ongoingNode, nextId, operations);
		
		JSONArray legacyNodes = jsonTree.optJSONArray("legacyNodes");
		if( null != legacyNodes ){
			for(int i=0,e=legacyNodes.length(); i infoObjs = tree.getOperations().getAllClusterInfo();
		Map infoByClusterId = new HashMap();
		for(TreeOperations.ClusterInfo info : infoObjs){
			infoByClusterId.put(info.clusterId, info);
		}
		
		TreeOperations.ClusterInfo rootInfo = infoByClusterId.get(null);
		int rootCount = 0;
		if( null != rootInfo ) {
			rootCount = rootInfo.count;
		}
		
		pw.println("digraph date {");
		pw.println("ROOT [label=\"id:null,count:"+rootCount+"\"];");
		
		if( null != tree.regularRootNode ){
			nodeToDot(tree.regularRootNode, pw, infoByClusterId);
			pw.println("ROOT -> n"+tree.regularRootNode.getClusterId()+";");
		}

		if( null != tree.ongoingRootNode ){
			nodeToDot(tree.ongoingRootNode, pw, infoByClusterId);
			pw.println("ROOT -> n"+tree.ongoingRootNode.getClusterId()+";");
		}
		
		List legacyNodes = tree.legacyNodes;
		if( null != legacyNodes ){
			for(TreeNode legacyNode : legacyNodes){
				nodeToDot(legacyNode, pw, infoByClusterId);
				pw.println("ROOT -> n"+legacyNode.getClusterId()+";");
			}
		}
		
		pw.println("}");
	}
	
	static public void nodeToDot(
			TreeNode node
			,PrintWriter pw
			,Map infoByClusterId) throws Exception{
		
		TreeOperations.ClusterInfo info = infoByClusterId.get(node.getClusterId());
		
		int count = 0;
		if( null != info ){
			count = info.count;
		}
		
		TimeInterval nodeInterval = node.getInterval();
		String minStr = ""+nodeInterval.getMin();
		String maxStr = "now";
		if( !nodeInterval.isOngoing() ){
			maxStr = ""+nodeInterval.getMax(null);
		}
		pw.println("n"+node.getClusterId()+" [label=\"id:"+node.getClusterId()+",count:"+count+",min:"+minStr+",max:"+maxStr+"\"];");
		
		if( null != node.getLowChildNode() ){
			nodeToDot(node.getLowChildNode(), pw, infoByClusterId);
			pw.println("n"+node.getClusterId()+" -> n"+node.getLowChildNode().getClusterId()+";");
		}
		
		if( null != node.getHighChildNode() ){
			nodeToDot(node.getHighChildNode(), pw, infoByClusterId);
			pw.println("n"+node.getClusterId()+" -> n"+node.getHighChildNode().getClusterId()+";");
		}
	}
	
	static private class TreeInfo {
		public int maxDepth = 0;
		public int nodeCount = 0;
		public int regularNodeCount = 0;
		public int ongoingNodeCount = 0;
		public int legacyNodeCount = 0;
		public long minInterval = 0;
	}
	
	static public void treeToInfo(Tree tree, PrintWriter pw) throws Exception {
		TreeInfo treeInfo = new TreeInfo();
		
		if( null != tree.getRegularRootNode() ){
			treeInfo.minInterval = tree.getRegularRootNode().getInterval().getSize(null);
		} else {
			treeInfo.minInterval = 0;
		}
		
		treeInfo.legacyNodeCount = tree.getLegacyNodes().size();
		
		if( null != tree.getRegularRootNode() ){
			nodeToInfo(tree.getRegularRootNode(), treeInfo, 1);
		}
		
		if( null != tree.getOngoingRootNode() ){
			nodeToInfo(tree.getOngoingRootNode(), treeInfo, 1);
		}
		
		pw.println("Node count: "+treeInfo.nodeCount);
		pw.println("Regular node count: "+treeInfo.regularNodeCount);
		pw.println("On-going node count: "+treeInfo.ongoingNodeCount);
		pw.println("Legacy node count: "+treeInfo.legacyNodeCount);
		pw.println("Max node depth: "+treeInfo.maxDepth);
		pw.println("Full interval: "+tree.getRegularRootNode().getInterval());
		pw.println("Min interval size: "+treeInfo.minInterval);
		
	}

	static private void nodeToInfo(TreeNode node, TreeInfo treeInfo, int depth) throws Exception {
		treeInfo.nodeCount++;
		
		if( treeInfo.maxDepth < depth ){
			treeInfo.maxDepth = depth;
		}
		
		if( node.getInterval().isOngoing() ) {
			++treeInfo.ongoingNodeCount;
			
		} else {
			++treeInfo.regularNodeCount;
			
			if( treeInfo.minInterval > node.getInterval().getSize(null) ){
				treeInfo.minInterval = node.getInterval().getSize(null);
			}
		}
		
		if( null != node.getLowChildNode() ){
			nodeToInfo(node.getLowChildNode(), treeInfo, depth+1);
		}
		
		if( null != node.getHighChildNode() ){
			nodeToInfo(node.getHighChildNode(), treeInfo, depth+1);
		}
	}

	final protected Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private TreeOperations operations;
	private TreeNodeRegular regularRootNode;
	private TreeNodeOngoing ongoingRootNode;
	private int nextId = 1;
	private List legacyNodes = new Vector();
	
	public Tree(
		TreeNodeRegular regularRootNode, 
		TreeNodeOngoing ongoingRootNode, 
		int nextId, 
		TreeOperations operations ){
		
		this.regularRootNode = regularRootNode;
		this.ongoingRootNode = ongoingRootNode;
		this.nextId = nextId;
		this.operations = operations;
	}

	public Tree(TreeRebalanceProcess.Result treeInfo, TreeOperations operations){
		regularRootNode = treeInfo.regularRootNode;
		ongoingRootNode = treeInfo.ongoingRootNode;
		nextId = treeInfo.nextClusterId;
		legacyNodes.addAll( treeInfo.legacyNodes );
		this.operations = operations;
	}

	public TreeOperations getOperations(){
		return operations;
	}
	
	synchronized public TreeNodeRegular getRegularRootNode(){
		return regularRootNode;
	}
	
	synchronized public TreeNodeOngoing getOngoingRootNode(){
		return ongoingRootNode;
	}
	
	synchronized public int getNextClusterId(){
		return nextId;
	}
	
	synchronized public void setNextClusterId(int nextId){
		this.nextId = nextId;
	}
	
	synchronized public List getLegacyNodes(){
		return legacyNodes;
	}

	synchronized public void addLegacyNode(TreeNode legacyNode) {
		legacyNodes.add(legacyNode);
	}

	synchronized public void clearLegacyNodes() {
		legacyNodes = new Vector();
	}
	
	public void reload() throws Exception {
		Tree updatedTree = null;
		try {
			updatedTree = operations.loadTree();
		} catch (Exception e) {
			logger.info("Unable to reload tree",e);
		}
		
		if( null == updatedTree ){
			try {
				updatedTree = operations.recoverTree();
				
				logger.info("Recovered date cluster tree");
				
			} catch(Exception e) {
				logger.error("Unable to recover date cluster tree",e);
				throw new Exception("Unable to recover date cluster tree",e);
			}
		}

		synchronized(this){
			regularRootNode = updatedTree.getRegularRootNode();
			nextId = updatedTree.getNextClusterId();
			legacyNodes = updatedTree.getLegacyNodes();
		}
	}
	
	synchronized public JSONObject toJSON() throws Exception {
		JSONObject jsonObj = new JSONObject();
		
		JSONObject regularRoot = saveNode(regularRootNode);
		if( null != regularRoot ){
			jsonObj.put("regularRoot", regularRoot);
		}
		
		JSONObject nowRoot = saveNode(ongoingRootNode);
		if( null != nowRoot ){
			jsonObj.put("nowRoot", nowRoot);
		}
		
		jsonObj.put("nextId", nextId);
		
		if( legacyNodes.size() > 0 ){
			JSONArray jsonLegacyNodes = new JSONArray();
			
			for(TreeNode legacyNode : legacyNodes){
				JSONObject jsonChild = saveNode(legacyNode);
				jsonLegacyNodes.put(jsonChild);
			}
			
			jsonObj.put("legacyNodes", jsonLegacyNodes);
		}
		
		return jsonObj;
	}

	@Override
	synchronized public List clusterIdsFromInterval(
			TimeInterval interval, 
			NowReference now) throws Exception {
		
		List clusterIds = new Vector();
		
		if( null != regularRootNode ){
			regularRootNode.accumulateClusterIdsFromInterval(interval, clusterIds, now);
		}
		
		if( null != ongoingRootNode ){
			ongoingRootNode.accumulateClusterIdsFromInterval(interval, clusterIds, now);
		}
		
		for(TreeNode legacyNode : legacyNodes){
			legacyNode.accumulateClusterIdsFromInterval(interval, clusterIds, now);
		}
		
		return clusterIds;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy