changeset 8:f96193babac0

changed byte[] to ByteBuffer added TreeEditor.updateTree(Node,NodeData,Node[]) for node path is known. added GUIEditor
author shoshi
date Thu, 31 Mar 2011 02:08:44 +0900
parents fc19e38b669b
children 17ed97ca9960
files CHANGELOG src/treecms/api/Forest.java src/treecms/api/Node.java src/treecms/api/NodeData.java src/treecms/api/TreeEditor.java src/treecms/gui/AddChildDialog.java src/treecms/gui/AttributeEditorTable.java src/treecms/gui/GUIEditor.java src/treecms/gui/NodeViewerTree.java src/treecms/memory/OnMemoryForest.java src/treecms/memory/OnMemoryNode.java src/treecms/memory/OnMemoryTree.java src/treecms/memory/OnMemoryTreeEditor.java src/treecms/test/ByteArrayTest.java src/treecms/tree/cassandra/v1/CassandraForest.java src/treecms/tree/util/PathNotFoundException.java
diffstat 16 files changed, 693 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG	Thu Mar 17 23:24:08 2011 +0900
+++ b/CHANGELOG	Thu Mar 31 02:08:44 2011 +0900
@@ -1,5 +1,8 @@
 ChangeLog.
-
+2011-03-29
+	changed byte[] to ByteBuffer
+	added TreeEditor.updateTree(Node,NodeData,Node[]) for node path is known.
+	added GUIEditor
 2011-03-17
 	added concurrent access client for cassandra
 2011-03-14
--- a/src/treecms/api/Forest.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/api/Forest.java	Thu Mar 31 02:08:44 2011 +0900
@@ -14,6 +14,20 @@
 	Node get(NodeID _id);
 	
 	/**
+	 * あるNodeをルートとしてTreeのオブジェクトを取得します。
+	 * @param _id 木のルートとなるNodeのNodeID
+	 * @return Tree
+	 */
+	Tree getAsTree(NodeID _id);
+	
+	/**
+	 * あるNodeをルートとしたTreeを非破壊的に編集するTreeEditorを取得します。
+	 * @param _id 木のルートとなるNodeのNodeID
+	 * @return TreeEditor
+	 */
+	TreeEditor getAsTreeEditor(NodeID _id);
+	
+	/**
 	 * 同じUUIDを持つNode中で最新のNodeを取得します.
 	 * @param _uuid NodeIDのUUID
 	 * @return UUIDと一致するNodeが見つからない場合はnullを返します.
@@ -33,4 +47,10 @@
 	 * @return NodeDataを保持した新しいNode
 	 */
 	Node create(NodeData _data);
+	
+	/**
+	 * このForestの現在の最新のMainTreeを取得します
+	 * @return このForestのMainTree、最新版
+	 */
+	Tree getMainTree();
 }
--- a/src/treecms/api/Node.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/api/Node.java	Thu Mar 31 02:08:44 2011 +0900
@@ -2,6 +2,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.nio.ByteBuffer;
 
 /**
  * 木構造の基本のデータ単位であるNodeを示します.Nodeは子供のリストとデータのマップを保持します.また,クライアントはノードが保持しているデータをNodeDataとして
@@ -38,14 +39,14 @@
 	 * このNodeが保持するデータをマップとしてすべて取得します.
 	 * @return Nodeが保持するすべてのデータのマップ
 	 */
-	public Map<byte[],byte[]> getAll();
+	public Map<ByteBuffer,ByteBuffer> getAll();
 	
 	/**
 	 * このNodeが保持する値の中で指定されたキーと対応する値を取得します.
 	 * @param _key データに対応するキー
 	 * @return キーと対応する値,見つからない場合はnull
 	 */
-	public byte[] get(byte[] _key);
+	public ByteBuffer get(ByteBuffer _key);
 	
 	/**
 	 * 指定されたリストに含まれるNodeを,すべて子供Nodeとして追加します.
@@ -60,15 +61,27 @@
 	public void add(Node _child);
 	
 	/**
+	 * 指定されたNodeを削除します。
+	 * @param _child
+	 */
+	public void remove(Node _child);
+	
+	/**
 	 * キーとそれに対応する値を保存します.キーが重複した場合は上書きされます.
 	 * @param _key キー
 	 * @param _value 値
 	 */
