Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
CheckListModel |
|
| 1.3666666666666667;1.367 | ||||
CheckListModel$1 |
|
| 1.3666666666666667;1.367 | ||||
CheckListModel$ColumnType |
|
| 1.3666666666666667;1.367 | ||||
CheckListModel$ColumnType$1 |
|
| 1.3666666666666667;1.367 | ||||
CheckListModel$ColumnType$2 |
|
| 1.3666666666666667;1.367 | ||||
CheckListModel$PoolListener |
|
| 1.3666666666666667;1.367 | ||||
CheckListModel$SelectionListener |
|
| 1.3666666666666667;1.367 |
1 | /* | |
2 | * Copyright (c) 2009, Swiss Federal Department of Defence Civil Protection and Sport | |
3 | * (http://www.vbs.admin.ch) | |
4 | * Copyright (c) 2009, Ergon Informatik AG (http://www.ergon.ch) | |
5 | * All rights reserved. | |
6 | * | |
7 | * Licensed under the Open Permis License which accompanies this distribution, | |
8 | * and is available at http://www.openpermis.org/BSDlicenceKent.txt | |
9 | */ | |
10 | package org.openpermis.editor.policy.gui.checklist; | |
11 | ||
12 | import java.util.List; | |
13 | import java.util.ListIterator; | |
14 | ||
15 | import javax.swing.table.AbstractTableModel; | |
16 | ||
17 | import org.jdesktop.observablecollections.ObservableList; | |
18 | import org.jdesktop.observablecollections.ObservableListListener; | |
19 | ||
20 | import org.openpermis.editor.policy.gui.binding.ObservableListAdapter; | |
21 | import org.openpermis.policy.Role; | |
22 | ||
23 | ||
24 | /** | |
25 | * Creates a model that manages a pool of items and a selection list from the pool. | |
26 | * @param <T> type of the items in the check list model. | |
27 | * @since 0.1.0 | |
28 | */ | |
29 | 24 | public class CheckListModel<T> |
30 | extends AbstractTableModel | |
31 | { | |
32 | ||
33 | //---- State | |
34 | ||
35 | /** | |
36 | * @since 0.3.0 | |
37 | */ | |
38 | private static final long serialVersionUID = 4791486544723792077L; | |
39 | ||
40 | /** | |
41 | * The pool of available items that can be chosen. | |
42 | * @since 0.1.0 | |
43 | */ | |
44 | private final ObservableList<T> pool; | |
45 | ||
46 | /** | |
47 | * Listener that synchronizes the state of the table model and the pool list. | |
48 | * @since 0.1.0 | |
49 | */ | |
50 | private final PoolListener poolListener; | |
51 | ||
52 | /** | |
53 | * The currently selected items from the pool. | |
54 | * @since 0.1.0 | |
55 | */ | |
56 | private final ObservableList<T> selection; | |
57 | ||
58 | /** | |
59 | * Listener that refreshes the check list whenever a change occurs. | |
60 | * @since 0.1.0 | |
61 | */ | |
62 | private final SelectionListener selectionListener; | |
63 | ||
64 | //---- Constructors | |
65 | ||
66 | /** | |
67 | * Creates a new check list model. | |
68 | * @param pool the poolof available items that can be chosen. | |
69 | * @param selection the currently selected items from the pool. | |
70 | * @since 0.1.0 | |
71 | */ | |
72 | 7 | public CheckListModel (ObservableList<T> pool, ObservableList<T> selection) { |
73 | 7 | this.pool = pool; |
74 | 7 | this.poolListener = new PoolListener(); |
75 | 7 | this.selection = selection; |
76 | 7 | this.selectionListener = new SelectionListener(); |
77 | 7 | } |
78 | ||
79 | //---- Methods | |
80 | ||
81 | /** | |
82 | * Adds listeners to synchronize the list with the table model. | |
83 | * @since 0.1.0 | |
84 | */ | |
85 | public void configureListeners () { | |
86 | 7 | this.pool.addObservableListListener(this.poolListener); |
87 | 7 | this.selection.addObservableListListener(this.selectionListener); |
88 | 7 | } |
89 | ||
90 | /** | |
91 | * Removes the listeners to synchronize the list with the table model added in | |
92 | * {@link #configureListeners()}. | |
93 | * @since 0.1.0 | |
94 | */ | |
95 | public void unconfigureListeners () { | |
96 | 7 | this.pool.removeObservableListListener(this.poolListener); |
97 | 7 | this.selection.removeObservableListListener(this.selectionListener); |
98 | 7 | } |
99 | ||
100 | /** | |
101 | * Returns the type of the column at the specified index. | |
102 | * @param columnIndex the index for which to retrieve the column type. | |
103 | * @return the type requested. | |
104 | * @since 0.1.0 | |
105 | */ | |
106 | private ColumnType getColumnType (int columnIndex) { | |
107 | 320 | return ColumnType.values()[columnIndex]; |
108 | } | |
109 | ||
110 | /** | |
111 | * Check if the column specified contains the value of the check list model. | |
112 | * @param columnIndex the column to check. | |
113 | * @return {@code true} if it is a value column, {@code false} otherwise. | |
114 | * @since 0.4.0 | |
115 | */ | |
116 | public boolean isValueColumn (int columnIndex) { | |
117 | 0 | return getColumnType(columnIndex).isValueType(); |
118 | } | |
119 | ||
120 | //---- TableModel | |
121 | ||
122 | /** | |
123 | * @since 0.1.0 | |
124 | */ | |
125 | @Override | |
126 | public boolean isCellEditable (int rowIndex, int columnIndex) { | |
127 | 0 | return getColumnType(columnIndex).isEditable(); |
128 | } | |
129 | ||
130 | /** | |
131 | * @since 0.1.0 | |
132 | */ | |
133 | @Override | |
134 | public Class<?> getColumnClass (int columnIndex) { | |
135 | 0 | return getColumnType(columnIndex).getColumnClass(); |
136 | } | |
137 | ||
138 | /** | |
139 | * @since 0.1.0 | |
140 | */ | |
141 | @Override | |
142 | public String getColumnName (int columnIndex) { | |
143 | 0 | return getColumnType(columnIndex).getColumnName(); |
144 | } | |
145 | ||
146 | /** | |
147 | * @since 0.1.0 | |
148 | */ | |
149 | public int getColumnCount () { | |
150 | 0 | return ColumnType.values().length; |
151 | } | |
152 | ||
153 | /** | |
154 | * @since 0.1.0 | |
155 | */ | |
156 | public int getRowCount () { | |
157 | 9 | return this.pool.size(); |
158 | } | |
159 | ||
160 | /** | |
161 | * @since 0.1.0 | |
162 | */ | |
163 | public Object getValueAt (int rowIndex, int columnIndex) { | |
164 | 320 | return getColumnType(columnIndex).getValue(rowIndex, this.pool, this.selection); |
165 | } | |
166 | ||
167 | /** | |
168 | * @since 0.1.0 | |
169 | */ | |
170 | @Override | |
171 | public void setValueAt (Object value, int rowIndex, int columnIndex) { | |
172 | 0 | getColumnType(columnIndex).setValue(rowIndex, value, this.pool, this.selection); |
173 | 0 | } |
174 | ||
175 | //---- ColumnType | |
176 | ||
177 | /** | |
178 | * Describes the type of a column, including getters and setters. | |
179 | * @since 0.1.0 | |
180 | */ | |
181 | 323 | enum ColumnType { |
182 | ||
183 | 1 | CHECKBOX("Checkbox", Boolean.class, true, false) { |
184 | @Override | |
185 | public Object getValue (int rowIndex, List<?> itemPool, List<?> selectionList) { | |
186 | 160 | return Boolean.valueOf(contains(selectionList, itemPool.get(rowIndex))); |
187 | } | |
188 | @Override | |
189 | 1 | public <T> void setValue ( |
190 | int rowIndex, Object value, List<T> itemPool, List<T> selectionList | |
191 | ) { | |
192 | 0 | final boolean add = Boolean.TRUE.equals(value); |
193 | 0 | final T item = itemPool.get(rowIndex); |
194 | 0 | if (add != contains(selectionList, item)) { |
195 | 0 | if (add) { |
196 | 0 | selectionList.add(item); |
197 | } else { | |
198 | 0 | selectionList.remove(indexOf(selectionList, item)); |
199 | } | |
200 | } | |
201 | 0 | } |
202 | }, | |
203 | 1 | ITEM("Item", Object.class, false, true) { |
204 | @Override | |
205 | public Object getValue (int rowIndex, List<?> itemPool, List<?> selectionList) { | |
206 | 160 | return itemPool.get(rowIndex); |
207 | } | |
208 | @Override | |
209 | 1 | public <T> void setValue ( |
210 | int rowIndex, Object value, List<T> itemPool, List<T> selectionList | |
211 | ) { | |
212 | 0 | throw new IllegalStateException( |
213 | "Editing of column [" + getColumnName() + "] not supported." | |
214 | ); | |
215 | } | |
216 | }; | |
217 | ||
218 | //---- State | |
219 | ||
220 | private final String columnName; | |
221 | private final Class<?> columnClass; | |
222 | private final boolean editable; | |
223 | private final boolean valueType; | |
224 | ||
225 | //---- Constructors | |
226 | ||
227 | private ColumnType ( | |
228 | String columnName, Class<?> columnClass, boolean editable, boolean valueType | |
229 | 2 | ) { |
230 | 2 | this.columnName = columnName; |
231 | 2 | this.columnClass = columnClass; |
232 | 2 | this.editable = editable; |
233 | 2 | this.valueType = valueType; |
234 | 2 | } |
235 | ||
236 | //---- Methods | |
237 | ||
238 | /** | |
239 | * Returns the index of the specified item in the given list. | |
240 | * @param list the list to find the item in. | |
241 | * @param item the item to search for by identity. | |
242 | * @return the index requested or -1 if there is no such item. | |
243 | * @since 0.1.0 | |
244 | */ | |
245 | protected int indexOf (List<?> list, Object item) { | |
246 | 955 | for (int i = 0; i < list.size(); i++) { |
247 | 846 | if ( |
248 | // The class Role role has an implementation of equals. | |
249 | list.get(i) instanceof Role && list.get(i).equals(item) | |
250 | || list.get(i) == item | |
251 | ) { | |
252 | 51 | return i; |
253 | } | |
254 | } | |
255 | 109 | return -1; |
256 | } | |
257 | ||
258 | /** | |
259 | * Checks if the specified item is in the given list. | |
260 | * @param list the list to search. | |
261 | * @param item the item to check. | |
262 | * @return {@code true} if the item is in the list, {@code false} otherwise. | |
263 | * @since 0.1.0 | |
264 | */ | |
265 | protected boolean contains (List<?> list, Object item) { | |
266 | 160 | return indexOf(list, item) != -1; |
267 | } | |
268 | ||
269 | /** | |
270 | * Returns the value of this column at the specified row index. | |
271 | * @param rowIndex the index for which to retrieve the value. | |
272 | * @param itemPool the pool of items. | |
273 | * @param selectionList the list of currently selected items. | |
274 | * @return the value at the given row/column. | |
275 | * @since 0.1.0 | |
276 | */ | |
277 | public abstract Object getValue (int rowIndex, List<?> itemPool, List<?> selectionList); | |
278 | ||
279 | /** | |
280 | * Sets the value of this column at the specified row index. | |
281 | * @param rowIndex the index for which to set the value. | |
282 | * @param itemPool the pool of items. | |
283 | * @param selectionList the list of currently selected items. | |
284 | * @since 0.1.0 | |
285 | */ | |
286 | public abstract <T> void setValue ( | |
287 | int rowIndex, Object value, List<T> itemPool, List<T> selectionList | |
288 | ); | |
289 | ||
290 | /** | |
291 | * The name of this column. | |
292 | * @return the name of this column. | |
293 | * @since 0.1.0 | |
294 | */ | |
295 | public String getColumnName () { | |
296 | 0 | return this.columnName; |
297 | } | |
298 | ||
299 | /** | |
300 | * The value class of this column. | |
301 | * @return the value class of this column. | |
302 | * @since 0.1.0 | |
303 | */ | |
304 | public Class<?> getColumnClass () { | |
305 | 0 | return this.columnClass; |
306 | } | |
307 | ||
308 | /** | |
309 | * @return the editable. | |
310 | * @since 0.1.0 | |
311 | */ | |
312 | public boolean isEditable () { | |
313 | 0 | return this.editable; |
314 | } | |
315 | ||
316 | /** | |
317 | * Check if this column denotes a type that reflects a value. | |
318 | * @since 0.4.0 | |
319 | */ | |
320 | public boolean isValueType () { | |
321 | 0 | return this.valueType; |
322 | } | |
323 | ||
324 | } | |
325 | ||
326 | //---- PoolListener | |
327 | ||
328 | /** | |
329 | * Synchronizes the check list model if a change in the pool list occurs. | |
330 | * @since 0.1.0 | |
331 | */ | |
332 | @SuppressWarnings("unchecked") | |
333 | 14 | private class PoolListener |
334 | implements ObservableListListener | |
335 | { | |
336 | ||
337 | /** | |
338 | * @since 0.1.0 | |
339 | */ | |
340 | public void listElementPropertyChanged (ObservableList list, int index) { | |
341 | 0 | CheckListModel.this.fireTableRowsUpdated(index, index); |
342 | 0 | } |
343 | ||
344 | /** | |
345 | * @since 0.1.0 | |
346 | */ | |
347 | public void listElementReplaced (ObservableList list, int index, Object oldElement) { | |
348 | 0 | CheckListModel.this.fireTableRowsUpdated(index, index); |
349 | 0 | } |
350 | ||
351 | /** | |
352 | * @since 0.1.0 | |
353 | */ | |
354 | public void listElementsAdded (ObservableList list, int index, int length) { | |
355 | 4 | CheckListModel.this.fireTableRowsInserted(index, index + length); |
356 | 4 | } |
357 | ||
358 | /** | |
359 | * @since 0.1.0 | |
360 | */ | |
361 | public void listElementsRemoved (ObservableList list, int index, List oldElements) { | |
362 | 5 | for (final Object obj : oldElements) { |
363 | 24 | final ListIterator<T> iterator = CheckListModel.this.selection.listIterator(); |
364 | 119 | while (iterator.hasNext()) { |
365 | 95 | final T item = iterator.next(); |
366 | 95 | if (obj == item) { |
367 | 9 | iterator.remove(); |
368 | } | |
369 | 95 | } |
370 | 24 | } |
371 | 5 | CheckListModel.this.fireTableDataChanged(); |
372 | 5 | } |
373 | ||
374 | } | |
375 | ||
376 | //---- SelectionListener | |
377 | ||
378 | /** | |
379 | * Synchronizes the check list model if a change in the selection list occurs. | |
380 | * @since 0.1.0 | |
381 | */ | |
382 | @SuppressWarnings("unchecked") | |
383 | 14 | private class SelectionListener |
384 | extends ObservableListAdapter | |
385 | { | |
386 | ||
387 | /** | |
388 | * @since 0.3.0 | |
389 | */ | |
390 | @Override | |
391 | protected void listChanged (ObservableList list) { | |
392 | 18 | CheckListModel.this.fireTableDataChanged(); |
393 | 18 | } |
394 | ||
395 | } | |
396 | ||
397 | } |