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 DataBasket}. 014 * 015 * @author Steffen Zschaler 016 * @version 2.0 23/08/1999 017 * @since v2.0 018 */ 019 public class DataBasketTableModel extends AbstractTableModel implements DataBasketListener, HelpableListener, 020 Serializable { 021 022 /** 023 * ID for serialization. 024 */ 025 private static final long serialVersionUID = -6060043450003111419L; 026 027 /** 028 * The DataBasket being modelled. 029 * 030 * @serial 031 */ 032 protected DataBasket m_dbBasket; 033 034 /** 035 * The condition specifying the items to be displayed. 036 * 037 * @serial 038 */ 039 protected DataBasketCondition m_dbcCondition; 040 041 /** 042 * A strategy that will group individual DataBasketEntries together for display. If <code>null</code>, no 043 * grouping will occur. 044 * 045 * @serial 046 */ 047 protected DataBasketEntryGrouper m_dbegGrouper; 048 049 /** 050 * The Comparator that defines the sorting order of records in the model. It compares 051 * {@link DataBasketEntry DataBasketEntries}. 052 * 053 * @serial 054 */ 055 protected Comparator<DataBasketEntry> m_cmpComparator = new SerializableComparator<DataBasketEntry>() { 056 private static final long serialVersionUID = 5930821156200392675L; 057 058 public int compare(DataBasketEntry dbe1, DataBasketEntry dbe2) { 059 int nRet = dbe1.getMainKey().compareTo(dbe2.getMainKey()); 060 061 if (nRet != 0) { 062 return nRet; 063 } 064 065 if ((dbe1.getSecondaryKey()instanceof Comparable<?>) && (dbe2.getSecondaryKey()instanceof Comparable<?>)) { 066 return (dbe1.getSecondaryKey()).compareTo(dbe2.getSecondaryKey()); 067 } 068 069 return 0; 070 } 071 }; 072 073 /** 074 * The internal model. A list of the DataBasketEntries. 075 * 076 * @serial 077 */ 078 protected List<DataBasketEntry> m_lEntries; 079 080 /** 081 * set the table's data. Data is {@link data.DataBasket} 082 */ 083 public void setData(Object n_dbBasket) { 084 m_dbBasket = (DataBasket) n_dbBasket; 085 updateModel(); 086 fireTableDataChanged(); 087 } 088 089 /** 090 * Create a new DataBasketTableModel. 091 * 092 * @param db the DataBasket to be modellled. 093 * @param dbc a condition specifying the DataBasketEntries to be part of the model. 094 * @param dbeg a strategy that will group individual DataBasketEntries together for display. If 095 * <code>null</code>, no grouping will occur. 096 * @param cmp a Comparator defining the sort order of the records. If <code>null</code>, records are ordered 097 * according to the main key of the entries first and of the secondary key second. 098 * @param ted a TableEntryDescriptor that can split individual DataBasketEntries into a table's cells. 099 */ 100 public DataBasketTableModel(DataBasket db, DataBasketCondition dbc, DataBasketEntryGrouper dbeg, 101 Comparator<DataBasketEntry> cmp, TableEntryDescriptor ted) { 102 super(ted); 103 104 m_dbBasket = db; 105 m_dbcCondition = dbc; 106 m_dbegGrouper = dbeg; 107 108 if (cmp != null) { 109 m_cmpComparator = cmp; 110 } 111 112 updateModel(); 113 114 listenerList = new ListenerHelper(this); 115 } 116 117 /** 118 * Get the record at the given index. 119 * 120 * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}). 121 * @return the {@link DataBasketEntry} to be displayed at the given index. May return <code>null</code> if 122 * there is no record at the indicated position. 123 * 124 * @override Never 125 */ 126 public Object getRecord(int row) { 127 ((ListenerHelper)listenerList).needModelUpdate(); 128 129 if ((row > -1) && (row < getRowCount())) { 130 return m_lEntries.get(row); 131 } else { 132 return null; 133 } 134 } 135 136 /** 137 * Get the number of records in this model. 138 * 139 * @override Never 140 */ 141 public int getRowCount() { 142 ((ListenerHelper)listenerList).needModelUpdate(); 143 144 return m_lEntries.size(); 145 } 146 147 // HelpableListener interface methods 148 149 /** 150 * Subscribe as a listener to the model. If the modelled {@link DataBasket} is a 151 * {@link ListenableDataBasket}, subscribe as a listener. 152 * 153 * @override Never 154 */ 155 public void subscribe() { 156 if (m_dbBasket instanceof ListenableDataBasket) { 157 ((ListenableDataBasket)m_dbBasket).addDataBasketListener(this); 158 } 159 } 160 161 /** 162 * Un-Subscribe as a listener from the model. If the modelled {@link DataBasket} is a 163 * {@link ListenableDataBasket}, un-subscribe as a listener. 164 * 165 * @override Never 166 */ 167 public void unsubscribe() { 168 if (m_dbBasket instanceof ListenableDataBasket) { 169 ((ListenableDataBasket)m_dbBasket).removeDataBasketListener(this); 170 } 171 } 172 173 /** 174 * Update the internal model based on the modelled {@link DataBasket}. 175 * 176 * @override Never 177 */ 178 public synchronized void updateModel() { 179 List<DataBasketEntry> lEntries = new LinkedList<DataBasketEntry>(); 180 181 for (Iterator<DataBasketEntry> i = m_dbBasket.iterator(m_dbcCondition); i.hasNext(); ) { 182 lEntries.add(i.next()); 183 } 184 185 List<DataBasketEntry> lGroupedEntries = null; 186 187 if (m_dbegGrouper != null) { 188 lGroupedEntries = new LinkedList<DataBasketEntry>(); 189 190 while (true) { 191 Iterator<DataBasketEntry> i = lEntries.iterator(); 192 193 if (i.hasNext()) { 194 DataBasketEntry dbeGrouped = i.next(); 195 i.remove(); 196 197 while (i.hasNext()) { 198 DataBasketEntry dbe2 = i.next(); 199 200 if (m_dbegGrouper.canGroup(dbeGrouped, dbe2)) { 201 i.remove(); 202 203 dbeGrouped = m_dbegGrouper.group(dbeGrouped, dbe2); 204 } 205 } 206 207 lGroupedEntries.add(dbeGrouped); 208 } else { 209 break; 210 } 211 } 212 } else { 213 lGroupedEntries = lEntries; 214 } 215 Collections.sort(lGroupedEntries, m_cmpComparator); 216 217 m_lEntries = lGroupedEntries; 218 } 219 220 // DataBasketListener interface methods 221 222 /** 223 * Update the internal model and inform any listeners according to the received event. 224 * 225 * <p>This method is public as an implementation detail and must not be called directly.</p> 226 * 227 * @override Never 228 */ 229 public void addedDBE(DataBasketEvent e) { 230 updateModel(); 231 fireTableDataChanged(); 232 } 233 234 /** 235 * Update the internal model and inform any listeners according to the received event. 236 * 237 * <p>This method is public as an implementation detail and must not be called directly.</p> 238 * 239 * @override Never 240 */ 241 public void removedDBE(DataBasketEvent e) { 242 updateModel(); 243 fireTableDataChanged(); 244 } 245 246 /** 247 * Update the internal model and inform any listeners according to the received event. 248 * 249 * <p>This method is public as an implementation detail and must not be called directly.</p> 250 * 251 * @override Never 252 */ 253 public void dataBasketChanged(DataBasketEvent e) { 254 updateModel(); 255 fireTableDataChanged(); 256 } 257 }