Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.impl / src / main / java / org / gvsig / fmap / dal / feature / impl / featureset / DefaultFeatureSet.java @ 45195

History | View | Annotate | Download (21.4 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
package org.gvsig.fmap.dal.feature.impl.featureset;
25

    
26
import java.util.ArrayList;
27
import java.util.Collections;
28
import java.util.Iterator;
29
import java.util.List;
30
import java.util.NoSuchElementException;
31

    
32
import org.gvsig.fmap.dal.exception.DataException;
33
import org.gvsig.fmap.dal.feature.EditableFeature;
34
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
35
import org.gvsig.fmap.dal.feature.Feature;
36
import org.gvsig.fmap.dal.feature.FeatureIndexes;
37
import org.gvsig.fmap.dal.feature.FeatureQuery;
38
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
39
import org.gvsig.fmap.dal.feature.FeatureQueryOrder.FeatureQueryOrderMember;
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.FeatureType;
44
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
45
import org.gvsig.fmap.dal.feature.exception.FeatureSetInitializeException;
46
import org.gvsig.fmap.dal.feature.impl.DefaultFeatureStore;
47
import org.gvsig.fmap.dal.feature.impl.DefaultFeatureStoreTransforms;
48
import org.gvsig.fmap.dal.feature.impl.DefaultFeatureType;
49
import org.gvsig.fmap.dal.feature.spi.FeatureSetProvider;
50
import org.gvsig.tools.ToolsLocator;
51
import org.gvsig.tools.dispose.DisposableIterator;
52
import org.gvsig.tools.dispose.DisposeUtils;
53
import org.gvsig.tools.evaluator.Evaluator;
54
import org.gvsig.tools.exception.BaseException;
55
import org.gvsig.tools.observer.Observable;
56
import org.gvsig.tools.observer.Observer;
57

    
58
public class DefaultFeatureSet extends AbstractFeatureSet implements
59
    FeatureSet, Observer {
60

    
61
    protected static final int NO_CHECKED = -1;
62
    protected static final int DEFAULT = 0;
63
    protected static final int FILTERED = 1;
64
    protected static final int ORDERED = 2;
65
    protected static final int ORDERED_FILTERED = 3;
66
    protected static final int EDITED = 4;
67
    protected static final int EDITED_FILTERED = 5;
68
    protected static final int ORDERD_EDITED = 6;
69
    protected static final int ORDERED_EDITED_FILTER = 7;
70

    
71
    protected boolean sourceStoreModified;
72
    protected boolean ownFeaturesModified;
73
    protected DefaultFeatureStore store;
74
    protected List featureTypes;
75
    protected FeatureQuery query;
76
    protected FeatureSetProvider provider;
77
    protected long size;
78
    protected int iteratorMode;
79
    protected List orderedData;
80
    protected Feature featureToIgnoreNotification;
81
    protected DefaultFeatureStoreTransforms transform;
82
    protected FeatureQuery queryForProvider;
83
    protected FeatureType defaultFeatureType;
84
    protected FeatureType defatulFeatureTypeForProvider;
85
    protected boolean ignoreChanges;
86
    private boolean disposed = false;
87

    
88
    public DefaultFeatureSet(DefaultFeatureStore store, FeatureQuery query)
89
        throws DataException {
90
        DisposeUtils.bind(this);
91
        this.featureToIgnoreNotification = null;
92
        this.iteratorMode = NO_CHECKED;
93
        this.sourceStoreModified = false;
94
        this.ownFeaturesModified = false;
95
        this.size = -1;
96
        this.orderedData = null;
97
        this.store = store;
98
        if (this.store.isEditing()) {
99
            this.transform = this.store.getFeatureTypeManager().getTransforms();
100
        } else {
101
            this.transform =
102
                (DefaultFeatureStoreTransforms) store.getTransforms();
103
        }
104
        this.query = query;
105
        try {
106
            this.queryForProvider = (FeatureQuery) query.clone();
107
        } catch (CloneNotSupportedException e) {
108
            throw new FeatureSetInitializeException(e);
109
        }
110

    
111
        this.featureTypes = new ArrayList();
112
        if (this.query.getFeatureTypeId() == null
113
            && this.query.getAttributeNames() == null) {
114
            this.defaultFeatureType = this.store.getDefaultFeatureType();
115
            this.featureTypes.addAll(this.store.getFeatureTypes());
116
        } else {
117
            this.defaultFeatureType = this.store.getFeatureType(this.query);
118
            List<EditableFeatureAttributeDescriptor> cols = this.query.getExtraColumn().getColumns();
119
            if (this.query!=null && cols!=null && !cols.isEmpty()) {
120
                DefaultFeatureType featureTypeExtraCols = (DefaultFeatureType) this.defaultFeatureType.getCopy();
121
                featureTypeExtraCols.setExtraColumn(this.query.getExtraColumn());
122
                this.defaultFeatureType = featureTypeExtraCols;
123
            }
124
            this.featureTypes.add(this.defaultFeatureType);
125
        }
126
        if (this.transform != null && !this.transform.isEmpty()) {
127
            this.fixQueryForProvider(this.queryForProvider, this.transform);
128
        } else {
129
            this.defatulFeatureTypeForProvider = this.defaultFeatureType;
130
        }
131

    
132
        FeatureIndexes indexes = store.getIndexes();
133
        if (this.queryForProvider.hasFilter() && indexes != null
134
            && indexes.areValid()) {
135
            this.provider =
136
                (FeatureSetProvider) indexes
137
                    .getFeatureSet(this.queryForProvider.getFilter());
138
        }
139
        if (this.provider == null) {
140
            this.provider =
141
                this.store.getProvider().createSet(this.queryForProvider,
142
                    this.defatulFeatureTypeForProvider);
143
        }
144
        this.store.addObserver(this);
145
    }
146

    
147
    private void fixQueryForProvider(FeatureQuery theQueryForProvider,
148
        DefaultFeatureStoreTransforms transformsToUse) throws DataException {
149
        theQueryForProvider.clearAttributeNames();
150
        FeatureType ftype =
151
            transformsToUse.getSourceFeatureTypeFrom(this.defaultFeatureType);
152
        theQueryForProvider.setFeatureTypeId(ftype.getId());
153
        this.defatulFeatureTypeForProvider = ftype;
154
        
155
        if (transformsToUse.isTransformsOriginalValues()) {
156
            theQueryForProvider.clearFilter();
157
            FeatureQueryOrder fqo = theQueryForProvider.getOrder();
158
            if (fqo != null) {
159
                fqo.clear();
160
            }
161
            return;
162

    
163
        }
164

    
165
        // Filter
166
        Evaluator filter = theQueryForProvider.getFilter();
167
        if (filter != null) {
168
            boolean canUseFilter;
169
            if (filter.getFieldsInfo() == null) {
170
                canUseFilter = false;
171
            } else {
172
                canUseFilter = areEvaluatorFieldsInAttributes(filter, ftype);
173
            }
174

    
175
            if (!canUseFilter) {
176
                theQueryForProvider.clearFilter();
177
            }
178

    
179
        }
180

    
181
        // Order
182
        if (theQueryForProvider.hasOrder()) {
183
            boolean canUseOrder = true;
184
            Iterator iter = theQueryForProvider.getOrder().iterator();
185
            FeatureQueryOrderMember item;
186
            while (iter.hasNext()) {
187
                item = (FeatureQueryOrderMember) iter.next();
188
                if (item.hasEvaluator()) {
189
                    if (!areEvaluatorFieldsInAttributes(item.getEvaluator(),
190
                        ftype)) {
191
                        canUseOrder = false;
192
                        break;
193
                    }
194
                } else {
195
                    if (ftype.get(item.getAttributeName()) == null) {
196
                        canUseOrder = false;
197
                        break;
198
                    }
199
                }
200
            }
201

    
202
            if (!canUseOrder) {
203
                theQueryForProvider.getOrder().clear();
204
            }
205
        }
206

    
207
    }
208

    
209
    private boolean areEvaluatorFieldsInAttributes(Evaluator evaluator,
210
        FeatureType fType) {
211
        if (evaluator.getFieldsInfo() == null) {
212
            return false;
213
        }
214
        String[] fieldNames = evaluator.getFieldsInfo().getFieldNames();
215
        if (fieldNames.length == 0) {
216
            return false;
217
        } else {
218
            for (String fieldName : fieldNames) {
219
                if (fType.get(fieldName) == null) {
220
                    return false;
221
                }
222
            }
223
        }
224
        return true;
225
    }
226

    
227
    @Override
228
    public FeatureType getDefaultFeatureType() {
229
        return this.defaultFeatureType;
230
    }
231

    
232
    @Override
233
    public List getFeatureTypes() {
234
        return Collections.unmodifiableList(this.featureTypes);
235
    }
236

    
237
    @Override
238
    public long getSize() throws DataException {
239
        this.checkSourceStoreModified();
240
        if (size < 0) {
241
            size = calculateSize();
242
        }
243
        return size;
244
    }
245

    
246
    private long calculateSize() throws DataException {
247
        long limit = this.query.getLimit();
248
        long mySize = 0;
249
        
250
        int mode = this.getIteratorMode();
251
        switch (mode) {
252
        case DEFAULT:
253
        case ORDERED:
254
            if (this.provider.isEmpty()) {
255
                return 0;
256
            }
257
            mySize = provider.getSize();
258
            return (limit>0 && mySize>limit)? limit:mySize;
259

    
260
        case FILTERED:
261
        case ORDERED_FILTERED:
262
            DisposableIterator iter = null;
263
            try {
264
                iter = this.fastIterator();
265
                while ((limit>0 && (mySize<limit)) || limit==0 ) {
266
                    iter.next();
267
                    mySize++;
268
                }
269
            } catch (NoSuchElementException e) {
270

    
271
            } finally {
272
                DisposeUtils.disposeQuietly(iter);
273
            }
274
            return mySize;
275

    
276
        case EDITED:
277
        case EDITED_FILTERED:
278
        case ORDERD_EDITED:
279
        case ORDERED_EDITED_FILTER:
280
            mySize = provider.getSize()
281
                + store.getFeatureManager().getDeltaSize();
282
            return (limit>0 && mySize>limit)? limit:mySize;
283

    
284
        default:
285
            throw new IllegalArgumentException();
286
        }
287
    }
288
    
289
    @Override
290
    public synchronized final void dispose() {
291
        // Check if we have already been disposed, and don't do it again
292
        if (!disposed) {
293
            if (DisposeUtils.release(this)) {
294
                try {
295
                    doDispose();
296
                } catch (Exception ex) {
297
                    LOG.error("Error performing dispose", ex);
298
                } finally {
299
                    disposed = true;
300
                }
301
            }
302
        }
303
    }
304

    
305
    public void doDispose() {
306
        if( this.store!=null ) {
307
            this.store.deleteObserver(this);
308
            this.store = null;
309
        }
310
        if( this.provider!=null ) {
311
            this.provider.dispose();
312
            this.provider = null;
313
        }
314
        if (orderedData != null) {
315
            orderedData.clear();
316
            this.orderedData = null;
317
        }
318
        this.featureToIgnoreNotification = null;
319
        this.transform = null;
320
        this.query = null;
321
        this.queryForProvider = null;
322
        this.featureTypes = null;
323
        this.defaultFeatureType = null;
324
        this.defatulFeatureTypeForProvider = null;
325
    }
326

    
327
    public void update(Observable obsevable, Object notification) {
328
        if (sourceStoreModified) {
329
            return;
330
        }
331

    
332
        String type = ((FeatureStoreNotification) notification).getType();
333

    
334
        if (type.equalsIgnoreCase(FeatureStoreNotification.AFTER_INSERT)
335
            || type.equalsIgnoreCase(FeatureStoreNotification.AFTER_DELETE)
336
            || type.equalsIgnoreCase(FeatureStoreNotification.AFTER_UPDATE)) {
337
            if (this.featureToIgnoreNotification == ((FeatureStoreNotification) notification)
338
                .getFeature()) {
339
                return;
340
            }
341
            sourceStoreModified = true;
342
            return;
343
        }
344
        if (type.equalsIgnoreCase(FeatureStoreNotification.AFTER_UPDATE_TYPE)
345
            || type.equalsIgnoreCase(FeatureStoreNotification.AFTER_REDO)
346
            || type.equalsIgnoreCase(FeatureStoreNotification.AFTER_UNDO)
347
            || type.equalsIgnoreCase(FeatureStoreNotification.AFTER_REFRESH)
348
            || type
349
                .equalsIgnoreCase(FeatureStoreNotification.COMPLEX_NOTIFICATION)
350
            || type.equalsIgnoreCase(FeatureStoreNotification.AFTER_CLOSE)
351
            || type.equalsIgnoreCase(FeatureStoreNotification.AFTER_DISPOSE)
352
            || type.equalsIgnoreCase(FeatureStoreNotification.TRANSFORM_CHANGE)) {
353
            sourceStoreModified = true;
354
            return;
355
        }
356
        if (type.equalsIgnoreCase(FeatureStoreNotification.RESOURCE_CHANGED)) {
357
            if(!this.ignoreChanges) {
358
                sourceStoreModified = true;
359
                return;
360
            }
361
        }
362
        if (type.equalsIgnoreCase(FeatureStoreNotification.AFTER_CANCELEDITING)) {
363
            if (ownFeaturesModified) {
364
                sourceStoreModified = true;
365
                return;
366
            }
367
        }
368
    }
369
  
370
    protected void checkSourceStoreModified() {
371
        if (sourceStoreModified) {
372
                        throw new ConcurrentDataModificationException(store == null ? ""
373
                                        : store.getName());
374
        }
375
    }
376

    
377
    @Override
378
    public DisposableIterator fastIterator(long index) throws DataException {
379
        return fastIterator(index, 0);
380
    }
381
    
382
    @Override
383
    public DisposableIterator fastIterator(long index, long elements) throws DataException {
384
        if (index < 0) {
385
            throw new IndexOutOfBoundsException("The index (" + index
386
                + ") is less than 0");
387
        }
388
        DisposableIterator it;
389
        int mode = this.getIteratorMode();
390

    
391
        switch (mode) {
392
        case DEFAULT:
393
            it = new FastDefaultIterator(this, index, elements);
394
            break;
395

    
396
        case FILTERED:
397
            it = new FastFilteredIterator(this, index);
398
            break;
399

    
400
        case ORDERED:
401
            if (this.orderedData != null) {
402
                it = new FastOrderedIterator(this, index);
403
            } else {
404
                it = new FastOrderedIterator(this, new FastDefaultIterator(this, 0, elements), index);
405
            }
406
            break;
407
            
408
        case ORDERED_FILTERED:
409
            if (this.orderedData != null) {
410
                it = new FastOrderedIterator(this, index);
411
            } else {
412
                it = new FastOrderedIterator(this, new FastFilteredIterator(
413
                    this, 0), index);
414
            }
415
            break;
416

    
417
        case EDITED:
418
            it = new FastEditedIterator(this, index);
419
            break;
420

    
421
        case EDITED_FILTERED:
422
            it = new FastEditedFilteredIterator(this, index);
423
            break;
424

    
425
        case ORDERD_EDITED:
426
            if (this.orderedData != null) {
427
                it = new FastOrderedIterator(this, index);
428
            } else {
429
                it = new FastOrderedIterator(this, new FastEditedIterator(
430
                    this, 0), index);
431
            }
432
            break;
433

    
434
        case ORDERED_EDITED_FILTER:
435
            if (this.orderedData != null) {
436
                it = new FastOrderedIterator(this, index);
437
            } else {
438
                it = new FastOrderedIterator(this,
439
                    new FastEditedFilteredIterator(this, 0), index);
440
            }
441
            break;
442
            
443
        default:
444
            throw new IllegalArgumentException();
445
        }
446
        if( this.query!=null && this.query.getLimit()>0 ) {
447
            it = new LimitIterator(it,this.query.getLimit());
448
        }
449
        return it;
450
    }
451

    
452
    private class LimitIterator implements DisposableIterator {
453

    
454
        private final DisposableIterator it;
455
        private final long limit;
456
        private int count;
457

    
458
        private LimitIterator(DisposableIterator it, long limit) {
459
            this.it = it;
460
            this.limit = limit;
461
            this.count = 0;
462
        }
463

    
464
        @Override
465
        public void dispose() {
466
            this.it.dispose();
467
        }
468

    
469
        @Override
470
        public boolean hasNext() {
471
            if( this.count>=this.limit ) {
472
                return false;
473
            }
474
            return this.it.hasNext();
475
        }
476

    
477
        @Override
478
        public Object next() {
479
            if( this.count>=this.limit ) {
480
                return null;
481
            }
482
            this.count++;
483
            return this.it.next();
484
        }
485

    
486
        @Override
487
        public void remove() {
488
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
489
        }
490
        
491
    }
492

    
493
    @Override
494
    public DisposableIterator iterator(long index) throws DataException {
495
        return iterator(index,0);
496
    }
497
    
498
    @Override
499
    public DisposableIterator iterator(long index, long elements) throws DataException {        
500
        if (index < 0) {
501
            throw new IndexOutOfBoundsException("The index (" + index
502
                + ") is less than 0");
503
        }
504
        DisposableIterator it;
505
        int mode = this.getIteratorMode();
506

    
507
        switch (mode) {
508
        case DEFAULT:
509
            it = new DefaultIterator(this, index, elements);
510
            break;
511

    
512
        case FILTERED:
513
            it = new FilteredIterator(this, index);
514
            break;
515

    
516
        case ORDERED:
517
            if (orderedData != null) {
518
                it = new OrderedIterator(this, index);
519

    
520
            } else {
521
                it = new OrderedIterator(this, new DefaultIterator(this, 0, elements),index);
522
            }
523
            break;
524

    
525
        case ORDERED_FILTERED:
526
            it = new OrderedIterator(this, new FilteredIterator(this, 0),
527
                index);
528
            break;
529

    
530
        case EDITED:
531
            it = new EditedIterator(this, index);
532
            break;
533

    
534
        case EDITED_FILTERED:
535
            it = new EditedFilteredIterator(this, index);
536
            break;
537

    
538
        case ORDERD_EDITED:
539
            it = new OrderedIterator(this, new EditedIterator(this, 0), index);
540
            break;
541

    
542
        case ORDERED_EDITED_FILTER:
543
            it = new OrderedIterator(this,
544
                new EditedFilteredIterator(this, 0), index);
545
            break;
546

    
547
        default:
548
            throw new IllegalArgumentException();
549
        }
550

    
551
        if( this.query!=null && this.query.getLimit()>0 ) {
552
            it = new LimitIterator(it,this.query.getLimit());
553
        }
554
        return it;
555
    }
556

    
557
    private boolean providerCanOrder() {
558
        return this.provider.canOrder();
559
    }
560

    
561
    private boolean providerCanFilter() {
562
        return this.provider.canFilter();
563
    }
564

    
565
    private int getIteratorMode() {
566

    
567
        if (this.iteratorMode != NO_CHECKED) {
568
            return this.iteratorMode;
569
        }
570

    
571
        // TODO Tener en cuenta las transformaciones ???
572

    
573
        if (store.isEditing() && (store.getFeatureTypeManager().hasChanges() || store.getFeatureManager().hasChanges())) {
574
            if (this.query.hasOrder()) { // En edicion siempre ordeno yo.
575
                if (this.query.hasFilter()) {
576
                    return ORDERED_EDITED_FILTER;
577
                } else {
578
                    return ORDERD_EDITED;
579
                }
580
            } else {
581
                if (this.query.hasFilter()) {
582
                    return EDITED_FILTERED;
583
                } else {
584
                    return EDITED;
585
                }
586
            }
587
        } else {
588
            boolean useMyFilter = this.query.hasFilter();
589
            boolean useMyOrder = this.query.hasOrder();
590
            if (this.providerCanOrder() && this.transform.isEmpty()) {
591
                useMyOrder = false;
592
            }
593
            if (this.providerCanFilter() && this.transform.isEmpty()) {
594
                useMyFilter = false;
595
            }
596

    
597
            if (useMyOrder) {
598
                if (useMyFilter) {
599
                    return ORDERED_FILTERED;
600
                } else {
601
                    return ORDERED;
602
                }
603
            } else {
604
                if (useMyFilter) {
605
                    return FILTERED;
606
                } else {
607
                    return DEFAULT;
608
                }
609
            }
610
        }
611

    
612
    }
613

    
614
    @Override
615
    public void delete(Feature feature) throws DataException {
616
        this.featureToIgnoreNotification = feature;
617
        this.store.delete(feature);
618
        if (this.size > 0) {
619
            this.size--;
620
        }
621
        this.featureToIgnoreNotification = null;
622
        this.ownFeaturesModified = true;
623
    }
624

    
625
    @Override
626
    public void insert(EditableFeature feature) throws DataException {
627
        this.featureToIgnoreNotification = feature;
628
        this.store.insert(feature);
629
        if (this.size >= 0) {
630
            this.size++;
631
        }
632
        this.featureToIgnoreNotification = null;
633
        this.ownFeaturesModified = true;
634
    }
635

    
636
    @Override
637
    public void update(EditableFeature feature) throws DataException {
638
        this.featureToIgnoreNotification = feature;
639
        this.store.update(feature);
640
        this.featureToIgnoreNotification = null;
641
        this.ownFeaturesModified = true;
642
    }
643
    
644
    @Override
645
    public void commitChanges() throws DataException {
646
        this.ignoreChanges = true;
647
        this.store.commitChanges();
648
        this.ignoreChanges = false;
649
        
650
    }
651

    
652
    @Override
653
    public FeatureStore getFeatureStore() {
654
        return store;
655
    }
656

    
657
}