Statistics
| Revision:

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 / FeatureSelectionModel.java @ 45425

History | View | Annotate | Download (16.6 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
 * 2010 {}  {{Task}}
27
 */
28
package org.gvsig.fmap.mapcontrol.dal.feature.swing;
29

    
30
import javax.swing.DefaultListSelectionModel;
31
import javax.swing.ListSelectionModel;
32
import javax.swing.event.EventListenerList;
33
import javax.swing.event.ListSelectionEvent;
34
import javax.swing.event.ListSelectionListener;
35

    
36
import org.gvsig.fmap.dal.exception.DataException;
37
import org.gvsig.fmap.dal.feature.Feature;
38
import org.gvsig.fmap.dal.feature.FeatureQuery;
39
import org.gvsig.fmap.dal.feature.FeatureSelection;
40
import org.gvsig.fmap.dal.feature.FeatureSet;
41
import org.gvsig.fmap.dal.feature.FeatureStore;
42
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
43
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
44
import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.FeatureTableModel;
45
import org.gvsig.tools.dispose.DisposableIterator;
46
import org.gvsig.tools.observer.Observable;
47
import org.gvsig.tools.observer.Observer;
48
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50

    
51
/**
52
 * @author 2010- C?sar Ordi?ana - gvSIG team
53
 */
54
@SuppressWarnings("Convert2Lambda")
55
public class FeatureSelectionModel implements ListSelectionModel, Observer {
56

    
57
  private static final Logger LOGGER= LoggerFactory.getLogger(FeatureSelectionModel.class);
58

    
59
  protected EventListenerList listenerList = new EventListenerList();
60

    
61
  private final FeatureTableModel featureTableModel;
62

    
63
  private int selectionMode = SINGLE_INTERVAL_SELECTION;
64

    
65
  private boolean isAdjusting = false;
66

    
67
  private int anchor = -1;
68

    
69
  private int lead = -1;
70

    
71
  private int currentFirst = -1;
72
  private int currentLast = -1;
73

    
74
  private ListSelectionModel selectionModelDelegated;
75

    
76
  /**
77
   * Creates a new {@link FeatureSelectionModel} with a
78
   * {@link FeatureTableModel} used to get the {@link Feature}s by position in
79
   * the table.
80
   *
81
   * @param featureTableModel to get Features from
82
   * @throws DataException if there is an error getting the store selection
83
   */
84
  @SuppressWarnings("LeakingThisInConstructor")
85
  public FeatureSelectionModel(FeatureTableModel featureTableModel)
86
          throws DataException {
87
    this.featureTableModel = featureTableModel;
88
    FeatureStore store = this.featureTableModel.getFeatureStore();
89
    store.addObserver(this);
90
    if( !store.getFeatureSelection().isAvailable() ) {
91
      this.selectionModelDelegated = new DefaultListSelectionModel();
92
    }
93
  }
94

    
95
  @Override
96
  public int getAnchorSelectionIndex() {
97
    if( this.selectionModelDelegated!=null ) {
98
      return this.selectionModelDelegated.getAnchorSelectionIndex();
99
    }
100
    return anchor;
101
  }
102

    
103
  @Override
104
  public int getLeadSelectionIndex() {
105
    if( this.selectionModelDelegated!=null ) {
106
      return this.selectionModelDelegated.getLeadSelectionIndex();
107
    }
108
    return lead;
109
  }
110

    
111
  @Override
112
  public int getMaxSelectionIndex() {
113
    if( this.selectionModelDelegated!=null ) {
114
      return this.selectionModelDelegated.getMaxSelectionIndex();
115
    }
116

    
117
    int resp = this.getSelectionIndex(true);
118
    return resp;
119
    /*
120
             *
121
             * The call to "featureTableModel.getFeatureAt(i)"
122
             * causes a lot of reloadPage all over
123
             *
124
                FeatureSelection selection = getFeatureSelection();
125
                try {
126
                        if (!selection.isEmpty()) {
127
                                for (int i = featureTableModel.getRowCount() - 1; i >= 0; i--) {
128
                                        if (selection.isSelected(featureTableModel.getFeatureAt(i))) {
129
                                                return i;
130
                                        }
131
                                }
132
                        }
133
                } catch (DataException e) {
134
                        throw new SelectionChangeException(e);
135
                }
136
                return -1;
137
     */
138
  }
139

    
140
  @Override
141
  public int getMinSelectionIndex() {
142
    if( this.selectionModelDelegated!=null ) {
143
      return this.selectionModelDelegated.getMinSelectionIndex();
144
    }
145

    
146
    int resp = this.getSelectionIndex(false);
147
    return resp;
148

    
149
    /*
150
             *
151
         * The call to "featureTableModel.getFeatureAt(i)"
152
         * causes a lot of reloadPage all over
153
             *
154
                try {
155

156
                    int ii = 0;
157

158
                    FeatureSelection selection = this.getFeatureSelection();
159
            for (int i = 0; i < featureTableModel.getRowCount(); i++) {
160
                if (selection.isSelected(featureTableModel.getFeatureAt(i))) {
161
                    ii = i;
162
                }
163
            }
164
                } catch (Exception e) {
165
                        throw new SelectionChangeException(e);
166
                }
167
     */
168
  }
169

    
170
  @Override
171
  @SuppressWarnings("UnnecessaryReturnStatement")
172
  public void insertIndexInterval(int index, int length, boolean before) {
173
    if( this.selectionModelDelegated!=null ) {
174
      this.selectionModelDelegated.insertIndexInterval(index, length, before);
175
      return;
176
    }
177
    // Nothing to do
178
  }
179

    
180
  @Override
181
  @SuppressWarnings("UnnecessaryReturnStatement")
182
  public void removeIndexInterval(int index0, int index1) {
183
    if( this.selectionModelDelegated!=null ) {
184
      this.selectionModelDelegated.removeIndexInterval(index0, index1);
185
      return;
186
    }
187
    // Nothing to do
188
  }
189

    
190
  @Override
191
  public void setAnchorSelectionIndex(int index) {
192
    if( this.selectionModelDelegated!=null ) {
193
      this.selectionModelDelegated.setAnchorSelectionIndex(index);
194
      return;
195
    }
196
    this.anchor = index;
197
  }
198

    
199
  @Override
200
  public void setLeadSelectionIndex(int index) {
201
    if( this.selectionModelDelegated!=null ) {
202
      this.selectionModelDelegated.setLeadSelectionIndex(index);
203
      return;
204
    }
205
    this.lead = index;
206
  }
207

    
208
  @Override
209
  public void addSelectionInterval(int index0, int index1) {
210
    if( this.selectionModelDelegated!=null ) {
211
      this.selectionModelDelegated.addSelectionInterval(index0, index1);
212
      return;
213
    }
214
    if ( featureTableModel.isSelectionLocked() ) {
215
       return;
216
    }
217
    doWithSelection(new FeatureSelectionOperation() {
218
      @Override
219
      public void doWithSelection(FeatureSelection selection, int first,
220
              int last) throws DataException {
221
        for (int i = first; i <= last; i++) {
222
          Feature feature = getFeature(i);
223
          if (!selection.isSelected(feature)) {
224
            selection.select(feature);
225
          }
226
        }
227
      }
228

    
229
    }, index0, index1, true);
230
  }
231

    
232
  @Override
233
  public void setSelectionInterval(int index0, int index1) {
234
    if( this.selectionModelDelegated!=null ) {
235
      this.selectionModelDelegated.setSelectionInterval(index0, index1);
236
      return;
237
    }
238
    if ( featureTableModel.isSelectionLocked() ) {
239
       return;
240
    }
241
    doWithSelection(new FeatureSelectionOperation() {
242

    
243
      @Override
244
      public void doWithSelection(FeatureSelection selection, int first,
245
              int last) throws DataException {
246
        selection.deselectAll();
247
        for (int i = first; i <= last; i++) {
248
          Feature feature = getFeature(i);
249
          selection.select(feature);
250
        }
251
      }
252

    
253
    }, index0, index1, true);
254
  }
255

    
256
  @Override
257
  public void removeSelectionInterval(int index0, int index1) {
258
    if( this.selectionModelDelegated!=null ) {
259
      this.selectionModelDelegated.removeSelectionInterval(index0, index1);
260
      return;
261
    }
262
    if ( featureTableModel.isSelectionLocked() ) {
263
       return;
264
    }
265
    doWithSelection(new FeatureSelectionOperation() {
266

    
267
      @Override
268
      public void doWithSelection(FeatureSelection selection, int first,
269
              int last) throws DataException {
270
        for (int i = first; i <= last; i++) {
271
          Feature feature = getFeature(i);
272
          if (selection.isSelected(feature)) {
273
            selection.deselect(feature);
274
          }
275
        }
276
      }
277

    
278
    }, index0, index1, false);
279
  }
280

    
281
  @Override
282
  public void clearSelection() {
283
    if( this.selectionModelDelegated!=null ) {
284
      this.selectionModelDelegated.clearSelection();
285
      return;
286
    }
287
    if ( featureTableModel.isSelectionLocked() ) {
288
       return;
289
    }
290
    try {
291
      getFeatureSelection().deselectAll();
292
    } catch (DataException e) {
293
      throw new SelectionChangeException(e);
294
    }
295
  }
296

    
297
  @Override
298
  public boolean isSelectedIndex(int index) {
299
    if( this.selectionModelDelegated!=null ) {
300
      return this.selectionModelDelegated.isSelectedIndex(index);
301
    }
302
    if (index == -1) {
303
      return false;
304
    }
305
    Feature feature = featureTableModel.getFeatureAt(index);
306
    return getFeatureSelection().isSelected(feature);
307
  }
308

    
309
  @Override
310
  public boolean isSelectionEmpty() {
311
    if( this.selectionModelDelegated!=null ) {
312
      return this.selectionModelDelegated.isSelectionEmpty();
313
    }
314
    try {
315
      return getFeatureSelection().isEmpty();
316
    } catch (Exception ex) {
317
      throw new SelectionChangeException(ex);
318
    }
319
  }
320

    
321
  @Override
322
  public boolean getValueIsAdjusting() {
323
    if( this.selectionModelDelegated!=null ) {
324
      return this.selectionModelDelegated.getValueIsAdjusting();
325
    }
326
    return isAdjusting;
327
  }
328

    
329
  @Override
330
  public void setValueIsAdjusting(boolean valueIsAdjusting) {
331
    if( this.selectionModelDelegated!=null ) {
332
      this.selectionModelDelegated.setValueIsAdjusting(valueIsAdjusting);
333
      return;
334
    }
335
    if (this.isAdjusting != valueIsAdjusting) {
336
      this.isAdjusting = valueIsAdjusting;
337
      if (this.isAdjusting) {
338
        getFeatureSelection().beginComplexNotification();
339
      } else {
340
        getFeatureSelection().endComplexNotification();
341
      }
342
    }
343
  }
344

    
345
  @Override
346
  public int getSelectionMode() {
347
    if( this.selectionModelDelegated!=null ) {
348
      return this.selectionModelDelegated.getSelectionMode();
349
    }
350
    return selectionMode;
351
  }
352

    
353
  @Override
354
  public void setSelectionMode(int selectionMode) {
355
    if( this.selectionModelDelegated!=null ) {
356
      this.selectionModelDelegated.setSelectionMode(selectionMode);
357
      return ;
358
    }
359
    this.selectionMode = selectionMode;
360
  }
361

    
362
  @Override
363
  public void addListSelectionListener(ListSelectionListener listener) {
364
    if( this.selectionModelDelegated!=null ) {
365
      this.selectionModelDelegated.addListSelectionListener(listener);
366
      return ;
367
    }
368
    listenerList.add(ListSelectionListener.class, listener);
369
  }
370

    
371
  @Override
372
  public void removeListSelectionListener(ListSelectionListener listener) {
373
    if( this.selectionModelDelegated!=null ) {
374
      this.selectionModelDelegated.removeListSelectionListener(listener);
375
      return ;
376
    }
377
    listenerList.remove(ListSelectionListener.class, listener);
378
  }
379

    
380
  @Override
381
  public void update(Observable observable, Object notification) {
382
    if (notification instanceof FeatureStoreNotification) {
383
      FeatureStoreNotification fnotification
384
              = (FeatureStoreNotification) notification;
385
      if (!fnotification.getSource().equals(getFeatureStore())) {
386
        return;
387
      }
388
      if (FeatureStoreNotification.SELECTION_CHANGE.equals(fnotification.getType())) {
389
        try {
390
          fireValueChanged(-1, -1, false);
391
        } catch (ConcurrentDataModificationException e) {
392
          LOGGER.warn("The store has been updated and the selection can not be refreshed", e);
393
        }
394
      }
395
    }
396
  }
397

    
398
  private FeatureSelection getFeatureSelection() {
399
    try {
400
      return (FeatureSelection) getFeatureStore().getSelection();
401
    } catch (DataException ex) {
402
      throw new SelectionChangeException(ex);
403
    }
404
  }
405

    
406
  /**
407
   * @param operation
408
   * @param index0
409
   * @param index1
410
   */
411
  private void doWithSelection(FeatureSelectionOperation operation,
412
          int index0, int index1, boolean select) {
413
    // Set the anchor and lead
414
    anchor = index0;
415
    lead = index1;
416

    
417
    // As index0 <= index1 is no guaranteed, calculate the first and second
418
    // values
419
    int first = (index0 <= index1) ? index0 : index1;
420
    int last = (index0 <= index1) ? index1 : index0;
421

    
422
    //If the new selection is not updated don't continue
423
    if ((currentFirst == first) && (currentLast == last)) {
424
      return;
425
    }
426

    
427
    int oldFirst = currentFirst;
428
    int oldLast = currentLast;
429
    currentFirst = first;
430
    currentLast = last;
431

    
432
    FeatureSelection selection = getFeatureSelection();
433

    
434
    // Perform the selection operation into a complex notification
435
    selection.beginComplexNotification();
436
    try {
437
      // Is a full select or deselect
438
      if (first == 00 && last == featureTableModel.getRowCount() - 1) {
439
        if (select) {
440
          selection.selectAll();
441
        } else {
442
          selection.deselectAll();
443
        }
444
      } else {
445
        operation.doWithSelection(selection, first, last);
446
      }
447
    } catch (DataException e) {
448
      throw new SelectionChangeException(e);
449
    } finally {
450
      selection.endComplexNotification();
451
    }
452

    
453
    // Para no lanzar dos veces el fireValueChanged sobre las mismas filas de la selecci?n
454
    if (oldFirst <= first) {
455
      if (oldLast < first) { //No intersectan los rangos
456
        fireValueChanged(oldFirst, oldLast, isAdjusting);
457
        fireValueChanged(first, last, isAdjusting);
458
      } else { // Intersectan los rangos oldFirst - first - ...
459
        if (last > oldLast) { // Intersectan los rangos oldFirst - first - oldLast - last
460
          fireValueChanged(oldFirst, last, isAdjusting);
461
        } else { // Intersectan los rangos oldFirst - first - last - oldLast
462
          fireValueChanged(oldFirst, oldLast, isAdjusting);
463
        }
464
      }
465
    }
466
    if (first <= oldFirst) {
467
      if (last < oldFirst) { //No intersectan los rangos
468
        fireValueChanged(oldFirst, oldLast, isAdjusting);
469
        fireValueChanged(first, last, isAdjusting);
470
      } else { // Intersectan los rangos first - oldFirst - ...
471
        if (last > oldLast) { // Intersectan los rangos first - oldFirst - oldLast - last
472
          fireValueChanged(first, last, isAdjusting);
473
        } else { // Intersectan los rangos first - oldFirst - last - oldLast
474
          fireValueChanged(first, oldLast, isAdjusting);
475
        }
476
      }
477
    }
478
  }
479

    
480
  /**
481
   * Returns a Feature by table row position.
482
   */
483
  private Feature getFeature(int index) {
484
    return featureTableModel.getFeatureAt(index);
485
  }
486

    
487
  /**
488
   * Returns the FeatureStore.
489
   */
490
  private FeatureStore getFeatureStore() {
491
    return featureTableModel.getFeatureStore();
492
  }
493

    
494
  /**
495
   * @param firstIndex the first index in the interval
496
   * @param lastIndex the last index in the interval
497
   * @param isAdjusting true if this is the final change in a series of
498
   * adjustments
499
   * @see EventListenerList
500
   */
501
  protected void fireValueChanged(int firstIndex, int lastIndex,
502
          boolean isAdjusting) {
503
    Object[] listeners = listenerList.getListenerList();
504
    ListSelectionEvent e = null;
505

    
506
    for (int i = listeners.length - 2; i >= 0; i -= 2) {
507
      if (listeners[i] == ListSelectionListener.class) {
508
        if (e == null) {
509
          e
510
                  = new ListSelectionEvent(this, firstIndex, lastIndex,
511
                          isAdjusting);
512
        }
513
        ((ListSelectionListener) listeners[i + 1]).valueChanged(e);
514
      }
515
    }
516
  }
517

    
518
  private interface FeatureSelectionOperation {
519

    
520
    void doWithSelection(FeatureSelection selection, int first, int last)
521
            throws DataException;
522
  }
523

    
524
  /**
525
   *
526
   * Return the index of the the first (last) selected feature
527
   *
528
   * @param last whether to return the index of the last selected feature
529
   * @return
530
   */
531
  private int getSelectionIndex(boolean last) {
532

    
533
    int ind = -1;
534
    int resp = -1;
535

    
536
    FeatureSet fs = null;
537
    DisposableIterator diter = null;
538

    
539
    try {
540
      FeatureSelection selection = getFeatureSelection();
541
      if (!selection.isEmpty()) {
542
        FeatureStore store = getFeatureStore();
543
        FeatureQuery query = store.createFeatureQuery();
544
        query.addEssentialAttributeNames(store);
545
        fs = store.getFeatureSet(query);
546
        diter = fs.fastIterator();
547
        Feature feat;
548
        while (diter.hasNext()) {
549
          ind++;
550
          feat = (Feature) diter.next();
551
          if (selection.isSelected(feat)) {
552
            resp = ind;
553
            if (!last) {
554
              break;
555
            }
556
          }
557
        }
558

    
559
      }
560
    } catch (DataException e) {
561
      throw new SelectionChangeException(e);
562
    } finally {
563
      if (diter != null) {
564
        diter.dispose();
565
      }
566

    
567
      if (fs != null) {
568
        fs.dispose();
569
      }
570
    }
571
    return resp;
572
  }
573

    
574
}