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 }