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