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 }