svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.fmap.control / src / main / java / org / gvsig / fmap / mapcontrol / dal / feature / swing / table / FeatureTableModel.java @ 47421
History | View | Annotate | Download (33.1 KB)
1 |
/**
|
---|---|
2 |
* gvSIG. Desktop Geographic Information System.
|
3 |
*
|
4 |
* Copyright (C) 2007-2013 gvSIG Association.
|
5 |
*
|
6 |
* This program is free software; you can redistribute it and/or
|
7 |
* modify it under the terms of the GNU General Public License
|
8 |
* as published by the Free Software Foundation; either version 3
|
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
|
14 |
* GNU General Public License for more details.
|
15 |
*
|
16 |
* You should have received a copy of the GNU General Public License
|
17 |
* along with this program; if not, write to the Free Software
|
18 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 |
* MA 02110-1301, USA.
|
20 |
*
|
21 |
* For any additional information, do not hesitate to contact us
|
22 |
* at info AT gvsig.com, or visit our website www.gvsig.com.
|
23 |
*/
|
24 |
/*
|
25 |
* AUTHORS (In addition to CIT):
|
26 |
* 2008 {DiSiD Technologies} {Create a JTable TableModel for a FeatureCollection}
|
27 |
*/
|
28 |
package org.gvsig.fmap.mapcontrol.dal.feature.swing.table; |
29 |
|
30 |
import java.awt.event.ActionEvent; |
31 |
import java.awt.event.ActionListener; |
32 |
import javax.swing.SwingUtilities; |
33 |
import javax.swing.Timer; |
34 |
import javax.swing.event.TableModelEvent; |
35 |
import javax.swing.table.AbstractTableModel; |
36 |
import org.gvsig.fmap.dal.DALLocator; |
37 |
import org.gvsig.fmap.dal.EditingNotification; |
38 |
import org.gvsig.fmap.dal.EditingNotificationManager; |
39 |
import org.gvsig.fmap.dal.exception.DataException; |
40 |
import org.gvsig.fmap.dal.feature.EditableFeature; |
41 |
import org.gvsig.fmap.dal.feature.Feature; |
42 |
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
43 |
import org.gvsig.fmap.dal.feature.FeatureQuery; |
44 |
import org.gvsig.fmap.dal.feature.FeatureStore; |
45 |
import org.gvsig.fmap.dal.feature.FeatureStoreNotification; |
46 |
import org.gvsig.fmap.dal.feature.FeatureType; |
47 |
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException; |
48 |
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper; |
49 |
import org.gvsig.fmap.dal.swing.DALSwingLocator; |
50 |
import org.gvsig.tools.dispose.DisposeUtils; |
51 |
import org.gvsig.tools.dispose.SupportDisposable; |
52 |
import org.gvsig.tools.dispose.impl.DisposableHelper; |
53 |
import org.gvsig.tools.exception.BaseException; |
54 |
import org.gvsig.tools.logger.FilteredLogger; |
55 |
import org.gvsig.tools.observer.ComplexNotification; |
56 |
import org.gvsig.tools.observer.ComplexObserver; |
57 |
import org.gvsig.tools.observer.Observable; |
58 |
import org.slf4j.Logger; |
59 |
import org.slf4j.LoggerFactory; |
60 |
|
61 |
/**
|
62 |
* TableModel to access data of Features.
|
63 |
*
|
64 |
* This table model can't handle a FeatureSet with more than Integer.MAX_VALUE
|
65 |
* elements. In that case, only the first Integer.MAX_VALUE elements will be
|
66 |
* shown.
|
67 |
*
|
68 |
* @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
|
69 |
*/
|
70 |
@SuppressWarnings("UseSpecificCatch") |
71 |
public class FeatureTableModel extends AbstractTableModel implements ComplexObserver, SupportDisposable { |
72 |
|
73 |
private static final Logger LOGGER = LoggerFactory |
74 |
.getLogger(FeatureTableModel.class); |
75 |
|
76 |
private static final long serialVersionUID = -2488157521902851301L; |
77 |
|
78 |
private FeaturePagingHelper pagingHelper;
|
79 |
|
80 |
private final DisposableHelper disposableHelper; |
81 |
|
82 |
|
83 |
/**
|
84 |
* Used to know if a modification in the FeatureStore is created by us.
|
85 |
*/
|
86 |
private EditableFeature editableFeature;
|
87 |
|
88 |
private boolean selectionLocked = false; |
89 |
private final FilteredLogger filterlogger; |
90 |
|
91 |
private static final int STATUS_OK = 0; |
92 |
private static final int STATUS_ERR_LOADFEATURES = 1; |
93 |
private static final int STATUS_ERR_LOADFEATURETYPE = 2; |
94 |
|
95 |
private int status; |
96 |
|
97 |
/**
|
98 |
* Constructs a TableModel from the features of a FeatureStore, with the
|
99 |
* default page size.
|
100 |
*
|
101 |
* @param featureStore to extract the features from
|
102 |
* @param featureQuery the query to get the features from the store
|
103 |
* @throws BaseException if there is an error reading data from the
|
104 |
* FeatureStore
|
105 |
*/
|
106 |
public FeatureTableModel(FeatureStore featureStore,
|
107 |
FeatureQuery featureQuery) throws BaseException {
|
108 |
this(featureStore, featureQuery, FeaturePagingHelper.DEFAULT_PAGE_SIZE);
|
109 |
} |
110 |
|
111 |
/**
|
112 |
* Constructs a TableModel from the features of a FeatureStore, with the
|
113 |
* default page size.
|
114 |
*
|
115 |
* @param featureStore to extract the features from
|
116 |
* @param featureQuery the query to get the features from the store
|
117 |
* @param pageSize the number of elements per page data
|
118 |
* @throws BaseException if there is an error reading data from the
|
119 |
* FeatureStore
|
120 |
*/
|
121 |
public FeatureTableModel(FeatureStore featureStore,
|
122 |
FeatureQuery featureQuery, int pageSize) throws BaseException { |
123 |
this(DALLocator.getDataManager().createFeaturePagingHelper(
|
124 |
featureStore, featureQuery, pageSize)); |
125 |
} |
126 |
|
127 |
/**
|
128 |
* Constructs a TableModel from a FeatureCollection and a Paging pagingHelper.
|
129 |
*
|
130 |
* @param helper
|
131 |
*/
|
132 |
@SuppressWarnings("OverridableMethodCallInConstructor") |
133 |
protected FeatureTableModel(FeaturePagingHelper helper) {
|
134 |
this.disposableHelper = new DisposableHelper(this); |
135 |
this.status = STATUS_OK;
|
136 |
this.pagingHelper = helper;
|
137 |
this.filterlogger = new FilteredLogger(LOGGER, "SimpleFeaturesTableModel", 30000L); |
138 |
initialize(); |
139 |
} |
140 |
|
141 |
@Override
|
142 |
public int getColumnCount() { |
143 |
switch(this.status) { |
144 |
case STATUS_OK:
|
145 |
case STATUS_ERR_LOADFEATURES:
|
146 |
break;
|
147 |
case STATUS_ERR_LOADFEATURETYPE:
|
148 |
return 0; |
149 |
} |
150 |
// Return the number of fields of the Features
|
151 |
FeatureType featureType = getFeatureType(); |
152 |
return featureType.size();
|
153 |
} |
154 |
|
155 |
@Override
|
156 |
public int getRowCount() { |
157 |
switch(this.status) { |
158 |
case STATUS_OK:
|
159 |
break;
|
160 |
case STATUS_ERR_LOADFEATURETYPE:
|
161 |
case STATUS_ERR_LOADFEATURES:
|
162 |
return 0; |
163 |
} |
164 |
// Return the total size of the collection
|
165 |
// If the size is bigger than INTEGER.MAX_VALUE, return that instead
|
166 |
try {
|
167 |
long totalSize = getHelper().getTotalSize();
|
168 |
if (totalSize > Integer.MAX_VALUE) { |
169 |
return Integer.MAX_VALUE; |
170 |
} else {
|
171 |
return (int) totalSize; |
172 |
} |
173 |
} catch (ConcurrentDataModificationException e) {
|
174 |
LOGGER.debug("Error while getting the total size of the set", e);
|
175 |
this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
|
176 |
return 0; |
177 |
} |
178 |
} |
179 |
|
180 |
@Override
|
181 |
public Object getValueAt(int rowIndex, int columnIndex) { |
182 |
switch(this.status) { |
183 |
case STATUS_OK:
|
184 |
break;
|
185 |
case STATUS_ERR_LOADFEATURETYPE:
|
186 |
case STATUS_ERR_LOADFEATURES:
|
187 |
return null; |
188 |
} |
189 |
// Get the Feature at row "rowIndex", and return the value of the
|
190 |
// attribute at "columnIndex"
|
191 |
try {
|
192 |
Feature feature = getFeatureAt(rowIndex); |
193 |
if (feature == null) { |
194 |
return null; |
195 |
} |
196 |
Object value;
|
197 |
value = getFeatureValue(feature, columnIndex); |
198 |
return value;
|
199 |
} catch (Throwable ex) { |
200 |
filterlogger.warn("Not been able to retrieve feature value", ex);
|
201 |
return null; |
202 |
} |
203 |
} |
204 |
|
205 |
/**
|
206 |
* Returns the value for a row position.
|
207 |
*
|
208 |
* @param rowIndex the row position
|
209 |
* @return the Feature
|
210 |
*/
|
211 |
public Feature getFeatureAt(int rowIndex) { |
212 |
try {
|
213 |
return getHelper().getFeatureAt(rowIndex);
|
214 |
} catch (BaseException ex) {
|
215 |
throw new GetFeatureAtException(rowIndex, ex); |
216 |
} |
217 |
} |
218 |
|
219 |
@Override
|
220 |
public Class<?> getColumnClass(int columnIndex) { |
221 |
// Return the class of the FeatureAttributeDescriptor for the value
|
222 |
FeatureAttributeDescriptor attributeDesc |
223 |
= internalGetFeatureDescriptorForColumn(columnIndex); |
224 |
if (attributeDesc == null) { |
225 |
return super.getColumnClass(columnIndex); |
226 |
} |
227 |
Class<?> clazz = attributeDesc.getObjectClass();
|
228 |
return (clazz == null ? super.getColumnClass(columnIndex) : clazz); |
229 |
} |
230 |
|
231 |
@Override
|
232 |
public String getColumnName(int column) { |
233 |
// Return the Feature attribute name
|
234 |
FeatureAttributeDescriptor attributeDesc |
235 |
= internalGetFeatureDescriptorForColumn(column); |
236 |
return attributeDesc.getName();
|
237 |
} |
238 |
|
239 |
@Override
|
240 |
public boolean isCellEditable(int rowIndex, int columnIndex) { |
241 |
if (getFeatureStore().isEditing()) {
|
242 |
FeatureAttributeDescriptor attributeDesc |
243 |
= internalGetFeatureDescriptorForColumn(columnIndex); |
244 |
return !attributeDesc.isReadOnly();
|
245 |
} |
246 |
|
247 |
return false; |
248 |
} |
249 |
|
250 |
@Override
|
251 |
public void setValueAt(Object value, int rowIndex, int columnIndex) { |
252 |
// Get the feature at rowIndex
|
253 |
Feature feature = getFeatureAt(rowIndex); |
254 |
// Only set the value if the feature exists
|
255 |
if (feature != null) { |
256 |
// We only need to update if the value to set is not equal to the
|
257 |
// current value
|
258 |
Object currentValue = getFeatureValue(feature, columnIndex);
|
259 |
if (value != currentValue
|
260 |
&& (value == null || !value.equals(currentValue))) {
|
261 |
try {
|
262 |
// Store the editable feature to ignore the related store
|
263 |
// change notification
|
264 |
editableFeature |
265 |
= setFeatureValue(feature, columnIndex, value); |
266 |
EditingNotificationManager editingNotificationManager = DALSwingLocator.getEditingNotificationManager(); |
267 |
EditingNotification notification = editingNotificationManager.notifyObservers( |
268 |
this,
|
269 |
EditingNotification.BEFORE_UPDATE_FEATURE, |
270 |
null,
|
271 |
this.getHelper().getFeatureStore(),
|
272 |
editableFeature); |
273 |
if (notification.isCanceled()) {
|
274 |
return;
|
275 |
} |
276 |
if (notification.shouldValidateTheFeature()) {
|
277 |
if (!editingNotificationManager.validateFeature(feature)) {
|
278 |
return;
|
279 |
} |
280 |
} |
281 |
this.getHelper().update(editableFeature);
|
282 |
// We'll have already received the event, so we can forget
|
283 |
// about it
|
284 |
getHelper().reloadCurrentPage(); |
285 |
fireTableCellUpdated(rowIndex, columnIndex); |
286 |
|
287 |
editingNotificationManager.notifyObservers( |
288 |
this,
|
289 |
EditingNotification.AFTER_UPDATE_FEATURE, |
290 |
null,
|
291 |
this.getHelper().getFeatureStore(),
|
292 |
editableFeature); |
293 |
editableFeature = null;
|
294 |
|
295 |
} catch (BaseException ex) {
|
296 |
throw new SetFeatureValueException(rowIndex, columnIndex, |
297 |
value, ex); |
298 |
} finally {
|
299 |
// Just in case
|
300 |
editableFeature = null;
|
301 |
} |
302 |
} |
303 |
} |
304 |
} |
305 |
|
306 |
/**
|
307 |
* Returns a reference to the Paging Helper used to load the data from the
|
308 |
* DataStore.
|
309 |
*
|
310 |
* @return the paging pagingHelper
|
311 |
*/
|
312 |
public FeaturePagingHelper getHelper() {
|
313 |
return pagingHelper;
|
314 |
} |
315 |
|
316 |
/**
|
317 |
* Sets the FeatureType to show in the table. Used for FeatureStores with
|
318 |
* many simultaneous FeatureTypes supported. Will cause a reload of the
|
319 |
* current data.
|
320 |
*
|
321 |
* @param featureType the FeatureType of the Features
|
322 |
*/
|
323 |
public void setFeatureType(FeatureType featureType) { |
324 |
getFeatureQuery().setFeatureType(featureType); |
325 |
reloadFeatures(); |
326 |
//Selection must be locked to avoid losing it when the table is refreshed
|
327 |
selectionLocked = true;
|
328 |
//The table is refreshed
|
329 |
try {
|
330 |
fireTableStructureChanged(); |
331 |
} catch (Exception e) { |
332 |
LOGGER.warn("Couldn't reload changed table");
|
333 |
} finally {
|
334 |
//The locked selection is unlocked.
|
335 |
selectionLocked = false;
|
336 |
} |
337 |
} |
338 |
|
339 |
/**
|
340 |
* Sets that the selected Features get returned first.
|
341 |
*
|
342 |
* @param selectionUp
|
343 |
*/
|
344 |
public void setSelectionUp(boolean selectionUp) { |
345 |
if (selectionUp) {
|
346 |
delayAction.addAction(DelayAction.ACTION_ENABLE_SELECTION_UP); |
347 |
} else {
|
348 |
delayAction.addAction(DelayAction.ACTION_DISABLE_SELECTION_UP); |
349 |
} |
350 |
} |
351 |
|
352 |
protected void fireTableChanged() { |
353 |
// LOGGER.info("fireTableChanged()");
|
354 |
fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1)); |
355 |
} |
356 |
public static class Bitmask { |
357 |
private int mask; |
358 |
|
359 |
public Bitmask(int initialmask) { |
360 |
this.mask = initialmask;
|
361 |
} |
362 |
|
363 |
public boolean isSetBit(int pos) { |
364 |
return (mask & (1<<pos)) != 0; |
365 |
} |
366 |
|
367 |
public boolean isSet(int bits) { |
368 |
return (mask & bits) == bits;
|
369 |
} |
370 |
|
371 |
public boolean isSetAny() { |
372 |
return mask!=0; |
373 |
} |
374 |
|
375 |
public boolean isCleanAll() { |
376 |
return mask == 0; |
377 |
} |
378 |
|
379 |
public void set(int bits) { |
380 |
mask = mask | bits; |
381 |
} |
382 |
|
383 |
public int get() { |
384 |
return mask;
|
385 |
} |
386 |
|
387 |
public void clear(int bits) { |
388 |
mask = ~bits & mask; |
389 |
} |
390 |
|
391 |
public void clearBit(int pos) { |
392 |
mask = ~(1<<pos) & mask;
|
393 |
} |
394 |
|
395 |
public void clearAll() { |
396 |
mask = 0;
|
397 |
} |
398 |
|
399 |
} |
400 |
|
401 |
private class DelayAction extends Timer implements ActionListener, Runnable { |
402 |
|
403 |
private static final int ACTION_NONE = 0; |
404 |
private static final int ACTION_RELOADALL = 1; |
405 |
private static final int ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED = 2; |
406 |
private static final int ACTION_RELOAD_IF_FEATURE_UPDATED = 4; |
407 |
private static final int ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED = 8; |
408 |
private static final int ACTION_RELOAD_FEATURE_TYPE = 16; |
409 |
private static final int ACTION_ENABLE_SELECTION_UP = 32; |
410 |
private static final int ACTION_RELOAD_ALL_FEATURES = 64; |
411 |
private static final int ACTION_DISABLE_SELECTION_UP = 128; |
412 |
private static final int ACTION_UPDATE_SELECTION = 256; |
413 |
|
414 |
private final Bitmask current_actions = new Bitmask(ACTION_NONE); |
415 |
private Feature feature;
|
416 |
private FeatureType featureType;
|
417 |
|
418 |
@SuppressWarnings("OverridableMethodCallInConstructor") |
419 |
public DelayAction() {
|
420 |
super(1000, null); |
421 |
this.setRepeats(false); |
422 |
this.reset();
|
423 |
this.addActionListener(this); |
424 |
} |
425 |
|
426 |
public final void reset() { |
427 |
this.current_actions.clearAll();
|
428 |
this.feature = null; |
429 |
this.featureType = null; |
430 |
} |
431 |
|
432 |
@Override
|
433 |
public void actionPerformed(ActionEvent ae) { |
434 |
this.run();
|
435 |
} |
436 |
|
437 |
@SuppressWarnings("UnusedAssignment") |
438 |
private String getActionsLabel(Bitmask actions) { |
439 |
if( actions.isCleanAll() ) {
|
440 |
return "NONE"; |
441 |
} |
442 |
StringBuilder builder = new StringBuilder(); |
443 |
boolean needSeparator = false; |
444 |
if( actions.isSet(ACTION_RELOADALL) ) {
|
445 |
if( needSeparator ) {
|
446 |
builder.append("|");
|
447 |
} |
448 |
needSeparator = true;
|
449 |
builder.append("RELOADALL");
|
450 |
} |
451 |
if( actions.isSet(ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED) ) {
|
452 |
if( needSeparator ) {
|
453 |
builder.append("|");
|
454 |
} |
455 |
needSeparator = true;
|
456 |
builder.append("RELOAD_IF_FEATURE_COUNT_CHANGED");
|
457 |
} |
458 |
if( actions.isSet(ACTION_RELOAD_IF_FEATURE_UPDATED) ) {
|
459 |
if( needSeparator ) {
|
460 |
builder.append("|");
|
461 |
} |
462 |
needSeparator = true;
|
463 |
builder.append("RELOAD_IF_FEATURE_UPDATED");
|
464 |
} |
465 |
if( actions.isSet(ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED) ) {
|
466 |
if( needSeparator ) {
|
467 |
builder.append("|");
|
468 |
} |
469 |
needSeparator = true;
|
470 |
builder.append("RELOAD_IF_FEATURE_TYPE_CHANGED");
|
471 |
} |
472 |
if( actions.isSet(ACTION_RELOAD_FEATURE_TYPE) ) {
|
473 |
if( needSeparator ) {
|
474 |
builder.append("|");
|
475 |
} |
476 |
needSeparator = true;
|
477 |
builder.append("RELOAD_FEATURE_TYPE");
|
478 |
} |
479 |
if( actions.isSet(ACTION_RELOAD_ALL_FEATURES) ) {
|
480 |
if( needSeparator ) {
|
481 |
builder.append("|");
|
482 |
} |
483 |
needSeparator = true;
|
484 |
builder.append("RELOAD_ALL_FEATURES");
|
485 |
} |
486 |
if( actions.isSet(ACTION_DISABLE_SELECTION_UP) ) {
|
487 |
if( needSeparator ) {
|
488 |
builder.append("|");
|
489 |
} |
490 |
needSeparator = true;
|
491 |
builder.append("DISABLE_SELECTION_UP");
|
492 |
} |
493 |
if( actions.isSet(ACTION_ENABLE_SELECTION_UP) ) {
|
494 |
if( needSeparator ) {
|
495 |
builder.append("|");
|
496 |
} |
497 |
needSeparator = true;
|
498 |
builder.append("ENABLE_SELECTION_UP");
|
499 |
} |
500 |
if( actions.isSet(ACTION_UPDATE_SELECTION) ) {
|
501 |
if( needSeparator ) {
|
502 |
builder.append("|");
|
503 |
} |
504 |
needSeparator = true;
|
505 |
builder.append("UPDATE_SELECTION");
|
506 |
} |
507 |
if( !needSeparator ) {
|
508 |
builder.append(actions.get()); |
509 |
} |
510 |
return builder.toString();
|
511 |
} |
512 |
|
513 |
@Override
|
514 |
public void run() { |
515 |
if (!SwingUtilities.isEventDispatchThread()) { |
516 |
SwingUtilities.invokeLater(this); |
517 |
return;
|
518 |
} |
519 |
this.stop();
|
520 |
// LOGGER.info("DelayAction.run begin ["+ getActionsLabel(this.current_actions) + "]");
|
521 |
boolean needFireTableChanged = false; |
522 |
if( this.current_actions.isSet(ACTION_RELOADALL) ) { |
523 |
this.current_actions.clear(
|
524 |
ACTION_RELOADALL | |
525 |
ACTION_RELOAD_FEATURE_TYPE | |
526 |
ACTION_RELOAD_ALL_FEATURES | |
527 |
ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED | |
528 |
ACTION_RELOAD_IF_FEATURE_UPDATED | |
529 |
ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED |
530 |
); |
531 |
reloadAll(); |
532 |
} |
533 |
if( this.current_actions.isSet(ACTION_RELOAD_FEATURE_TYPE) ) { |
534 |
this.current_actions.clear(
|
535 |
ACTION_RELOAD_FEATURE_TYPE | |
536 |
ACTION_RELOAD_ALL_FEATURES | |
537 |
ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED | |
538 |
ACTION_RELOAD_IF_FEATURE_UPDATED | |
539 |
ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED |
540 |
); |
541 |
reloadFeatureType(); |
542 |
updatePaginHelperWithHiddenColums(); |
543 |
} |
544 |
if( this.current_actions.isSet(ACTION_RELOAD_ALL_FEATURES) ) { |
545 |
this.current_actions.clear(
|
546 |
ACTION_RELOAD_ALL_FEATURES | |
547 |
ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED | |
548 |
ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED | |
549 |
ACTION_RELOAD_IF_FEATURE_UPDATED |
550 |
); |
551 |
reloadFeatures(); |
552 |
needFireTableChanged = true;
|
553 |
} |
554 |
if( this.current_actions.isSet(ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED) ) { |
555 |
if( reloadFeatureTypeIfTypeChanged(featureType) ) {
|
556 |
this.current_actions.clear(
|
557 |
ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED | |
558 |
ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED | |
559 |
ACTION_RELOAD_IF_FEATURE_UPDATED |
560 |
); |
561 |
} |
562 |
} |
563 |
if( this.current_actions.isSet(ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED) ) { |
564 |
if( reloadFeaturesIfFeatureCountChanged(feature) ) {
|
565 |
this.current_actions.clear(
|
566 |
ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED | |
567 |
ACTION_RELOAD_IF_FEATURE_UPDATED |
568 |
); |
569 |
} |
570 |
} |
571 |
if( this.current_actions.isSet(ACTION_RELOAD_IF_FEATURE_UPDATED) ) { |
572 |
if( reloadFeaturesIfFeatureUpdated(feature) ) {
|
573 |
this.current_actions.clear(
|
574 |
ACTION_RELOAD_IF_FEATURE_UPDATED |
575 |
); |
576 |
needFireTableChanged = true;
|
577 |
} |
578 |
} |
579 |
|
580 |
if( this.current_actions.isSet(ACTION_ENABLE_SELECTION_UP) ) { |
581 |
if (!getHelper().isSelectionUp()) {
|
582 |
getHelper().setSelectionUp(true);
|
583 |
this.current_actions.clear(
|
584 |
ACTION_UPDATE_SELECTION |
585 |
); |
586 |
} |
587 |
this.current_actions.clear(
|
588 |
ACTION_ENABLE_SELECTION_UP | |
589 |
ACTION_DISABLE_SELECTION_UP |
590 |
); |
591 |
needFireTableChanged = true;
|
592 |
} else if( this.current_actions.isSet(ACTION_DISABLE_SELECTION_UP) ) { |
593 |
if (getHelper().isSelectionUp()) {
|
594 |
getHelper().setSelectionUp(false);
|
595 |
this.current_actions.clear(
|
596 |
ACTION_UPDATE_SELECTION |
597 |
); |
598 |
} |
599 |
this.current_actions.clear(
|
600 |
ACTION_ENABLE_SELECTION_UP | |
601 |
ACTION_DISABLE_SELECTION_UP |
602 |
); |
603 |
needFireTableChanged = true;
|
604 |
} |
605 |
|
606 |
if( this.current_actions.isSet(ACTION_UPDATE_SELECTION) ) { |
607 |
if (getHelper().isSelectionUp()) {
|
608 |
// Se ha a?adido o quitado elementos de la seleccion, y estamos
|
609 |
// en "seleccion arriba", asi que forzamos un refresco.
|
610 |
try {
|
611 |
getHelper().reloadCurrentPage(); |
612 |
} catch(Throwable th) { |
613 |
LOGGER.debug("No se ha podido recargar la pagina actual de la tabla", th);
|
614 |
} |
615 |
} |
616 |
needFireTableChanged = true;
|
617 |
} |
618 |
|
619 |
if( needFireTableChanged ) {
|
620 |
fireTableChanged(); |
621 |
} |
622 |
this.reset();
|
623 |
// LOGGER.info("DelayAction.run end ["+ getActionsLabel(this.current_actions) + "]");
|
624 |
} |
625 |
|
626 |
public void addAction(int action) { |
627 |
this.addAction(action, null, null); |
628 |
} |
629 |
|
630 |
public void addAction(int action, Feature feature) { |
631 |
this.addAction(action, feature, null); |
632 |
} |
633 |
|
634 |
public void addAction(int action, FeatureType featureType) { |
635 |
this.addAction(action, null, featureType); |
636 |
} |
637 |
|
638 |
public void addAction(int action, Feature feature, FeatureType featureType) { |
639 |
this.feature = feature;
|
640 |
this.featureType = featureType;
|
641 |
this.current_actions.set(action);
|
642 |
// LOGGER.info("addAction: "+ this.getActionsLabel(current_actions));
|
643 |
if ( this.current_actions.isSetAny() ) { |
644 |
// LOGGER.info("addAction: start");
|
645 |
this.start();
|
646 |
} |
647 |
} |
648 |
|
649 |
} |
650 |
|
651 |
private final DelayAction delayAction = new DelayAction(); |
652 |
|
653 |
@Override
|
654 |
public void update(final Observable observable, final Object notification) { |
655 |
// LOGGER.info("update: " +
|
656 |
// (observable==null? "null":observable.getClass().getSimpleName()) +
|
657 |
// ", " +
|
658 |
// (notification==null? "null":notification.getClass().getSimpleName())
|
659 |
// );
|
660 |
if (notification instanceof ComplexNotification) { |
661 |
// A lot of things might have happened in the store, so don't
|
662 |
// bother looking into each notification.
|
663 |
this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
|
664 |
} else if (observable.equals(getFeatureStore()) |
665 |
&& notification instanceof FeatureStoreNotification) {
|
666 |
FeatureStoreNotification fsNotification |
667 |
= (FeatureStoreNotification) notification; |
668 |
String type = fsNotification.getType();
|
669 |
|
670 |
// LOGGER.info("update: "+type);
|
671 |
switch(type) {
|
672 |
case FeatureStoreNotification.AFTER_DELETE:
|
673 |
case FeatureStoreNotification.AFTER_INSERT:
|
674 |
this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED, fsNotification.getFeature());
|
675 |
break;
|
676 |
|
677 |
case FeatureStoreNotification.AFTER_UPDATE:
|
678 |
this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_UPDATED, fsNotification.getFeature());
|
679 |
break;
|
680 |
|
681 |
case FeatureStoreNotification.AFTER_UPDATE_TYPE:
|
682 |
this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED, fsNotification.getFeatureType());
|
683 |
break;
|
684 |
|
685 |
case FeatureStoreNotification.TRANSFORM_CHANGE:
|
686 |
case FeatureStoreNotification.AFTER_UNDO:
|
687 |
case FeatureStoreNotification.AFTER_REDO:
|
688 |
case FeatureStoreNotification.AFTER_REFRESH:
|
689 |
this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
|
690 |
break;
|
691 |
|
692 |
case FeatureStoreNotification.AFTER_FINISHEDITING:
|
693 |
case FeatureStoreNotification.AFTER_STARTEDITING:
|
694 |
case FeatureStoreNotification.AFTER_CANCELEDITING:
|
695 |
/*
|
696 |
No tengo nada claro por que es necesario llamar al reloadFeatureType
|
697 |
pero si no se incluye hay problemas si durante la edicion se a?aden
|
698 |
campos a la tabla. Sin esto, al cerrar la edicion, los campos a?adidos
|
699 |
desaparecen de la tabla aunque estan en el fichero.
|
700 |
Ver ticket #2434 https://devel.gvsig.org/redmine/issues/2434
|
701 |
*/
|
702 |
this.delayAction.addAction(DelayAction.ACTION_RELOAD_FEATURE_TYPE, fsNotification.getFeatureType());
|
703 |
break;
|
704 |
|
705 |
case FeatureStoreNotification.SELECTION_CHANGE:
|
706 |
this.delayAction.addAction(DelayAction.ACTION_UPDATE_SELECTION);
|
707 |
break;
|
708 |
|
709 |
default:
|
710 |
LOGGER.debug("update: skip notification("+type+")"); |
711 |
} |
712 |
} |
713 |
} |
714 |
|
715 |
protected void updatePaginHelperWithHiddenColums() { |
716 |
FeatureQuery query = this.getHelper().getFeatureQuery();
|
717 |
if (this.getHelper().getFeatureStore().isEditing()) { |
718 |
if (query.hasConstantsAttributeNames()) {
|
719 |
query.clearConstantsAttributeNames(); |
720 |
} |
721 |
} else {
|
722 |
query.setConstantsAttributeNames(this.getHiddenColumnNames());
|
723 |
} |
724 |
try {
|
725 |
this.getHelper().reload();
|
726 |
} catch (BaseException ex) {
|
727 |
LOGGER.warn("Can't reload paging-helper.", ex);
|
728 |
} |
729 |
} |
730 |
|
731 |
protected String[] getHiddenColumnNames() { |
732 |
return null; |
733 |
} |
734 |
|
735 |
/**
|
736 |
* Returns the FeatureStore of the Collection.
|
737 |
*
|
738 |
* @return the FeatureStore
|
739 |
*/
|
740 |
public FeatureStore getFeatureStore() {
|
741 |
return getHelper().getFeatureStore();
|
742 |
} |
743 |
|
744 |
/**
|
745 |
* Returns the descriptor of a Feature attribute for a table column.
|
746 |
*
|
747 |
* @param columnIndex, the column index
|
748 |
* @return
|
749 |
*/
|
750 |
public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) { |
751 |
return internalGetFeatureDescriptorForColumn(columnIndex);
|
752 |
} |
753 |
|
754 |
/**
|
755 |
* @param columnIndex
|
756 |
* @return
|
757 |
*/
|
758 |
protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
|
759 |
int columnIndex) {
|
760 |
FeatureType featureType = getFeatureType(); |
761 |
return featureType == null ? null : featureType |
762 |
.getAttributeDescriptor(columnIndex); |
763 |
} |
764 |
|
765 |
/**
|
766 |
* Initialize the TableModel
|
767 |
*/
|
768 |
protected void initialize() { |
769 |
// Add as observable to the FeatureStore, to detect data and selection
|
770 |
// changes
|
771 |
pagingHelper.getFeatureStore().addObserver(this);
|
772 |
} |
773 |
|
774 |
/**
|
775 |
* Returns the value of a Feature attribute, at the given position.
|
776 |
*
|
777 |
* @param feature the feature to get the value from
|
778 |
* @param columnIndex the Feature attribute position
|
779 |
* @return the value
|
780 |
*/
|
781 |
protected Object getFeatureValue(Feature feature, int columnIndex) { |
782 |
return feature.get(columnIndex);
|
783 |
} |
784 |
|
785 |
/**
|
786 |
* Sets the value of an Feature attribute at the given position.
|
787 |
*
|
788 |
* @param feature the feature to update
|
789 |
* @param columnIndex the attribute position
|
790 |
* @param value the value to set
|
791 |
* @return
|
792 |
*/
|
793 |
protected EditableFeature setFeatureValue(Feature feature, int columnIndex, |
794 |
Object value) {
|
795 |
EditableFeature theEditableFeature = feature.getEditable(); |
796 |
theEditableFeature.set(columnIndex, value); |
797 |
return theEditableFeature;
|
798 |
} |
799 |
|
800 |
/**
|
801 |
* Returns the FeatureQuery used to get the Features.
|
802 |
*
|
803 |
* @return the FeatureQuery
|
804 |
*/
|
805 |
public FeatureQuery getFeatureQuery() {
|
806 |
return getHelper().getFeatureQuery();
|
807 |
} |
808 |
|
809 |
/**
|
810 |
* Returns the type of the
|
811 |
*
|
812 |
* @return features.
|
813 |
*/
|
814 |
protected FeatureType getFeatureType() {
|
815 |
return getHelper().getFeatureType();
|
816 |
} |
817 |
|
818 |
/**
|
819 |
* Reloads the table data if a feature has been changed, not through the
|
820 |
* table.
|
821 |
*/
|
822 |
private boolean reloadFeaturesIfFeatureCountChanged(Feature feature) { |
823 |
// Is any data is changed in the FeatureStore, notify the model
|
824 |
// listeners. Ignore the case where the updated feature is
|
825 |
// changed through us.
|
826 |
if (editableFeature == null || !editableFeature.equals(feature)) { |
827 |
reloadFeatures(); |
828 |
//Selection must be locked to avoid losing it when the table is refreshed
|
829 |
selectionLocked = true;
|
830 |
//The table is refreshed
|
831 |
try {
|
832 |
fireTableDataChanged(); |
833 |
} catch (Exception e) { |
834 |
LOGGER.warn("Couldn't reload changed table");
|
835 |
} finally {
|
836 |
//The locked selection is unlocked.
|
837 |
selectionLocked = false;
|
838 |
} |
839 |
return true; |
840 |
} |
841 |
return false; |
842 |
} |
843 |
|
844 |
private boolean reloadFeaturesIfFeatureUpdated(Feature feature) { |
845 |
// Is any data is changed in the FeatureStore, notify the model
|
846 |
// listeners. Ignore the case where the updated feature is
|
847 |
// changed through us.
|
848 |
if (editableFeature == null || !editableFeature.equals(feature)) { |
849 |
reloadFeatures(); |
850 |
return true; |
851 |
} |
852 |
return false; |
853 |
} |
854 |
|
855 |
/**
|
856 |
* Reloads data and structure if the {@link FeatureType} of the features
|
857 |
* being shown has changed.
|
858 |
*/
|
859 |
private boolean reloadFeatureTypeIfTypeChanged(FeatureType updatedType) { |
860 |
// If the updated featured type is the one currently being
|
861 |
// shown, reload the table.
|
862 |
if (updatedType != null && updatedType.getId().equals(getFeatureType().getId())) { |
863 |
setFeatureType(updatedType); |
864 |
return true; |
865 |
} |
866 |
return false; |
867 |
} |
868 |
|
869 |
private void reloadAll() { |
870 |
reloadFeatureType(); |
871 |
} |
872 |
|
873 |
private void reloadFeatureType() { |
874 |
try {
|
875 |
String ftypeid = getHelper().getFeatureType().getId();
|
876 |
FeatureType ftype = getHelper().getFeatureStore().getFeatureType(ftypeid); |
877 |
setFeatureType(ftype); |
878 |
} catch (DataException e) {
|
879 |
LOGGER.warn("Can't reload featuretype",e);
|
880 |
this.status = STATUS_ERR_LOADFEATURETYPE;
|
881 |
// throw new FeaturesDataReloadException(e);
|
882 |
} |
883 |
} |
884 |
|
885 |
/**
|
886 |
* Reloads the features shown on the table.
|
887 |
*/
|
888 |
private void reloadFeatures() { |
889 |
switch(this.status) { |
890 |
case STATUS_OK:
|
891 |
case STATUS_ERR_LOADFEATURES:
|
892 |
break;
|
893 |
case STATUS_ERR_LOADFEATURETYPE:
|
894 |
return;
|
895 |
} |
896 |
try {
|
897 |
getHelper().reload(); |
898 |
} catch (BaseException ex) {
|
899 |
LOGGER.warn("Can't reload features",ex);
|
900 |
this.status = STATUS_ERR_LOADFEATURES;
|
901 |
// throw new FeaturesDataReloadException(ex);
|
902 |
} |
903 |
} |
904 |
|
905 |
/**
|
906 |
* Returns true if selection must not be changed.
|
907 |
*
|
908 |
* @return
|
909 |
*/
|
910 |
public boolean isSelectionLocked() { |
911 |
return selectionLocked;
|
912 |
} |
913 |
|
914 |
|
915 |
@Override
|
916 |
public void dispose() { |
917 |
this.disposableHelper.dispose(this); |
918 |
} |
919 |
|
920 |
@Override
|
921 |
public void doDispose() throws BaseException { |
922 |
DisposeUtils.disposeQuietly(this.pagingHelper);
|
923 |
this.pagingHelper = null; |
924 |
} |
925 |
|
926 |
|
927 |
} |