-	public void put(byte[] _key,byte[] _value);
+	public void put(ByteBuffer _key,ByteBuffer _value);
+	
+	/**
+	 * キーとそれに対応する値を削除します。
+	 * @param _key キー
+	 */
+	public void remove(ByteBuffer _key);
 	
 	/**
 	 * キーとそれに対応する値を複数保持するマップを引数としてとり,マップが保持する値をすべて追加します.
 	 * @param _map 追加される値のマップ
 	 */
-	public void putAll(Map<byte[],byte[]> _map);
+	public void putAll(Map<ByteBuffer,ByteBuffer> _map);
 }
\ No newline at end of file
--- a/src/treecms/api/NodeData.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/api/NodeData.java	Thu Mar 31 02:08:44 2011 +0900
@@ -1,5 +1,6 @@
 package treecms.api;
 
+import java.nio.ByteBuffer;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -23,7 +24,7 @@
 	/**
 	 * キーと対応する値のマップ
 	 */
-	private Map<byte[],byte[]> m_attrs;
+	private Map<ByteBuffer,ByteBuffer> m_attrs;
 	
 	/**
 	 * コンストラクタです.なにもしません
@@ -41,11 +42,11 @@
 	{
 		if(_data != null){
 			m_children = new CopyOnWriteArrayList<Node>(_data.m_children);
-			m_attrs = new ConcurrentHashMap<byte[],byte[]>(_data.m_attrs);
+			m_attrs = new ConcurrentHashMap<ByteBuffer,ByteBuffer>(_data.m_attrs);
 			return;
 		}
 		m_children = new CopyOnWriteArrayList<Node>();
-		m_attrs = new ConcurrentHashMap<byte[],byte[]>();
+		m_attrs = new ConcurrentHashMap<ByteBuffer,ByteBuffer>();
 	}
 	
 	/**
@@ -61,7 +62,7 @@
 	 * キーのセットを取得します.
 	 * @return キーのセット
 	 */
-	public Set<byte[]> keySet()
+	public Set<ByteBuffer> keySet()
 	{
 		return m_attrs.keySet();
 	}
@@ -71,7 +72,7 @@
 	 * @param _name
 	 * @return キーに対応する値
 	 */
-	public byte[] get(byte[] _name)
+	public ByteBuffer get(ByteBuffer _name)
 	{
 		return m_attrs.get(_name);
 	}
@@ -81,16 +82,25 @@
 	 * @param _name キー
 	 * @param _value 値
 	 */
-	public void put(byte[] _name,byte[] _value)
+	public void put(ByteBuffer _name,ByteBuffer _value)
 	{
 		m_attrs.put(_name,_value);
 	}
 	
 	/**
+	 * キーとその対応する値をマップから削除します
+	 * @param _name
+	 */
+	public void remove(ByteBuffer _name)
+	{
+		m_attrs.remove(_name);
+	}
+	
+	/**
 	 * キーとそれに対応する値のマップ全体を追加します.
 	 * @param _map
 	 */
-	public void putAll(Map<byte[],byte[]> _map)
+	public void putAll(Map<ByteBuffer,ByteBuffer> _map)
 	{
 		m_attrs.putAll(_map);
 	}
@@ -145,7 +155,7 @@
 	 * NodeDataが保持しているすべてのキーと値の組のマップを返します.
 	 * @return すべてのキーと値のマップ
 	 */
-	public Map<byte[],byte[]> getAll()
+	public Map<ByteBuffer,ByteBuffer> getAll()
 	{
 		return Collections.unmodifiableMap(m_attrs);
 	}
--- a/src/treecms/api/TreeEditor.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/api/TreeEditor.java	Thu Mar 31 02:08:44 2011 +0900
@@ -49,4 +49,15 @@
 	 * @throws PathNotFoundException パスが見つからない場合
 	 */
 	public Node updateTree(Node _target,NodeData _newData) throws PathNotFoundException;
