View Javadoc

1   /*
2    *  Copyright (C) 2003-2005 SINTEF
3    *  Author:  Fredrik Vraalsen (fredrik dot vraalsen at sintef dot no)
4    *  Webpage: http://coras.sourceforge.net/
5    *
6    *  This program is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public License
8    *  as published by the Free Software Foundation; either version 2.1
9    *  of the License, or (at your option) any later version.
10   *
11   *  This program is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this program; if not, write to the Free
18   *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19   *  02111-1307 USA
20   */
21  package coras.table;
22  
23  import java.beans.PropertyChangeListener;
24  import java.beans.PropertyChangeSupport;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javax.swing.table.AbstractTableModel;
33  
34  import no.sintef.xml.XmlHelper;
35  
36  import org.apache.log4j.Logger;
37  
38  import coras.table.columnchoices.jaxb.ChoiceType;
39  import coras.table.columnchoices.jaxb.ColumnChoicesType;
40  import coras.table.columnchoices.jaxb.ColumnType;
41  import coras.table.jaxb.CellType;
42  import coras.table.jaxb.ColumnDefType;
43  import coras.table.jaxb.RowType;
44  import coras.table.jaxb.TableType;
45  import coras.table.jaxb.impl.CellTypeImpl;
46  import coras.table.jaxb.impl.ColumnDefTypeImpl;
47  import coras.table.jaxb.impl.RowTypeImpl;
48  
49  /***
50   * @author fvr
51   *
52   * To change the template for this generated type comment go to
53   * Window - Preferences - Java - Code Generation - Code and Comments
54   */
55  public class CorasTableModel extends AbstractTableModel {
56  	
57  	public static final String DIRTY = "dirty";
58  	private static final Logger LOGGER = Logger.getLogger(CorasTableModel.class);
59  
60  	private TableType table = null;
61  	private Map columnChoicesMap = new HashMap();
62  
63  	private boolean dirty = false;
64  	private PropertyChangeSupport propertyChangeHandler = new PropertyChangeSupport(this);
65  	
66  	public TableType getTable() {
67  		return table;
68  	}
69  	
70  	public void setTable(TableType table) {
71  		this.table = table;
72  		fireTableChanged(null);
73  	}
74  	
75  	public boolean isFixedStructure() {
76  		return table == null || table.isFixedStructure();
77  	}
78  	
79  	public boolean isDirty() {
80  		return dirty;
81  	}
82  
83  	private void setDirty(boolean newDirty) {
84  		boolean oldDirty = dirty;
85  		dirty = newDirty;
86  		if (oldDirty != newDirty) {
87  			propertyChangeHandler.firePropertyChange(DIRTY, oldDirty, newDirty);
88  		}
89  	}
90  	
91  	public void makeClean() {
92  		setDirty(false);
93  	}
94  	
95  	private void makeDirty() {
96  		setDirty(true);
97  	}
98  	
99  	/* (non-Javadoc)
100 	 * @see javax.swing.table.TableModel#getColumnCount()
101 	 */
102 	public int getColumnCount() {
103 		if (table == null) {
104 			return 0;
105 		} else {
106 			return getVisibleColumnDefs().size();
107 		}
108 	}
109 	
110 	/* (non-Javadoc)
111 	 * @see javax.swing.table.TableModel#getRowCount()
112 	 */
113 	public int getRowCount() {
114 		if (table == null) {
115 			return 0;
116 		} else {
117 			return table.getRows().getRow().size();
118 		}
119 	}
120 	
121 	/* (non-Javadoc)
122 	 * @see javax.swing.table.TableModel#getValueAt(int, int)
123 	 */
124 	public Object getValueAt(int rowIndex, int columnIndex) {
125 		CellType cell = getCell(rowIndex, columnIndex);
126 		if (cell != null)
127 			return cell.getValue(); // TODO: clone!? Otherwise content may be changed but model remain clean?
128 		else
129 			return "";
130 	}
131 	
132 	/* (non-Javadoc)
133 	 * @see javax.swing.table.TableModel#getColumnName(int)
134 	 */
135 	public String getColumnName(int columnIndex) {
136 		if (table == null) {
137 			return null;
138 		}
139 
140 		ColumnDefType columnDef = getColumnDef(columnIndex);
141 		if (columnDef == null) {
142 			return null;
143 		}
144 
145 		// Attemt to first look up externalized column name,
146 		// fall back to column name defined in table.
147 		String tableType = getTable().getType();
148 		String columnId = columnDef.getId();
149 		String key = tableType.replaceAll("//s", "_") + "." + columnId; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
150 		String name = Messages.getColumnName(key);
151 		if (name.equals('!' + key + '!')) {
152 			key = columnId;
153 			name = Messages.getColumnName(key);
154 		}
155 		if (name.equals('!' + key + '!')) {
156 			name = null;
157 		}
158 		if (name != null) {
159 			return name;
160 		} else {
161 			return columnDef.getName();
162 		}
163 	}
164 
165 	/* (non-Javadoc)
166 	 * @see javax.swing.table.TableModel#getColumnName(int)
167 	 */
168 	public String getColumnDescription(int columnIndex) {
169 		if (table == null) {
170 			return null;
171 		}
172 
173         // Attempt to get column description.
174         // First look for table type + column ID, then only column ID
175 		String tableType = getTable().getType();
176         String columnId = getColumnId(columnIndex);
177 		String key = tableType.replaceAll("//s", "_") + "." + columnId; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
178 		String description = Messages.getColumnDescription(key);
179         if (description.equals('!' + key + '!')) {
180         	key = columnId;
181         	description = Messages.getColumnDescription(key);
182         }
183         if (description.equals('!' + key + '!')) {
184         	description = null;
185         }
186         return description;
187 	}
188 	
189 	/* (non-Javadoc)
190 	 * @see javax.swing.table.TableModel#getColumnClass(int)
191 	 */
192 	public Class getColumnClass(int columnIndex) {
193 		return String.class;
194 	}
195 
196 	/* (non-Javadoc)
197 	 * @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
198 	 */
199 	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
200 		if (aValue instanceof String) {
201 			if (!isValidValue(aValue, rowIndex, columnIndex)) {
202 				System.err.println("invalid cell value: " + aValue);
203 				Thread.dumpStack();
204 				return;
205 			}
206 			CellType cell = getCell(rowIndex, columnIndex);
207 			if (cell == null) {
208 				System.err.println("This should never happen: cell is null");
209 				Thread.dumpStack();
210 				return;
211 			}
212 			if (aValue.equals(cell.getValue())) {
213 				return;
214 			}
215 			cell.setValue((String) aValue);
216 			makeDirty();
217 			fireTableCellUpdated(rowIndex, columnIndex);
218 		} else {
219 			System.err.println("Unsupported cell value type: " + aValue + (aValue != null ? " [" + aValue.getClass() + "]" : ""));
220 			Thread.dumpStack();
221 		}
222 	}
223 	
224 	/* (non-Javadoc)
225 	 * @see javax.swing.table.TableModel#isCellEditable(int, int)
226 	 */
227 	public boolean isCellEditable(int rowIndex, int columnIndex) {
228 		ColumnDefType columnDef = getColumnDef(columnIndex);
229 		return columnDef != null && columnDef.isEditable();
230 	}
231 
232 	/***
233 	 * 
234 	 */
235 	public void addRow(int rowIndex) {
236 		if (table == null) {
237 			return;
238 		}
239 		if (table.isFixedStructure()) {
240 			return; // FIXME throw exception?
241 		}
242 		List rows = table.getRows().getRow();
243 		LOGGER.debug("addRow(" + rowIndex + ") of " + rows.size());
244 		if (rowIndex < 0 || rowIndex > rows.size())
245 			return;
246 		RowType row = new RowTypeImpl();
247 		List cells = row.getCell();
248 		for (Iterator i = getColumnDefs().iterator(); i.hasNext(); ) {
249 			ColumnDefType columnDef = (ColumnDefType) i.next();
250 			CellType cell = new CellTypeImpl();
251 			cell.setColumnId(columnDef.getId());
252 			cell.setValue("");
253 			cells.add(cell);
254 		}
255 		rows.add(rowIndex, row);
256 		makeDirty();
257 		fireTableRowsInserted(rowIndex, rowIndex);
258 		// fireTableStructureChanged();
259 	}
260 
261 	public void deleteRow(int rowIndex) {
262 		if (table == null) {
263 			return;
264 		}
265 		if (table.isFixedStructure()) {
266 			return; // FIXME throw exception?
267 		}
268 		List rows = table.getRows().getRow();
269 		if (rows.size() <= 1) {
270 			return; // FIXME throw exception?
271 		}
272 		if (rowIndex < 0 || rowIndex >= rows.size()) {
273 			return;
274 		}
275 		rows.remove(rowIndex);
276 		makeDirty();
277 		fireTableRowsDeleted(rowIndex, rowIndex);
278 	}
279 	
280 
281 	/***
282 	 * @param columnChoices
283 	 */
284 	public void setColumnChoices(ColumnChoicesType columnChoices) {
285 		columnChoicesMap = createColumnChoicesMap(columnChoices); 
286 	}
287 	
288 	public void addPropertyChangeListener(PropertyChangeListener listener) {
289 		propertyChangeHandler.addPropertyChangeListener(listener);
290 	}
291 	
292 	public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
293 		propertyChangeHandler.addPropertyChangeListener(propertyName, listener);
294 	}
295 	
296 	public void removePropertyChangeListener(PropertyChangeListener listener) {
297 		propertyChangeHandler.removePropertyChangeListener(listener);
298 	}
299 	
300 	public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
301 		propertyChangeHandler.removePropertyChangeListener(propertyName, listener);
302 	}
303 	
304 	protected boolean isValidValue(Object aValue, int rowIndex, int columnIndex) {
305 		if (isUserEditable(columnIndex)) {
306 			return true;
307 		}
308 		Collection choices = getColumnChoices(columnIndex);
309 		if (choices == null || choices.size() == 0) {
310 			return true;
311 		}
312 		for (Iterator i = choices.iterator(); i.hasNext();) {
313 			ChoiceType choice = (ChoiceType) i.next();
314 			if (choice.getValue().equals(aValue)) {
315 				return true;
316 			}
317 		}
318 		return false;
319 	}
320 
321 	protected Collection getColumnChoices(int col) {
322 		String columnId = getColumnId(col);
323 		// TableColumn tableColumn = getColumnModel().getColumn(i);
324 		ColumnType column = (ColumnType) columnChoicesMap.get(columnId);
325 		return column != null ? column.getChoice() : null;
326 	}
327 
328 	/***
329 	 * @param column
330 	 * @return
331 	 */
332 	protected boolean isUserEditable(int col) {
333 		String columnId = getColumnId(col);
334 		// TableColumn tableColumn = getColumnModel().getColumn(i);
335 		ColumnType column = (ColumnType) columnChoicesMap.get(columnId);
336 		return column != null ? column.isUserEditable() : false;
337 	}
338 
339 	/***
340 	 * @param string
341 	 * @return
342 	 */
343 	private String getColumnId(int columnIndex) {
344 		if (table == null) {
345 			return null;
346 		}
347 		ColumnDefType columnDef = getColumnDef(columnIndex);
348 		if (columnDef != null)
349 			return columnDef.getId();
350 		else
351 			return null;
352 	}
353 
354 	private CellType getCell(int rowIndex, int columnIndex) {
355 		if (table == null) {
356 			return null;
357 		}
358 		List rows = table.getRows().getRow();
359 		if (rowIndex >= rows.size()) {
360             return null;
361         }
362 		RowType row = (RowType) rows.get(rowIndex);
363 		ColumnDefType columnDef = getColumnDef(columnIndex);
364 		if (row == null || columnDef == null) {
365 			return null;
366 		}
367 		String columnId = columnDef.getId();
368 		List cells = row.getCell();
369 		for (Iterator i = cells.iterator(); i.hasNext(); ) {
370 			CellType cell = (CellType) i.next();
371 			if (columnId.equals(cell.getColumnId()))
372 				return cell;
373 		}
374 		// Add missing cell
375 		CellType cell = new CellTypeImpl();
376 		cell.setColumnId(columnId);
377 		cell.setValue("");
378 		cells.add(cell);
379 		return cell;
380 	}
381 
382 	/***
383 	 * @param includeHidden TODO
384 	 * @return
385 	 */
386 	private List getVisibleColumnDefs() {
387 		return getColumnDefs(true, false);
388 	}
389 
390 	public List getHiddenColumnDefs() {
391 		return getColumnDefs(false, true);
392 	}
393 	
394 	private List getColumnDefs() {
395 		return getColumnDefs(true, true);
396 	}
397 	
398 	private List getColumnDefs(boolean visible, boolean hidden) {
399 		List defs = table.getColumnDefs().getColumnDef();
400 		List result = new ArrayList();
401 		for (Iterator i = defs.iterator(); i.hasNext();) {
402 			ColumnDefType def = (ColumnDefType) i.next();
403 			if ((hidden && def.isHidden()) || (visible && !def.isHidden())) {
404 				result.add(def);
405 			}
406 		}
407 		return result;
408 	}
409 	
410 	/***
411 	 * @param columnIndex
412 	 * @return
413 	 */
414 	private ColumnDefType getColumnDef(int columnIndex) {
415 		ColumnDefType columnDef = (ColumnDefType) getVisibleColumnDefs().get(columnIndex);
416 		return columnDef;
417 	}
418 
419 	private ColumnDefType getColumnDef(String columnId) {
420 		if (columnId == null) {
421 			return null;
422 		}
423 		List defs = table.getColumnDefs().getColumnDef();
424 		for (Iterator i = defs.iterator(); i.hasNext();) {
425 			ColumnDefType col = (ColumnDefType) i.next();
426 			if (columnId.equals(col.getId())) {
427 				return col;
428 			}
429 		}
430 		return null;
431 	}
432 
433 	/***
434 	 * @param columnChoices
435 	 * @return
436 	 */
437 	private Map createColumnChoicesMap(ColumnChoicesType columnChoices) {
438 		Map map = new HashMap();
439 		if (columnChoices == null)
440 			return map;
441 		List columns = columnChoices.getColumn();
442 		for (Iterator i = columns.iterator(); i.hasNext(); ) {
443 			ColumnType column = (ColumnType) i.next();
444 			map.put(column.getId(), column);
445 		}
446 		return map;
447 	}
448 	
449 	public boolean canHideColumn(int columnIndex) {
450 		return getVisibleColumnDefs().size() > 1;
451 	}
452 	
453 	/***
454 	 * @param selectedColumn
455 	 */
456 	public void hideColumn(int columnIndex) {
457 		if (canHideColumn(columnIndex)) {
458 			return;
459 		}
460 		ColumnDefType columnDef = getColumnDef(columnIndex);
461 		columnDef.setHidden(true);
462 		fireTableStructureChanged();
463 	}
464 
465 	public void showColumn(String columnId) {
466 		ColumnDefType columnDef = getColumnDef(columnId);
467 		columnDef.setHidden(false);
468 		fireTableStructureChanged();
469 	}
470 	
471 	public void addColumn(int columnIndex, String name) {
472 		if (table.isFixedStructure()) {
473 			return; // FIXME throw exception?
474 		}
475 		List columnDefs = table.getColumnDefs().getColumnDef();
476 		int realIndex = getColumnDefIndex(columnIndex, columnDefs);
477 		if (realIndex == -1) { // one past the end of the column list
478 			realIndex = columnDefs.size();
479 		} else if (realIndex < -1) {
480 			return;
481 		}  
482 		ColumnDefType newColumnDef = new ColumnDefTypeImpl();
483 		newColumnDef.setId(XmlHelper.genID());
484 		newColumnDef.setName(name);
485 		columnDefs.add(realIndex, newColumnDef);
486 		// cells are added automagically by getCell as necessary
487 		fireTableStructureChanged();
488 	}
489 
490 	public void deleteColumn(int columnIndex) {
491 		if (table.isFixedStructure()) {
492 			return; // FIXME throw exception?
493 		}
494 		List columnDefs = table.getColumnDefs().getColumnDef();
495 		int realIndex = getColumnDefIndex(columnIndex, columnDefs);
496 		if (realIndex < 0) {
497 			return;
498 		}
499 		ColumnDefType columnDef = (ColumnDefType) columnDefs.remove(realIndex);
500 		String columnId = columnDef.getId();
501 		List rows = table.getRows().getRow();
502 		for (Iterator i = rows.iterator(); i.hasNext();) {
503 			RowType row = (RowType) i.next();
504 			List cells = row.getCell();
505 			int j = 0;
506 			boolean found = false;
507 			while (!found && j < cells.size()) {
508 				CellType cell = (CellType) cells.get(j);
509 				if (columnId.equals(cell.getColumnId())) {
510 					cells.remove(j);
511 					found = true;
512 				} else {
513 					j++;
514 				}
515 			}
516 		}
517 		fireTableStructureChanged();
518 	}
519 
520 	public void fireTableStructureChanged() {
521 	    super.fireTableStructureChanged();
522 	    makeDirty();
523 	}
524 
525 	/***
526 	 * @param columnIndex
527 	 * @param columnDefs
528 	 * @return
529 	 */
530 	private int getColumnDefIndex(int columnIndex, List columnDefs) {
531 		int realIndex = 0;
532 		while (realIndex < columnDefs.size() && ((ColumnDefType)columnDefs.get(realIndex)).isHidden()) {
533 			realIndex++;
534 		}
535 		while (columnIndex > 0 && realIndex < columnDefs.size()) {
536 			ColumnDefType columnDef = (ColumnDefType) columnDefs.get(realIndex);
537 			if (!columnDef.isHidden()) {
538 				columnIndex--;
539 			}
540 			realIndex++;
541 		}
542 		return columnIndex == 0 ? realIndex : -columnIndex;
543 	}
544 	
545 }