View Javadoc
1   /**
2    * 
3    * Copyright (c) 2014, Openflexo
4    * 
5    * This file is part of Gina-swing, a component of the software infrastructure 
6    * developed at Openflexo.
7    * 
8    * 
9    * Openflexo is dual-licensed under the European Union Public License (EUPL, either 
10   * version 1.1 of the License, or any later version ), which is available at 
11   * https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
12   * and the GNU General Public License (GPL, either version 3 of the License, or any 
13   * later version), which is available at http://www.gnu.org/licenses/gpl.html .
14   * 
15   * You can redistribute it and/or modify under the terms of either of these licenses
16   * 
17   * If you choose to redistribute it and/or modify under the terms of the GNU GPL, you
18   * must include the following additional permission.
19   *
20   *          Additional permission under GNU GPL version 3 section 7
21   *
22   *          If you modify this Program, or any covered work, by linking or 
23   *          combining it with software containing parts covered by the terms 
24   *          of EPL 1.0, the licensors of this Program grant you additional permission
25   *          to convey the resulting work. * 
26   * 
27   * This software is distributed in the hope that it will be useful, but WITHOUT ANY 
28   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
29   * PARTICULAR PURPOSE. 
30   *
31   * See http://www.openflexo.org/license.html for details.
32   * 
33   * 
34   * Please contact Openflexo (openflexo-contacts@openflexo.org)
35   * or visit www.openflexo.org if you need additional information.
36   * 
37   */
38  
39  package org.openflexo.gina.swing.utils.table;
40  
41  import java.awt.Component;
42  import java.util.Enumeration;
43  import java.util.Observable;
44  import java.util.Observer;
45  import java.util.Vector;
46  import java.util.logging.Level;
47  import java.util.logging.Logger;
48  
49  import javax.swing.JTable;
50  import javax.swing.event.EventListenerList;
51  import javax.swing.event.TableModelEvent;
52  import javax.swing.table.DefaultTableCellRenderer;
53  import javax.swing.table.DefaultTableModel;
54  import javax.swing.table.TableModel;
55  
56  import org.openflexo.connie.BindingEvaluationContext;
57  import org.openflexo.gina.view.widget.table.impl.EditableColumn;
58  
59  /**
60   * Represents a TableModel used by a TabularView
61   * 
62   * @author sguerin
63   * 
64   */
65  public abstract class AbstractModel<M extends Observable, D> extends DefaultTableModel implements Observer {
66  
67  	private static final Logger LOGGER = Logger.getLogger(AbstractModel.class.getPackage().getName());
68  
69  	private M _model;
70  
71  	private final Vector<AbstractColumn<D, ?>> _columns;
72  
73  	private int _rowHeight = -1;
74  
75  	private final Vector<D> _observedObjects;
76  
77  	public AbstractModel(M model) {
78  		super();
79  		_model = model;
80  		_columns = new Vector<>();
81  		_observedObjects = new Vector<>();
82  	}
83  
84  	public M getModel() {
85  		return _model;
86  	}
87  
88  	public void setModel(M model) {
89  		if (LOGGER.isLoggable(Level.FINE)) {
90  			LOGGER.fine("Set model for " + getClass().getName() + " with " + model);
91  		}
92  		M oldModel = _model;
93  		_model = model;
94  		fireModelObjectHasChanged(oldModel, model);
95  		fireTableDataChanged();
96  	}
97  
98  	/**
99  	 * Notifies all listeners that represented model has changed
100 	 * 
101 	 * @see TableModelEvent
102 	 * @see EventListenerList
103 	 * @see javax.swing.JTable#tableChanged(TableModelEvent)
104 	 */
105 	public void fireModelObjectHasChanged(M oldModel, M newModel) {
106 		fireTableChanged(new ModelObjectHasChanged(this, oldModel, newModel));
107 	}
108 
109 	/**
110 	 * Notifies all listeners that supplied object must be selected
111 	 * 
112 	 * @see TableModelEvent
113 	 * @see EventListenerList
114 	 * @see javax.swing.JTable#tableChanged(TableModelEvent)
115 	 */
116 	public void selectObject(D selectedObject) {
117 		fireTableChanged(new SelectObjectEvent(this, selectedObject));
118 	}
119 
120 	public class TableStructureHasChanged extends TableModelEvent {
121 		public TableStructureHasChanged(TableModel source) {
122 			super(source);
123 		}
124 	}
125 
126 	public class ModelObjectHasChanged extends TableModelEvent {
127 		private final M _oldModel;
128 
129 		private final M _newModel;
130 
131 		public ModelObjectHasChanged(TableModel source, M oldModel, M newModel) {
132 			super(source);
133 			_oldModel = oldModel;
134 			_newModel = newModel;
135 		}
136 
137 		public M getNewModel() {
138 			return _newModel;
139 		}
140 
141 		public M getOldModel() {
142 			return _oldModel;
143 		}
144 	}
145 
146 	public class SelectObjectEvent extends TableModelEvent {
147 		private final D _selectedObject;
148 
149 		public SelectObjectEvent(TableModel source, D selectedObject) {
150 			super(source);
151 			_selectedObject = selectedObject;
152 		}
153 
154 		public D getSelectedObject() {
155 			return _selectedObject;
156 		}
157 	}
158 
159 	public class RowMoveForObjectEvent extends TableModelEvent {
160 		private final D _editedObject;
161 		private final int _newRow;
162 		private final int _column;
163 
164 		public RowMoveForObjectEvent(TableModel source, D editedObject, int newRow, int column) {
165 			super(source);
166 			_editedObject = editedObject;
167 			_column = column;
168 			_newRow = newRow;
169 		}
170 
171 		public D getEditedObject() {
172 			return _editedObject;
173 		}
174 
175 		@Override
176 		public int getColumn() {
177 			return _column;
178 		}
179 
180 		public int getNewRow() {
181 			return _newRow;
182 		}
183 	}
184 
185 	@Override
186 	public void fireTableStructureChanged() {
187 		// logger.info("fireTableStructureChanged()");
188 		super.fireTableStructureChanged();
189 		fireTableChanged(new TableStructureHasChanged(this));
190 	}
191 
192 	@Override
193 	public void fireTableRowsDeleted(int firstRow, int lastRow) {
194 		// logger.info("fireTableRowsDeleted("+firstRow+","+lastRow+")");
195 		super.fireTableRowsDeleted(firstRow, lastRow);
196 	}
197 
198 	/**
199 	 * Notifies all listeners that represented model has changed
200 	 * 
201 	 * @see TableModelEvent
202 	 * @see EventListenerList
203 	 * @see javax.swing.JTable#tableChanged(TableModelEvent)
204 	 */
205 	@Override
206 	public void fireTableDataChanged() {
207 		super.fireTableDataChanged();
208 		if (LOGGER.isLoggable(Level.FINE)) {
209 			LOGGER.fine("fireTableDataChanged() in " + getClass().getName() + " " + hashCode());
210 		}
211 		updateObservedObjects();
212 	}
213 
214 	private void updateObservedObjects() {
215 		for (Enumeration<D> en = _observedObjects.elements(); en.hasMoreElements();) {
216 			Object observed = en.nextElement();
217 			if (observed instanceof Observable) {
218 				((Observable) observed).deleteObserver(this);
219 			}
220 		}
221 		_observedObjects.clear();
222 		for (int i = 0; i < getRowCount(); i++) {
223 			D observed = elementAt(i);
224 			_observedObjects.add(observed);
225 			if (observed instanceof Observable) {
226 				((Observable) observed).addObserver(this);
227 			}
228 		}
229 	}
230 
231 	@Override
232 	public void update(Observable observable, Object dataModification) {
233 		int row = indexOf((D) observable);
234 		fireTableRowsUpdated(row, row);
235 		if (LOGGER.isLoggable(Level.FINE)) {
236 			LOGGER.fine("Update row " + row + " for object " + observable);
237 		}
238 	}
239 
240 	@Override
241 	public abstract int getRowCount();
242 
243 	public int getRowHeight() {
244 		return _rowHeight;
245 	}
246 
247 	public void setRowHeight(int aRowHeight) {
248 		_rowHeight = aRowHeight;
249 	}
250 
251 	public abstract D elementAt(int row);
252 
253 	public int indexOf(D object) {
254 		// logger.info("Search index of="+object+" model="+getModel()+" "+getClass().getName());
255 		for (int i = 0; i < getRowCount(); i++) {
256 			D obj = elementAt(i);
257 			// logger.info("Examine object="+obj);
258 			if (obj == object) {
259 				return i;
260 			}
261 		}
262 		return -1;
263 	}
264 
265 	public void addToColumns(AbstractColumn<D, ?> aColumn) {
266 		_columns.add(aColumn);
267 		aColumn.setModel(this);
268 	}
269 
270 	public void insertColumnAtIndex(AbstractColumn<D, ?> aColumn, int index) {
271 		_columns.insertElementAt(aColumn, index);
272 		aColumn.setModel(this);
273 	}
274 
275 	public void replaceColumnByColumn(AbstractColumn<D, ?> oldColumn, AbstractColumn<D, ?> newColumn) {
276 		int index = _columns.indexOf(oldColumn);
277 		if (index < 0) {
278 			if (LOGGER.isLoggable(Level.WARNING)) {
279 				LOGGER.warning("Try to replaced a inexisting column. Not going further.");
280 			}
281 			return;
282 		}
283 		_columns.remove(index);
284 		_columns.insertElementAt(newColumn, index);
285 	}
286 
287 	public void removeFromColumns(AbstractColumn<D, ?> aColumn) {
288 		_columns.remove(aColumn);
289 		aColumn.setModel(null);
290 	}
291 
292 	public AbstractColumn<D, ?> columnAt(int index) {
293 		return _columns.elementAt(index);
294 	}
295 
296 	public int getTotalPreferredWidth() {
297 		int returned = 0;
298 		for (int i = 0; i < getColumnCount(); i++) {
299 			returned += getDefaultColumnSize(i);
300 		}
301 		return returned;
302 	}
303 
304 	@Override
305 	public int getColumnCount() {
306 		return _columns.size();
307 	}
308 
309 	@Override
310 	public String getColumnName(int col) {
311 		AbstractColumn<?, ?> column = columnAt(col);
312 		if (column != null) {
313 			return column.getLocalizedTitle();
314 		}
315 		return "???";
316 	}
317 
318 	public int getIndexForColumnWithName(String colName) {
319 		if (colName == null) {
320 			return -1;
321 		}
322 		for (int i = 0; i < getColumnCount(); i++) {
323 			if (colName.equals(getColumnName(i))) {
324 				return i;
325 			}
326 		}
327 		return -1;
328 	}
329 
330 	public String getColumnTooltip(int col) {
331 		AbstractColumn<?, ?> column = columnAt(col);
332 		if (column != null) {
333 			return column.getLocalizedTooltip();
334 		}
335 		return "-";
336 	}
337 
338 	public int getDefaultColumnSize(int col) {
339 		AbstractColumn<?, ?> column = columnAt(col);
340 		if (column != null) {
341 			return column.getDefaultWidth();
342 		}
343 		return 75;
344 	}
345 
346 	public boolean getColumnResizable(int col) {
347 		AbstractColumn<?, ?> column = columnAt(col);
348 		if (column != null) {
349 			return column.getResizable();
350 		}
351 		return true;
352 	}
353 
354 	@Override
355 	public Class<?> getColumnClass(int col) {
356 		AbstractColumn<?, ?> column = columnAt(col);
357 		if (column != null) {
358 			return column.getValueClass();
359 		}
360 		return null;
361 	}
362 
363 	@Override
364 	public boolean isCellEditable(int row, int col) {
365 		AbstractColumn<D, ?> column = columnAt(col);
366 		if (column != null) {
367 			D object = elementAt(row);
368 			return column.isCellEditableFor(object);
369 		}
370 		return false;
371 	}
372 
373 	@Override
374 	public Object getValueAt(int row, int col) {
375 		AbstractColumn<D, ?> column = columnAt(col);
376 		if (column != null) {
377 			D object = elementAt(row);
378 			return column.getValueFor(object);
379 		}
380 		return null;
381 
382 	}
383 
384 	@Override
385 	public void setValueAt(Object value, int row, int col) {
386 		AbstractColumn<?, ?> column = columnAt(col);
387 		if (column != null && column instanceof EditableColumn) {
388 			D object = elementAt(row);
389 			((EditableColumn<D, Object>) column).setValueFor(object, value/*, getBindingEvaluationContext()*/);
390 			fireCellUpdated(object, row, col);
391 		}
392 	}
393 
394 	/**
395 	 * Notifies all listeners that the value of the cell at <code>[row, column]</code> has been updated.
396 	 * 
397 	 * @param editedObject
398 	 *            object that was updated
399 	 * @param row
400 	 *            row of cell which has been updated
401 	 * @param column
402 	 *            column of cell which has been updated
403 	 * @see TableModelEvent
404 	 * @see EventListenerList
405 	 */
406 	public void fireCellUpdated(D editedObject, int row, int column) {
407 		// fireTableChanged(new TableModelEvent(this, row, row, column));
408 		int newRow = indexOf(editedObject);
409 		if (row != newRow) {
410 			fireTableChanged(new RowMoveForObjectEvent(this, editedObject, newRow, column));
411 		}
412 	}
413 
414 	protected class DMCellRenderer extends DefaultTableCellRenderer {
415 		@Override
416 		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
417 				int column) {
418 			Component returned = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
419 			return returned;
420 		}
421 	}
422 
423 	public abstract BindingEvaluationContext getBindingEvaluationContext();
424 }