view src/treecms/memory/OnMemoryTreeEditor.java @ 9:17ed97ca9960

commit
author shoshi
date Mon, 18 Apr 2011 01:07:27 +0900
parents f96193babac0
children
line wrap: on
line source

package treecms.memory;

import java.util.List;
import treecms.api.Forest;
import treecms.api.Node;
import treecms.api.NodeData;
import treecms.api.TreeEditor;
import treecms.merger.Merger;
import treecms.merger.ReplaceMerger;
import treecms.tree.util.NodePathFinder;
import treecms.tree.util.PathNotFoundException;

/**
 * 木構造を非破壊的に編集するオンメモリの実装です。 
 * @author shoshi
 */
final class OnMemoryTreeEditor implements TreeEditor
{
	private OnMemoryTree m_tree;
	private OnMemoryNode m_backup;
	private OnMemoryNode m_root;
	
	/**
	 * コンストラクタです。
	 * @param _tree 監視の対象とするOnMemoryTree
	 */
	public OnMemoryTreeEditor(OnMemoryTree _tree)
	{
		m_root = (OnMemoryNode)_tree.getRoot();
		m_backup = m_root;
	}
	
	/**
	 * 変更を元の木構造にコミットします。この操作はコミット先が先にアップデートされていた場合は失敗します
	 * @param _force trueの場合は強制的に置き換えます。
	 * @return コミットが成功した場合true
	 */
	@Override
	public boolean commit(boolean _force)
	{
		return m_tree.compareAndSwapRootNode(m_backup,m_root,_force);
	}

	/**
	 * 監視している木構造のルートと自身のルートに上書きします。 
	 * @return true
	 */
	@Override
	public boolean pull()
	{
		m_root = (OnMemoryNode)m_tree.getRoot();
		m_backup = m_root;
		return true;
	}

	/**
	 * 監視している木構造が更新されていないか確認します。
	 * @return 更新されている場合true、されていない場合はfalse
	 */
	@Override
	public boolean check()
	{
		if(m_tree.getRoot().getID().equals(m_root.getID())){
			return false;
		}
		return true;
	}

	/**
	 * 監視している木構造の内容を自分の木構造にマージします
	 * 最新版の木構造とマージする場合は、先にpull()を実行してください。
	 */
	@Override
	public void merge()
	{
		Merger merger = new ReplaceMerger();
		m_root = (OnMemoryNode)merger.merge(m_backup,m_root);
	}
	
	/**
	 * 木構造を非破壊的に更新します.
	 * @param _target 更新する対象
	 * @param _newData 更新に適用されるNodeData
	 * @param _path 対象ノードまでのパス
	 * @return 更新されたNode
	 * @throws ルートNodeから_targetまでのパスが見つからない場合、PathNotFoundException
	 */
	@Override
	public Node updateTree(Node _target,NodeData _newData,Node[] _path) throws PathNotFoundException
	{
		//パスの正当性の検証
		if(_path == null){
			throw new NullPointerException("_path is null");
		}
		if(_path.length == 0){
			throw new PathNotFoundException("node path is empty");
		}else{
			//ここでごちゃごちゃする
			if(!m_root.getID().equals(_path[0].getID())){
				throw new PathNotFoundException("wrong root node");
			}
			int size = _path.length;
			Node prev = _path[0];
			//重そう
			for(int i = 1;i < size;i ++){
				if(!prev.children().contains(_path[i])){
					throw new PathNotFoundException("invalid node path");
				}
			}
		}
		
		return _updateTree(_target,_newData,_path);
	}
	
	/**
	 * 木構造を非破壊的に更新します.
	 * @param _target 更新する対象
	 * @param _newData 更新に適用されるNodeData
	 * @return 更新されたNode
	 * @throws ルートNodeから_targetまでのパスが見つからない場合、PathNotFoundException
	 */
	@Override
	public Node updateTree(Node _target,NodeData _newData) throws PathNotFoundException
	{
		NodePathFinder finder = new NodePathFinder(m_root,_target);
		List<Node> path = finder.list();
		
		return _updateTree(_target,_newData,(Node[])path.toArray());
	}
	
	private Node _updateTree(Node _target,NodeData _newData,Node[] _path)
	{
		Forest forest = m_root.getForest();
		
		/*
		 * 改良する必要がある、ランダムアクセスの性能はデータ構造による。
		 * とりあえず、配列使っとく
		 */
		Node ret = forest.create(_newData);
		Node prev = ret;
		for(int i = _path.length-1;i >= 0;i --){
			Node prevParent = _path[i]; //prevの親
			NodeData data = prevParent.getData();
			
			//子供をOrderdSetにすれば早いかもね
			NodeData newData = new NodeData();
			newData.putAll(data.getAll());
			for(Node child : data.children()){
				//UUIDに対応するノードを削除し、追加する
				if(child.getID().isFamily(prev.getID())){
					newData.add(prev);
				}else{
					newData.add(child);
				}
			}
			
			prev = forest.create(newData);
		}
		m_root = (OnMemoryNode)prev;
		return ret;
	}
	
	/*
	private LinkedList<OnMemoryNode> findAndClone(OnMemoryNode _parent,OnMemoryNode _target,NodeData _newData)
	{
		OnMemoryForest forest = (OnMemoryForest)_target.getForest();
		if(_parent.getID().equals(_target.getID())){
			LinkedList<OnMemoryNode> path = new LinkedList<OnMemoryNode>();
			OnMemoryNode clone = forest.createNode(_target.getID().update(),_newData);
			path.addFirst(clone);
			return path;
		}
		
		for(Node child : _parent.children()){
			LinkedList<OnMemoryNode> path = findAndClone((OnMemoryNode)child,_target,_newData);
			if(path != null){
				path.addFirst(_parent);
				return path;
			}
		}
		
		return null;
	}
	*/
	
	/**
	 * ルートNodeを取得します。
	 * @return この木構造のルートNode
	 * */
	@Override
	public Node getRoot()
	{
		return m_root;
	}
}