001 /* 002 Copyright (C) 2002-2003 Renaud Pawlak <renaud@aopsys.com>, 003 Laurent Martelli <laurent@aopsys.com> 004 005 This program is free software; you can redistribute it and/or modify 006 it under the terms of the GNU Lesser General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This program is distributed in the hope that it will be useful, but 011 WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 Lesser General Public License for more details. 014 015 You should have received a copy of the GNU Lesser General Public 016 License along with this program; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 018 USA */ 019 020 package org.objectweb.jac.aspects.gui; 021 022 import java.util.Hashtable; 023 import java.util.Iterator; 024 import java.util.Map; 025 import javax.swing.tree.MutableTreeNode; 026 import javax.swing.tree.TreePath; 027 import org.apache.log4j.Logger; 028 import org.objectweb.jac.core.Collaboration; 029 import org.objectweb.jac.core.rtti.ClassRepository; 030 import org.objectweb.jac.core.rtti.CollectionItem; 031 import org.objectweb.jac.core.rtti.FieldItem; 032 import org.objectweb.jac.util.Stack; 033 034 /** 035 * This class represents a tree node for an object. */ 036 037 public class ObjectNode extends AbstractNode 038 implements ObjectUpdate, FieldUpdate, CollectionUpdate 039 { 040 static Logger logger = Logger.getLogger("gui.treeview"); 041 static Logger loggerEvents = Logger.getLogger("gui.events"); 042 static Logger loggerCol = Logger.getLogger("gui.collectionupdate"); 043 044 FieldItem relation; 045 Object substance; 046 047 /** 048 * Constructor. 049 * 050 * @param model the tree modelto notify when changes occur 051 * @param value the value that the node represents 052 * @param substance the owner of the relation 053 * @param relation the relation the value is part of 054 */ 055 public ObjectNode(TreeView model, Object value, 056 Object substance, FieldItem relation, 057 boolean showRelations) 058 { 059 super(model,value, showRelations); 060 this.substance = substance; 061 this.relation = relation; 062 Utils.registerObject(value,this); 063 if (!(relation instanceof CollectionItem)) { 064 Utils.registerField(value,relation,this); 065 } 066 if (GuiAC.getGraphicContext()!=null) 067 context.addAll(GuiAC.getGraphicContext()); 068 if (relation!=null) 069 context.push(relation); 070 rebuildData(); 071 } 072 073 /** FieldItem -> Integer(Index of firt node of the relation) */ 074 Hashtable relationIndices; 075 076 /** 077 * Insert a node at the correct place (considering sorting) 078 * @param node the node to insert 079 * @return the position the node was inserted at 080 */ 081 public int addNode(ObjectNode node) { 082 logger.debug("Inserting "+node.getText()+"..."); 083 FieldItem relation = node.getRelation(); 084 if (relationIndices==null) 085 relationIndices = new Hashtable(); 086 Integer relIndex = (Integer)relationIndices.get(relation); 087 if (relIndex==null) { 088 relIndex = new Integer(getChildCount()); 089 relationIndices.put(relation,relIndex); 090 } 091 // Find where to insert the node (sort according to text) 092 int i; 093 for (i=relIndex.intValue(); i<getChildCount(); i++) { 094 ObjectNode current = (ObjectNode)getChildAt(i); 095 logger.debug(" current "+current); 096 if (current.getText().compareToIgnoreCase(node.getText())>0) 097 break; 098 } 099 logger.debug("Inserting "+node.getText()+" at "+relation.getName()+"["+i+"]"); 100 insert(node, i); 101 102 // Update relation start indices 103 Iterator it = relationIndices.entrySet().iterator(); 104 while (it.hasNext()) { 105 Map.Entry entry = (Map.Entry)it.next(); 106 int index = ((Integer)entry.getValue()).intValue(); 107 if (index>=i && entry.getKey()!=relation) { 108 entry.setValue(new Integer(index+1)); 109 } 110 } 111 return i; 112 } 113 114 public void removeAllChildren() { 115 super.removeAllChildren(); 116 if (relationIndices!=null) 117 relationIndices.clear(); 118 } 119 120 Stack context = new Stack(); 121 122 /** 123 * Returns the relation (reference or collection) the substance of 124 * the node belongs to. */ 125 public FieldItem getRelation() { 126 return relation; 127 } 128 129 /** 130 * Returns the substance of this node. */ 131 public Object getSubstance() { 132 return substance; 133 } 134 135 /** 136 * Rebuild the data of this node again. */ 137 138 protected void rebuildData() { 139 Object object = getUserObject(); 140 logger.debug("refresh("+object+")"); 141 if (object==null) { 142 icon = null; 143 text = "<null>"; 144 tooltip = null; 145 } else { 146 text = GuiAC.toString(object,context); 147 tooltip = GuiAC.getToolTip(object,context); 148 logger.debug("text="+text); 149 icon = GuiAC.getIcon(ClassRepository.get().getClass(object),object); 150 } 151 } 152 153 /** 154 * Unregisters from the events this node is notified. */ 155 156 public void unregisterEvents() { 157 Object value = getUserObject(); 158 Utils.unregister(value,this); 159 } 160 161 /** 162 * Find a node in the children. 163 * @param relation the relation the requested node must be part of 164 * @param userObject the userObject value the requested node must have 165 * @return an ObjectNode with the requested features, or null if no 166 * such node can be found. 167 */ 168 protected ObjectNode findNode(FieldItem relation, Object userObject) { 169 logger.debug("Looking for node "+relation+" -> "+userObject); 170 for (int i=0; i<getChildCount(); i++) { 171 if (getChildAt(i) instanceof ObjectNode) { 172 ObjectNode current = (ObjectNode)getChildAt(i); 173 if (current.getUserObject()==userObject && 174 current.getRelation()==relation) 175 return current; 176 } 177 } 178 return null; 179 } 180 181 // ObjectUpdate interface 182 183 public void objectUpdated(Object substance, Object param) { 184 rebuildData(); 185 model.nodeChanged(this); 186 } 187 188 // FieldUpdate interface 189 190 public void fieldUpdated(Object substance, FieldItem collection, 191 Object value, Object param) { 192 logger.debug("fieldUpdated on "+this); 193 rebuildData(); 194 TreeModel.addNodes(model,this,getUserObject(),showRelations); 195 ObjectNode newNode = findNode(collection,value); 196 //model.nodesWereInserted(this,indices); 197 if (newNode!=null) { 198 model.setSelection(new TreePath(newNode.getPath())); 199 } 200 model.nodeChanged(this); 201 } 202 203 // CollectionUpdate interface 204 205 public void onChange(Object substance, CollectionItem collection, 206 Object value, Object param) { 207 loggerCol.debug("ObjectNode collectionUpdated "+collection.getLongName()); 208 // remove all children nodes 209 int[] indices = new int[getChildCount()]; 210 AbstractNode[] removedNodes = new AbstractNode[getChildCount()]; 211 for(int i=0; i<indices.length; i++) { 212 indices[i] = i; 213 removedNodes[i] = (AbstractNode)getChildAt(i); 214 } 215 removeAllChildren(); 216 model.nodesWereRemoved(this,indices,removedNodes); 217 218 // rebuild children nodes 219 TreeModel.addNodes(model,this,getUserObject(),showRelations); 220 221 indices = new int[getChildCount()]; 222 for(int i=0;i<indices.length;i++) { 223 indices[i] = i; 224 } 225 model.nodesWereInserted(this,indices); 226 } 227 228 public void onAdd(Object substance, CollectionItem collection, 229 Object value, Object added, Object param) { 230 loggerCol.debug("onAdd "+collection.getLongName()+" "+added+" - "+areChildrenUptodate); 231 ObjectNode newNode; 232 if (areChildrenUptodate) { 233 newNode = new ObjectNode(model,added,substance,collection,showRelations); 234 newNode.setLeaf(TreeModel.isLeafNode(model,newNode,added,showRelations)); 235 int pos = addNode(newNode); 236 model.nodesWereInserted(this,new int[] {pos}); 237 setChildrenUptodate(true); 238 } else { 239 TreeModel.addNodes(model,this,getUserObject(),showRelations); 240 int[] indices = new int[getChildCount()]; 241 for(int i=0; i<indices.length; i++) { 242 indices[i] = i; 243 } 244 newNode = findNode(collection,added); 245 model.nodesWereInserted(this,indices); 246 } 247 if (newNode!=null) { 248 model.setSelection(new TreePath(newNode.getPath())); 249 } 250 } 251 252 public void onRemove(Object substance, CollectionItem collection, 253 Object value, Object removed, Object param) { 254 loggerCol.debug("onRemove "+collection.getLongName()+" "+removed); 255 AbstractNode removedNode = 256 (AbstractNode)Collaboration.get().getAttribute(GuiAC.REMOVED_NODE); 257 if (removedNode!=null) { 258 loggerEvents.debug("removing = "+removedNode+" from "+this); 259 int removedIndex = getIndex(removedNode); 260 remove(removedNode); 261 model.nodesWereRemoved( 262 this, 263 new int[] {removedIndex}, 264 new Object[] {removedNode}); 265 } else { 266 onChange(substance,collection,value,param); 267 } 268 } 269 270 public void updateChildren() { 271 if (!areChildrenUptodate()) { 272 loggerEvents.debug("updateChildren "+this); 273 TreeModel.addNodes(model,this,getUserObject(),showRelations); 274 int[] indices = new int[getChildCount()]; 275 for(int i=0; i<indices.length; i++) { 276 indices[i] = i; 277 } 278 model.nodesWereInserted(this,indices); 279 setChildrenUptodate(true); 280 } else { 281 loggerEvents.debug("children are uptodate for "+this); 282 } 283 } 284 285 public String toString() { 286 return substance+"."+relation+" -> "+getUserObject(); 287 } 288 } 289