001 /* 002 Copyright (C) 2001-2003 Laurent Martelli <laurent@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 017 018 package org.objectweb.jac.aspects.gui; 019 020 import java.util.Arrays; 021 import java.util.Collection; 022 import java.util.Iterator; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.Vector; 026 import javax.swing.table.AbstractTableModel; 027 import org.apache.log4j.Logger; 028 import org.objectweb.jac.core.Collaboration; 029 import org.objectweb.jac.core.Wrappee; 030 import org.objectweb.jac.core.rtti.ClassItem; 031 import org.objectweb.jac.core.rtti.ClassRepository; 032 import org.objectweb.jac.core.rtti.CollectionItem; 033 import org.objectweb.jac.core.rtti.FieldItem; 034 import org.objectweb.jac.core.rtti.MemberItem; 035 import org.objectweb.jac.core.rtti.MetaItem; 036 import org.objectweb.jac.core.rtti.MethodItem; 037 import org.objectweb.jac.core.rtti.RttiAC; 038 import org.objectweb.jac.util.Classes; 039 import org.objectweb.jac.util.Enum; 040 import org.objectweb.jac.util.ExtArrays; 041 import org.objectweb.jac.util.Stack; 042 043 /** 044 * The data model for tables. */ 045 046 public class TableModel extends AbstractTableModel 047 implements ExtendedTableModel, ObjectUpdate, CollectionUpdate 048 { 049 static Logger logger = Logger.getLogger("gui.generic"); 050 static Logger loggerTable = Logger.getLogger("gui.table"); 051 static Logger loggerAssoc = Logger.getLogger("gui.events"); 052 static Logger loggerEvents = Logger.getLogger("associations"); 053 static Logger loggerReg = Logger.getLogger("gui.register"); 054 055 List rows = new Vector(); 056 List objects = new Vector(); 057 String[] headers; 058 ClassItem[] classes; 059 MemberItem[] members; 060 061 CollectionItem collection; 062 Object substance; 063 064 CollectionItemView collectionView; 065 066 /** 067 * Creates a new table model. 068 * 069 * @param collection the substance collection 070 * @param substance the object that holds the collection 071 * @param factory the used view factory 072 */ 073 public TableModel(CollectionItem collection, 074 Object substance, String viewName, 075 ViewFactory factory) 076 { 077 this.collection = collection; 078 this.substance = substance; 079 collectionView = GuiAC.getView(collection,viewName); 080 081 members = collectionView.getMembersOrder(); 082 logger.debug("membersOrder : " + 083 (members!=null?(Arrays.asList(members).toString()):"[]")); 084 if (members==null) { 085 ClassItem clit = collection.getComponentType(); 086 if (clit!=null && !(collection.isMap() && !RttiAC.isIndex(collection))) { 087 logger.debug("class : " + clit.getName()); 088 ObjectView objectView = GuiAC.getView(clit,viewName); 089 if (members==null) 090 members = objectView.getTableMembersOrder(); 091 logger.debug("class tableMembersOrder : " + 092 (members!=null?(Arrays.asList(members)+""):"")); 093 if (members==null) 094 members = (MemberItem[])objectView.getAttributesOrder(); 095 logger.debug("class attributesOrder : " + 096 (members!=null?(Arrays.asList(members)+""):"")); 097 if (members==null) 098 members = clit.getFields(); 099 } else if (!RttiAC.isIndex(collection)) { 100 members = new MemberItem[2]; 101 clit = ClassRepository.get().getClass(Map.Entry.class); 102 members[0] = clit.getField("key"); 103 members[1] = clit.getField("value"); 104 } else { 105 members = new MemberItem[0]; 106 } 107 } 108 logger.debug("columns : " + Arrays.asList(members)); 109 110 FieldItem oppositeRole = 111 (FieldItem)Collaboration.get().getAttribute(GuiAC.OPPOSITE_ROLE); 112 if (oppositeRole instanceof CollectionItem) { 113 loggerAssoc.debug("Ignoring collection oppositeRole "+oppositeRole); 114 oppositeRole = null; 115 } 116 117 // Ensure that members contains no oppositeRole 118 MemberItem[] tmp = new MemberItem[members.length]; 119 int j=0; 120 for(int i=0; i<members.length; i++) { 121 if (members[i]!=oppositeRole) { 122 tmp[j] = members[i]; 123 j++; 124 } 125 } 126 members = new MemberItem[j]; 127 System.arraycopy(tmp,0,members,0,j); 128 129 int nbColumns = members.length; 130 headers = new String[nbColumns]; 131 classes = new ClassItem[nbColumns]; 132 133 Stack context = GuiAC.getGraphicContext(); 134 for (int i=0; i<nbColumns; i++) { 135 if (members[i] instanceof FieldItem) { 136 headers[i] = GuiAC.getLabel(members[i],context); 137 } 138 classes[i] = members[i].getTypeItem(); 139 } 140 141 buildData(); 142 Utils.registerCollection(substance,collection,this); 143 } 144 145 public CollectionItem getCollection() { 146 return collection; 147 } 148 149 static class CellLocation { 150 int row; 151 int column; 152 public CellLocation(int row, int column) { 153 this.row = row; 154 this.column = column; 155 } 156 } 157 158 public Object getCellRenderer(View tableView, int column, 159 ViewFactory factory, DisplayContext context) { 160 return getCellRenderer(tableView,substance, 161 members[column],headers[column], 162 collectionView, 163 factory,context); 164 } 165 166 public static Object getCellRenderer(View tableView, 167 Object substance, 168 MemberItem member, 169 String header, 170 CollectionItemView collectionView, 171 ViewFactory factory, 172 DisplayContext context) 173 { 174 if (member instanceof FieldItem) { 175 FieldItem field = (FieldItem)member; 176 Stack typeNames = new Stack(); 177 178 if (field.isReference()) 179 typeNames.push("cell:Reference"); 180 else if (field instanceof CollectionItem) 181 typeNames.push("cell:List"); 182 typeNames.push("cell:"+field.getTypeItem().getName()); 183 184 MetaItem type = RttiAC.getFieldType(field); 185 if (type!=null) 186 typeNames.push("cell:"+type.getName()); 187 188 Enum enum = GuiAC.getEnum(field); 189 if (enum!=null) 190 typeNames.push("cell:Enum"); 191 192 if (collectionView!=null) { 193 String viewType = collectionView.getViewType(field); 194 if (viewType!=null) { 195 typeNames.push(viewType); 196 } 197 } 198 199 loggerTable.debug("types for "+field+": "+typeNames); 200 while (!typeNames.empty()) { 201 String typeName = (String)typeNames.pop(); 202 if (factory.hasViewerFor(typeName)) { 203 try { 204 FieldView cellViewer = (FieldView)factory.createView( 205 "table["+field.getName()+"]", 206 typeName, 207 ExtArrays.emptyObjectArray,context); 208 if (cellViewer instanceof TableCellViewer) { 209 ((TableCellViewer)cellViewer).setTable(tableView); 210 } 211 loggerTable.debug("viewer = "+cellViewer); 212 cellViewer.setField(field); 213 return cellViewer; 214 } catch (Exception e) { 215 logger.error("Failed to instanciate TableCellRenderer "+ 216 field.getName()+" for column "+header, 217 e); 218 } 219 } else { 220 loggerTable.debug("no viewer found for type "+typeName); 221 } 222 } 223 } else if (member instanceof MethodItem) { 224 MethodItem method = (MethodItem)member; 225 try { 226 MethodView cellViewer = (MethodView)factory.createView( 227 "table["+method.getName()+"]", 228 "cell:Method", 229 new Object[] {substance, method},context); 230 if (cellViewer instanceof TableCellViewer) { 231 ((TableCellViewer)cellViewer).setTable(tableView); 232 } 233 loggerTable.debug("viewer = "+cellViewer); 234 return cellViewer; 235 } catch (Exception e) { 236 logger.error("Failed to instanciate TableCellRenderer "+ 237 method.getName()+" for column "+header, 238 e); 239 } 240 } 241 return null; 242 } 243 244 /** 245 * Populate the table model with the objects from the collection. 246 */ 247 void buildData() { 248 Collection c = collection.getActualCollectionThroughAccessor(substance); 249 logger.debug("substance : " + substance); 250 logger.debug("objects : " + new Vector(c)); 251 int nbColumns = members.length; 252 Iterator i = c.iterator(); 253 int row = 0; 254 while (i.hasNext()) { 255 Object obj = i.next(); 256 Object[] data = new Object[nbColumns]; 257 if (obj!=null) { 258 for (int j=0; j<nbColumns; j++) { 259 try { 260 if (members[j] instanceof FieldItem) { 261 data[j] = ((FieldItem)members[j]).getThroughAccessor(obj); 262 if (data[j] instanceof Wrappee && !(data[j] instanceof Collection)) { 263 Utils.registerObject(data[j],this,new CellLocation(row,j)); 264 } 265 } else if (members[j] instanceof MethodItem) { 266 data[j] = members[j].getName(); 267 } 268 } catch (Exception e) { 269 logger.error("Failed to build table cell for "+obj+"."+members[j].getName(),e); 270 } 271 } 272 } 273 logger.debug("add "+Arrays.asList(data)); 274 addRow(obj,data); 275 row++; 276 } 277 } 278 279 public MemberItem[] getMembers() { 280 return members; 281 } 282 283 public String[] getHeaders() { 284 return headers; 285 } 286 287 /** 288 * Gets the column number of a field 289 */ 290 public int getColumnIndex(FieldItem field) { 291 for (int i=0; i<members.length; i++) { 292 if (field==members[i]) 293 return i; 294 } 295 return -1; 296 } 297 298 public int getRowCount() { 299 return rows.size(); 300 } 301 public int getColumnCount() { 302 return headers.length; 303 } 304 305 public String getColumnName(int column) { 306 return headers[column]; 307 } 308 public Class getColumnClass(int column) { 309 loggerTable.debug("getColumnClass("+column+") -> "+classes[column].getName()); 310 return Classes.getPrimitiveTypeWrapper(classes[column].getActualClass()); 311 } 312 public Object getValueAt(int row, int column) { 313 loggerTable.debug("getValueAt("+row+","+column+") -> "+ 314 ((Object[])rows.get(row))[column]); 315 return ((Object[])rows.get(row))[column]; 316 } 317 public Object getObject(int index) { 318 return objects.get(index); 319 } 320 public int indexOf(Object object) { 321 return objects.indexOf(object); 322 } 323 public Object getObject(int row, int col) { 324 return ((Object[])rows.get(row))[col]; 325 } 326 public Object[] getRow(int row) { 327 return (Object[])rows.get(row); 328 } 329 public boolean isCellEditable(int row, int column) { 330 return false; 331 } 332 333 public void addRow(Object object, Object[] data) { 334 objects.add(object); 335 rows.add(data); 336 Utils.registerObject(object,this); 337 } 338 339 // ObjectUpdate interface 340 public void objectUpdated(Object substance,Object param) { 341 if (param==null) { 342 int index = objects.indexOf(substance); 343 if (index!=-1) { 344 int nbColumns = getColumnCount(); 345 Object[] data = new Object[nbColumns]; 346 for (int j=0; j<nbColumns; j++) { 347 if(members[j] instanceof FieldItem) { 348 data[j] = ((FieldItem)members[j]).getThroughAccessor(substance); 349 } 350 } 351 rows.set(index,data); 352 } 353 fireTableRowsUpdated(index,index); 354 } else { 355 CellLocation location = (CellLocation)param; 356 fireTableCellUpdated(location.row,location.column); 357 } 358 } 359 360 // CollectionUpdate 361 public void onChange(Object substance, CollectionItem collection, 362 Object value, Object param) { 363 loggerEvents.debug("onChange "+substance+"."+collection); 364 unregisterViews(); 365 int numRows = objects.size(); 366 loggerEvents.debug(" numRows="+numRows); 367 objects.clear(); 368 rows.clear(); 369 if (numRows>0) 370 fireTableRowsDeleted(0,numRows-1); 371 buildData(); 372 fireTableRowsInserted(0,objects.size()-1); 373 } 374 375 public void onAdd(Object substance, CollectionItem collection, 376 Object value, Object added, Object param) { 377 loggerEvents.debug("onAdd "+substance+"."+collection); 378 // it's not that easy to optimize because we don't know the 379 // position of the added object 380 onChange(substance,collection,value,param); 381 } 382 383 public void onRemove(Object substance, CollectionItem collection, 384 Object value, Object removed, Object param) { 385 loggerEvents.debug("onRemove "+substance+"."+collection); 386 onChange(substance,collection,value,param); 387 } 388 389 /** 390 * Register ourself as a view on all objects of the collection 391 */ 392 protected void registerViews() { 393 loggerReg.debug("TableModel.registerViews "+objects.size()); 394 Iterator i = objects.iterator(); 395 while (i.hasNext()) { 396 Object object = i.next(); 397 Utils.registerObject(object,this); 398 } 399 } 400 401 /** 402 * Unregister ourself as a view on all objects of the collection 403 */ 404 protected void unregisterViews() { 405 loggerReg.debug("TableModel.unRegisterViews "+objects.size()); 406 Iterator i = objects.iterator(); 407 while (i.hasNext()) { 408 Object object = i.next(); 409 Utils.unregisterObject(object,this); 410 } 411 } 412 413 public void close() { 414 unregisterViews(); 415 Utils.unregisterCollection(substance,collection,this); 416 } 417 418 public TableFilter getFilter() { 419 return null; 420 } 421 public TableSorter getSorter() { 422 return null; 423 } 424 }