changeset 21:f3150b37f9be

commit
author shoshi
date Mon, 06 Jun 2011 21:49:04 +0900
parents 084de6909451
children fa784faafc78
files src/treecms/api/Forest.java src/treecms/api/MonotonicTreeNode.java src/treecms/api/Node.java src/treecms/memory/OnMemoryForest.java src/treecms/memory/OnMemoryMonotonicTree.java src/treecms/memory/OnMemoryMonotonicTreeNode.java src/treecms/memory/OnMemoryNode.java src/treecms/test/SwingTest1.java src/treecms/tree/id/AbstractNodeID.java src/treecms/tree/id/RandomNodeID.java src/treecms/tree/util/NodeChildrenImpl.java
diffstat 11 files changed, 350 insertions(+), 205 deletions(-) [+]
line wrap: on
line diff
--- a/src/treecms/api/Forest.java	Wed Jun 01 15:35:50 2011 +0900
+++ b/src/treecms/api/Forest.java	Mon Jun 06 21:49:04 2011 +0900
@@ -1,9 +1,12 @@
 package treecms.api;
 
+import treecms.tree.util.NodeData;
+
 public interface Forest
 {
 	MonotonicTree get(NodeID _id);
 	MonotonicTree getTip(String _uuid);
 	MonotonicTree create();
+	MonotonicTree create(NodeData<Node> _data);
 	MonotonicTree getMainTree();
 }
