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