001 package data.swing; 002 003 import java.util.*; 004 import java.io.*; 005 import java.beans.*; 006 007 import util.*; 008 import util.swing.*; 009 010 import data.*; 011 import data.events.*; 012 013 /** 014 * A {@link javax.swing.table.TableModel} that models the contents of a {@link Catalog}. 015 * 016 * @author Steffen Zschaler 017 * @version 2.0 23/08/1999 018 * @since v2.0 019 */ 020 public class CatalogTableModel extends AbstractTableModel implements HelpableListener, CatalogChangeListener, 021 PropertyChangeListener, Serializable { 022 023 /** 024 * The DataBasket used to determine visibility. 025 * 026 * @serial 027 */ 028 protected DataBasket m_dbBasket; 029 030 /** 031 * The Catalog that is being modelled. 032 * 033 * @serial 034 */ 035 protected Catalog m_cModel; 036 037 /** 038 * The Comparator that defines the sorting order of records in the model. It compares 039 * {@link CatalogItem CatalogItems}. 040 * 041 * @serial 042 */ 043 protected Comparator m_cmpComparator = new NaturalComparator(); 044 045 /** 046 * The internal model. A list of the CatalogItems' keys. 047 * 048 * @serial 049 */ 050 protected List m_lKeys; 051 052 /** 053 * Set the table's data. Data is {@link data.Catalog} 054 */ 055 public void setData(Object n_cModel) { 056 m_cModel = (Catalog) n_cModel; 057 updateModel(); 058 fireTableDataChanged(); 059 } 060 061 public Comparator getComparator() { 062 return m_cmpComparator; 063 } 064 065 /** 066 * Create a new CatalogTableModel. 067 * 068 * @param c the Catalog to be modelled. May be {@link data.filters.CatalogFilter filtered}. 069 * @param db the DataBasket to be used to determine visibility. 070 * @param cmp a Comparator defining the sort order of the records. If <code>null</code>, records are ordered 071 * according to the natural ordering of the CatalogItems. 072 * @param ted a TableEntryDescriptor that can split individual CatalogItems into a table's cells. 073 */ 074 public CatalogTableModel(Catalog c, DataBasket db, Comparator cmp, TableEntryDescriptor ted) { 075 super(ted); 076 077 m_dbBasket = db; 078 m_cModel = c; 079 080 if (cmp != null) { 081 m_cmpComparator = cmp; 082 } 083 084 listenerList = new ListenerHelper(this); 085 086 updateModel(); 087 } 088 089 /** 090 * Get the record at the given index. 091 * 092 * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}). 093 * @return the {@link CatalogItem} to be displayed at the given index. May return <code>null</code> if 094 * either there is no record at the indicated position or an exception occurs. 095 * 096 * @override Never 097 */ 098 public Object getRecord(int row) { 099 ((ListenerHelper)listenerList).needModelUpdate(); 100 101 try { 102 if ((row > -1) && (row < getRowCount())) { 103 return m_cModel.get((String)m_lKeys.get(row), m_dbBasket, false); 104 } else { 105 return null; 106 } 107 } 108 catch (VetoException ex) { 109 return null; 110 } 111 } 112 113 /** 114 * Get the number of records in this model. 115 * 116 * @override Never 117 */ 118 public int getRowCount() { 119 ((ListenerHelper)listenerList).needModelUpdate(); 120 121 return m_lKeys.size(); 122 } 123 124 // HelpableListener interface methods 125 126 /** 127 * Subscribe as a listener to the model. If the modelled {@link Catalog} is a {@link ListenableCatalog}, 128 * subscribe as a listener. 129 * 130 * @override Never 131 */ 132 public void subscribe() { 133 if (m_cModel instanceof ListenableCatalog) { 134 ((ListenableCatalog)m_cModel).addCatalogChangeListener(this); 135 } 136 } 137 138 /** 139 * Un-Subscribe as a listener from the model. If the modelled {@link Catalog} is a {@link ListenableCatalog}, 140 * un-subscribe as a listener. 141 * 142 * @override Never 143 */ 144 public void unsubscribe() { 145 if (m_cModel instanceof ListenableCatalog) { 146 ((ListenableCatalog)m_cModel).removeCatalogChangeListener(this); 147 } 148 } 149 150 /** 151 * Update the internal model based on the modelled {@link Catalog}. 152 * 153 * @override Never 154 */ 155 public synchronized void updateModel() { 156 List lKeys = new LinkedList(m_cModel.keySet(m_dbBasket)); 157 Collections.sort(lKeys, new Comparator() { 158 public int compare(Object o1, Object o2) { 159 try { 160 return m_cmpComparator.compare(m_cModel.get((String)o1, m_dbBasket, false), 161 m_cModel.get((String)o2, m_dbBasket, false)); 162 } 163 catch (VetoException ex) { 164 return 0; 165 } 166 } 167 }); 168 m_lKeys = lKeys; 169 } 170 171 // CatalogChangeListener interface methods 172 173 /** 174 * Update the internal model and inform any listeners according to the received event. 175 * 176 * <p>This method is public as an implementation detail and must not be called directly.</p> 177 * 178 * @override Never 179 */ 180 public void addedCatalogItem(CatalogChangeEvent e) { 181 if ((e.getBasket() == m_dbBasket) || (e.getBasket() == null)) { 182 checkAdd(e.getAffectedItem()); 183 } 184 } 185 186 /** 187 * Update the internal model and inform any listeners according to the received event. 188 * 189 * <p>This method is public as an implementation detail and must not be called directly.</p> 190 * 191 * @override Never 192 */ 193 public void commitedAddCatalogItem(CatalogChangeEvent e) { 194 checkAdd(e.getAffectedItem()); 195 } 196 197 /** 198 * Update the internal model and inform any listeners according to the received event. 199 * 200 * <p>This method is public as an implementation detail and must not be called directly.</p> 201 * 202 * @override Never 203 */ 204 public void rolledbackAddCatalogItem(CatalogChangeEvent e) { 205 if (e.getBasket() == m_dbBasket) { 206 checkRemove(e.getAffectedItem()); 207 } 208 } 209 210 /** 211 * Update the internal model and inform any listeners according to the received event. 212 * 213 * <p>This method is public as an implementation detail and must not be called directly.</p> 214 * 215 * @override Never 216 */ 217 public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException {} 218 219 /** 220 * Update the internal model and inform any listeners according to the received event. 221 * 222 * <p>This method is public as an implementation detail and must not be called directly.</p> 223 * 224 * @override Never 225 */ 226 public void noRemoveCatalogItem(CatalogChangeEvent e) {} 227 228 /** 229 * Update the internal model and inform any listeners according to the received event. 230 * 231 * <p>This method is public as an implementation detail and must not be called directly.</p> 232 * 233 * @override Never 234 */ 235 public void removedCatalogItem(CatalogChangeEvent e) { 236 checkRemove(e.getAffectedItem()); 237 } 238 239 /** 240 * Update the internal model and inform any listeners according to the received event. 241 * 242 * <p>This method is public as an implementation detail and must not be called directly.</p> 243 * 244 * @override Never 245 */ 246 public void commitedRemoveCatalogItem(CatalogChangeEvent e) {} 247 248 /** 249 * Update the internal model and inform any listeners according to the received event. 250 * 251 * <p>This method is public as an implementation detail and must not be called directly.</p> 252 * 253 * @override Never 254 */ 255 public void rolledbackRemoveCatalogItem(CatalogChangeEvent e) { 256 checkAdd(e.getAffectedItem()); 257 } 258 259 /** 260 * Update the internal model and inform any listeners according to the received event. 261 * 262 * <p>This method is public as an implementation detail and must not be called directly.</p> 263 * 264 * @override Never 265 */ 266 public void canEditCatalogItem(CatalogChangeEvent e) throws VetoException {} 267 268 /** 269 * Update the internal model and inform any listeners according to the received event. 270 * 271 * <p>This method is public as an implementation detail and must not be called directly.</p> 272 * 273 * @override Never 274 */ 275 public void noEditCatalogItem(CatalogChangeEvent e) {} 276 277 /** 278 * Update the internal model and inform any listeners according to the received event. 279 * 280 * <p>This method is public as an implementation detail and must not be called directly.</p> 281 * 282 * @override Never 283 */ 284 public void editingCatalogItem(CatalogChangeEvent e) { 285 if (e.getBasket() != m_dbBasket) { 286 checkRemove(e.getAffectedItem()); 287 } else { 288 e.getAffectedItem().addPropertyChangeListener(this); 289 } 290 } 291 292 /** 293 * Update the internal model and inform any listeners according to the received event. 294 * 295 * <p>This method is public as an implementation detail and must not be called directly.</p> 296 * 297 * @override Never 298 */ 299 public void commitEditCatalogItem(CatalogChangeEvent e) { 300 if (e.getBasket() != m_dbBasket) { 301 checkAdd(e.getAffectedItem()); 302 } else { 303 e.getAffectedItem().removePropertyChangeListener(this); 304 305 updateModel(); 306 } 307 } 308 309 /** 310 * Update the internal model and inform any listeners according to the received event. 311 * 312 * <p>This method is public as an implementation detail and must not be called directly.</p> 313 * 314 * @override Never 315 */ 316 public void rollbackEditCatalogItem(CatalogChangeEvent e) { 317 if (e.getBasket() != m_dbBasket) { 318 checkAdd(e.getAffectedItem()); 319 } else { 320 e.getAffectedItem().removePropertyChangeListener(this); 321 322 updateModel(); 323 fireTableDataChanged(); 324 } 325 } 326 327 /** 328 * Update the internal model and inform any listeners according to the received event. 329 * 330 * <p>This method is public as an implementation detail and must not be called directly.</p> 331 * 332 * @override Never 333 */ 334 public void propertyChange(PropertyChangeEvent e) { 335 if (e.getSource()instanceof CatalogItem) { 336 checkUpdate((CatalogItem)e.getSource()); 337 } 338 } 339 340 /** 341 * Internal helper method. Check where, if at all, the given CatalogItem has been added with respect to the 342 * internal model. 343 * 344 * @param ci the added CatalogItem 345 * 346 * @override Never 347 */ 348 protected synchronized void checkAdd(CatalogItem ci) { 349 updateModel(); 350 351 int nIdx = m_lKeys.indexOf(ci.getName()); 352 353 if (nIdx > -1) { 354 fireTableRowsInserted(nIdx, nIdx); 355 } 356 } 357 358 /** 359 * Internal helper method. Check from where, if at all, the given CatalogItem has been removed with respect 360 * to the internal model. 361 * 362 * @param ci the removed CatalogItem 363 * 364 * @override Never 365 */ 366 protected synchronized void checkRemove(CatalogItem ci) { 367 int nIdx = m_lKeys.indexOf(ci.getName()); 368 369 updateModel(); 370 371 if (nIdx > -1) { 372 fireTableRowsDeleted(nIdx, nIdx); 373 } 374 } 375 376 /** 377 * Internal helper method. Check for updates in the given CatalogItem. 378 * 379 * @param ci the updated CatalogItem 380 * 381 * @override Never 382 */ 383 protected synchronized void checkUpdate(CatalogItem ci) { 384 int nIdx1 = m_lKeys.indexOf(ci.getName()); 385 386 updateModel(); 387 388 int nIdx2 = m_lKeys.indexOf(ci.getName()); 389 390 if (nIdx1 == -1) { 391 if (nIdx2 > -1) { 392 fireTableRowsInserted(nIdx2, nIdx2); 393 } else { 394 return; 395 } 396 } else { 397 if (nIdx2 == -1) { 398 fireTableRowsDeleted(nIdx1, nIdx1); 399 } else { 400 if (nIdx1 > nIdx2) { 401 int nTemp = nIdx2; 402 nIdx2 = nIdx1; 403 nIdx1 = nIdx2; 404 } 405 406 fireTableRowsUpdated(nIdx1, nIdx2); 407 } 408 } 409 } 410 }