--- a/src/treecms/api/MonotonicTreeNode.java	Wed Jun 01 15:35:50 2011 +0900
+++ b/src/treecms/api/MonotonicTreeNode.java	Mon Jun 06 21:49:04 2011 +0900
@@ -7,5 +7,6 @@
  */
 public interface MonotonicTreeNode extends NodeContext , NodeAttributes , NodeChildren<MonotonicTreeNode>
 {
+	public Node getNode();
 	public MonotonicTreeNode getParent();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/api/Node.java	Mon Jun 06 21:49:04 2011 +0900
@@ -0,0 +1,5 @@
+package treecms.api;
+
+public interface Node extends NodeContext , NodeAttributes , NodeChildren<Node>
+{
+}
--- a/src/treecms/memory/OnMemoryForest.java	Wed Jun 01 15:35:50 2011 +0900
+++ b/src/treecms/memory/OnMemoryForest.java	Mon Jun 06 21:49:04 2011 +0900
@@ -1,191 +1,74 @@
 package treecms.memory;
 
 import java.util.Map;
-import java.util.Random;
-import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import treecms.api.Forest;
-import treecms.api.MonotonicTreeNode;
+import treecms.api.Node;
 import treecms.api.NodeID;
 import treecms.api.MonotonicTree;
-import treecms.tree.id.AbstractRandomNodeID;
+import treecms.tree.id.RandomNodeID;
 import treecms.tree.util.NodeData;
 
-/**
- * Forestのオンメモリ上の実装.
- * @author shoshi
- */
 public class OnMemoryForest implements Forest
 {
-	//Nodeのマップ
 	private final Map<NodeID,OnMemoryNode> m_table;
-	//最新版Nodeのマップ
 	private final Map<String,OnMemoryNode> m_tipTable;
+	private final OnMemoryMonotonicTree m_mainTree;
 	
-	//MainTreeのUUID
-	private final String m_mainID;
-	
-	/**
-	 * コンストラクタ
-	 */
 	public OnMemoryForest()
 	{
 		m_table = new ConcurrentHashMap<NodeID,OnMemoryNode>();
 		m_tipTable = new ConcurrentHashMap<String,OnMemoryNode>();
 		
-		m_mainID = createNode(null,null).getID().getFamilyID();
+		OnMemoryNode root = (OnMemoryNode)create();
+		m_mainTree = new OnMemoryMonotonicTree(root);
 	}
 	
-	/**
-	 * 新しくNodeを作成します
-	 * @param _id Nodeに適用されるNodeID
-	 * @param _newData Nodeが保持するNodeData
-	 * @return 作成されたOnMemoryNode
-	 */
-	public OnMemoryNode createNode(NodeID _id,NodeData<OnMemoryNode> _newData)
+	private NodeID createID(String _fid)
 	{
-		NodeID newID = (_id != null) ? _id : createID();
-		NodeData<OnMemoryNode> newData = (_newData != null) ? _newData : new NodeData<OnMemoryNode>();
-		
-		//newIDとnewDataを元にOnMemoryNodeを生成する.
-		OnMemoryNode newNode = new OnMemoryNode(this,newID,newData);
-		
-		//登録
-		m_table.put(newID,newNode);
-		m_tipTable.put(newID.getFamilyID(),newNode);
-		
-		return newNode;
-	}
-	
-	/**
-	 * 新しいNodeIDを作成します
-	 * @return 新しいNodeID
-	 */
-	private NodeID createID()
-	{
-		return new RandomNodeID(null);
+		return new RandomNodeID(_fid);
 	}
 
-	/**
-	 * NodeIDに対応するNodeを取得します 
-	 * @return NodeIDに対応するNode
-	 */
 	@Override
 	public MonotonicTree get(NodeID _id)
 	{
-		return m_table.get(_id);
+		OnMemoryNode node = m_table.get(_id);
+		return new OnMemoryMonotonicTree(node);
 	}
 	
-	/**
-	 * 新しくNodeを作成します.
-	 * @return 新しいNode
-	 */
 	@Override
 	public MonotonicTree create()
 	{
-		return createNode(null,null);
-	}
-
-	/**
-	 * NodeDataを保持した新しいNodeを作成します
-	 * @return NodeDataを保持した新しいNode 
-	 */
-	@Override
-	public SingleNode create(NodeData<SingleNode> _data)
-	{
-		return createNode(null,_data);
+		OnMemoryNode node = createNode(createID(null),null);
+		OnMemoryMonotonicTree tree = new OnMemoryMonotonicTree(node);
+		return tree;
 	}
 
-	/**
-	 * 最新のNodeを取得します.
-	 * @return UUIDに対応する最新のNode
-	 */
 	@Override
-	public SingleNode getTip(String _uuid)
+	public MonotonicTree create(NodeData<Node> _data)
 	{
-		return m_tipTable.get(_uuid);
+		OnMemoryNode node = createNode(null,_data);
+		return new OnMemoryMonotonicTree(node);
 	}
 
-	/**
-	 * MainTreeを取得します。
-	 * @return このForestのMainTree
-	 */
+	@Override
+	public MonotonicTree getTip(String _fid)
+	{
+		OnMemoryNode node = m_tipTable.get(_fid);
+		return new OnMemoryMonotonicTree(node);
+	}
+
 	@Override
 	public MonotonicTree getMainTree()
 	{
-		return new OnMemoryTree(m_tipTable.get(m_mainID));
-	}
-	
-	@Override
-	public SingleNode create(NodeID _id,NodeData<SingleNode> _data)
-	{
-		return createNode(_id,_data);
+		return m_mainTree;
 	}
 	
-	/**
-	 * ランダムにバージョン番号を生成するNodeIDです.ファクトリを内包します.
-	 * @author shoshi
-	 */
-	private static class RandomNodeID extends AbstractRandomNodeID
+	public OnMemoryNode createNode(NodeID _newID,NodeData<Node> _newData)
 	{
-		/**
-		 * UUID
-		 */
-		private String m_uuid;
-		
-		/**
-		 * バージョン番号(ランダム値)
-		 */
-		private long m_version;
-		
-		/**
-		 * コンストラクタ
-		 * @param _uuid 継承するUUID
-		 */
-		public RandomNodeID(String _uuid)
-		{
-			m_uuid = (_uuid != null) ? _uuid : UUID.randomUUID().toString();
-			m_version = (new Random()).nextLong();
-		}
-
-		/**
-		 * 新しいRandomNodeIDを作成します。
-		 * @return 新しいRandomNodeID
-		 */
-		@Override
-		public NodeID create()
-		{
-			return new RandomNodeID(null);
-		}
-
-		/**
-		 * UUIDを継承したRandomNodeIDを作成します.
-		 * @return 新しいRandomNodeID
-		 */
-		@Override
-		public NodeID update()
-		{
-			return new RandomNodeID(m_uuid);
-		}
-
-		/**
-		 * UUIDを取得します.
-		 * @return UUID
-		 */
-		@Override
-		public String getUUID()
-		{
-			return m_uuid;
-		}
-
-		/**
-		 * バージョンを取得します.
-		 * @return バージョン
-		 */
-		@Override
-		public String getVersion()
-		{
-			return Long.toHexString(m_version);
-		}
+		OnMemoryNode newNode = new OnMemoryNode(this,_newID,_newData);
+		m_table.put(newNode.getID(),newNode);
+		m_tipTable.put(newNode.getID().getFamilyID(),newNode);
+		return newNode;
 	}
 }
--- a/src/treecms/memory/OnMemoryMonotonicTree.java	Wed Jun 01 15:35:50 2011 +0900
+++ b/src/treecms/memory/OnMemoryMonotonicTree.java	Mon Jun 06 21:49:04 2011 +0900
@@ -1,62 +1,65 @@
 package treecms.memory;
 
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
 import treecms.api.MonotonicTree;
-
 import treecms.api.MonotonicTreeNode;
-import treecms.api.SingleNode;
-import treecms.api.Tree;
+import treecms.api.Node;
 import treecms.merger.Merger;
 import treecms.merger.ReplaceMerger;
 
 public class OnMemoryMonotonicTree implements MonotonicTree
 {
-	private volatile OnMemoryTree m_tree;
 	private OnMemoryNode m_old;
 	private OnMemoryMonotonicTreeNode m_root;
-	private static final Merger<SingleNode> m_merger = new ReplaceMerger<SingleNode>();
+	
+	private static final Merger<Node> m_merger = new ReplaceMerger<Node>();
 	
-	public OnMemoryMonotonicTree(OnMemoryTree _tree)
+	private final ConcurrentMap<String,OnMemoryMonotonicTreeNode> m_members;
+	private final OnMemoryMonotonicTree m_tree; //Remote Repository
+	
+	public OnMemoryMonotonicTree(OnMemoryNode _root)
 	{
-		m_tree = _tree;
-		m_old = (OnMemoryNode)m_tree.getRoot().getNode();
+		m_tree = null;
+		m_old = _root;
 		m_root = new OnMemoryMonotonicTreeNode((OnMemoryNode)m_old,null);
+		m_members = new ConcurrentHashMap<String,OnMemoryMonotonicTreeNode>();
+	}
+	
+	public OnMemoryMonotonicTree(OnMemoryMonotonicTree _tree)
+	{
+		this((OnMemoryNode)_tree.getRoot().getNode());
 	}
 
 	@Override
 	public boolean commit(boolean _force)
 	{
-		return m_tree.compareAndSwapRootNode(m_old,(OnMemoryNode)m_root.getNode(),_force);
+		return true;
 	}
 
 	@Override
 	public boolean pull()
 	{
-		m_old = (OnMemoryNode)m_tree.getRoot().getNode();
+		OnMemoryNode node = (OnMemoryNode)m_tree.getRoot().getNode();
+		OnMemoryMonotonicTreeNode node = new OnMemoryMonotonicTreeNode();
 		return true;
 	}
 
 	@Override
 	public boolean check()
 	{
-		return m_tree.compareAndSwapRootNode(m_old,m_old,false);
+		return m_tree.getRoot().getNode().equals(m_old);
 	}
 
 	@Override
 	public void merge()
 	{
-		//merge here
-		m_old = (OnMemoryNode)m_merger.merge(m_tree.getRoot().getNode(),m_root.getNode());
 	}
 
 	@Override
 	public MonotonicTreeNode getRoot()
 	{
-		return this.m_root;
-	}
-
-	@Override
-	public Tree getTree()
-	{
-		return (Tree)m_tree;
+		return m_root;
 	}
 }
--- a/src/treecms/memory/OnMemoryMonotonicTreeNode.java	Wed Jun 01 15:35:50 2011 +0900
+++ b/src/treecms/memory/OnMemoryMonotonicTreeNode.java	Mon Jun 06 21:49:04 2011 +0900
@@ -11,6 +11,7 @@
 
 import treecms.api.Forest;
 import treecms.api.MonotonicTreeNode;
+import treecms.api.Node;
 import treecms.api.NodeAttributes;
 import treecms.api.NodeChildren;
 import treecms.api.NodeID;
@@ -23,20 +24,20 @@
  */
 public class OnMemoryMonotonicTreeNode implements MonotonicTreeNode
 {
-	private OnMemoryNode m_node;
+	private Node m_node;
 	
 	private static final AtomicReferenceFieldUpdater<OnMemoryMonotonicTreeNode,OnMemoryMonotonicTreeNode> m_parentUpdater 
 		= AtomicReferenceFieldUpdater.newUpdater(OnMemoryMonotonicTreeNode.class,OnMemoryMonotonicTreeNode.class,"m_parent");
 	
 	private OnMemoryMonotonicTreeNode m_parent;
 	
-	private final Map<NodeID,OnMemoryMonotonicTreeNode> m_cache;
+	private final Map<String,OnMemoryMonotonicTreeNode> m_cache;
 	
 	public OnMemoryMonotonicTreeNode(OnMemoryNode _target,OnMemoryMonotonicTreeNode _parent)
 	{
 		m_node  = _target;
 		m_parent = _parent;
-		m_cache = new ConcurrentHashMap<NodeID,OnMemoryMonotonicTreeNode>();
+		m_cache = new ConcurrentHashMap<String,OnMemoryMonotonicTreeNode>();
 	}
 	
 	@Override
@@ -72,7 +73,7 @@
 	@Override
 	public synchronized void put(ByteBuffer _key, ByteBuffer _value)
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		d.put(_key,_value);
 		
 		cloneAndTransmit(d);
@@ -81,7 +82,7 @@
 	@Override
 	public synchronized void putAll(NodeAttributes _map)
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		d.putAll(_map);
 		
 		cloneAndTransmit(d);
@@ -90,7 +91,7 @@
 	@Override
 	public synchronized void remove(ByteBuffer _key)
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		d.remove(_key);
 		
 		cloneAndTransmit(d);
@@ -99,7 +100,7 @@
 	@Override
 	public synchronized void removeAll(Set<ByteBuffer> _keys)
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		d.removeAll(_keys);
 		
 		cloneAndTransmit(d);
@@ -108,32 +109,22 @@
 	@Override
 	public synchronized void clearAttributes()
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		d.clearAttributes();
 		
 		cloneAndTransmit(d);
 	}
 	
-	public synchronized void cloneAndTransmit(NodeData<OnMemoryNode> _d)
+	public synchronized void cloneAndTransmit(NodeData<Node> _d)
 	{
 		OnMemoryForest f = (OnMemoryForest)m_node.getForest();
-		OnMemoryNode clone = f.createNode(m_node.getID().update(),_d);
+		Node clone = f.createNode(m_node.getID().update(),_d);
 		transmit(m_node,clone);
 		
 		m_node = clone;
 	}
 	
-	/**
-	 * このMonotonicNodeに変更が加えられたら,こちらのメソッドが呼び出されます.
-	 * 非破壊的に変更するために,SingleLinkedNodeのクローンを作成し,親にも複製を依頼します.
-	 * 
-	 * RootNodeまで伝搬すると親のNodeはnullとなる.親Nodeは担当するMonotonicTreeのオブジェクトを保持しており,そこにRootNodeが変更されたことを通知する.
-	 * 通知が終わり,処理が戻ってきた時に自身のOnMemoryNodeをクローンした新しいものに書き換えます.
-	 * 
-	 * _fromがnullの場合は,自身が編集元であることを示します.
-	 * @param _from 編集元のOnMemoryMonotonicTreeNode
-	 */
-	public synchronized void transmit(OnMemoryNode _orig,OnMemoryNode _edit)
+	public synchronized void transmit(Node _orig,Node _edit)
 	{
 		OnMemoryForest f = (OnMemoryForest)m_node.getForest();
 		OnMemoryNode clone = f.createNode(m_node.getID().update(),null);
@@ -160,11 +151,17 @@
 	}
 	
 	@Override
+	public synchronized Node getNode()
+	{
+		return m_node;
+	}
+	
+	@Override
 	public synchronized List<MonotonicTreeNode> getList()
 	{
 		//NodeのリストよりMonotonicTreeNodeのリストを作成する.
 		NodeChildren<MonotonicTreeNode> res = new NodeChildrenImpl<MonotonicTreeNode>();
-		for(Iterator<OnMemoryNode> it = m_node.getList().iterator();it.hasNext();){
+		for(Iterator<Node> it = m_node.getList().iterator();it.hasNext();){
 			OnMemoryNode n = (OnMemoryNode)it.next();
 			res.add(getCache(n));
 		}
@@ -176,7 +173,7 @@
 	public synchronized boolean add(MonotonicTreeNode _n)
 	{
 		OnMemoryMonotonicTreeNode mono = (OnMemoryMonotonicTreeNode)_n;
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		boolean ret = d.add(mono.m_node);
 		if(ret){
 			cloneAndTransmit(d);
@@ -189,13 +186,13 @@
 	public synchronized boolean addAll(NodeChildren<MonotonicTreeNode> _list)
 	{
 		//MotonicTreeNodeのリストからNodeのリストを作成する.
-		NodeChildren<OnMemoryNode> c = new NodeChildrenImpl<OnMemoryNode>();
+		NodeChildren<Node> c = new NodeChildrenImpl<Node>();
 		for(Iterator<MonotonicTreeNode> it = _list.getList().iterator();it.hasNext();){
 			OnMemoryMonotonicTreeNode mono = (OnMemoryMonotonicTreeNode)it.next();
 			c.add(mono.m_node);
 		}
 		
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		boolean ret = d.addAll(c);
 		if(ret){
 			cloneAndTransmit(d);
@@ -207,8 +204,8 @@
 	@Override
 	public synchronized MonotonicTreeNode remove(int _index)
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
-		OnMemoryNode n = d.remove(_index);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
+		OnMemoryNode n = (OnMemoryNode) d.remove(_index);
 		if(n != null){
 			cloneAndTransmit(d);
 			return new OnMemoryMonotonicTreeNode((OnMemoryNode)n,null);
@@ -220,7 +217,7 @@
 	@Override
 	public synchronized void clearChildren()
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
 		d.clearChildren();
 		
 		cloneAndTransmit(d);
@@ -241,7 +238,7 @@
 	@Override
 	public synchronized MonotonicTreeNode get(int _index)
 	{
-		OnMemoryNode n = m_node.get(_index);
+		OnMemoryNode n = (OnMemoryNode) m_node.get(_index);
 		return getCache(n);
 	}
 
@@ -249,8 +246,8 @@
 	public synchronized MonotonicTreeNode replace(MonotonicTreeNode _newChild)
 	{
 		OnMemoryMonotonicTreeNode mono = (OnMemoryMonotonicTreeNode)_newChild;
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
-		OnMemoryNode n = d.replace(mono.m_node);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
+		OnMemoryNode n = (OnMemoryNode) d.replace(mono.m_node);
 		
 		if(n != null){
 			cloneAndTransmit(d);
@@ -281,7 +278,7 @@
 	@Override
 	public synchronized MonotonicTreeNode get(String _fid)
 	{
-		OnMemoryNode n = m_node.get(_fid);
+		OnMemoryNode n = (OnMemoryNode) m_node.get(_fid);
 		OnMemoryMonotonicTreeNode mono = getCache(n);
 		
 		return mono;
@@ -290,8 +287,8 @@
 	@Override
 	public synchronized MonotonicTreeNode remove(String _fid)
 	{
-		NodeData<OnMemoryNode> d = new NodeData<OnMemoryNode>(m_node);
-		OnMemoryNode n = d.remove(_fid);
+		NodeData<Node> d = new NodeData<Node>(m_node,m_node);
+		OnMemoryNode n = (OnMemoryNode) d.remove(_fid);
 		
 		cloneAndTransmit(d);
 		
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/memory/OnMemoryNode.java	Mon Jun 06 21:49:04 2011 +0900
@@ -0,0 +1,166 @@
+package treecms.memory;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import treecms.api.Forest;
+import treecms.api.Node;
+import treecms.api.NodeAttributes;
+import treecms.api.NodeChildren;
+import treecms.api.NodeID;
+import treecms.tree.util.NodeData;
+
+class OnMemoryNode implements Node
+{
+	private final OnMemoryForest m_forest;
+	private final NodeID m_id;
+	private final NodeData<Node> m_data;
+	
+	private static final String ERR_READONLY = "this is a readonly node.";
+	
+	public OnMemoryNode(OnMemoryForest _forest,NodeID _id,NodeData<Node> _newData)
+	{
+		m_id = _id;
+		m_forest = _forest;
+		m_data = new NodeData<Node>(_newData);
+	}
+
+	@Override
+	public NodeID getID()
+	{
+		return m_id;
+	}
+
+	@Override
+	public Forest getForest()
+	{
+		return m_forest;
+	}
+
+	@Override
+	public List<Node> getList()
+	{
+		return m_data.getList();
+	}
+
+	@Override
+	public Map<ByteBuffer, ByteBuffer> asMap()
+	{
+		return m_data.asMap();
+	}
+
+	@Override
+	public Set<ByteBuffer> getKeySet()
+	{
+		return m_data.getKeySet();
+	}
+
+	@Override
+	public ByteBuffer get(ByteBuffer _name)
+	{
+		return m_data.get(_name);
+	}
+
+	@Override
+	public NodeAttributes getAll()
+	{
+		return m_data.getAll();
+	}
+
+	@Override
+	public Set<String> getFamilyIDSet()
+	{
+		return m_data.getFamilyIDSet();
+	}
+
+	@Override
+	public Node get(String _uuid)
+	{
+		return m_data.get(_uuid);
+	}
+
+	@Override
+	public Node get(int _index)
+	{
+		return m_data.get(_index);
+	}
+
+	@Override
+	public boolean contains(NodeID _id)
+	{
+		return m_data.contains(_id);
+	}
+
+	@Override
+	public boolean add(Node _child)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public boolean addAll(NodeChildren<Node> _list)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public Node remove(String _uuid)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public Node remove(int _index)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public Node replace(Node _new)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public boolean swap(String _uuid, String _uuid2)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public void clearChildren()
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public void put(ByteBuffer _name, ByteBuffer _value)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public void putAll(NodeAttributes _attrs)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public void remove(ByteBuffer _name)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public void removeAll(Set<ByteBuffer> _keySet)
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+
+	@Override
+	public void clearAttributes()
+	{
+		throw new UnsupportedOperationException(ERR_READONLY);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/test/SwingTest1.java	Mon Jun 06 21:49:04 2011 +0900
@@ -0,0 +1,37 @@
+package treecms.test;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.*;
+
+public class SwingTest1
+{
+	public static void main(String _args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
+	{
+		UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
+		JFrame f = new JFrame("");
+		JButton btn = new JButton("btn!");
+		Container cnt = f.getContentPane();
+		cnt.setLayout(new BorderLayout());
+		cnt.add(btn,BorderLayout.CENTER);
+		
+		
+		final JPopupMenu menu = new JPopupMenu();
+		JMenuItem item = new JMenuItem("test1");
+		menu.add(item);
+		
+		btn.addActionListener(new ActionListener(){
+			@Override
+			public void actionPerformed(ActionEvent e)
+			{
+				menu.setVisible(true);
+			}
+		});
+		
+		f.setVisible(true);
+		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+	}
+}
--- a/src/treecms/tree/id/AbstractNodeID.java	Wed Jun 01 15:35:50 2011 +0900
+++ b/src/treecms/tree/id/AbstractNodeID.java	Mon Jun 06 21:49:04 2011 +0900
@@ -6,7 +6,7 @@
 {
 	public abstract NodeID create();
 	public abstract NodeID update();
-	public abstract String getUUID();
+	public abstract String getFamilyID();
 	public abstract String getVersion();
 	
 	public abstract boolean isFamily(NodeID _id);
@@ -14,17 +14,22 @@
 	@Override
 	public String toString()
 	{
-		return (new StringBuffer(getUUID())).append('@').append(getVersion()).toString();
+		return (new StringBuffer(getFamilyID())).append('@').append(getVersion()).toString();
 	}
 	
+	private int m_hashCode = -1;
+	
 	@Override
 	public int hashCode()
 	{
-		int hash = 17;
-		hash = 37*hash + getUUID().hashCode();
-		hash = 37*hash + getVersion().hashCode();
+		if(m_hashCode == -1){
+			int hash = 17;
+			hash = 37*hash + getFamilyID().hashCode();
+			hash = 37*hash + getVersion().hashCode();
+			m_hashCode = hash;
+		}
 		
-		return hash;
+		return m_hashCode;
 	}
 	
 	@Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/tree/id/RandomNodeID.java	Mon Jun 06 21:49:04 2011 +0900
@@ -0,0 +1,41 @@
+package treecms.tree.id;
+
+import java.util.Random;
+import java.util.UUID;
+import treecms.api.NodeID;
+
+public class RandomNodeID extends AbstractRandomNodeID
+{
+	private String m_fid;
+	private long m_version;
+	
+	public RandomNodeID(String _fid)
+	{
+		m_fid = (_fid != null) ? _fid : UUID.randomUUID().toString();
+		m_version = (new Random()).nextLong();
+	}
+
+	@Override
+	public NodeID create()
+	{
+		return new RandomNodeID(null);
+	}
+
+	@Override
+	public NodeID update()
+	{
+		return new RandomNodeID(m_fid);
+	}
+
+	@Override
+	public String getFamilyID()
+	{
+		return m_fid;
+	}
+
+	@Override
+	public String getVersion()
+	{
+		return Long.toHexString(m_version);
+	}
+}
\ No newline at end of file
--- a/src/treecms/tree/util/NodeChildrenImpl.java	Wed Jun 01 15:35:50 2011 +0900
+++ b/src/treecms/tree/util/NodeChildrenImpl.java	Mon Jun 06 21:49:04 2011 +0900
@@ -92,6 +92,10 @@
 	@Override
 	public synchronized boolean addAll(NodeChildren<T> _list)
 	{
+		if(_list == this){
+			return false;
+		}
+		
 		if(Collections.disjoint(getFamilyIDSet(),_list.getFamilyIDSet())){
 		
 			HashMap<String,T> map = new HashMap<String,T>();