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