+	
+	/**
+	 * 木構造を非破壊的に更新します.Nodeへのパスが既知な場合このメソッドを使用できます。
+	 * このメソッドは使用時にパスの正当性を検証します。見つからない場合PathNotFoundExceptionがスローされます
+	 * @param _target
+	 * @param _newData
+	 * @param _path
+	 * @return 更新された新しいNode
+	 * @throws PathNotFoundException 
+	 */
+	public Node updateTree(Node _target,NodeData _newData,Node[] _path) throws PathNotFoundException;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/gui/AddChildDialog.java	Thu Mar 31 02:08:44 2011 +0900
@@ -0,0 +1,63 @@
+package treecms.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JScrollPane;
+
+class AddChildDialog
+{
+	private JDialog m_dialog;
+	private AttributeEditorTable m_table;
+	private JButton m_submit;
+	private Map<ByteBuffer,ByteBuffer> m_data;
+	private JFrame m_parent;
+	
+	public AddChildDialog(JFrame _parent,boolean _modal)
+	{
+		m_dialog = new JDialog(_parent,_modal);
+		m_dialog.setSize(300,200);
+		m_dialog.setTitle("Add new child");
+		m_parent = _parent;
+		m_table = new AttributeEditorTable();
+		m_submit = new JButton("submit");
+		m_submit.addActionListener(new ActionListener(){
+			@Override
+			public void actionPerformed(ActionEvent _e)
+			{
+				m_data = m_table.getAttributeMap();
+				m_dialog.setVisible(false);
+			}
+		});
+		m_dialog.addWindowListener(new WindowAdapter(){
+			@Override
+			public void windowClosing(WindowEvent _e)
+			{
+				m_data = null;
+			}
+		});
+		
+		Container cnt = m_dialog.getContentPane();
+		cnt.setLayout(new BorderLayout());
+		cnt.add(new JLabel("Attributes"),BorderLayout.NORTH);
+		cnt.add(new JScrollPane(m_table),BorderLayout.CENTER);
+		cnt.add(m_submit,BorderLayout.SOUTH);
+	}
+	
+	public Map<ByteBuffer,ByteBuffer> setVisible(boolean _show)
+	{
+		m_dialog.setLocationRelativeTo(m_parent);
+		m_dialog.setVisible(_show);
+		
+		return m_data;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/gui/AttributeEditorTable.java	Thu Mar 31 02:08:44 2011 +0900
@@ -0,0 +1,99 @@
+package treecms.gui;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+class AttributeEditorTable extends JTable
+{
+	private static final long serialVersionUID = -3520371233895896649L;
+	private static final String ADD_ROW_STR = "add new attribute...";
+	private static final Object ADD_ROW = new Object(){
+		@Override
+		public String toString()
+		{
+			return ADD_ROW_STR;
+		}
+	};
+	
+	private static final String[] COLUMNS = new String[]{"Key","Value"};
+	private static final String[] DEFAULT_VALUES = new String[]{"new key","new value"};
+	
+	public AttributeEditorTable()
+	{
+		super(new AETableModel());
+		
+		addMouseListener(new MouseAdapter(){
+			@Override
+			public void mouseClicked(MouseEvent _e)
+			{
+				if(_e.getClickCount() == 2){
+					int row = getSelectedRow();
+					Object val = getValueAt(row,0);
+					if(val == ADD_ROW){
+						DefaultTableModel model = (DefaultTableModel)AttributeEditorTable.this.getModel();
+						model.insertRow(row,DEFAULT_VALUES);
+					}
+				}
+			}
+		});
+	}
+	
+	public void setAttributeMap(Map<ByteBuffer,ByteBuffer> _map)
+	{
+		AETableModel model = (AETableModel)getModel();
+		model.clear();
+		for(ByteBuffer _key : _map.keySet()){
+			model.insertRow(0,new String[]{new String(_key.array()),new String(_map.get(_key).array())});
+		}
+	}
+	
+	public Map<ByteBuffer,ByteBuffer> getAttributeMap()
+	{
+		Map<ByteBuffer,ByteBuffer> map = new HashMap<ByteBuffer,ByteBuffer>();
+		for(int i = 0;true;i ++){
+			Object key = getValueAt(i,0);
+			Object value = getValueAt(i,1);
+			
+			if(key == ADD_ROW){
+				break;
+			}
+			
+			ByteBuffer bufKey = ByteBuffer.wrap(key.toString().getBytes());
+			ByteBuffer bufValue = ByteBuffer.wrap(value.toString().getBytes());
+			map.put(bufKey,bufValue);
+		}
+		
+		return map;
+	}
+	
+	private static class AETableModel extends DefaultTableModel
+	{
+		private static final long serialVersionUID = -5641721228388548196L;
+
+		public AETableModel()
+		{
+			super();
+			setColumnIdentifiers(COLUMNS);
+			addRow(new Object[]{ADD_ROW,""});
+		}
+		
+		public void clear()
+		{
+			setRowCount(0);
+			addRow(new Object[]{ADD_ROW,""});
+		}
+		
+		@Override
+		public boolean isCellEditable(int _row,int _col)
+		{
+			Object val = this.getValueAt(_row,_col);
+			return (val == ADD_ROW) ? false : true;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/gui/GUIEditor.java	Thu Mar 31 02:08:44 2011 +0900
@@ -0,0 +1,249 @@
+package treecms.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+
+import treecms.api.Node;
+import treecms.api.NodeData;
+import treecms.api.Tree;
+import treecms.api.TreeEditor;
+import treecms.memory.OnMemoryForest;
+
+public class GUIEditor
+{
+	private static final String TITLE = "GUIEditor";
+	
+	//GUIコンポーネント
+	private JFrame m_frame;
+	private JTree m_nodeTree;
+	private AttributeEditorTable m_attrTable;
+	private JButton m_commit,m_pull,m_check,m_merge;
+	private JButton m_attrSave,m_attrCancel;
+	private JPopupMenu m_popup;
+	private JMenuItem m_addChild,m_removeChild;
+	
+	//TreeEditor
+	private TreeEditor m_editor;
+	
+	//イベントハンドラ
+	private ActionListener m_actionListener;
+	private MouseListener m_mouseListener;
+	private TreeSelectionListener m_treeSelectionListener;
+	
+	public static void main(String _args[])
+	{
+		OnMemoryForest forest = new OnMemoryForest();
+		Tree root = forest.getMainTree();
+		TreeEditor editor = forest.getAsTreeEditor(root.getID());
+		new GUIEditor(editor);
+	}
+	
+	public GUIEditor(TreeEditor _editor)
+	{
+		m_editor = _editor;
+		m_frame = new JFrame(TITLE+":"+m_editor.toString());
+		m_frame.setSize(500,500);
+		
+		m_actionListener = new EditorActionListener();
+		m_mouseListener = new EditorMouseListener();
+		m_treeSelectionListener = new EditorTreeSelectionListener();
+		
+		Container pane = m_frame.getContentPane();
+		pane.setLayout(new BorderLayout());
+		
+		JPanel treePanel = initNodeTree();
+		JPanel menuPanel = initMenuButtons();
+		JPanel attrPanel = initAttributeTable();
+		
+		JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,treePanel,attrPanel);
+		
+		pane.add(menuPanel,BorderLayout.NORTH);
+		pane.add(splitPane,BorderLayout.CENTER);
+		m_frame.setVisible(true);
+		splitPane.setDividerLocation(0.5);
+		m_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+	}
+	
+	private JPanel initMenuButtons()
+	{
+		JPanel panel = new JPanel();
+		m_commit = new JButton("commit");
+		m_pull = new JButton("pull");
+		m_check = new JButton("check");
+		m_merge = new JButton("merge");
+		
+		panel.setBorder(new TitledBorder("Editor Menu"));
+		panel.setLayout(new GridLayout(1,4));
+		panel.add(m_commit);
+		panel.add(m_pull);
+		panel.add(m_check);
+		panel.add(m_merge);
+		
+		return panel;
+	}
+	
+	private JPanel initAttributeTable()
+	{
+		JPanel panel = new JPanel();
+		m_attrTable = new AttributeEditorTable();
+		m_attrSave = new JButton("save");
+		m_attrSave.addActionListener(m_actionListener);
+		m_attrCancel = new JButton("cancel");
+		m_attrCancel.addActionListener(m_actionListener);
+		
+		JPanel btnPanel = new JPanel();
+		btnPanel.setLayout(new GridLayout(1,2));
+		btnPanel.add(m_attrSave);
+		btnPanel.add(m_attrCancel);
+		
+		panel.setBorder(new TitledBorder("Attributes"));
+		panel.setLayout(new BorderLayout());
+		panel.add(new JScrollPane(m_attrTable),BorderLayout.CENTER);
+		panel.add(btnPanel,BorderLayout.SOUTH);
+		
+		return panel;
+	}
+	
+	private JPanel initNodeTree()
+	{
+		JPanel panel = new JPanel();
+		m_nodeTree = new NodeViewerTree(m_editor.getRoot());
+		m_nodeTree.addTreeSelectionListener(m_treeSelectionListener);
+		
+		m_popup = new JPopupMenu();
+		m_addChild = new JMenuItem("Add Child");
+		m_removeChild = new JMenuItem("Remove Child");
+		m_popup.add(m_addChild);
+		m_popup.add(m_removeChild);
+		m_addChild.addActionListener(m_actionListener);
+		m_removeChild.addActionListener(m_actionListener);
+		m_nodeTree.addMouseListener(m_mouseListener);
+		
+		panel.setBorder(new TitledBorder("Tree Viewer"));
+		panel.setLayout(new BorderLayout());
+		panel.add(new JScrollPane(m_nodeTree),BorderLayout.CENTER);
+		
+		return panel;
+	}
+	
+	private class EditorActionListener implements ActionListener
+	{
+		@Override
+		public void actionPerformed(ActionEvent _e)
+		{
+			Object source = _e.getSource();
+			if(source == m_addChild){
+				//新しいノードを追加する
+				AddChildDialog addDialog = new AddChildDialog(GUIEditor.this.m_frame,true);
+				Map<ByteBuffer,ByteBuffer> result = addDialog.setVisible(true);
+				if(result != null){
+					//キャンセルされなかった場合
+					NodeData data = new NodeData();
+					data.putAll(result);
+					
+					DefaultMutableTreeNode node = (DefaultMutableTreeNode)m_nodeTree.getLastSelectedPathComponent();
+					
+					Node parent = (Node)node.getUserObject();
+					Node child = parent.getForest().create(data);
+					parent.add(child);
+					
+					DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(child);
+					node.add(newNode);
+					
+					DefaultTreeModel model = (DefaultTreeModel)m_nodeTree.getModel();
+					model.reload();
+				}
+			}
+			
+			if(source == m_removeChild){
+				//ノードを削除する
+				DefaultMutableTreeNode guiNode = (DefaultMutableTreeNode)m_nodeTree.getLastSelectedPathComponent();
+				DefaultMutableTreeNode guiParent = (DefaultMutableTreeNode)guiNode.getParent();
+				if(guiParent == null){
+					JOptionPane.showMessageDialog(m_frame,"RootNodeは削除できません");
+					return;
+				}
+				
+				guiParent.remove(guiNode);
+				
+				Node treeNode = (Node)guiNode.getUserObject();
+				Node treeParent = (Node)guiNode.getUserObject();
+				treeParent.remove(treeNode);
+				
+				DefaultTreeModel model = (DefaultTreeModel)m_nodeTree.getModel();
+				model.reload();
+			}
+			
+			if(source == m_attrSave){
+				//ノードの変更を保存する
+				DefaultMutableTreeNode guiNode = (DefaultMutableTreeNode)m_nodeTree.getLastSelectedPathComponent();
+				if(guiNode == null){
+					//ノードが選択されずに編集された
+					JOptionPane.showMessageDialog(m_frame,"編集対象のノードが選択されていません。");
+					return;
+				}
+				
+				Node treeNode = (Node)guiNode.getUserObject();
+				Map<ByteBuffer,ByteBuffer> attrMap = m_attrTable.getAttributeMap();
+				treeNode.putAll(attrMap);
+			}
+			
+			if(source == m_attrCancel){
+				//ノードへの編集を捨てる
+				DefaultMutableTreeNode guiNode = (DefaultMutableTreeNode)m_nodeTree.getLastSelectedPathComponent();
+				Node treeNode = (Node)guiNode.getUserObject();
+				m_attrTable.setAttributeMap(treeNode.getAll());
+			}
+		}
+	}
+	
+	private class EditorTreeSelectionListener implements TreeSelectionListener
+	{
+		@Override
+		public void valueChanged(TreeSelectionEvent _e)
+		{
+			DefaultMutableTreeNode guiNode = (DefaultMutableTreeNode)m_nodeTree.getLastSelectedPathComponent();
+			if(guiNode == null){
+				return;
+			}
+			
+			Node treeNode = (Node)guiNode.getUserObject();
+			m_attrTable.setAttributeMap(treeNode.getAll());
+		}
+	}
+	
+	private class EditorMouseListener extends MouseAdapter
+	{
+		@Override
+		public void mouseClicked(MouseEvent _e)
+		{
+			if(SwingUtilities.isRightMouseButton(_e) && !m_nodeTree.isSelectionEmpty()){
+				//ポップアップメニューを表示する
+				m_popup.show(m_nodeTree,_e.getX(),_e.getY());
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/gui/NodeViewerTree.java	Thu Mar 31 02:08:44 2011 +0900
@@ -0,0 +1,65 @@
+package treecms.gui;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+
+import treecms.api.Node;
+import treecms.api.TreeEditor;
+
+public class NodeViewerTree extends JTree
+{
+	private static final long serialVersionUID = 8257838201307216301L;
+	
+	public NodeViewerTree(Node _node)
+	{
+		super(new NVTreeModel(_node));
+	}
+	
+	public void addChildToSelectedNode(Node _child)
+	{
+		DefaultMutableTreeNode node = (DefaultMutableTreeNode)this.getLastSelectedPathComponent();
+		if(node == null){
+			return;
+		}
+		
+		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode();
+		newNode.setUserObject(_child);
+		node.add(newNode);
+	}
+	
+	private static class NVTreeModel extends DefaultTreeModel
+	{
+		public NVTreeModel(Node _node)
+		{
+			super(new DefaultMutableTreeNode());
+			setTree(_node,false);
+		}
+		
+		public void setTree(Node _node,boolean _reload)
+		{
+			DefaultMutableTreeNode root = (DefaultMutableTreeNode)getRoot();
+			root.removeAllChildren();
+			root.setUserObject(_node);
+			
+			for(Node child : _node.children()){
+				treewalk(child,root);
+			}
+			
+			if(_reload){
+				reload();
+			}
+		}
+		
+		private void treewalk(Node _node,DefaultMutableTreeNode _treeNode)
+		{
+			DefaultMutableTreeNode treeChild = new DefaultMutableTreeNode();
+			treeChild.setUserObject(_node);
+			_treeNode.add(treeChild);
+			
+			for(Node child : _node.children()){
+				treewalk(child,treeChild);
+			}
+		}
+	}
+}
--- a/src/treecms/memory/OnMemoryForest.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/memory/OnMemoryForest.java	Thu Mar 31 02:08:44 2011 +0900
@@ -8,6 +8,8 @@
 import treecms.api.Node;
 import treecms.api.NodeData;
 import treecms.api.NodeID;
+import treecms.api.Tree;
+import treecms.api.TreeEditor;
 import treecms.tree.id.AbstractRandomNodeID;
 
 /**
@@ -22,6 +24,9 @@
 	//最新版Nodeのマップ
 	Map<String,OnMemoryNode> m_tipTable;
 	
+	//MainTreeのUUID
+	String m_mainID;
+	
 	/**
 	 * コンストラクタ
 	 */
@@ -29,6 +34,8 @@
 	{
 		m_table = new ConcurrentHashMap<NodeID,OnMemoryNode>();
 		m_tipTable = new ConcurrentHashMap<String,OnMemoryNode>();
+		
+		m_mainID = createNode(null,null).getID().getUUID();
 	}
 	
 	/**
@@ -37,7 +44,7 @@
 	 * @param _newData Nodeが保持するNodeData
 	 * @return 作成されたOnMemoryNode
 	 */
-	OnMemoryNode createNode(NodeID _id,NodeData _newData)
+	public OnMemoryNode createNode(NodeID _id,NodeData _newData)
 	{
 		NodeID newID = (_id != null) ? _id : createID();
 		NodeData newData = (_newData != null) ? _newData : new NodeData();
@@ -72,6 +79,28 @@
 	}
 	
 	/**
+	 * あるNodeをルートとしてTreeのオブジェクトを取得します。
+	 * @param _id 木のルートとなるNodeのNodeID
+	 * @return Tree
+	 */
+	@Override
+	public Tree getAsTree(NodeID _id)
+	{
+		return new OnMemoryTree((OnMemoryNode) get(_id));
+	}
+	
+	/**
+	 * あるNodeをルートとしたTreeを非破壊的に編集するTreeEditorを取得します。
+	 * @param _id 木のルートとなるNodeのNodeID
+	 * @return TreeEditor
+	 */
+	@Override
+	public TreeEditor getAsTreeEditor(NodeID _id)
+	{
+		return new OnMemoryTreeEditor((OnMemoryTree) getAsTree(_id));
+	}
+	
+	/**
 	 * 新しくNodeを作成します.
 	 * @return 新しいNode
 	 */
@@ -100,6 +129,16 @@
 	{
 		return m_tipTable.get(_uuid);
 	}
+
+	/**
+	 * MainTreeを取得します。
+	 * @return このForestのMainTree
+	 */
+	@Override
+	public Tree getMainTree()
+	{
+		return new OnMemoryTree(m_tipTable.get(m_mainID));
+	}
 	
 	/**
 	 * ランダムにバージョン番号を生成するNodeIDです.ファクトリを内包します.
--- a/src/treecms/memory/OnMemoryNode.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/memory/OnMemoryNode.java	Thu Mar 31 02:08:44 2011 +0900
@@ -1,5 +1,6 @@
 package treecms.memory;
 
+import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.Map;
 import treecms.api.Forest;
@@ -70,6 +71,16 @@
 	{
 		m_data.add(_child);
 	}
+	
+	/**
+	 * 子供Nodeを削除します。
+	 * @param _child
+	 */
+	@Override
+	public void remove(Node _child)
+	{
+		m_data.remove(_child);
+	}
 
 	/**
 	 * 指定されたリストに含まれるNodeを,すべて子供Nodeとして追加します.
@@ -97,7 +108,7 @@
 	 * @return キーと対応する値,見つからない場合はnull
 	 */
 	@Override
-	public byte[] get(byte[] _key)
+	public ByteBuffer get(ByteBuffer _key)
 	{
 		return m_data.get(_key);
 	}
@@ -107,7 +118,7 @@
 	 * @return Nodeが保持するすべてのデータのマップ
 	 */
 	@Override
-	public Map<byte[],byte[]> getAll()
+	public Map<ByteBuffer,ByteBuffer> getAll()
 	{
 		return m_data.getAll();
 	}
@@ -118,7 +129,7 @@
 	 * @param _value 値
 	 */
 	@Override
-	public void put(byte[] _key, byte[] _value)
+	public void put(ByteBuffer _key, ByteBuffer _value)
 	{
 		m_data.put(_key,_value);
 	}
@@ -128,8 +139,24 @@
 	 * @param _map 追加される値のマップ
 	 */
 	@Override
-	public void putAll(Map<byte[], byte[]> _map)
+	public void putAll(Map<ByteBuffer, ByteBuffer> _map)
 	{
 		m_data.putAll(_map);
 	}
+
+	/**
+	 * キーとそれに対応する値を削除します。
+	 * @param _key キー
+	 */
+	@Override
+	public void remove(ByteBuffer _key)
+	{
+		m_data.remove(_key);
+	}
+	
+	@Override
+	public String toString()
+	{
+		return getID().toString();
+	}
 }
--- a/src/treecms/memory/OnMemoryTree.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/memory/OnMemoryTree.java	Thu Mar 31 02:08:44 2011 +0900
@@ -1,5 +1,6 @@
 package treecms.memory;
 
+import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
@@ -17,7 +18,6 @@
  */
 final class OnMemoryTree implements Tree
 {
-	private OnMemoryNode m_root;
 	private AtomicReference<OnMemoryNode> m_ref;
 	
 	/**
@@ -26,7 +26,6 @@
 	 */
 	public OnMemoryTree(OnMemoryNode _newRoot)
 	{
-		m_root = _newRoot;
 		m_ref = new AtomicReference<OnMemoryNode>(_newRoot);
 	}
 	
@@ -106,7 +105,7 @@
 	 * @return キーと対応する値,見つからない場合はnull
 	 */
 	@Override
-	public byte[] get(byte[] _key)
+	public ByteBuffer get(ByteBuffer _key)
 	{
 		return m_ref.get().get(_key);
 	}
@@ -116,7 +115,7 @@
 	 * @return Nodeが保持するすべてのデータのマップ
 	 */
 	@Override
-	public Map<byte[],byte[]> getAll()
+	public Map<ByteBuffer,ByteBuffer> getAll()
 	{
 		return m_ref.get().getAll();
 	}
@@ -127,7 +126,7 @@
 	 * @param _value 値
 	 */
 	@Override
-	public void put(byte[] _key,byte[] _value)
+	public void put(ByteBuffer _key,ByteBuffer _value)
 	{
 		m_ref.get().put(_key,_value);
 	}
@@ -137,12 +136,32 @@
 	 * @param _map 追加される値のマップ
 	 */
 	@Override
-	public void putAll(Map<byte[], byte[]> _map)
+	public void putAll(Map<ByteBuffer, ByteBuffer> _map)
 	{
 		m_ref.get().putAll(_map);
 	}
 	
 	/**
+	 * キーとそれに対応する値を削除します。
+	 * @param _key キー
+	 */
+	@Override
+	public void remove(ByteBuffer _key)
+	{
+		m_ref.get().remove(_key);
+	}
+	
+	/**
+	 * 子供Nodeを削除します。
+	 * @param _child
+	 */
+	@Override
+	public void remove(Node _child)
+	{
+		m_ref.get().remove(_child);
+	}
+	
+	/**
 	 * ルートNodeを比較して置き換えます.
 	 * @param _except 比較する対象 
 	 * @param _newRoot 一致した場合置き換える対象
@@ -156,4 +175,5 @@
 		m_ref.set(_newRoot);
 		return true;
 	}
+
 }
--- a/src/treecms/memory/OnMemoryTreeEditor.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/memory/OnMemoryTreeEditor.java	Thu Mar 31 02:08:44 2011 +0900
@@ -1,7 +1,6 @@
 package treecms.memory;
 
 import java.util.LinkedList;
-
 import treecms.api.Node;
 import treecms.api.NodeData;
 import treecms.api.NodeID;
@@ -79,6 +78,27 @@
 	}
 	
 	/**
+	 * 実装しろ
+	 */
+	@Override
+	public synchronized Node updateTree(Node _target,NodeData _newData,Node[] _path) throws PathNotFoundException
+	{
+		//パスの正当性の検証
+		if(_path.length == 0){
+			throw new PathNotFoundException("node path is empty");
+		}else{
+			//ここでごちゃごちゃする
+			//めんどくさい
+		}
+		
+		
+		
+		
+		
+		return null;
+	}
+	
+	/**
 	 * 木構造を非破壊的に更新します.
 	 * @param _target 更新する対象
 	 * @param _newData 更新に適用されるNodeData
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/treecms/test/ByteArrayTest.java	Thu Mar 31 02:08:44 2011 +0900
@@ -0,0 +1,15 @@
+package treecms.test;
+
+import java.util.*;
+
+public class ByteArrayTest
+{
+	public static void main(String _args[])
+	{
+		byte key1[] = new byte[]{12,23,23,43};
+		byte key2[] = new byte[]{12,23,23,43};
+		
+		System.out.println(key1.equals(key2));
+		
+	}
+}
--- a/src/treecms/tree/cassandra/v1/CassandraForest.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/tree/cassandra/v1/CassandraForest.java	Thu Mar 31 02:08:44 2011 +0900
@@ -79,9 +79,14 @@
 	public CassandraForest(String _host,int _port,String _ks,int _threads)
 	{
 		m_service = Executors.newFixedThreadPool(_threads,new ClientThreadFactory(_host,_port));
+		
 		m_cache = new ConcurrentHashMap<NodeID,CassandraNode>();
 		m_tipCache = new ConcurrentHashMap<String,CassandraNode>();
 	}
+	
+	private void loadContents()
+	{
+	}
 
 	@Override
 	public Node get(NodeID _id)
--- a/src/treecms/tree/util/PathNotFoundException.java	Thu Mar 17 23:24:08 2011 +0900
+++ b/src/treecms/tree/util/PathNotFoundException.java	Thu Mar 31 02:08:44 2011 +0900
@@ -19,4 +19,13 @@
 	{
 		super("Path Not Found from "+_from.getID()+" to "+_to.getID());
 	}
+	
+	/**
+	 * コンストラクタです。
+	 * @param _message メッセージ
+	 */
+	public PathNotFoundException(String _message)
+	{
+		super(_message);
+	}
 }