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