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 / DefaultFeatureStore.java @ 46309

History | View | Annotate | Download (137 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;
25

    
26
import java.util.ArrayList;
27
import java.util.Collection;
28
import java.util.Collections;
29
import java.util.HashMap;
30
import java.util.HashSet;
31
import java.util.Iterator;
32
import java.util.List;
33
import java.util.Map;
34
import java.util.Map.Entry;
35
import java.util.Objects;
36
import java.util.Set;
37
import java.util.UUID;
38
import javax.json.JsonObject;
39
import org.apache.commons.io.FilenameUtils;
40
import org.apache.commons.io.IOUtils;
41
import org.apache.commons.lang3.StringUtils;
42
import org.apache.commons.lang3.mutable.MutableObject;
43
import org.cresques.cts.IProjection;
44
import org.gvsig.expressionevaluator.Expression;
45
import org.gvsig.expressionevaluator.ExpressionBuilder;
46
import org.gvsig.expressionevaluator.ExpressionUtils;
47
import org.gvsig.expressionevaluator.GeometryExpressionUtils;
48
import org.gvsig.fmap.dal.BaseStoresRepository;
49
import org.gvsig.fmap.dal.DALLocator;
50
import org.gvsig.fmap.dal.DataManager;
51
import static org.gvsig.fmap.dal.DataManager.DAL_STORE_ENVELOPE;
52
import static org.gvsig.fmap.dal.DataManager.DAL_USE_LARGE_SELECTION;
53
import org.gvsig.fmap.dal.DataQuery;
54
import org.gvsig.fmap.dal.DataServerExplorer;
55
import org.gvsig.fmap.dal.DataSet;
56
import org.gvsig.fmap.dal.DataStore;
57
import org.gvsig.fmap.dal.DataStoreNotification;
58
import org.gvsig.fmap.dal.DataStoreParameters;
59
import org.gvsig.fmap.dal.DataStoreProviderFactory;
60
import org.gvsig.fmap.dal.DataTransaction;
61
import org.gvsig.fmap.dal.DataTypes;
62
import org.gvsig.fmap.dal.StoresRepository;
63
import org.gvsig.fmap.dal.SupportTransactions;
64
import org.gvsig.fmap.dal.exception.CloneException;
65
import org.gvsig.fmap.dal.exception.CloseException;
66
import org.gvsig.fmap.dal.exception.CreateException;
67
import org.gvsig.fmap.dal.exception.DataException;
68
import org.gvsig.fmap.dal.exception.DataRuntimeException;
69
import org.gvsig.fmap.dal.exception.InitializeException;
70
import org.gvsig.fmap.dal.exception.OpenException;
71
import org.gvsig.fmap.dal.exception.ReadException;
72
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
73
import org.gvsig.fmap.dal.exception.WriteException;
74
import org.gvsig.fmap.dal.feature.EditableFeature;
75
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
76
import org.gvsig.fmap.dal.feature.EditableFeatureType;
77
import org.gvsig.fmap.dal.feature.Feature;
78
import static org.gvsig.fmap.dal.feature.Feature.CHECK_BASIC;
79
import static org.gvsig.fmap.dal.feature.Feature.CHECK_REQUIREDS;
80
import static org.gvsig.fmap.dal.feature.Feature.CHECK_RULES_AT_EDITING;
81
import static org.gvsig.fmap.dal.feature.Feature.CHECK_RULES_AT_FINISH;
82
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
83
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
84
import org.gvsig.fmap.dal.feature.FeatureCache;
85
import org.gvsig.fmap.dal.feature.FeatureIndex;
86
import org.gvsig.fmap.dal.feature.FeatureIndexes;
87
import org.gvsig.fmap.dal.feature.FeatureLocks;
88
import org.gvsig.fmap.dal.feature.FeatureQuery;
89
import org.gvsig.fmap.dal.feature.FeatureReference;
90
import org.gvsig.fmap.dal.feature.FeatureReferenceSelection;
91
import org.gvsig.fmap.dal.feature.FeatureSelection;
92
import org.gvsig.fmap.dal.feature.FeatureSet;
93
import org.gvsig.fmap.dal.feature.FeatureSet.DisposableFeatureSetIterable;
94
import org.gvsig.fmap.dal.feature.FeatureStore;
95
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
96
import org.gvsig.fmap.dal.feature.FeatureStoreProviderFactory;
97
import org.gvsig.fmap.dal.feature.FeatureStoreTimeSupport;
98
import org.gvsig.fmap.dal.feature.FeatureStoreTransform;
99
import org.gvsig.fmap.dal.feature.FeatureStoreTransforms;
100
import org.gvsig.fmap.dal.feature.FeatureType;
101
import org.gvsig.fmap.dal.feature.FeatureType.FeatureTypeChanged;
102
import org.gvsig.fmap.dal.feature.NewFeatureStoreParameters;
103
import org.gvsig.fmap.dal.feature.exception.AlreadyEditingException;
104
import org.gvsig.fmap.dal.feature.exception.CreateFeatureException;
105
import org.gvsig.fmap.dal.feature.exception.DataExportException;
106
import org.gvsig.fmap.dal.feature.exception.FeatureIndexException;
107
import org.gvsig.fmap.dal.feature.exception.FinishEditingException;
108
import org.gvsig.fmap.dal.feature.exception.GetFeatureTypeException;
109
import org.gvsig.fmap.dal.feature.exception.IllegalFeatureException;
110
import org.gvsig.fmap.dal.feature.exception.IllegalFeatureTypeException;
111
import org.gvsig.fmap.dal.feature.exception.NeedEditingModeException;
112
import org.gvsig.fmap.dal.feature.exception.NoNewFeatureInsertException;
113
import org.gvsig.fmap.dal.feature.exception.NullFeatureTypeException;
114
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
115
import org.gvsig.fmap.dal.feature.exception.PersistenceCantFindFeatureTypeException;
116
import org.gvsig.fmap.dal.feature.exception.PersistenceStoreAlreadyLoadedException;
117
import org.gvsig.fmap.dal.feature.exception.SelectionNotAllowedException;
118
import org.gvsig.fmap.dal.feature.exception.StoreCancelEditingException;
119
import org.gvsig.fmap.dal.feature.exception.StoreDeleteEditableFeatureException;
120
import org.gvsig.fmap.dal.feature.exception.StoreDeleteFeatureException;
121
import org.gvsig.fmap.dal.feature.exception.StoreEditException;
122
import org.gvsig.fmap.dal.feature.exception.StoreInsertFeatureException;
123
import org.gvsig.fmap.dal.feature.exception.StoreUpdateFeatureException;
124
import org.gvsig.fmap.dal.feature.exception.StoreUpdateFeatureTypeException;
125
import org.gvsig.fmap.dal.feature.exception.ValidateFeaturesException;
126
import org.gvsig.fmap.dal.feature.exception.WriteNotAllowedException;
127
import org.gvsig.fmap.dal.feature.impl.dynobjectutils.DynObjectFeatureFacade;
128
import org.gvsig.fmap.dal.feature.impl.editing.memory.FeatureManager;
129
import org.gvsig.fmap.dal.feature.impl.editing.memory.FeatureTypeManager;
130
import org.gvsig.fmap.dal.feature.impl.editing.memory.SpatialManager;
131
import org.gvsig.fmap.dal.feature.impl.featurereference.FeatureReferenceFactory;
132
import org.gvsig.fmap.dal.feature.impl.featurereference.FeatureReferenceIteratorToFeatureIterator;
133
import org.gvsig.fmap.dal.feature.impl.featureset.DefaultFeatureSet;
134
import org.gvsig.fmap.dal.feature.impl.undo.DefaultFeatureCommandsStack;
135
import org.gvsig.fmap.dal.feature.impl.undo.FeatureCommandsStack;
136
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
137
import org.gvsig.fmap.dal.feature.spi.DefaultFeatureProvider;
138
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
139
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
140
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProvider;
141
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
142
import org.gvsig.fmap.dal.feature.spi.cache.FeatureCacheProvider;
143
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProviderServices;
144
import org.gvsig.fmap.dal.impl.DefaultDataManager;
145
import org.gvsig.fmap.dal.resource.Resource;
146
import org.gvsig.fmap.dal.spi.AbstractDataStore;
147
import org.gvsig.fmap.dal.spi.DataStoreInitializer2;
148
import org.gvsig.fmap.dal.spi.DataStoreProvider;
149
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
150
import org.gvsig.fmap.dal.spi.DataTransactionServices;
151
import org.gvsig.fmap.geom.Geometry;
152
import org.gvsig.fmap.geom.GeometryUtils;
153
import org.gvsig.fmap.geom.SpatialIndex;
154
import org.gvsig.fmap.geom.primitive.Envelope;
155
import org.gvsig.metadata.MetadataLocator;
156
import org.gvsig.metadata.MetadataManager;
157
import org.gvsig.metadata.exceptions.MetadataException;
158
import org.gvsig.timesupport.Interval;
159
import org.gvsig.tools.ToolsLocator;
160
import org.gvsig.tools.dispose.DisposableIterator;
161
import org.gvsig.tools.dispose.DisposeUtils;
162
import org.gvsig.tools.dynobject.DelegatedDynObject;
163
import org.gvsig.tools.dynobject.DynClass;
164
import org.gvsig.tools.dynobject.DynObject;
165
import org.gvsig.tools.dynobject.DynObjectManager;
166
import org.gvsig.tools.dynobject.DynObject_v2;
167
import org.gvsig.tools.dynobject.DynStruct;
168
import org.gvsig.tools.dynobject.Tags;
169
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
170
import org.gvsig.tools.dynobject.exception.DynMethodException;
171
import org.gvsig.tools.exception.BaseException;
172
import org.gvsig.tools.exception.NotYetImplemented;
173
import org.gvsig.tools.identitymanagement.SimpleIdentityManager;
174
import org.gvsig.tools.observer.Observable;
175
import org.gvsig.tools.observer.Observer;
176
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
177
import org.gvsig.tools.persistence.PersistenceManager;
178
import org.gvsig.tools.persistence.Persistent;
179
import org.gvsig.tools.persistence.PersistentState;
180
import org.gvsig.tools.persistence.exception.PersistenceException;
181
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
182
import org.gvsig.tools.undo.RedoException;
183
import org.gvsig.tools.undo.UndoException;
184
import org.gvsig.tools.undo.command.Command;
185
import org.gvsig.tools.util.CachedValue;
186
import org.gvsig.tools.util.ChainedIterator;
187
import org.gvsig.tools.util.GetItemWithSizeIsEmptyAndIterator64;
188
import org.gvsig.tools.util.HasAFile;
189
import org.gvsig.tools.util.PropertiesSupportHelper;
190
import org.gvsig.tools.util.UnmodifiableBasicMap;
191
import org.gvsig.tools.visitor.VisitCanceledException;
192
import org.gvsig.tools.visitor.Visitor;
193

    
194
@SuppressWarnings("UseSpecificCatch")
195
public class DefaultFeatureStore extends AbstractDataStore implements
196
        DataStoreInitializer2, FeatureStoreProviderServices, FeatureStore,
197
        SupportTransactions, Observer {
198

    
199
    public static long sample_feature_cache_timeout_ms = 15000;
200
    
201
    private static final String PERSISTENCE_DEFINITION_NAME = "FeatureStore";
202

    
203
    private DataStoreParameters parameters = null;
204
    private FeatureSelection selection;
205
    private FeatureLocks locks;
206

    
207
    private DelegateWeakReferencingObservable delegateObservable
208
            = new DelegateWeakReferencingObservable(this);
209

    
210
    private FeatureCommandsStack commands;
211

    
212
    /*
213
    TODO: Sustituir estos tres manager por un EditingManager
214
     */
215
    private FeatureTypeManager featureTypeManager;
216
    private FeatureManager featureManager;
217
    private SpatialManager spatialManager;
218

    
219
    private FeatureType defaultFeatureType = null;
220
    private List<FeatureType> featureTypes = new ArrayList<>();
221

    
222
    private int mode = MODE_QUERY;
223
    private long versionOfUpdate = 0;
224
    private boolean hasStrongChanges = true;
225
    private boolean hasInserts = true;
226

    
227
    private DefaultDataManager dataManager = null;
228

    
229
    private FeatureStoreProvider provider = null;
230

    
231
    private DefaultFeatureIndexes indexes;
232

    
233
    private DefaultFeatureStoreTransforms transforms;
234

    
235
    /*friend*/ DelegatedDynObject metadata;
236

    
237
    private Set metadataChildren;
238

    
239
    private Long featureCount = null;
240

    
241
    private long temporalOid = 0;
242

    
243
    private FeatureCacheProvider cache;
244

    
245
    private final StateInformation state;
246

    
247
    private FeatureStoreTimeSupport timeSupport;
248

    
249
    private PropertiesSupportHelper propertiesSupportHelper;
250
    private DataTransaction transaction;
251

    
252
    private String editingSessionCode;
253
    
254
    private CachedValue<Feature> sampleFeatureCache;
255

    
256
    private class StateInformation extends HashMap<Object, Object> {
257

    
258
        private static final long serialVersionUID = 4109026189635185666L;
259

    
260
        private boolean broken;
261
        private Throwable breakingsCause;
262

    
263
        @SuppressWarnings("OverridableMethodCallInConstructor")
264
        public StateInformation() {
265
            this.clear();
266
        }
267

    
268
        @Override
269
        public void clear() {
270
            this.broken = false;
271
            this.breakingsCause = null;
272
            super.clear();
273
        }
274

    
275
        public boolean isBroken() {
276
            return this.broken;
277
        }
278

    
279
        public void broken() {
280
            this.broken = true;
281
        }
282

    
283
        public Throwable getBreakingsCause() {
284
            return this.breakingsCause;
285
        }
286

    
287
        public void setBreakingsCause(Throwable cause) {
288
            if (this.breakingsCause == null) {
289
                this.breakingsCause = cause;
290
            }
291
            this.broken = true;
292
        }
293
    }
294

    
295
    /*
296
     * TODO:
297
     *
298
     * - Comprobar que solo se pueden a?adir reglas de validacion sobre un
299
     * EditableFeatureType. - Comprobar que solo se puede hacer un update con un
300
     * featureType al que se le han cambiado las reglas de validacion cuando
301
     * hasStrongChanges=false.
302
     */
303
    public DefaultFeatureStore() {
304
        this.state = new StateInformation();
305
        this.sampleFeatureCache = null;
306
    }
307

    
308
    @Override
309
    protected DataManager getDataManager() {
310
        return this.dataManager;
311
    }
312

    
313
    @Override
314
    public void intialize(DataManager dataManager,
315
            DataStoreParameters parameters) throws InitializeException {
316

    
317
        DynObjectManager dynManager = ToolsLocator.getDynObjectManager();
318

    
319
        this.metadata = (DelegatedDynObject) dynManager.createDynObject(
320
                FeatureStore.METADATA_DEFINITION_NAME,
321
                MetadataManager.METADATA_NAMESPACE
322
        );
323

    
324
        this.dataManager = (DefaultDataManager) dataManager;
325

    
326
        this.parameters = parameters;
327
        this.transforms = new DefaultFeatureStoreTransforms(this);
328
        try {
329
            indexes = new DefaultFeatureIndexes(this);
330
        } catch (DataException e) {
331
            throw new InitializeException(e);
332
        }
333

    
334
    }
335

    
336
    @Override
337
    public void setProvider(org.gvsig.fmap.dal.DataStoreProvider provider) {
338
        this.provider = (FeatureStoreProvider) provider;
339
        this.delegate((DynObject) provider);
340
        this.metadataChildren = new HashSet();
341
        this.metadataChildren.add(provider);
342
        if (!this.ignoreDALResource) {
343
            loadDALFile();
344

    
345
            // Habria que crear un metodo en el proveedor para que de prioidad
346
            // a los parametros frente a lo que se a leido en el fichero dal
347
            // DataStoreProvider arreglalo segun los parametros del usuario
348
            // fixFeatureTypeFromParameters
349
            this.provider.fixFeatureTypeFromParameters();
350
            try {
351
                if (defaultFeatureType != null) {
352
                    FeatureAttributeDescriptor attrGeom = defaultFeatureType.getDefaultGeometryAttribute();
353
                    if (attrGeom != null) {
354
                        DefaultFeatureAttributeDescriptor gattr = (DefaultFeatureAttributeDescriptor) attrGeom;
355
                        IProjection srs = (IProjection) this.getDynValue(METADATA_CRS);
356
                        if (srs != null && srs != gattr.getSRS()) {
357
                            gattr.setSRSForced(srs);
358
                        }
359
                    }
360
                }
361
            } catch (Throwable th) {
362
                LOGGER.warn("Can't patch DAL file", th);
363
            }
364
        }
365
    }
366

    
367
    @Override
368
    public DataStoreParameters getParameters() {
369
        if (this.parameters == null) {
370
            LOGGER.warn("Store parametes are null");
371
        }
372
        return parameters;
373
    }
374

    
375
    @Override
376
    public int getMode() {
377
        return this.mode;
378
    }
379

    
380
    @Override
381
    public DataManager getManager() {
382
        return this.dataManager;
383
    }
384

    
385
    @Override
386
    public UnmodifiableBasicMap<String, DataStore> getChildren() {
387
        UnmodifiableBasicMap<String, DataStore> children = this.provider.getChildren();
388
        if (children == null) {
389
            return UnmodifiableBasicMap.EMPTY_UNMODIFIABLEBASICMAP;
390
        }
391
        return children;
392
    }
393

    
394
    @Override
395
    public FeatureStoreProvider getProvider() {
396
        return this.provider;
397
    }
398

    
399
    public FeatureManager getFeatureManager() {
400
        return this.featureManager;
401
    }
402

    
403
    @Override
404
    public void setFeatureTypes(List types, FeatureType defaultType) {
405
        this.featureTypes = types;
406
        this.defaultFeatureType = defaultType;
407
    }
408

    
409
    public void open() throws OpenException {
410
        if (this.mode != MODE_QUERY) {
411
            // TODO: Se puede hacer un open estando en edicion ?
412
            try {
413
                throw new IllegalStateException();
414
            } catch (Exception ex) {
415
                LOGGER.warn("Opening a store in editing/append mode (" + this.getFullName() + ").", ex);
416
            }
417
        }
418
        if (this.notifyChange(DataStoreNotification.BEFORE_OPEN).isCanceled()) {
419
            return;
420
        }
421
        this.provider.open();
422
        this.notifyChange(DataStoreNotification.AFTER_OPEN);
423
    }
424

    
425
    @Override
426
    public void refresh() throws OpenException, InitializeException {
427
        if (this.mode != MODE_QUERY) {
428
            throw new IllegalStateException();
429
        }
430
        if (this.notifyChange(FeatureStoreNotification.BEFORE_REFRESH).isCanceled()) {
431
            return;
432
        }
433
        if (state.isBroken()) {
434
            this.load(state);
435
        } else {
436
            this.featureCount = null;
437
            this.provider.refresh();
438
        }
439
        this.notifyChange(FeatureStoreNotification.AFTER_REFRESH);
440
    }
441

    
442
    public void close() throws CloseException {
443
        if (this.mode != MODE_QUERY) {
444
            // TODO: Se puede hacer un close estando en edicion ?
445
            try {
446
                throw new IllegalStateException();
447
            } catch (Exception ex) {
448
                LOGGER.warn("Clossing a store in editing/append mode (" + this.getFullName() + ").", ex);
449
            }
450
        }
451
        if (this.notifyChange(DataStoreNotification.BEFORE_CLOSE).isCanceled()) {
452
            return;
453
        }
454
        this.featureCount = null;
455
        this.provider.close();
456
        this.notifyChange(DataStoreNotification.AFTER_CLOSE);
457
    }
458

    
459
    @Override
460
    protected void doDispose() throws BaseException {
461
        if (this.mode != MODE_QUERY) {
462
            // TODO: Se puede hacer un dispose estando en edicion ?
463
            try {
464
                throw new IllegalStateException();
465
            } catch (Exception ex) {
466
                LOGGER.warn("Dispossing a store in editing/append mode (" + this.getFullName() + ").", ex);
467
            }
468
        }
469
        if (this.notifyChange(DataStoreNotification.BEFORE_DISPOSE).isCanceled()) {
470
            return;
471
        }
472
        this.disposeIndexes();
473
        if (this.provider != null) {
474
            this.provider.dispose();
475
        }
476
        if (this.selection != null) {
477
            this.selection.dispose();
478
            this.selection = null;
479
        }
480
        this.commands = null;
481
        this.featureCount = null;
482
        if (this.locks != null) {
483
            // this.locks.dispose();
484
            this.locks = null;
485
        }
486

    
487
        if (this.featureTypeManager != null) {
488
            this.featureTypeManager.dispose();
489
            this.featureTypeManager = null;
490
        }
491

    
492
        this.featureManager = null;
493
        this.spatialManager = null;
494

    
495
        this.parameters = null;
496
        this.notifyChange(DataStoreNotification.AFTER_DISPOSE);
497
        if (delegateObservable != null) {
498
            this.delegateObservable.deleteObservers();
499
            this.delegateObservable = null;
500
        }
501
        DisposeUtils.disposeQuietly(this.resourcesStorage);
502
    }
503

    
504
    @Override
505
    public boolean allowWrite() {
506
        SimpleIdentityManager identityManager = ToolsLocator.getIdentityManager();
507
        if (!identityManager.getCurrentIdentity().isAuthorized(DataManager.WRITE_STORE_AUTHORIZATION, this.getParameters(), this.getName())) {
508
            return false;
509
        }
510
        return this.provider.allowWrite();
511
    }
512

    
513
    @Override
514
    public boolean canWriteGeometry(int geometryType) throws DataException {
515
        return this.provider.canWriteGeometry(geometryType, 0);
516
    }
517

    
518
    @Override
519
    public DataServerExplorer getExplorer() throws ReadException,
520
            ValidateDataParametersException {
521
        if (this.state.isBroken()) {
522
            try {
523
                return this.provider.getExplorer();
524
            } catch (Throwable th) {
525
                return null;
526
            }
527
        } else {
528
            return this.provider.getExplorer();
529
        }
530
    }
531

    
532
    /*
533
     * public Metadata getMetadata() throws MetadataNotFoundException {
534
     * // TODO:
535
     * // Si el provider devuelbe null habria que ver de construir aqui
536
     * // los metadatos basicos, como el Envelope y el SRS.
537
     *
538
     * // TODO: Estando en edicion el Envelope deberia de
539
     * // actualizarse usando el spatialManager
540
     * return this.provider.getMetadata();
541
     * }
542
     */
543
    @Override
544
    public Envelope getEnvelope() throws DataException {
545
        FeatureType featureType = this.getDefaultFeatureTypeQuietly();
546
        if( featureType!=null ) {
547
            Tags tags = featureType.getTags();
548
            if( tags.has(DAL_STORE_ENVELOPE) ) {
549
                String geom_s = tags.getString(DAL_STORE_ENVELOPE,"");
550
                if( StringUtils.isNotBlank(geom_s) ) {
551
                    Geometry geom = GeometryUtils.createFrom(geom_s);
552
                    if( geom!=null ) {
553
                        return geom.getEnvelope();
554
                    }
555
                }
556
            }
557
        }
558
        
559
        if (this.mode == MODE_FULLEDIT) {
560
            // Just in case another thread tries to write in the store
561
            synchronized (this) {
562
                return this.spatialManager.getEnvelope();
563
            }
564
        }
565
        if (hasDynValue(DataStore.METADATA_ENVELOPE)) {
566
            return (Envelope) getDynValue(DataStore.METADATA_ENVELOPE);
567
        }
568
        Envelope envelope = this.provider.getEnvelope();
569
        if (envelope != null) {
570
            return envelope;
571
        }
572
        FeatureAttributeDescriptor attrdesc = this.getDefaultFeatureType().getDefaultGeometryAttribute();
573
        if (attrdesc == null || !attrdesc.isComputed()) {
574
            return null;
575
        }
576
        final int index = attrdesc.getIndex();
577
        final MutableObject<Envelope> envelopeValue = new MutableObject<>();
578
        try {
579
            this.accept((Object obj) -> {
580
                Feature f = (Feature) obj;
581
                Geometry g = (Geometry) f.get(index);
582
                if (g == null) {
583
                    return;
584
                }
585
                if (envelopeValue.getValue() == null) {
586
                    envelopeValue.setValue(g.getEnvelope());
587
                } else {
588
                    envelopeValue.getValue().add(g);
589
                }
590
            });
591
        } catch (Throwable th) {
592
            LOGGER.warn("Can't calculate envelope", th);
593
            return null;
594
        }
595
        return envelopeValue.getValue();
596
    }
597

    
598
    /**
599
     * @throws org.gvsig.fmap.dal.exception.DataException
600
     * @deprecated use getDefaultFeatureType().getDefaultSRS()
601
     */
602
    @Override
603
    public IProjection getSRSDefaultGeometry() throws DataException {
604
        return this.getDefaultFeatureType().getDefaultSRS();
605
    }
606

    
607
    @Override
608
    public FeatureSelection createDefaultFeatureSelection()
609
            throws DataException {
610
        return new DefaultFeatureSelection(this);
611
    }
612

    
613
    @Override
614
    public FeatureProvider createDefaultFeatureProvider(FeatureType type)
615
            throws DataException {
616
        if (type.hasOID()) {
617
            return new DefaultFeatureProvider(type,
618
                    this.provider.createNewOID());
619
        }
620
        return new DefaultFeatureProvider(type);
621
    }
622

    
623
    @Override
624
    public void saveToState(PersistentState state) throws PersistenceException {
625
        /*if (this.mode != FeatureStore.MODE_QUERY) {
626
            throw new PersistenceException(new IllegalStateException(
627
                this.getName()));
628
        }*/
629
        state.set("dataStoreName", this.getName());
630
        state.set("parameters", this.parameters);
631
        state.set("selection", this.selection);
632
        state.set("transforms", this.transforms);
633
        // TODO locks persistence
634
        // state.set("locks", this.locks);
635
        // TODO indexes persistence
636
        // state.set("indexes", this.indexes);
637
        Map evaluatedAttr = new HashMap(1);
638
        Iterator iterType = featureTypes.iterator();
639
        Iterator iterAttr;
640
        FeatureType type;
641
        DefaultFeatureAttributeDescriptor attr;
642
        List attrs;
643
        while (iterType.hasNext()) {
644
            type = (FeatureType) iterType.next();
645
            attrs = new ArrayList();
646
            iterAttr = type.iterator();
647
            while (iterAttr.hasNext()) {
648
                attr = (DefaultFeatureAttributeDescriptor) iterAttr.next();
649
                if ((attr.getEvaluator() != null)
650
                        && (attr.getEvaluator() instanceof Persistent)) {
651
                    attrs.add(attr);
652
                }
653
            }
654
            if (!attrs.isEmpty()) {
655
                evaluatedAttr.put(type.getId(), attrs);
656
            }
657

    
658
        }
659

    
660
        if (evaluatedAttr.isEmpty()) {
661
            evaluatedAttr = null;
662
        }
663

    
664
        state.set("evaluatedAttributes", evaluatedAttr);
665
        state.set("defaultFeatureTypeId", defaultFeatureType.getId());
666

    
667
    }
668

    
669
    @Override
670
    public void loadFromState(final PersistentState persistentState)
671
            throws PersistenceException {
672
        if (this.provider != null) {
673
            throw new PersistenceStoreAlreadyLoadedException(this.getName());
674
        }
675
        if (this.getManager() == null) {
676
            this.dataManager = (DefaultDataManager) DALLocator.getDataManager();
677
        }
678
        state.clear();
679
        try {
680
            state.put("parameters", persistentState.get("parameters"));
681
        } catch (Throwable th) {
682
            state.setBreakingsCause(th);
683
        }
684
        try {
685
            state.put("selection", persistentState.get("selection"));
686
        } catch (Throwable th) {
687
            state.setBreakingsCause(th);
688
        }
689
        try {
690
            state.put("transforms", persistentState.get("transforms"));
691
        } catch (Throwable th) {
692
            state.setBreakingsCause(th);
693
        }
694
        try {
695
            state.put("evaluatedAttributes", persistentState.get("evaluatedAttributes"));
696
        } catch (Throwable th) {
697
            state.setBreakingsCause(th);
698
        }
699
        try {
700
            state.put("defaultFeatureTypeId", persistentState.getString("defaultFeatureTypeId"));
701
        } catch (Throwable th) {
702
            state.setBreakingsCause(th);
703
        }
704
        load(state);
705
        ((DefaultDataManager) this.getDataManager()).addObservers(this);
706
    }
707

    
708
    private void load(StateInformation state) {
709
        this.featureTypes = new ArrayList();
710
        this.defaultFeatureType = null;
711
        this.featureCount = null;
712

    
713
        DataStoreParameters params = (DataStoreParameters) state.get("parameters");
714
        try {
715
            intialize(dataManager, params);
716
        } catch (Throwable th) {
717
            state.setBreakingsCause(th);
718
        }
719

    
720
        try {
721
            DataStoreProvider prov = dataManager.createProvider(
722
                    getStoreProviderServices(),
723
                    params
724
            );
725
            setProvider(prov);
726
        } catch (Throwable th) {
727
            LOGGER.warn("Can't load store from state.", th);
728
            state.setBreakingsCause(th);
729
        }
730
        try {
731
            selection = (FeatureSelection) state.get("selection");
732
        } catch (Throwable th) {
733
            state.setBreakingsCause(th);
734
        }
735

    
736
        try {
737
            this.transforms = (DefaultFeatureStoreTransforms) state.get("transforms");
738
            this.transforms.setFeatureStore(this);
739
            for (FeatureStoreTransform transform : this.transforms) {
740
                try {
741
                    transform.setUp();
742
                } catch (Throwable th) {
743
                    state.setBreakingsCause(th);
744
                }
745
            }
746
        } catch (Throwable th) {
747
            state.setBreakingsCause(th);
748
        }
749

    
750
        try {
751
            Map evaluatedAttributes = (Map) state.get("evaluatedAttributes");
752
            if ((evaluatedAttributes != null) && !evaluatedAttributes.isEmpty()) {
753
                Iterator iterEntries = evaluatedAttributes.entrySet().iterator();
754
                while (iterEntries.hasNext()) {
755
                    Entry entry = (Entry) iterEntries.next();
756
                    List attrs = (List) entry.getValue();
757
                    if (attrs.isEmpty()) {
758
                        continue;
759
                    }
760
                    int fTypePos = -1;
761
                    DefaultFeatureType type = null;
762
                    for (int i = 0; i < featureTypes.size(); i++) {
763
                        type = (DefaultFeatureType) featureTypes.get(i);
764
                        if (type.getId().equals(entry.getKey())) {
765
                            fTypePos = i;
766
                            break;
767
                        }
768
                    }
769
                    if (type == null) {
770
                        throw new PersistenceCantFindFeatureTypeException(
771
                                getName(), (String) entry.getKey());
772
                    }
773
                    DefaultEditableFeatureType eType = (DefaultEditableFeatureType) type.getEditable();
774
                    Iterator<FeatureAttributeDescriptor> iterAttr = attrs.iterator();
775
                    while (iterAttr.hasNext()) {
776
                        FeatureAttributeDescriptor attr = iterAttr.next();
777
                        eType.addLike(attr);
778
                    }
779
                    featureTypes.set(fTypePos, eType.getNotEditableCopy());
780

    
781
                }
782

    
783
            }
784
        } catch (Throwable th) {
785
            state.setBreakingsCause(th);
786
        }
787

    
788
        try {
789
            String defaultFeatureTypeId = (String) state.get("defaultFeatureTypeId");
790
            FeatureType ftype;
791

    
792
            if (defaultFeatureType == null
793
                    || defaultFeatureType.getId() == null
794
                    || !defaultFeatureType.getId().equals(defaultFeatureTypeId)) {
795

    
796
                ftype = getFeatureType(defaultFeatureTypeId);
797
                if (ftype == null) {
798
                    /*
799
                             * Un error en el m?todo de PostgreSQL getName(), hace que
800
                             * el nombre del featureType sea valor retornado por el getProviderName()
801
                             * De momento se pone este parche para apa?arlo y poder mantener compatibilidad
802
                             * con proyectos antiguos (2.1 y 2.2)
803
                     */
804
                    ftype = getFeatureType(getName());
805
                    if (ftype == null) {
806
                        throw new RuntimeException("Can't locate feature type");
807
                    }
808
                }
809
                defaultFeatureType = ftype;
810
            }
811
        } catch (Throwable th) {
812
            state.setBreakingsCause(th);
813
        }
814

    
815
        LOGGER.debug("load() broken:{}, {}, {}.",
816
                new Object[]{state.isBroken(), this.getProviderName(), params}
817
        );
818
    }
819

    
820
    public DataStoreProviderServices getStoreProviderServices() {
821
        return this;
822
    }
823

    
824
    public static void selfRegister(List<Exception> exs) {
825
        registerPersistenceDefinition();
826
        try {
827
            registerMetadataDefinition();
828
        } catch (MetadataException e) {
829
            exs.add(e);
830
        }
831
        try {
832
            DynObjectManager dynObjectManager = ToolsLocator.getDynObjectManager();
833
            dynObjectManager.registerTag(
834
                    DAL_USE_LARGE_SELECTION,
835
                    "Indicates whether the store should use a memory-based or a disk-based selection."
836
            ).setType(DataTypes.BOOLEAN);
837
            dynObjectManager.registerTag(
838
                    DAL_STORE_ENVELOPE,
839
                    "If specified, this geometry will be used to calculate the store envelope."
840
            ).setType(DataTypes.STRING);
841
        } catch (Exception e) {
842
            exs.add(e);
843
        }
844
    }
845

    
846
    private static void registerPersistenceDefinition() {
847
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
848
        if (manager.getDefinition(PERSISTENCE_DEFINITION_NAME) == null) {
849
            DynStruct definition
850
                    = manager.addDefinition(DefaultFeatureStore.class,
851
                            PERSISTENCE_DEFINITION_NAME, PERSISTENCE_DEFINITION_NAME
852
                            + " Persistent definition", null, null);
853
            definition.addDynFieldString("dataStoreName").setMandatory(true)
854
                    .setPersistent(true);
855

    
856
            definition.addDynFieldObject("parameters")
857
                    .setClassOfValue(DynObject.class).setMandatory(true)
858
                    .setPersistent(true);
859

    
860
            definition.addDynFieldObject("selection")
861
                    .setClassOfValue(FeatureSelection.class).setMandatory(false)
862
                    .setPersistent(true);
863

    
864
            definition.addDynFieldObject("transforms")
865
                    .setClassOfValue(DefaultFeatureStoreTransforms.class)
866
                    .setMandatory(true).setPersistent(true);
867

    
868
            definition.addDynFieldMap("evaluatedAttributes")
869
                    .setClassOfItems(List.class) // List<DefaultFeatureAttributeDescriptor>
870
                    .setMandatory(false).setPersistent(true);
871

    
872
            definition.addDynFieldString("defaultFeatureTypeId")
873
                    .setMandatory(true).setPersistent(true);
874
        }
875
    }
876

    
877
    private static void registerMetadataDefinition() throws MetadataException {
878
        MetadataManager manager = MetadataLocator.getMetadataManager();
879
        if (manager.getDefinition(FeatureStore.METADATA_DEFINITION_NAME) == null) {
880
            DynStruct metadataDefinition
881
                    = manager.addDefinition(FeatureStore.METADATA_DEFINITION_NAME, null);
882
            metadataDefinition.extend(manager
883
                    .getDefinition(DataStore.METADATA_DEFINITION_NAME));
884
        }
885
    }
886

    
887
    //
888
    // ====================================================================
889
    // Gestion de la seleccion
890
    //
891
    @Override
892
    public void setSelection(DataSet selection) throws DataException {
893
        this.setSelection((FeatureSet) selection);
894
    }
895

    
896
    @Override
897
    public DataSet createSelection() throws DataException {
898
        return createFeatureSelection();
899
    }
900

    
901
    @Override
902
    public DataSet getSelection() throws DataException {
903
        return this.getFeatureSelection();
904
    }
905

    
906
    @Override
907
    public void setSelection(FeatureSet selection) throws DataException {
908
        setSelection(selection, true);
909
    }
910

    
911
    public void setSelection(FeatureSet selection, boolean undoable)
912
            throws DataException {
913
        if (selection == null) {
914
            if (undoable) {
915
                throw new SelectionNotAllowedException(getName());
916
            }
917

    
918
        } else {
919
            if (selection.equals(this.selection)) {
920
                return;
921
            }
922
            if (!selection.isFromStore(this)) {
923
                throw new SelectionNotAllowedException(getName());
924
            }
925
        }
926

    
927
        if (this.selection != null) {
928
            this.selection.deleteObserver(this);
929
        }
930
        if (selection == null) {
931
            if (this.selection != null) {
932
                this.selection.dispose();
933
            }
934
            this.selection = null;
935
            return;
936
        }
937
        if (selection instanceof FeatureSelection) {
938
            if (undoable && isEditing()) {
939
                commands.selectionSet(this, this.selection,
940
                        (FeatureSelection) selection);
941
            }
942
            if (this.selection != null) {
943
                this.selection.dispose();
944
            }
945
            this.selection = (FeatureSelection) selection;
946
        } else {
947
            if (undoable && isEditing()) {
948
                commands.startComplex("_selectionSet");
949
            }
950
            if (selection instanceof DefaultFeatureSelection) {
951
                DefaultFeatureSelection defSelection
952
                        = (DefaultFeatureSelection) selection;
953
                defSelection.deselectAll(undoable);
954
                defSelection.select(selection, undoable);
955
            } else {
956
                this.selection.deselectAll();
957
                this.selection.select(selection);
958
            }
959
            if (undoable && isEditing()) {
960
                commands.endComplex();
961
            }
962
        }
963
        this.selection.addObserver(this);
964

    
965
        this.notifyChange(DataStoreNotification.SELECTION_CHANGE);
966
    }
967

    
968
    @Override
969
    public FeatureSelection createFeatureSelection() throws DataException {
970
        FeatureType featureType = this.getDefaultFeatureTypeQuietly();
971
        if( featureType!=null ) {
972
            Tags tags = featureType.getTags();
973
            if( tags.has(DataManager.DAL_USE_LARGE_SELECTION) ) {
974
                boolean useLargeSelection = tags.getBoolean(DataManager.DAL_USE_LARGE_SELECTION,true);
975
                if( useLargeSelection ) {
976
                    return createLargeFeatureSelection();
977
                }
978
                return this.provider.createFeatureSelection();
979
            }
980
        }
981
        long maxSize = dataManager.getMaxSizeForSmallFeatureSelection();
982
        if (this.provider.getFeatureCount() > maxSize) {
983
            return createLargeFeatureSelection();
984
        }
985
        return this.provider.createFeatureSelection();
986
    }
987

    
988
    @Override
989
    public FeatureSelection createLargeFeatureSelection() throws DataException {
990
        return new LargeFeatureSelection(this);
991

    
992
    }
993

    
994
    @Override
995
    public FeatureSelection createMemoryFeatureSelection() throws DataException {
996
        return this.provider.createFeatureSelection();
997
    }
998

    
999
    @Override
1000
    public FeatureSelection getFeatureSelection() throws DataException {
1001
        if (selection == null) {
1002
            this.selection = createFeatureSelection();
1003
            this.selection.addObserver(this);
1004
        }
1005
        return selection;
1006
    }
1007

    
1008
    @Override
1009
    public boolean isFeatureSelectionEmpty() {
1010
        if( selection == null ) {
1011
            return true;
1012
        }
1013
        return selection.isEmpty();
1014
    }
1015
    
1016
    //
1017
    // ====================================================================
1018
    // Gestion de notificaciones
1019
    //
1020
    @Override
1021
    public FeatureStoreNotification notifyChange(FeatureStoreNotification storeNotification) {
1022
        if (delegateObservable != null) {
1023
            try {
1024
                delegateObservable.notifyObservers(storeNotification);
1025
            } catch (Throwable ex) {
1026
                LOGGER.warn("Problems notifying changes in the store '" + this.getName() + " (" + storeNotification.getType() + ").", ex);
1027
            }
1028
        }
1029
        return storeNotification;
1030
    }
1031

    
1032
    @Override
1033
    public FeatureStoreNotification notifyChange(String notification) {
1034
        return notifyChange(new DefaultFeatureStoreNotification(this, notification));
1035
    }
1036

    
1037
    public FeatureStoreNotification notifyChange(String notification, String editingSessionCode) {
1038
        return notifyChange(new DefaultFeatureStoreNotification(this, notification, editingSessionCode));
1039
    }
1040

    
1041
    public FeatureStoreNotification notifyChange(String notification, String editingSessionCode, int editMode) {
1042
        return notifyChange(new DefaultFeatureStoreNotification(this, notification, editingSessionCode, editMode));
1043
    }
1044

    
1045
    public FeatureStoreNotification notifyChange(String notification,
1046
            String editingSessionCode,
1047
            Iterator<FeatureReference> deleteds,
1048
            Iterator<EditableFeature> inserteds,
1049
            Iterator<EditableFeature> updateds,
1050
            Iterator<FeatureTypeChanged> featureTypesChanged,
1051
            boolean isSelectionCompromised) {
1052
        return notifyChange(new DefaultFeatureStoreNotification(this, notification, editingSessionCode,
1053
                deleteds, inserteds, updateds, featureTypesChanged, isSelectionCompromised));
1054
    }
1055

    
1056
    @Override
1057
    public FeatureStoreNotification notifyChange(String notification, FeatureProvider data) {
1058
        Feature f = null;
1059
        if (data != null) {
1060
            try {
1061
                f = createFeature(data);
1062
            } catch (Throwable ex) {
1063
                LOGGER.warn("Problems creating a feature to notifying changes in the store '" + this.getName() + " (" + notification + ").", ex);
1064
            }
1065
        }
1066
        return notifyChange(notification, f);
1067
    }
1068

    
1069
    public FeatureStoreNotification notifyChange(String notification, Feature feature) {
1070
        return notifyChange(new DefaultFeatureStoreNotification(this, notification,
1071
                feature));
1072
    }
1073

    
1074
    public FeatureStoreNotification notifyChange(String notification, Command command) {
1075
        return notifyChange(new DefaultFeatureStoreNotification(this, notification,
1076
                command));
1077
    }
1078

    
1079
    public FeatureStoreNotification notifyChange(String notification, EditableFeatureType type) {
1080
        return notifyChange(new DefaultFeatureStoreNotification(this, notification,
1081
                type));
1082
    }
1083

    
1084
    @Override
1085
    public FeatureStoreNotification notifyChange(String notification, Resource resource) {
1086
        return notifyChange(new DefaultFeatureStoreNotification(this,
1087
                DataStoreNotification.RESOURCE_CHANGED));
1088
    }
1089

    
1090
    //
1091
    // ====================================================================
1092
    // Gestion de bloqueos
1093
    //
1094
    @Override
1095
    public boolean isLocksSupported() {
1096
        return this.provider.isLocksSupported();
1097
    }
1098

    
1099
    @Override
1100
    public FeatureLocks getLocks() throws DataException {
1101
        if (!this.provider.isLocksSupported()) {
1102
            LOGGER.warn("Locks not supported");
1103
            return null;
1104
        }
1105
        if (locks == null) {
1106
            this.locks = this.provider.createFeatureLocks();
1107
        }
1108
        return locks;
1109
    }
1110

    
1111
    //
1112
    // ====================================================================
1113
    // Interface Observable
1114
    //
1115
    @Override
1116
    public void disableNotifications() {
1117
        this.delegateObservable.disableNotifications();
1118

    
1119
    }
1120

    
1121
    @Override
1122
    public void enableNotifications() {
1123
        this.delegateObservable.enableNotifications();
1124
    }
1125

    
1126
    @Override
1127
    public void beginComplexNotification() {
1128
        this.delegateObservable.beginComplexNotification();
1129

    
1130
    }
1131

    
1132
    @Override
1133
    public void endComplexNotification() {
1134
        this.delegateObservable.endComplexNotification();
1135

    
1136
    }
1137

    
1138
    @Override
1139
    public void addObserver(Observer observer) {
1140
        if (delegateObservable != null) {
1141
            this.delegateObservable.addObserver(observer);
1142
        }
1143
    }
1144

    
1145
    @Override
1146
    public void deleteObserver(Observer observer) {
1147
        if (delegateObservable != null) {
1148
            this.delegateObservable.deleteObserver(observer);
1149
        }
1150
    }
1151

    
1152
    @Override
1153
    public void deleteObservers() {
1154
        this.delegateObservable.deleteObservers();
1155

    
1156
    }
1157

    
1158
    //
1159
    // ====================================================================
1160
    // Interface Observer
1161
    //
1162
    // Usado para observar:
1163
    // - su seleccion
1164
    // - sus bloqueos
1165
    // - sus recursos
1166
    //
1167
    @Override
1168
    public void update(Observable observable, Object notification) {
1169
        if (observable instanceof FeatureSet) {
1170
            if (observable == this.selection) {
1171
                this.notifyChange(DataStoreNotification.SELECTION_CHANGE);
1172
            } else if (observable == this.locks) {
1173
                this.notifyChange(FeatureStoreNotification.LOCKS_CHANGE);
1174
            }
1175

    
1176
        } else if (observable instanceof FeatureStoreProvider) {
1177
            if (observable == this.provider) {
1178

    
1179
            }
1180
        } else if (observable instanceof FeatureReferenceSelection) {
1181
            if (notification instanceof String) {
1182
                this.notifyChange((String) notification);
1183
            }
1184
        }
1185
    }
1186

    
1187
    //
1188
    // ====================================================================
1189
    // Edicion
1190
    //
1191
    private void newVersionOfUpdate() {
1192
        this.versionOfUpdate++;
1193
    }
1194

    
1195
    private long currentVersionOfUpdate() {
1196
        return this.versionOfUpdate;
1197
    }
1198

    
1199
    private void checkInEditingMode() throws NeedEditingModeException {
1200
        if (mode != MODE_FULLEDIT) {
1201
            throw new NeedEditingModeException(this.getName());
1202
        }
1203
    }
1204

    
1205
    private void checkNotInAppendMode() throws IllegalStateException {
1206
        if (mode == MODE_APPEND) {
1207
            throw new IllegalStateException("Error: store "
1208
                    + this.getFullName() + " is in append mode");
1209
        }
1210
    }
1211

    
1212
    private void checkIsOwnFeature(Feature feature)
1213
            throws IllegalFeatureException {
1214
        if (((DefaultFeature) feature).getStore() != this) {
1215
            throw new IllegalFeatureException(this.getName());
1216
        }
1217
        // FIXME: fixFeatureType no vale para el checkIsOwnFeature
1218
        // fixFeatureType((DefaultFeatureType) feature.getType());
1219
    }
1220

    
1221
    private void exitEditingMode() {
1222
        if (commands != null) {
1223
            try {
1224
                commands.clear();
1225
            } catch (Exception ex) {
1226
                LOGGER.trace("Can't clear commands", ex);
1227
            }
1228
            commands = null;
1229
        }
1230

    
1231
        if (featureTypeManager != null) {
1232
            DisposeUtils.disposeQuietly(featureTypeManager);
1233
            featureTypeManager = null;
1234

    
1235
        }
1236

    
1237
        // TODO implementar un dispose para estos dos
1238
        featureManager = null;
1239
        spatialManager = null;
1240

    
1241
        featureCount = null;
1242

    
1243
        mode = MODE_QUERY;
1244
        hasStrongChanges = true; // Lo deja a true por si las moscas
1245
        hasInserts = true;
1246

    
1247
        this.editingSessionCode = null;
1248
    }
1249

    
1250
    @Override
1251
    synchronized public void edit() throws DataException {
1252
        edit(MODE_FULLEDIT);
1253
    }
1254

    
1255
    @Override
1256
    synchronized public void edit(int mode) throws DataException {
1257
        LOGGER.debug("Starting editing in mode: {}", mode);
1258
        String newSessionCode = this.createUniqueID();
1259
        try {
1260
            if (this.mode != MODE_QUERY) {
1261
                throw new AlreadyEditingException(this.getName());
1262
            }
1263
            if (!this.provider.supportsAppendMode()) {
1264
                mode = MODE_FULLEDIT;
1265
            }
1266
            switch (mode) {
1267
                case MODE_QUERY:
1268
                    throw new IllegalStateException(this.getName());
1269

    
1270
                case MODE_FULLEDIT:
1271
                    if (!this.transforms.isEmpty()) {
1272
                        throw new IllegalStateException(this.getName());
1273
                    }
1274
                    if (notifyChange(FeatureStoreNotification.BEFORE_STARTEDITING,
1275
                            newSessionCode, mode).isCanceled()) {
1276
                        return;
1277
                    }
1278
                    this.editingSessionCode = newSessionCode;
1279
                    invalidateIndexes();
1280
                    featureManager = new FeatureManager(this);
1281
                    featureTypeManager = new FeatureTypeManager(this);
1282
                    spatialManager = new SpatialManager(this, provider.getEnvelope());
1283

    
1284
                    commands = new DefaultFeatureCommandsStack(
1285
                            this, featureManager,
1286
                            spatialManager, featureTypeManager);
1287
                    this.mode = MODE_FULLEDIT;
1288
                    hasStrongChanges = false;
1289
                    hasInserts = false;
1290
                    notifyChange(FeatureStoreNotification.AFTER_STARTEDITING, newSessionCode, this.mode);
1291
                    break;
1292

    
1293
                case MODE_APPEND:
1294
                    if (!this.transforms.isEmpty()) {
1295
                        throw new IllegalStateException(this.getName());
1296
                    }
1297
                    if (notifyChange(FeatureStoreNotification.BEFORE_STARTEDITING,
1298
                            newSessionCode, mode).isCanceled()) {
1299
                        return;
1300
                    }
1301
                    this.editingSessionCode = newSessionCode;
1302
                    invalidateIndexes();
1303
                    this.provider.beginAppend();
1304
                    this.mode = MODE_APPEND;
1305
                    hasInserts = false;
1306
                    notifyChange(FeatureStoreNotification.AFTER_STARTEDITING,
1307
                            newSessionCode, this.mode);
1308
                    break;
1309
                case MODE_PASS_THROUGH:
1310
                    if (!this.provider.supportsPassThroughMode()) {
1311
                        throw new IllegalStateException(this.getName());
1312
                    }
1313
                    if (!this.transforms.isEmpty()) {
1314
                        throw new IllegalStateException(this.getName());
1315
                    }
1316
                    if (notifyChange(FeatureStoreNotification.BEFORE_STARTEDITING,
1317
                            newSessionCode, mode).isCanceled()) {
1318
                        return;
1319
                    }
1320
                    this.editingSessionCode = newSessionCode;
1321
                    invalidateIndexes();
1322
                    this.mode = MODE_PASS_THROUGH;
1323
                    hasInserts = false;
1324
                    notifyChange(FeatureStoreNotification.AFTER_STARTEDITING,
1325
                            newSessionCode, this.mode);
1326
                    break;
1327

    
1328
            }
1329
        } catch (Exception e) {
1330
            try {
1331
                if (this.mode != MODE_QUERY) {
1332
                    exitEditingMode();
1333
                }
1334
                notifyChange(FeatureStoreNotification.FAILED_STARTEDITING,
1335
                        newSessionCode, mode);
1336
            } catch (Throwable th) {
1337
                LOGGER.warn("Can't cleanup after error in start editing.", th);
1338
            }
1339
            throw new StoreEditException(e, this.getName());
1340
        }
1341
    }
1342

    
1343
    private void invalidateIndexes() {
1344
        setIndexesValidStatus(false);
1345
    }
1346

    
1347
    private void setIndexesValidStatus(boolean valid) {
1348
        FeatureIndexes theIndexes = getIndexes();
1349
        LOGGER.debug("Setting the store indexes to valid status {}: {}", (valid
1350
                ? Boolean.TRUE : Boolean.FALSE), theIndexes);
1351
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1352
            FeatureIndex index = (FeatureIndex) iterator.next();
1353
            if (index instanceof FeatureIndexProviderServices) {
1354
                FeatureIndexProviderServices indexServices
1355
                        = (FeatureIndexProviderServices) index;
1356
                indexServices.setValid(valid);
1357
            }
1358
        }
1359
    }
1360

    
1361
    private void updateIndexes() throws FeatureIndexException {
1362
        FeatureIndexes theIndexes = getIndexes();
1363
        LOGGER.debug("Refilling indexes: {}", theIndexes);
1364
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1365
            FeatureIndex index = (FeatureIndex) iterator.next();
1366
            if (index instanceof FeatureIndexProviderServices) {
1367
                FeatureIndexProviderServices indexServices
1368
                        = (FeatureIndexProviderServices) index;
1369
                indexServices.fill(true, null);
1370
            }
1371
        }
1372
    }
1373

    
1374
    private void waitForIndexes() {
1375
        FeatureIndexes theIndexes = getIndexes();
1376
        LOGGER.debug("Waiting for indexes to finish filling: {}", theIndexes);
1377
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1378
            FeatureIndex index = (FeatureIndex) iterator.next();
1379
            if (index instanceof FeatureIndexProviderServices) {
1380
                FeatureIndexProviderServices indexServices
1381
                        = (FeatureIndexProviderServices) index;
1382
                indexServices.waitForIndex();
1383
            }
1384
        }
1385
    }
1386

    
1387
    private void disposeIndexes() {
1388
        FeatureIndexes theIndexes = getIndexes();
1389
        LOGGER.debug("Disposing indexes: {}", theIndexes);
1390
        if (theIndexes == null) {
1391
            return;
1392
        }
1393
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1394
            FeatureIndex index = (FeatureIndex) iterator.next();
1395
            if (index instanceof FeatureIndexProviderServices) {
1396
                FeatureIndexProviderServices indexServices
1397
                        = (FeatureIndexProviderServices) index;
1398
                indexServices.dispose();
1399
            }
1400
        }
1401
    }
1402

    
1403
    @Override
1404
    public boolean isEditing() {
1405
        return mode == MODE_FULLEDIT;
1406
    }
1407

    
1408
    @Override
1409
    public boolean isAppending() {
1410
        return mode == MODE_APPEND;
1411
    }
1412

    
1413
    @Override
1414
    synchronized public void update(EditableFeatureType type)
1415
            throws DataException {
1416
        try {
1417
            if (type == null) {
1418
                throw new NullFeatureTypeException(getName());
1419
            }
1420

    
1421
            switch (this.mode) {
1422
                case MODE_QUERY:
1423
                    if (type.hasOnlyMetadataChanges(this.defaultFeatureType)) {
1424
                        if (notifyChange(FeatureStoreNotification.BEFORE_UPDATE_TYPE, type).isCanceled()) {
1425
                            return;
1426
                        }
1427
                        FeatureType theType = type.getNotEditableCopy();
1428
                        if (defaultFeatureType.getId().equals(theType.getId())) {
1429
                            defaultFeatureType = theType;
1430
                        }
1431
                        List newtypes = new ArrayList();
1432
                        for (FeatureType featureType : this.featureTypes) {
1433
                            if (featureType.getId().equals(theType.getId())) {
1434
                                newtypes.add(theType);
1435
                            } else {
1436
                                newtypes.add(featureType);
1437
                            }
1438
                        }
1439
                        this.featureTypes = newtypes;
1440
                        saveDALFile();
1441
                        notifyChange(FeatureStoreNotification.AFTER_UPDATE_TYPE, type);
1442
                    }
1443

    
1444
                    break;
1445
                case MODE_FULLEDIT:
1446
                    if (notifyChange(FeatureStoreNotification.BEFORE_UPDATE_TYPE, type).isCanceled()) {
1447
                        return;
1448
                    }
1449
                    newVersionOfUpdate();
1450

    
1451
                    FeatureType oldt = type.getSource().getCopy();
1452
                    FeatureType newt = type.getCopy();
1453
                    commands.update(newt, oldt);
1454
                    hasStrongChanges = ((DefaultEditableFeatureType) type).hasStrongChanges();
1455
                    notifyChange(FeatureStoreNotification.AFTER_UPDATE_TYPE, type);
1456
                    break;
1457
                case MODE_APPEND:
1458
                case MODE_PASS_THROUGH:
1459
                    throw new NeedEditingModeException(this.getName());
1460

    
1461
            }
1462
        } catch (Exception e) {
1463
            throw new StoreUpdateFeatureTypeException(e, this.getName());
1464
        }
1465
    }
1466

    
1467
    @Override
1468
    public void delete(Feature feature) throws DataException {
1469
        switch (this.mode) {
1470
            case MODE_PASS_THROUGH:
1471
                checkIsOwnFeature(feature);
1472
                if (notifyChange(FeatureStoreNotification.BEFORE_DELETE, feature).isCanceled()) {
1473
                    return;
1474
                }
1475
                this.provider.passThroughDelete((FeatureReferenceProviderServices) feature.getReference());
1476
                notifyChange(FeatureStoreNotification.AFTER_DELETE, feature);
1477
                break;
1478
            default:
1479
                this.commands.delete(feature);
1480
                break;
1481

    
1482
        }
1483
    }
1484

    
1485
    @Override
1486
    public void delete(String filter) {
1487
        if (StringUtils.isBlank(filter)) {
1488
            return;
1489
        }
1490
        this.delete(ExpressionUtils.createExpression(filter));
1491
    }
1492

    
1493
    @Override
1494
    public void delete(Expression filter) {
1495
        // TODO: Optimizar pasandolo directamente al proveedor si no estamos en edicion y lo soporta.
1496
        if (filter == null) {
1497
            return;
1498
        }
1499
        boolean pendingFinishEditing = false;
1500
        DisposableFeatureSetIterable features = null;
1501
        try {
1502
            switch (this.mode) {
1503
                case MODE_QUERY:
1504
                    pendingFinishEditing = true;
1505
                    this.edit();
1506
                    break;
1507
                case MODE_APPEND:
1508
                    throw new IllegalStateException("Delete not allowed in append mode.");
1509
                case MODE_FULLEDIT:
1510
                    break;
1511
                case MODE_PASS_THROUGH:
1512
//                    this.provider.passThroughDelete(filter);
1513
//                    return;
1514
                    break;
1515
                default:
1516
                    throw new IllegalStateException("Mode " + this.mode + " not supported.");
1517
            }
1518

    
1519
            FeatureSet fset = this.getFeatureSet(filter);
1520
            features = fset.iterable();
1521
            for (Feature f : features) {
1522
                fset.delete(f);
1523
            }
1524
        } catch (DataException ex) {
1525
            throw new DataRuntimeException(ex.getFormatString(), ex.getMessageKey(), ex.getCode()) {
1526
            };
1527
        } catch (Exception ex) {
1528
            throw new RuntimeException("Can't delete features (" + filter.getPhrase() + ").", ex);
1529
        } finally {
1530
            if (pendingFinishEditing) {
1531
                this.finishEditingQuietly();
1532
            }
1533
            DisposeUtils.disposeQuietly(features);
1534
        }
1535
    }
1536

    
1537
    synchronized public void doDelete(Feature feature) throws DataException {
1538
        if (feature == null) {
1539
            throw new IllegalArgumentException("feature argument can't be null.");
1540
        }
1541
        try {
1542
            checkInEditingMode();
1543
            checkIsOwnFeature(feature);
1544
            if (feature instanceof EditableFeature && !((EditableFeature)feature).isUpdatable()) {
1545
                //La feature no est? persistida en disco
1546
                throw new StoreDeleteEditableFeatureException(getName());
1547
            }
1548
            if (notifyChange(FeatureStoreNotification.BEFORE_DELETE, feature).isCanceled()) {
1549
                return;
1550
            }
1551

    
1552
            //Update the featureManager and the spatialManager
1553
            featureManager.delete(feature);
1554
            spatialManager.deleteFeature(feature);
1555

    
1556
            newVersionOfUpdate();
1557
            hasStrongChanges = true;
1558
            notifyChange(FeatureStoreNotification.AFTER_DELETE, feature);
1559
        } catch (Exception e) {
1560
            throw new StoreDeleteFeatureException(e, this.getName());
1561
        }
1562
    }
1563

    
1564
    @Override
1565
    public synchronized void insert(FeatureSet set) throws DataException {
1566
        switch (mode) {
1567
            case MODE_QUERY:
1568
                throw new NeedEditingModeException(this.getName());
1569

    
1570
            case MODE_APPEND:
1571
            case MODE_FULLEDIT:
1572
            case MODE_PASS_THROUGH:
1573
            try {
1574
                set.accept((Object obj) -> {
1575
                    EditableFeature ef = createNewFeature((Feature) obj);
1576
                    insert(ef);
1577
                });
1578
            } catch (BaseException ex) {
1579
                throw new StoreInsertFeatureException(ex, this.getName());
1580
            }
1581
            break;
1582
        }
1583
    }
1584

    
1585
    private static EditableFeature lastChangedFeature = null;
1586

    
1587
    @Override
1588
    public synchronized void insert(EditableFeature feature)
1589
            throws DataException {
1590
        LOGGER.debug("In editing mode {}, insert feature: {}", mode, feature);
1591
        try {
1592
            switch (mode) {
1593
                case MODE_QUERY:
1594
                    throw new NeedEditingModeException(this.getName());
1595

    
1596
                case MODE_APPEND:
1597
                    checkIsOwnFeature(feature);
1598
                    if (feature.isUpdatable()) {
1599
                        throw new NoNewFeatureInsertException(this.getName());
1600
                    }
1601
                    if (notifyChange(FeatureStoreNotification.BEFORE_INSERT, feature).isCanceled()) {
1602
                        return;
1603
                    }
1604
                    this.featureCount = null;
1605
                    feature.validate(CHECK_RULES_AT_EDITING);
1606
                    provider.append(((DefaultEditableFeature) feature).getData());
1607
                    hasStrongChanges = true;
1608
                    hasInserts = true;
1609
                    notifyChange(FeatureStoreNotification.AFTER_INSERT, feature);
1610
                    break;
1611

    
1612
                case MODE_FULLEDIT:
1613
                    if (feature.isUpdatable()) {
1614
                        throw new NoNewFeatureInsertException(this.getName());
1615
                    }
1616
                    feature.validate(CHECK_RULES_AT_EDITING);
1617
                    commands.insert(feature);
1618
                    break;
1619

    
1620
                case MODE_PASS_THROUGH:
1621
                    checkIsOwnFeature(feature);
1622
                    if (feature.isUpdatable()) {
1623
                        throw new NoNewFeatureInsertException(this.getName());
1624
                    }
1625
                    if (notifyChange(FeatureStoreNotification.BEFORE_INSERT, feature).isCanceled()) {
1626
                        return;
1627
                    }
1628
                    feature.validate(CHECK_RULES_AT_EDITING);
1629
                    this.provider.passThroughInsert(((DefaultEditableFeature) feature).getData());
1630
                    notifyChange(FeatureStoreNotification.AFTER_INSERT, feature);
1631
                    break;
1632
            }
1633
        } catch (Exception e) {
1634
            throw new StoreInsertFeatureException(e, this.getName());
1635
        }
1636
    }
1637

    
1638
    synchronized public void doInsert(EditableFeature feature)
1639
            throws DataException {
1640
        checkIsOwnFeature(feature);
1641

    
1642
        waitForIndexes();
1643

    
1644
        if (notifyChange(FeatureStoreNotification.BEFORE_INSERT, feature).isCanceled()) {
1645
            return;
1646
        }
1647
        newVersionOfUpdate();
1648
        if ((lastChangedFeature == null)
1649
                || (lastChangedFeature.getSource() != feature.getSource())) {
1650
            lastChangedFeature = feature;
1651
            feature.validate(CHECK_RULES_AT_EDITING);
1652
            lastChangedFeature = null;
1653
        }
1654
        //Update the featureManager and the spatialManager
1655
        ((DefaultFeature) feature).setInserted(true);
1656
        DefaultFeature newFeature = (DefaultFeature) feature.getNotEditableCopy();
1657

    
1658
        featureManager.add(feature);
1659
        spatialManager.insertFeature(newFeature);
1660

    
1661
        hasStrongChanges = true;
1662
        hasInserts = true;
1663
        notifyChange(FeatureStoreNotification.AFTER_INSERT, feature);
1664
    }
1665

    
1666
    @Override
1667
    public void update(EditableFeature feature)
1668
            throws DataException {
1669
        switch (this.mode) {
1670
            case MODE_PASS_THROUGH:
1671
                checkIsOwnFeature(feature);
1672
                if (!feature.isUpdatable()) {
1673
                    throw new NoNewFeatureInsertException(this.getName());
1674
                }
1675
                if (notifyChange(FeatureStoreNotification.BEFORE_UPDATE, feature).isCanceled()) {
1676
                    return;
1677
                }
1678
                feature.validate(CHECK_RULES_AT_EDITING);
1679
                this.provider.passThroughUpdate(((DefaultEditableFeature) feature).getData());
1680
                notifyChange(FeatureStoreNotification.AFTER_UPDATE, feature);
1681
                break;
1682
            case MODE_FULLEDIT:
1683
                if (feature.isUpdatable()) {
1684
                    commands.update(feature, feature.getSource());
1685
                    return;
1686
                }
1687
                // FIXME: Deberiamos lanzar aqui un error en lugar de hacer el insert.
1688
                //        O lanzar un mensaje al log?
1689
                insert(feature);
1690
                break;
1691
            default:
1692
                throw new NeedEditingModeException(this.getName());
1693
        }
1694
    }
1695

    
1696
    @Override
1697
    public void update(Object... parameters) throws DataException {
1698
        if (parameters.length == 1) {
1699
            Object param0 = parameters[0];
1700
            if (param0 instanceof EditableFeature) {
1701
                this.update((EditableFeature) param0);
1702
            } else if (param0 instanceof EditableFeatureType) {
1703
                this.update((EditableFeatureType) param0);
1704
            } else {
1705
                throw new IllegalArgumentException("Type of first parameter isn't supported");
1706
            }
1707
            return;
1708
        }
1709

    
1710
        Expression filter = null;
1711
        long end = parameters.length;
1712
        if (parameters.length % 2 == 1) { //IMPAR
1713
            Object param = parameters[parameters.length - 1];
1714
            if (param != null) {
1715
                if (param instanceof Expression) {
1716
                    filter = (Expression) param;
1717
                } else {
1718
                    filter = ExpressionUtils.createExpression(param.toString());
1719
                }
1720
            }
1721
        } else {
1722
            end = parameters.length - 1;
1723
        }
1724

    
1725
        switch (this.mode) {
1726
            case MODE_PASS_THROUGH:
1727
                this.provider.passThroughUpdate(
1728
                        //                    this.getName(), 
1729
                        parameters,
1730
                        filter);
1731
                break;
1732
            case MODE_FULLEDIT:
1733
                FeatureSet set = this.getFeatureSet(filter);
1734
                DisposableIterator it = set.fastIterator();
1735
                while (it.hasNext()) {
1736
                    Feature feature = (Feature) it.next();
1737
                    EditableFeature ef = feature.getEditable();
1738
                    for (int i = 0; i < end; i += 2) {
1739
                        String name = (String) parameters[i];
1740
                        Object value = parameters[i + 1];
1741
                        ef.set(name, value);
1742
                    }
1743
                    set.update(ef);
1744
                }
1745
                DisposeUtils.disposeQuietly(it);
1746
                DisposeUtils.disposeQuietly(set);
1747
                break;
1748
            default:
1749
                throw new NeedEditingModeException(this.getName());
1750
        }
1751
    }
1752

    
1753
    synchronized public void doUpdate(EditableFeature feature, Feature oldFeature)
1754
            throws DataException {
1755
        try {
1756
            checkInEditingMode();
1757
            checkIsOwnFeature(feature);
1758
            if (notifyChange(FeatureStoreNotification.BEFORE_UPDATE, feature).isCanceled()) {
1759
                return;
1760
            }
1761
            newVersionOfUpdate();
1762
            if ((lastChangedFeature == null)
1763
                    || (lastChangedFeature.getSource() != feature.getSource())) {
1764
                lastChangedFeature = feature;
1765
                feature.validate(CHECK_RULES_AT_EDITING);
1766
                lastChangedFeature = null;
1767
            }
1768

    
1769
            //Update the featureManager and the spatialManager
1770
            Feature newf = feature.getNotEditableCopy();
1771
            featureManager.update(feature, oldFeature);
1772
            spatialManager.updateFeature(newf, oldFeature);
1773

    
1774
            hasStrongChanges = true;
1775
            notifyChange(FeatureStoreNotification.AFTER_UPDATE, feature);
1776
        } catch (Exception e) {
1777
            throw new StoreUpdateFeatureException(e, this.getName());
1778
        }
1779
    }
1780

    
1781
    @Override
1782
    synchronized public void redo() throws RedoException {
1783
        Command redo = commands.getNextRedoCommand();
1784
        try {
1785
            checkInEditingMode();
1786
        } catch (NeedEditingModeException ex) {
1787
            throw new RedoException(redo, ex);
1788
        }
1789
        if (notifyChange(FeatureStoreNotification.BEFORE_REDO, redo).isCanceled()) {
1790
            return;
1791
        }
1792
        newVersionOfUpdate();
1793
        commands.redo();
1794
        hasStrongChanges = true;
1795
        notifyChange(FeatureStoreNotification.AFTER_REDO, redo);
1796
    }
1797

    
1798
    @Override
1799
    synchronized public void undo() throws UndoException {
1800
        Command undo = commands.getNextUndoCommand();
1801
        try {
1802
            checkInEditingMode();
1803
        } catch (NeedEditingModeException ex) {
1804
            throw new UndoException(undo, ex);
1805
        }
1806
        if (notifyChange(FeatureStoreNotification.BEFORE_UNDO, undo).isCanceled()) {
1807
            return;
1808
        }
1809
        newVersionOfUpdate();
1810
        commands.undo();
1811
        hasStrongChanges = true;
1812
        notifyChange(FeatureStoreNotification.AFTER_UNDO, undo);
1813
    }
1814

    
1815
    @Override
1816
    public List getRedoInfos() {
1817
        if (isEditing() && (commands != null)) {
1818
            return commands.getRedoInfos();
1819
        } else {
1820
            return null;
1821
        }
1822
    }
1823

    
1824
    @Override
1825
    public List getUndoInfos() {
1826
        if (isEditing() && (commands != null)) {
1827
            return commands.getUndoInfos();
1828
        } else {
1829
            return null;
1830
        }
1831
    }
1832

    
1833
    public synchronized FeatureCommandsStack getCommandsStack()
1834
            throws DataException {
1835
        checkInEditingMode();
1836
        return commands;
1837
    }
1838

    
1839
    @Override
1840
    public boolean cancelEditingQuietly() {
1841
        try {
1842
            this.cancelEditing();
1843
            return true;
1844
        } catch (Exception ex) {
1845
            LOGGER.debug("Can't cancel editing", ex);
1846
            return false;
1847
        }
1848
    }
1849

    
1850
    @Override
1851
    synchronized public void cancelEditing() throws DataException {
1852
        if (spatialManager != null) {
1853
            spatialManager.cancelModifies();
1854
        }
1855
        try {
1856
            switch (mode) {
1857
                case MODE_QUERY:
1858
                    throw new NeedEditingModeException(this.getName());
1859

    
1860
                case MODE_APPEND:
1861
                    if (notifyChange(FeatureStoreNotification.BEFORE_CANCELEDITING).isCanceled()) {
1862
                        return;
1863
                    }
1864
                    provider.abortAppend();
1865
                    exitEditingMode();
1866
                    ((FeatureSelection) this.getSelection()).deselectAll();
1867
                    updateIndexes();
1868
                    notifyChange(FeatureStoreNotification.AFTER_CANCELEDITING);
1869
                    break;
1870

    
1871
                case MODE_FULLEDIT:
1872
                    boolean clearSelection = this.hasStrongChanges;
1873
                    if (this.selection instanceof FeatureReferenceSelection) {
1874
                        clearSelection = this.hasInserts;
1875
                    }
1876
                    if (notifyChange(FeatureStoreNotification.BEFORE_CANCELEDITING).isCanceled()) {
1877
                        return;
1878
                    }
1879
                    exitEditingMode();
1880
                    if (clearSelection) {
1881
                        ((FeatureSelection) this.getSelection()).deselectAll();
1882
                    }
1883
                    updateIndexes();
1884
                    notifyChange(FeatureStoreNotification.AFTER_CANCELEDITING);
1885
                    break;
1886

    
1887
                case MODE_PASS_THROUGH:
1888
                    if (notifyChange(FeatureStoreNotification.BEFORE_CANCELEDITING).isCanceled()) {
1889
                        return;
1890
                    }
1891
                    exitEditingMode();
1892
                    ((FeatureSelection) this.getSelection()).deselectAll();
1893
                    updateIndexes();
1894
                    notifyChange(FeatureStoreNotification.AFTER_CANCELEDITING);
1895
                    break;
1896
            }
1897
        } catch (Exception e) {
1898
            throw new StoreCancelEditingException(e, this.getName());
1899
        }
1900
    }
1901

    
1902
    @Override
1903
    public boolean finishEditingQuietly() {
1904
        try {
1905
            this.finishEditing();
1906
            return true;
1907
        } catch (Exception ex) {
1908
            LOGGER.debug("Can't finish editing", ex);
1909
            return false;
1910
        }
1911
    }
1912

    
1913
    @Override
1914
    synchronized public void finishEditing() throws DataException {
1915
        LOGGER.debug("finish editing of mode: {}", mode);
1916
        try {
1917

    
1918
            /*
1919
             * Selection needs to be cleared when editing stops
1920
             * to prevent conflicts with selection remaining from
1921
             * editing mode.
1922
             */
1923
//            ((FeatureSelection) this.getSelection()).deselectAll();
1924
            Map<String, List<FeatureAttributeDescriptor>> computedFields = this.getComputedFields();
1925
            switch (mode) {
1926
                case MODE_QUERY:
1927
                    throw new NeedEditingModeException(this.getName());
1928

    
1929
                case MODE_APPEND:
1930
                    if (selection != null) {
1931
                        selection = null;
1932
                    }
1933
                    if (notifyChange(FeatureStoreNotification.BEFORE_FINISHEDITING, this.editingSessionCode).isCanceled()) {
1934
                        return;
1935
                    }
1936
                    saveDALFile();
1937
                    provider.endAppend();
1938
                    exitEditingMode();
1939
                    this.updateComputedFields(computedFields);
1940
                    loadDALFile();
1941
                    updateIndexes();
1942
                    notifyChange(FeatureStoreNotification.AFTER_FINISHEDITING, this.editingSessionCode);
1943
                    break;
1944

    
1945
                case MODE_FULLEDIT:
1946
                    if (featureManager.hasChanges() || featureTypeManager.hasChanges()) {
1947
                        if (hasStrongChanges && !this.allowWrite()) {
1948
                            throw new WriteNotAllowedException(getName());
1949
                        }
1950
                        if (notifyChange(FeatureStoreNotification.PREPARING_FINISHEDITING,
1951
                                this.editingSessionCode).isCanceled()) {
1952
                            return;
1953
                        }
1954
                        if (hasStrongChanges) {
1955
                            validateFeaturesAtFinishEditing();
1956
                        }
1957
                        if (notifyChange(FeatureStoreNotification.BEFORE_FINISHEDITING,
1958
                                this.editingSessionCode,
1959
                                featureManager.getDeleted(),
1960
                                featureManager.getInsertedFeatures(),
1961
                                featureManager.getUpdatedFeatures(),
1962
                                featureTypeManager.getFeatureTypesChanged().iterator(),
1963
                                featureManager.isSelectionCompromised()).isCanceled()) {
1964
                            return;
1965
                        }
1966
                        saveDALFile();
1967
                        if (featureManager.isSelectionCompromised() && selection != null) {
1968
                            selection = null;
1969
                        }
1970
                        if (hasStrongChanges) {
1971
                            /*
1972
                         * This will throw a PerformEditingExceptionif the provider
1973
                         * does not accept the changes (for example, an invalid field name)
1974
                             */
1975
                            provider.performChanges(featureManager.getDeleted(),
1976
                                    featureManager.getInserted(),
1977
                                    featureManager.getUpdated(),
1978
                                    removeCalculatedAttributes(featureTypeManager.getFeatureTypesChanged()).iterator());
1979

    
1980
                        }
1981
                        this.updateComputedFields(computedFields);
1982
                        exitEditingMode();
1983
                        loadDALFile();
1984
                        updateIndexes();
1985
                        notifyChange(FeatureStoreNotification.AFTER_FINISHEDITING, this.editingSessionCode);
1986
                    } else {
1987
                        exitEditingMode();
1988
                    }
1989
                    break;
1990
                case MODE_PASS_THROUGH:
1991
                    if (selection != null) {
1992
                        selection = null;
1993
                    }
1994
                    if (notifyChange(FeatureStoreNotification.BEFORE_FINISHEDITING, this.editingSessionCode).isCanceled()) {
1995
                        return;
1996
                    }
1997
                    exitEditingMode();
1998
                    updateIndexes();
1999
                    notifyChange(FeatureStoreNotification.AFTER_FINISHEDITING, this.editingSessionCode);
2000
                    break;
2001
            }
2002
        } catch (ValidateFeaturesException | WriteNotAllowedException ex) {
2003
            // Don't notify failed.
2004
            throw ex;
2005
        } catch (PerformEditingException pee) {
2006
            notifyChange(FeatureStoreNotification.FAILED_FINISHEDITING, this.editingSessionCode);
2007
            throw new WriteException(provider.getSourceId().toString(), pee);
2008
        } catch (Exception e) {
2009
            notifyChange(FeatureStoreNotification.FAILED_FINISHEDITING, this.editingSessionCode);
2010
            throw new FinishEditingException(e);
2011
        }
2012
    }
2013

    
2014
    @Override
2015
    public String getEditingSession() {
2016
        return this.editingSessionCode;
2017
    }
2018

    
2019
    private Map<String, List<FeatureAttributeDescriptor>> getComputedFields() throws DataException {
2020
        Map<String, List<FeatureAttributeDescriptor>> r = new HashMap<>();
2021

    
2022
        List<FeatureType> theTypes = new ArrayList<>();
2023
        theTypes.addAll(this.getFeatureTypes());
2024
        theTypes.add(this.getDefaultFeatureType());
2025
        for (int n = 0; n < theTypes.size(); n++) {
2026
            FeatureType type = theTypes.get(n);
2027
            for (FeatureAttributeDescriptor attrdesc : type) {
2028
                FeatureAttributeEmulator emulator = attrdesc.getFeatureAttributeEmulator();
2029
                if (emulator != null) {
2030
                    List<FeatureAttributeDescriptor> l = r.get(type.getId());
2031
                    if (l == null) {
2032
                        l = new ArrayList<>();
2033
                        r.put(type.getId(), l);
2034
                    }
2035
                    l.add(attrdesc);
2036
                }
2037
            }
2038
        }
2039
        return r;
2040
    }
2041

    
2042
    private void updateComputedFields(Map<String, List<FeatureAttributeDescriptor>> computedFields) throws DataException {
2043

    
2044
        List<FeatureType> theTypes = new ArrayList<>();
2045
        theTypes.addAll(this.getFeatureTypes());
2046
        theTypes.add(this.getDefaultFeatureType());
2047
        for (int n = 0; n < theTypes.size(); n++) {
2048
            DefaultFeatureType type = (DefaultFeatureType) theTypes.get(n);
2049
            List<FeatureAttributeDescriptor> x = computedFields.get(type.getId());
2050
            if (x != null && !x.isEmpty()) {
2051
                for (FeatureAttributeDescriptor attrdesc : x) {
2052
                    if (type.get(attrdesc.getName()) == null) {
2053
                        type.add(attrdesc);
2054
                    }
2055
                }
2056
            }
2057
        }
2058

    
2059
    }
2060

    
2061
    private List<FeatureTypeChanged> removeCalculatedAttributes(List<FeatureTypeChanged> ftypes) {
2062
        // FIXME: Falta por implementar
2063
//        for (FeatureStoreProvider.FeatureTypeChanged ftype : ftypes) {
2064
//            EditableFeatureType target = (EditableFeatureType) ftype.getTarget();
2065
//            for (FeatureAttributeDescriptor attributeDescriptor : ftype.getSource().getAttributeDescriptors()) {
2066
//                if (attributeDescriptor.isComputed()) {
2067
//                    target.remove(attributeDescriptor.getName());
2068
//                }
2069
//            }
2070
//        }
2071
        return ftypes;
2072
    }
2073

    
2074
    private void saveDALFile() {
2075
        if( this.ignoreDALResource ) {
2076
            return;
2077
        }
2078
        org.gvsig.tools.resourcesstorage.ResourcesStorage.Resource resource = null;
2079
        ResourcesStorage theResourcesStorage = null;
2080
        try {
2081
            theResourcesStorage = this.getResourcesStorage();
2082
            if (theResourcesStorage == null || theResourcesStorage.isReadOnly()) {
2083
                return;
2084
            }
2085
            resource = theResourcesStorage.getResource("dal");
2086
            if (resource == null || resource.isReadOnly()) {
2087
                return;
2088
            }
2089
            DALFile dalFile = DALFile.getDALFile();
2090
            dalFile.setStore(this);
2091
            if (!dalFile.isEmpty()) {
2092
                dalFile.write(resource);
2093
            }
2094
        } catch (Throwable ex) {
2095
            LOGGER.warn("Can't save DAL resource", ex);
2096
        } finally {
2097
            IOUtils.closeQuietly(resource);
2098
            DisposeUtils.disposeQuietly(theResourcesStorage);
2099
        }
2100
    }
2101

    
2102
    private void loadDALFile() {
2103
        if( this.ignoreDALResource ) {
2104
            return;
2105
        }
2106
        org.gvsig.tools.resourcesstorage.ResourcesStorage.Resource resource = null;
2107
        ResourcesStorage theResourcesStorage = null;
2108
        try {
2109
            theResourcesStorage = this.getResourcesStorage();
2110
            if (theResourcesStorage == null) {
2111
                return;
2112
            }
2113
            resource = theResourcesStorage.getResource("dal");
2114
            if (resource == null || !resource.exists()) {
2115
                return;
2116
            }
2117
            DALFile dalFile = DALFile.getDALFile(resource);
2118
            if (!dalFile.isEmpty()) {
2119
                dalFile.updateStore(this);
2120
            }
2121
        } catch (Throwable ex) {
2122
            if (resource == null || theResourcesStorage == null) {
2123
                if (theResourcesStorage == null) {
2124
                    LOGGER.warn("Can't load DAL resource (resname=null, resurl=null, storage=null)", ex);
2125
                } else {
2126
                    LOGGER.warn("Can't load DAL resource (resname=null, resurl=null, storage=" + theResourcesStorage.getClass().getName() + ").", ex);
2127
                }
2128
            } else {
2129
                LOGGER.warn("Can't load DAL resource (resname=" + resource.getName() + ", resurl=" + Objects.toString(resource.getURL()) + ", storage=" + theResourcesStorage.getClass().getName() + ").", ex);
2130
            }
2131
        } finally {
2132
            IOUtils.closeQuietly(resource);
2133
            DisposeUtils.disposeQuietly(theResourcesStorage);
2134
        }
2135
    }
2136

    
2137
    /**
2138
     * Save changes in the provider without leaving the edit mode. Do not call
2139
     * observers to communicate a change of ediding mode. The operation's
2140
     * history is eliminated to prevent inconsistencies in the data.
2141
     *
2142
     * @throws DataException
2143
     */
2144
    @Override
2145
    synchronized public void commitChanges() throws DataException {
2146
        LOGGER.debug("commitChanges of mode: {}", mode);
2147
        if (!canCommitChanges()) {
2148
            throw new WriteNotAllowedException(getName());
2149
        }
2150
        try {
2151
            switch (mode) {
2152
                case MODE_QUERY:
2153
                    throw new NeedEditingModeException(this.getName());
2154

    
2155
                case MODE_APPEND:
2156
                    this.provider.endAppend();
2157
                    exitEditingMode();
2158
                    invalidateIndexes();
2159
                    this.provider.beginAppend();
2160
                    hasInserts = false;
2161
                    break;
2162

    
2163
                case MODE_FULLEDIT:
2164
                    if (hasStrongChanges && !this.allowWrite()) {
2165
                        throw new WriteNotAllowedException(getName());
2166
                    }
2167
                    // FIXME: OOhh!!!! no se disparan eventos, VCSGis no se entera de estos cambios.
2168
                    if (hasStrongChanges) {
2169
                        validateFeaturesAtFinishEditing();
2170
                        provider.performChanges(featureManager.getDeleted(),
2171
                                featureManager.getInserted(),
2172
                                featureManager.getUpdated(),
2173
                                removeCalculatedAttributes(featureTypeManager.getFeatureTypesChanged()).iterator());
2174
                    }
2175
                    invalidateIndexes();
2176
                    featureManager = new FeatureManager(this);
2177
                    featureTypeManager = new FeatureTypeManager(this);
2178
                    spatialManager = new SpatialManager(this, provider.getEnvelope());
2179

    
2180
                    commands
2181
                            = new DefaultFeatureCommandsStack(this, featureManager,
2182
                                    spatialManager, featureTypeManager);
2183
                    featureCount = null;
2184
                    hasStrongChanges = false;
2185
                    hasInserts = false;
2186
                    break;
2187
            }
2188
        } catch (Exception e) {
2189
            throw new FinishEditingException(e);
2190
        }
2191
    }
2192

    
2193
    @Override
2194
    synchronized public boolean canCommitChanges() throws DataException {
2195
        if (!this.allowWrite()) {
2196
            return false;
2197
        }
2198
        switch (mode) {
2199
            default:
2200
            case MODE_QUERY:
2201
                return false;
2202

    
2203
            case MODE_APPEND:
2204
                return true;
2205

    
2206
            case MODE_FULLEDIT:
2207
                List types = this.getFeatureTypes();
2208
                for (int i = 0; i < types.size(); i++) {
2209
                    Object type = types.get(i);
2210
                    if (type instanceof DefaultEditableFeatureType) {
2211
                        if (((DefaultEditableFeatureType) type).hasStrongChanges()) {
2212
                            return false;
2213
                        }
2214
                    }
2215
                }
2216
                return true;
2217
        }
2218
    }
2219

    
2220
    @Override
2221
    public void beginEditingGroup(String description)
2222
            throws NeedEditingModeException {
2223
        checkInEditingMode();
2224
        commands.startComplex(description);
2225
    }
2226

    
2227
    @Override
2228
    public void endEditingGroup() throws NeedEditingModeException {
2229
        checkInEditingMode();
2230
        commands.endComplex();
2231
    }
2232

    
2233
    @Override
2234
    public boolean isAppendModeSupported() {
2235
        return this.provider.supportsAppendMode();
2236
    }
2237

    
2238
    @Override
2239
    public void export(DataServerExplorer explorer, String provider,
2240
            NewFeatureStoreParameters params, String name) throws DataException {
2241

    
2242
        if (this.getFeatureTypes().size() != 1) {
2243
            throw new NotYetImplemented(
2244
                    "export whith more than one type not yet implemented");
2245
        }
2246
        FeatureSelection featureSelection = (FeatureSelection) getSelection();
2247
        FeatureStore target = null;
2248
        FeatureSet features = null;
2249
        DisposableIterator iterator = null;
2250
        try {
2251
            FeatureType type = this.getDefaultFeatureType();
2252
            if ((params.getDefaultFeatureType() == null)
2253
                    || (params.getDefaultFeatureType().size() == 0)) {
2254
                params.setDefaultFeatureType(type.getEditable());
2255

    
2256
            }
2257
            explorer.add(provider, params, true);
2258
            DataManager manager = DALLocator.getDataManager();
2259

    
2260
            DataStoreParameters openParams = explorer.get(name); //OpenFeatureStoreParameters) manager.createStoreParameters(explorer.getProviderName());
2261
//            ToolsLocator.getDynObjectManager().copy(params, openParams);
2262

    
2263
            target = (FeatureStore) manager.openStore(provider, openParams);
2264
            FeatureType targetType = target.getDefaultFeatureType();
2265

    
2266
            target.edit(MODE_APPEND);
2267
            FeatureAttributeDescriptor[] pkattrs = type.getPrimaryKey();
2268
            if (featureSelection.getSize() > 0) {
2269
                features = this.getFeatureSelection();
2270
            } else {
2271
                if ((pkattrs != null) && (pkattrs.length > 0)) {
2272
                    FeatureQuery query = createFeatureQuery();
2273
                    for (FeatureAttributeDescriptor pkattr : pkattrs) {
2274
                        query.getOrder().add(pkattr.getName(), true);
2275
                    }
2276
                    features = this.getFeatureSet(query);
2277
                } else {
2278
                    features = this.getFeatureSet();
2279
                }
2280
            }
2281
            iterator = features.fastIterator();
2282
            while (iterator.hasNext()) {
2283
                DefaultFeature feature = (DefaultFeature) iterator.next();
2284
                target.insert(target.createNewFeature(targetType, feature));
2285
            }
2286
            target.finishEditing();
2287
            target.dispose();
2288
        } catch (Exception e) {
2289
            throw new DataExportException(e, params.toString());
2290
        } finally {
2291
            dispose(iterator);
2292
            dispose(features);
2293
            dispose(target);
2294
        }
2295
    }
2296

    
2297
    @Override
2298
    public void copyTo(final FeatureStore target) {
2299
        boolean finishEditingAtEnd = false;
2300
        try {
2301
            if (!target.isEditing() && !target.isAppending()) {
2302
                finishEditingAtEnd = true;
2303
                target.edit(MODE_APPEND);
2304
            }
2305
            this.accept((Object obj) -> {
2306
                Feature f_src = (Feature) obj;
2307
                EditableFeature f_dst = target.createNewFeature(f_src);
2308
                target.insert(f_dst);
2309
            });
2310
            if (finishEditingAtEnd) {
2311
                target.finishEditing();
2312
            }
2313

    
2314
        } catch (Exception ex) {
2315
            try {
2316
                if (finishEditingAtEnd) {
2317
                    target.cancelEditing();
2318
                }
2319
            } catch (Exception ex1) {
2320
            }
2321
            throw new RuntimeException("Can't copy store.", ex);
2322
        }
2323

    
2324
    }
2325

    
2326
    //
2327
    // ====================================================================
2328
    // Obtencion de datos
2329
    // getDataCollection, getFeatureCollection
2330
    //
2331
    @Override
2332
    public DataSet getDataSet() throws DataException {
2333
        checkNotInAppendMode();
2334
        FeatureQuery query
2335
                = new DefaultFeatureQuery(this.getDefaultFeatureType());
2336
        return new DefaultFeatureSet(this, query);
2337
    }
2338

    
2339
    @Override
2340
    public DataSet getDataSet(DataQuery dataQuery) throws DataException {
2341
        checkNotInAppendMode();
2342
        return new DefaultFeatureSet(this, (FeatureQuery) dataQuery);
2343
    }
2344

    
2345
    @Override
2346
    public void getDataSet(Observer observer) throws DataException {
2347
        checkNotInAppendMode();
2348
        this.getFeatureSet(null, observer);
2349
    }
2350

    
2351
    @Override
2352
    public void getDataSet(DataQuery dataQuery, Observer observer)
2353
            throws DataException {
2354
        checkNotInAppendMode();
2355
        this.getFeatureSet((FeatureQuery) dataQuery, observer);
2356
    }
2357

    
2358
    @Override
2359
    public FeatureSet getFeatureSet() throws DataException {
2360
        return this.getFeatureSet((FeatureQuery) null);
2361
    }
2362

    
2363
    @Override
2364
    public FeatureSet getFeatureSet(FeatureQuery featureQuery)
2365
            throws DataException {
2366
        checkNotInAppendMode();
2367
        if (featureQuery == null) {
2368
            featureQuery = new DefaultFeatureQuery(this.getDefaultFeatureType());
2369
        }
2370
        return new DefaultFeatureSet(this, featureQuery);
2371
    }
2372

    
2373
    @Override
2374
    public FeatureSet getFeatureSet(String filter) throws DataException {
2375
        return this.getFeatureSet(filter, null, true);
2376
    }
2377

    
2378
    @Override
2379
    public FeatureSet getFeatureSet(String filter, String sortBy) throws DataException {
2380
        return this.getFeatureSet(filter, sortBy, true);
2381
    }
2382

    
2383
    @Override
2384
    public FeatureSet getFeatureSet(Expression filter) throws DataException {
2385
        return this.getFeatureSet(filter, null, true);
2386
    }
2387

    
2388
    @Override
2389
    public FeatureSet getFeatureSet(Expression filter, String sortBy) throws DataException {
2390
        return this.getFeatureSet(filter, sortBy, true);
2391
    }
2392

    
2393
    @Override
2394
    public FeatureSet getFeatureSet(Expression filter, String sortBy, boolean asc) throws DataException {
2395
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2396
        return this.getFeatureSet(query);
2397
    }
2398

    
2399
    @Override
2400
    public FeatureSet getFeatureSet(String filter, String sortBy, boolean asc) throws DataException {
2401
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2402
        return this.getFeatureSet(query);
2403
    }
2404

    
2405
    @Override
2406
    public List<Feature> getFeatures(String filter) {
2407
        return this.getFeatures(filter, null, true);
2408
    }
2409

    
2410
    @Override
2411
    public List<Feature> getFeatures(String filter, String sortBy) {
2412
        return this.getFeatures(filter, sortBy, true);
2413
    }
2414

    
2415
    @Override
2416
    public List<Feature> getFeatures(String filter, String sortBy, boolean asc) {
2417
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2418
        return this.getFeatures(query, 0);
2419
    }
2420

    
2421
    @Override
2422
    public List<Feature> getFeatures(Expression filter) {
2423
        return this.getFeatures(filter, null, true);
2424
    }
2425

    
2426
    @Override
2427
    public List<Feature> getFeatures(Expression filter, String sortBy) {
2428
        return this.getFeatures(filter, sortBy, true);
2429
    }
2430

    
2431
    @Override
2432
    public List<Feature> getFeatures(Expression filter, String sortBy, boolean asc) {
2433
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2434
        return this.getFeatures(query, 0);
2435
    }
2436

    
2437
    @Override
2438
    public List<Feature> getFeatures(FeatureQuery query) {
2439
        return this.getFeatures(query, 0);
2440
    }
2441

    
2442
    @Override
2443
    public List<Feature> getFeatures(FeatureQuery query, int pageSize) {
2444
        try {
2445
            if (pageSize <= 0) {
2446
                pageSize = 100;
2447
            }
2448
            FeaturePagingHelper pager = this.dataManager.createFeaturePagingHelper(this, query, pageSize);
2449
            return pager.asList();
2450
        } catch (BaseException ex) {
2451
            throw new RuntimeException("Can't create the list of features.", ex);
2452
        }
2453
    }
2454
        
2455
    @Override
2456
    public List<Feature> getFeatures() {
2457
        return this.getFeatures(null, 0);
2458
    }
2459

    
2460
    @Override
2461
    public GetItemWithSizeIsEmptyAndIterator64<Feature> getFeatures64() {
2462
        return this.getFeatures64(null, 0);
2463
    }
2464

    
2465
    @Override
2466
    public GetItemWithSizeIsEmptyAndIterator64<Feature> getFeatures64(String filter) {
2467
        return this.getFeatures64(filter, null, true);
2468
    }
2469

    
2470
    @Override
2471
    public GetItemWithSizeIsEmptyAndIterator64<Feature> getFeatures64(String filter, String sortBy, boolean asc) {
2472
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2473
        return this.getFeatures64(query, 0);
2474
    }
2475

    
2476
    @Override
2477
    public GetItemWithSizeIsEmptyAndIterator64<Feature> getFeatures64(FeatureQuery query, int pageSize) {
2478
        try {
2479
            if (pageSize <= 0) {
2480
                pageSize = 100;
2481
            }
2482
            FeaturePagingHelper pager = this.dataManager.createFeaturePagingHelper(this, query, pageSize);
2483
            return pager;
2484
        } catch (BaseException ex) {
2485
            throw new RuntimeException("Can't create the list of features.", ex);
2486
        }
2487
    }
2488

    
2489
    @Override
2490
    public Feature first() throws DataException {
2491
        return this.findFirst((FeatureQuery) null);
2492
    }
2493

    
2494
    @Override
2495
    public Feature findFirst(String filter) throws DataException {
2496
        return this.findFirst(filter, (String) null, true);
2497
    }
2498

    
2499
    @Override
2500
    public Feature findFirst(String filter, String sortBy) throws DataException {
2501
        return this.findFirst(filter, sortBy, true);
2502
    }
2503

    
2504
    @Override
2505
    public Feature findFirst(String filter, String sortBy, boolean asc) throws DataException {
2506
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2507
        return findFirst(query);
2508
    }
2509

    
2510
    @Override
2511
    public Feature findFirst(String filter, Expression sortBy, boolean asc) throws DataException {
2512
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2513
        return findFirst(query);
2514
    }
2515

    
2516
    @Override
2517
    public Feature findFirst(Expression filter) throws DataException {
2518
        return this.findFirst(filter, (String) null, true);
2519
    }
2520

    
2521
    @Override
2522
    public Feature findFirst(Expression filter, String sortBy) throws DataException {
2523
        return this.findFirst(filter, sortBy, true);
2524
    }
2525

    
2526
    @Override
2527
    public Feature findFirst(Expression filter, String sortBy, boolean asc) throws DataException {
2528
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2529
        return findFirst(query);
2530
    }
2531

    
2532
    @Override
2533
    public Feature findFirst(Expression filter, Expression sortBy, boolean asc) throws DataException {
2534
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2535
        return findFirst(query);
2536
    }
2537

    
2538
    @Override
2539
    public Feature findFirst(FeatureQuery query) throws DataException {
2540
        if (query == null) {
2541
            query = this.createFeatureQuery();
2542
        } else {
2543
            query = query.getCopy();
2544
        }
2545
        query.setLimit(1);
2546
        final MutableObject<Feature> feature = new MutableObject<>();
2547
        try {
2548
            this.accept((Object obj) -> {
2549
                feature.setValue((Feature) obj);
2550
                throw new VisitCanceledException();
2551
            }, query);
2552
        } catch (VisitCanceledException ex) {
2553

    
2554
        } catch (DataException ex) {
2555
            throw ex;
2556
        } catch (Exception ex) {
2557
            throw new RuntimeException("", ex);
2558
        }
2559
        return feature.getValue();
2560
    }
2561

    
2562
    @Override
2563
    public void accept(Visitor visitor) throws BaseException {
2564
        this.accept(visitor, null);
2565
    }
2566

    
2567
    @Override
2568
    public void accept(Visitor visitor, DataQuery dataQuery)
2569
            throws BaseException {
2570
        FeatureSet set = getFeatureSet((FeatureQuery) dataQuery);
2571
        try {
2572
            set.accept(visitor);
2573
        } finally {
2574
            set.dispose();
2575
        }
2576
    }
2577

    
2578
    public FeatureType getFeatureType(FeatureQuery featureQuery)
2579
            throws DataException {
2580
        DefaultFeatureType fType
2581
                = (DefaultFeatureType) this.getFeatureType(featureQuery
2582
                        .getFeatureTypeId());
2583
        if (featureQuery.hasAttributeNames()
2584
                || featureQuery.hasConstantsAttributeNames()
2585
                || fType.hasRequiredFields()) {
2586
            if (featureQuery.hasGroupByColumns()) {
2587
                return fType.getSubtype(featureQuery.getAttributeNames(), featureQuery.getConstantsAttributeNames(), false);
2588
            } else {
2589
                return fType.getSubtype(featureQuery.getAttributeNames(), featureQuery.getConstantsAttributeNames());
2590
            }
2591
        }
2592
        return fType;
2593
    }
2594

    
2595
    @Override
2596
    public void getFeatureSet(Observer observer) throws DataException {
2597
        checkNotInAppendMode();
2598
        this.getFeatureSet(null, observer);
2599
    }
2600

    
2601
    @Override
2602
    public void getFeatureSet(FeatureQuery query, Observer observer)
2603
            throws DataException {
2604
        class LoadInBackGround implements Runnable {
2605

    
2606
            private final FeatureStore store;
2607
            private final FeatureQuery query;
2608
            private final Observer observer;
2609

    
2610
            public LoadInBackGround(FeatureStore store, FeatureQuery query,
2611
                    Observer observer) {
2612
                this.store = store;
2613
                this.query = query;
2614
                this.observer = observer;
2615
            }
2616

    
2617
            void notify(FeatureStoreNotification theNotification) {
2618
                observer.update(store, theNotification);
2619
            }
2620

    
2621
            @Override
2622
            public void run() {
2623
                FeatureSet set = null;
2624
                try {
2625
                    set = store.getFeatureSet(query);
2626
                    notify(new DefaultFeatureStoreNotification(store,
2627
                            FeatureStoreNotification.LOAD_FINISHED, set));
2628
                } catch (Exception e) {
2629
                    notify(new DefaultFeatureStoreNotification(store,
2630
                            FeatureStoreNotification.LOAD_FINISHED, e));
2631
                } finally {
2632
                    dispose(set);
2633
                }
2634
            }
2635
        }
2636

    
2637
        checkNotInAppendMode();
2638
        if (query == null) {
2639
            query = new DefaultFeatureQuery(this.getDefaultFeatureType());
2640
        }
2641
        LoadInBackGround task = new LoadInBackGround(this, query, observer);
2642
        Thread thread = new Thread(task, "Load Feature Set in background");
2643
        thread.start();
2644
    }
2645

    
2646
    @Override
2647
    public Feature getFeatureByReference(FeatureReference reference)
2648
            throws DataException {
2649
        checkNotInAppendMode();
2650
        FeatureReferenceProviderServices ref = (FeatureReferenceProviderServices) reference;
2651
        FeatureType featureType;
2652
        if (ref.getFeatureTypeId() == null) {
2653
            featureType = this.getDefaultFeatureType();
2654
        } else {
2655
            featureType = this.getFeatureType(ref.getFeatureTypeId());
2656
        }
2657
        return this.getFeatureByReference(reference, featureType);
2658
    }
2659

    
2660
    @Override
2661
    public Feature getFeatureByReference(FeatureReference reference,
2662
            FeatureType featureType) throws DataException {
2663
        checkNotInAppendMode();
2664
        featureType = fixFeatureType((DefaultFeatureType) featureType);
2665
        if (this.mode == MODE_FULLEDIT) {
2666
            Feature f = featureManager.get(reference, this, featureType);
2667
            if (f != null) {
2668
                return f;
2669
            }
2670
        }
2671

    
2672
        FeatureType sourceFeatureType = featureType;
2673
        if (!this.transforms.isEmpty()) {
2674
            sourceFeatureType = this.transforms.getSourceFeatureTypeFrom(featureType);
2675
        }
2676
        // TODO comprobar que el id es de este store
2677

    
2678
        DefaultFeature feature
2679
                = new DefaultFeature(this,
2680
                        this.provider.getFeatureProviderByReference(
2681
                                (FeatureReferenceProviderServices) reference, sourceFeatureType));
2682

    
2683
        if (!this.transforms.isEmpty()) {
2684
            return this.transforms.applyTransform(feature, featureType);
2685
        }
2686
        return feature;
2687
    }
2688

    
2689
    //
2690
    // ====================================================================
2691
    // Gestion de features
2692
    //
2693
    private FeatureType fixFeatureType(DefaultFeatureType type)
2694
            throws DataException {
2695
        FeatureType original = this.getDefaultFeatureType();
2696

    
2697
        if ((type == null) || type.equals(original)) {
2698
            return original;
2699
        } else {
2700
            if (!type.isSubtypeOf(original)) {
2701
                Iterator iter = this.getFeatureTypes().iterator();
2702
                FeatureType tmpType;
2703
                boolean found = false;
2704
                while (iter.hasNext()) {
2705
                    tmpType = (FeatureType) iter.next();
2706
                    if (type.equals(tmpType)) {
2707
                        return type;
2708

    
2709
                    } else if (type.isSubtypeOf(tmpType)) {
2710
                        found = true;
2711
                        original = tmpType;
2712
                        break;
2713
                    }
2714

    
2715
                }
2716
                if (!found) {
2717
                    throw new IllegalFeatureTypeException(getName());
2718
                }
2719
            }
2720
        }
2721

    
2722
        // Checks that type has all fields of pk
2723
        // else add the missing attributes at the end.
2724
        if (!original.hasOID()) {
2725
            // Gets original pk attributes
2726
            DefaultEditableFeatureType edOriginal
2727
                    = (DefaultEditableFeatureType) original.getEditable();
2728
            FeatureAttributeDescriptor orgAttr;
2729
            Iterator edOriginalIter = edOriginal.iterator();
2730
            while (edOriginalIter.hasNext()) {
2731
                orgAttr = (FeatureAttributeDescriptor) edOriginalIter.next();
2732
                if (!orgAttr.isPrimaryKey()) {
2733
                    edOriginalIter.remove();
2734
                }
2735
            }
2736

    
2737
            // Checks if all pk attributes are in type
2738
            Iterator typeIterator;
2739
            edOriginalIter = edOriginal.iterator();
2740
            FeatureAttributeDescriptor attr;
2741
            while (edOriginalIter.hasNext()) {
2742
                orgAttr = (FeatureAttributeDescriptor) edOriginalIter.next();
2743
                typeIterator = type.iterator();
2744
                while (typeIterator.hasNext()) {
2745
                    attr = (FeatureAttributeDescriptor) typeIterator.next();
2746
                    if (attr.getName().equals(orgAttr.getName())) {
2747
                        edOriginalIter.remove();
2748
                        break;
2749
                    }
2750
                }
2751
            }
2752

    
2753
            // add missing pk attributes if any
2754
            if (edOriginal.size() > 0) {
2755
                boolean isEditable = type instanceof DefaultEditableFeatureType;
2756
                DefaultEditableFeatureType edType
2757
                        = (DefaultEditableFeatureType) original.getEditable();
2758
                edType.clear();
2759
                edType.addAll(type);
2760
                edType.addAll(edOriginal);
2761
                if (!isEditable) {
2762
                    type = (DefaultFeatureType) edType.getNotEditableCopy();
2763
                }
2764
            }
2765

    
2766
        }
2767

    
2768
        return type;
2769
    }
2770

    
2771
    private void validateFeaturesAtFinishEditing() throws ValidateFeaturesException {
2772
        try {
2773
            checkInEditingMode();
2774
            FeatureType type = this.getDefaultFeatureTypeQuietly();
2775
            DefaultFeatureRules rules = (DefaultFeatureRules) this.getDefaultFeatureType().getRules();
2776

    
2777
            int checks = type.isCheckFeaturesAtFinishEditing() ? CHECK_REQUIREDS : 0;
2778
            checks = rules.isEmpty() ? checks : checks | CHECK_RULES_AT_FINISH;
2779
            if (checks == 0) {
2780
                return;
2781
            }
2782

    
2783
            Iterator<EditableFeature> features = new ChainedIterator<>(
2784
                    featureManager.getInsertedFeatures(),
2785
                    featureManager.getUpdatedFeatures()
2786
            );
2787
            while (features.hasNext()) {
2788
                EditableFeature feature = features.next();
2789
                rules.validate(feature, checks);
2790
            }
2791
        } catch (Exception ex) {
2792
            throw new ValidateFeaturesException(this.getName(), ex);
2793
        }
2794
    }
2795

    
2796
    @Override
2797
    public FeatureType getDefaultFeatureType() throws DataException {
2798
        try {
2799

    
2800
            if (isEditing()) {
2801
                FeatureType auxFeatureType
2802
                        = featureTypeManager.getType(defaultFeatureType.getId());
2803
                if (auxFeatureType != null) {
2804
                    return avoidEditable(auxFeatureType);
2805
                }
2806
            }
2807
            FeatureType type = this.transforms.getDefaultFeatureType();
2808
            if (type != null) {
2809
                return avoidEditable(type);
2810
            }
2811

    
2812
            return avoidEditable(defaultFeatureType);
2813

    
2814
        } catch (Exception e) {
2815
            throw new GetFeatureTypeException(e, getName());
2816
        }
2817
    }
2818

    
2819
    @Override
2820
    public FeatureType getDefaultFeatureTypeQuietly() {
2821
        try {
2822
            return this.getDefaultFeatureType();
2823
        } catch (Exception ex) {
2824
            return null;
2825
        }
2826
    }
2827

    
2828
    private FeatureType avoidEditable(FeatureType ft) {
2829
        if (ft instanceof EditableFeatureType) {
2830
            return ((EditableFeatureType) ft).getNotEditableCopy();
2831
        } else {
2832
            return ft;
2833
        }
2834
    }
2835

    
2836
    @Override
2837
    public FeatureType getFeatureType(String featureTypeId)
2838
            throws DataException {
2839
        if (featureTypeId == null) {
2840
            return this.getDefaultFeatureType();
2841
        }
2842
        try {
2843
            if (isEditing()) {
2844
                FeatureType auxFeatureType
2845
                        = featureTypeManager.getType(featureTypeId);
2846
                if (auxFeatureType != null) {
2847
                    return auxFeatureType;
2848
                }
2849
            }
2850
            FeatureType type = this.transforms.getFeatureType(featureTypeId);
2851
            if (type != null) {
2852
                return type;
2853
            }
2854
            Iterator iter = this.featureTypes.iterator();
2855
            while (iter.hasNext()) {
2856
                type = (FeatureType) iter.next();
2857
                if (type.getId().equals(featureTypeId)) {
2858
                    return type;
2859
                }
2860
            }
2861
            return null;
2862
        } catch (Exception e) {
2863
            throw new GetFeatureTypeException(e, getName());
2864
        }
2865
    }
2866

    
2867
    public FeatureType getProviderDefaultFeatureType() {
2868
        return defaultFeatureType;
2869
    }
2870

    
2871
    @Override
2872
    public List getFeatureTypes() throws DataException {
2873
        try {
2874
            List types;
2875
            if (isEditing()) {
2876
                types = new ArrayList();
2877
                for (FeatureType type : featureTypes) {
2878
                    FeatureType typeaux = featureTypeManager.getType(type.getId());
2879
                    if (typeaux != null) {
2880
                        types.add(typeaux);
2881
                    } else {
2882
                        types.add(type);
2883
                    }
2884
                }
2885
                Iterator it = featureTypeManager.newsIterator();
2886
                while (it.hasNext()) {
2887
                    FeatureType type = (FeatureType) it.next();
2888
                    types.add(type);
2889
                }
2890
            } else {
2891
                types = this.transforms.getFeatureTypes();
2892
                if (types == null) {
2893
                    types = featureTypes;
2894
                }
2895
            }
2896
            return Collections.unmodifiableList(types);
2897
        } catch (Exception e) {
2898
            throw new GetFeatureTypeException(e, getName());
2899
        }
2900
    }
2901

    
2902
    public List getProviderFeatureTypes() throws DataException {
2903
        return Collections.unmodifiableList(this.featureTypes);
2904
    }
2905

    
2906
    @Override
2907
    public Feature createFeature(FeatureProvider data) throws DataException {
2908
        DefaultFeature feature = new DefaultFeature(this, data);
2909
        return feature;
2910
    }
2911

    
2912
    public Feature createFeature(FeatureProvider data, FeatureType type)
2913
            throws DataException {
2914
        // FIXME: falta por implementar
2915
        // Comprobar si es un subtipo del feature de data
2916
        // y construir un feature usando el subtipo.
2917
        // Probablemente requiera generar una copia del data.
2918
        throw new NotYetImplemented();
2919
    }
2920

    
2921
    @Override
2922
    public EditableFeature createNewFeature(FeatureType type,
2923
            Feature defaultValues) throws DataException {
2924
        try {
2925
            FeatureProvider data = createNewFeatureProvider(type);
2926
            DefaultEditableFeature feature
2927
                    = new DefaultEditableFeature(this, data);
2928
            feature.initializeValues(defaultValues);
2929
            data.setNew(true);
2930

    
2931
            return feature;
2932
        } catch (Exception e) {
2933
            throw new CreateFeatureException(e, getName());
2934
        }
2935
    }
2936

    
2937
    private FeatureProvider createNewFeatureProvider(FeatureType type)
2938
            throws DataException {
2939
        type = this.fixFeatureType((DefaultFeatureType) type);
2940
        FeatureProvider data = this.provider.createFeatureProvider(type);
2941
        data.setNew(true);
2942
        if (type.hasOID() && (data.getOID() == null)) {
2943
            data.setOID(this.provider.createNewOID());
2944
        } else {
2945
            data.setOID(this.getTemporalOID());
2946
        }
2947
        return data;
2948

    
2949
    }
2950

    
2951
    @Override
2952
    public EditableFeature createNewFeature(FeatureType type,
2953
            boolean defaultValues) throws DataException {
2954
        try {
2955
            FeatureProvider data = createNewFeatureProvider(type);
2956
            DefaultEditableFeature feature
2957
                    = new DefaultEditableFeature(this, data);
2958
            if (defaultValues) {
2959
                feature.initializeValues();
2960
            }
2961
            return feature;
2962
        } catch (Exception e) {
2963
            throw new CreateFeatureException(e, getName());
2964
        }
2965
    }
2966

    
2967
    @Override
2968
    public EditableFeature createNewFeature(boolean defaultValues)
2969
            throws DataException {
2970
        return this.createNewFeature(this.getDefaultFeatureType(),
2971
                defaultValues);
2972
    }
2973

    
2974
    @Override
2975
    public EditableFeature createNewFeature() throws DataException {
2976
        return this.createNewFeature(this.getDefaultFeatureType(), true);
2977
    }
2978

    
2979
    @Override
2980
    public EditableFeature createNewFeature(Feature defaultValues) throws DataException {
2981
        FeatureType ft = this.getDefaultFeatureType();
2982
        EditableFeature f = this.createNewFeature(ft, false);
2983
        f.copyFrom(defaultValues);
2984
        return f;
2985
    }
2986

    
2987
    @Override
2988
    public EditableFeature createNewFeature(JsonObject defaultValues) throws DataException {
2989
        FeatureType ft = this.getDefaultFeatureType();
2990
        EditableFeature f = this.createNewFeature(ft, false);
2991
        f.copyFrom(defaultValues);
2992
        return f;
2993
    }
2994

    
2995
    @Override
2996
    public EditableFeatureType createFeatureType() {
2997
        EditableFeatureType ftype = new DefaultEditableFeatureType(this);
2998
        return ftype;
2999
    }
3000

    
3001
    @Override
3002
    public EditableFeatureType createFeatureType(String id) {
3003
        DefaultEditableFeatureType ftype = new DefaultEditableFeatureType(this, id);
3004
        return ftype;
3005
    }
3006

    
3007
    //
3008
    // ====================================================================
3009
    // Index related methods
3010
    //
3011
    @Override
3012
    public FeatureIndexes getIndexes() {
3013
        return this.indexes;
3014
    }
3015

    
3016
    @Override
3017
    public FeatureIndex createIndex(FeatureType featureType,
3018
            String attributeName, String indexName) throws DataException {
3019
        return createIndex(null, featureType, attributeName, indexName);
3020
    }
3021

    
3022
    @Override
3023
    public FeatureIndex createIndex(String indexTypeName,
3024
            FeatureType featureType, String attributeName, String indexName)
3025
            throws DataException {
3026

    
3027
        return createIndex(indexTypeName, featureType, attributeName,
3028
                indexName, false, null);
3029
    }
3030

    
3031
    @Override
3032
    public FeatureIndex createIndex(FeatureType featureType,
3033
            String attributeName, String indexName, Observer observer)
3034
            throws DataException {
3035
        return createIndex(null, featureType, attributeName, indexName,
3036
                observer);
3037
    }
3038

    
3039
    @Override
3040
    public FeatureIndex createIndex(String indexTypeName,
3041
            FeatureType featureType, String attributeName, String indexName,
3042
            final Observer observer) throws DataException {
3043

    
3044
        return createIndex(indexTypeName, featureType, attributeName,
3045
                indexName, true, observer);
3046
    }
3047

    
3048
    private FeatureIndex createIndex(String indexTypeName,
3049
            FeatureType featureType, String attributeName, String indexName,
3050
            boolean background, final Observer observer) throws DataException {
3051

    
3052
        checkNotInAppendMode();
3053
        FeatureIndexProviderServices index;
3054
        index = dataManager.createFeatureIndexProvider(indexTypeName, this,
3055
                featureType, indexName,
3056
                featureType.getAttributeDescriptor(attributeName));
3057

    
3058
        try {
3059
            index.fill(background, observer);
3060
        } catch (FeatureIndexException e) {
3061
            throw new InitializeException(index.getName(), e);
3062
        }
3063

    
3064
        ((DefaultFeatureIndexes) getIndexes()).addIndex(index);
3065
        return index;
3066
    }
3067

    
3068
    //
3069
    // ====================================================================
3070
    // Transforms related methods
3071
    //
3072
    @Override
3073
    public FeatureStoreTransforms getTransforms() {
3074
        return this.transforms;
3075
    }
3076

    
3077
    @Override
3078
    public FeatureQuery createFeatureQuery() {
3079
        return new DefaultFeatureQuery(this.getName());
3080
    }
3081

    
3082
    @Override
3083
    public FeatureQuery createFeatureQuery(Expression filter, String sortBy, boolean asc) {
3084
        FeatureQuery query = null;
3085
        if (filter != null) {
3086
            query = this.createFeatureQuery();
3087
            query.setFilter(filter);
3088
        }
3089
        if (!StringUtils.isBlank(sortBy)) {
3090
            if (query == null) {
3091
                query = this.createFeatureQuery();
3092
            }
3093
            if (StringUtils.containsAny(sortBy, "(", ")")) {
3094
                throw new IllegalArgumentException("Incorrect sortBy expression");
3095
            }
3096
            String[] attrnames;
3097
            if (sortBy.contains(",")) {
3098
                attrnames = StringUtils.split(sortBy, ",");
3099
            } else {
3100
                attrnames = new String[]{sortBy};
3101
            }
3102
            for (String attrname : attrnames) {
3103
                attrname = attrname.trim();
3104
                if (attrname.startsWith("-")) {
3105
                    query.getOrder().add(attrname.substring(1).trim(), false);
3106
                } else if (attrname.endsWith("-")) {
3107
                    query.getOrder().add(attrname.substring(0, sortBy.length() - 1).trim(), false);
3108
                } else if (attrname.startsWith("+")) {
3109
                    query.getOrder().add(attrname.substring(1).trim(), true);
3110
                } else if (attrname.endsWith("-")) {
3111
                    query.getOrder().add(attrname.substring(0, sortBy.length() - 1).trim(), true);
3112
                } else {
3113
                    query.getOrder().add(attrname, asc);
3114
                }
3115
            }
3116
        }
3117
        if (query != null) {
3118
            query.retrievesAllAttributes();
3119
        }
3120
        return query;
3121
    }
3122

    
3123
    @Override
3124
    public FeatureQuery createFeatureQuery(String filter) {
3125
        return this.createFeatureQuery(
3126
                ExpressionUtils.createExpression(filter),
3127
                (String) null,
3128
                true
3129
        );
3130
    }
3131

    
3132
    @Override
3133
    public FeatureQuery createFeatureQuery(Expression filter) {
3134
        return this.createFeatureQuery(
3135
                filter,
3136
                (String) null,
3137
                true
3138
        );
3139
    }
3140

    
3141
    @Override
3142
    public FeatureQuery createFeatureQuery(String filter, String sortBy, boolean asc) {
3143
        if (StringUtils.isBlank(filter)) {
3144
            return this.createFeatureQuery(
3145
                    (Expression) null,
3146
                    sortBy,
3147
                    asc
3148
            );
3149
        } else {
3150
            return this.createFeatureQuery(
3151
                    ExpressionUtils.createExpression(filter),
3152
                    sortBy,
3153
                    asc
3154
            );
3155
        }
3156
    }
3157

    
3158
    @Override
3159
    public FeatureQuery createFeatureQuery(Expression filter, Expression sortBy, boolean asc) {
3160
        FeatureQuery query = null;
3161
        if (filter != null) {
3162
            query = this.createFeatureQuery();
3163
            query.setFilter(filter);
3164
        }
3165
        if (sortBy != null) {
3166
            if (query == null) {
3167
                query = this.createFeatureQuery();
3168
            }
3169
            query.getOrder().add(sortBy, asc);
3170
        }
3171

    
3172
        if (query != null) {
3173
            query.retrievesAllAttributes();
3174
        }
3175
        return query;
3176
    }
3177

    
3178
    @Override
3179
    public FeatureQuery createFeatureQuery(String filter, Expression sortBy, boolean asc) {
3180
        if (StringUtils.isBlank(filter)) {
3181
            return this.createFeatureQuery(
3182
                    (Expression) null,
3183
                    sortBy,
3184
                    asc
3185
            );
3186
        } else {
3187
            return this.createFeatureQuery(
3188
                    ExpressionUtils.createExpression(filter),
3189
                    sortBy,
3190
                    asc
3191
            );
3192
        }
3193
    }
3194

    
3195
    @Override
3196
    public DataQuery createQuery() {
3197
        return createFeatureQuery();
3198
    }
3199

    
3200
    //
3201
    // ====================================================================
3202
    // UndoRedo related methods
3203
    //
3204
    @Override
3205
    public boolean canRedo() {
3206
        return commands.canRedo();
3207
    }
3208

    
3209
    @Override
3210
    public boolean canUndo() {
3211
        return commands.canUndo();
3212
    }
3213

    
3214
    @Override
3215
    public void redo(int num) throws RedoException {
3216
        for (int i = 0; i < num; i++) {
3217
            redo();
3218
        }
3219
    }
3220

    
3221
    @Override
3222
    public void undo(int num) throws UndoException {
3223
        for (int i = 0; i < num; i++) {
3224
            undo();
3225
        }
3226
    }
3227

    
3228
    //
3229
    // ====================================================================
3230
    // Metadata related methods
3231
    //
3232
    @Override
3233
    public Object getMetadataID() {
3234
        return this.provider.getSourceId();
3235
    }
3236

    
3237
    @Override
3238
    public void delegate(DynObject dynObject) {
3239
        this.metadata.delegate(dynObject);
3240
    }
3241

    
3242
    @Override
3243
    public DynClass getDynClass() {
3244
        return this.metadata.getDynClass();
3245
    }
3246

    
3247
    @Override
3248
    public Object getDynValue(String name) throws DynFieldNotFoundException {
3249
        try {
3250
            if (this.transforms.hasDynValue(name)) {
3251
                return this.transforms.getDynValue(name);
3252
            }
3253
            if (this.metadata.hasDynValue(name)) {
3254
                return this.metadata.getDynValue(name);
3255
            }
3256
            if (METADATA_PROVIDER.equalsIgnoreCase(name)) {
3257
                return this.provider.getProviderName();
3258
            } else if (METADATA_CONTAINERNAME.equalsIgnoreCase(name)) {
3259
                return this.provider.getSourceId();
3260
            } else if (METADATA_FEATURETYPE.equalsIgnoreCase(name)) {
3261
                try {
3262
                    return this.getDefaultFeatureType();
3263
                } catch (DataException e) {
3264
                    return null;
3265
                }
3266
            }
3267
            return this.metadata.getDynValue(name);
3268
        } catch (Exception ex) {
3269
            LOGGER.debug("Can't retrieve the value of '" + name + "' in store '" + this.getName() + "'.", ex);
3270
            return null;
3271
        }
3272
    }
3273

    
3274
    @Override
3275
    public boolean hasDynValue(String name) {
3276
        if (this.transforms.hasDynValue(name)) {
3277
            return true;
3278
        }
3279
        return this.metadata.hasDynValue(name);
3280
    }
3281

    
3282
    @Override
3283
    public boolean hasDynMethod(String name) {
3284
        return ((DynObject_v2) this.metadata).hasDynMethod(name);
3285
    }
3286

    
3287
    @Override
3288
    public void implement(DynClass dynClass) {
3289
        this.metadata.implement(dynClass);
3290
    }
3291

    
3292
    @Override
3293
    public Object invokeDynMethod(String name, Object[] args)
3294
            throws DynMethodException {
3295
        return this.metadata.invokeDynMethod(this, name, args);
3296
    }
3297

    
3298
    @Override
3299
    public Object invokeDynMethod(int code, Object[] args)
3300
            throws DynMethodException {
3301
        return this.metadata.invokeDynMethod(this, code, args);
3302
    }
3303

    
3304
    @Override
3305
    public void setDynValue(String name, Object value)
3306
            throws DynFieldNotFoundException {
3307
        if (this.transforms.hasDynValue(name)) {
3308
            this.transforms.setDynValue(name, value);
3309
            return;
3310
        }
3311
        this.metadata.setDynValue(name, value);
3312

    
3313
    }
3314

    
3315
    /*
3316
     * (non-Javadoc)
3317
     *
3318
     * @see org.gvsig.metadata.Metadata#getMetadataChildren()
3319
     */
3320
    @Override
3321
    public Set getMetadataChildren() {
3322
        return this.metadataChildren;
3323
    }
3324

    
3325
    /*
3326
     * (non-Javadoc)
3327
     *
3328
     * @see org.gvsig.metadata.Metadata#getMetadataName()
3329
     */
3330
    @Override
3331
    public String getMetadataName() {
3332
        return this.provider.getProviderName();
3333
    }
3334

    
3335
    public FeatureTypeManager getFeatureTypeManager() {
3336
        return this.featureTypeManager;
3337
    }
3338

    
3339
    @Override
3340
    public long getFeatureCount() throws DataException {
3341
        if (featureCount == null) {
3342
            featureCount = this.provider.getFeatureCount();
3343
        }
3344
        if (this.isEditing()) {
3345
            if (this.isAppending()) {
3346
                try {
3347
                    throw new IllegalStateException();
3348
                } catch (IllegalStateException e) {
3349
                    LOGGER.info("Call DefaultFeatureStore.getFeatureCount editing in mode APPEND", e);
3350
                }
3351
                return -1;
3352
            } else {
3353
                return featureCount
3354
                        + this.featureManager.getDeltaSize();
3355
            }
3356
        }
3357
        return featureCount;
3358
    }
3359

    
3360
    private Long getTemporalOID() {
3361
        return this.temporalOid++;
3362
    }
3363

    
3364
    @Override
3365
    public FeatureType getProviderFeatureType(String featureTypeId) {
3366
        if (featureTypeId == null) {
3367
            return this.defaultFeatureType;
3368
        }
3369
        FeatureType type;
3370
        Iterator iter = this.featureTypes.iterator();
3371
        while (iter.hasNext()) {
3372
            type = (FeatureType) iter.next();
3373
            if (type.getId().equals(featureTypeId)) {
3374
                return type;
3375
            }
3376
        }
3377
        return null;
3378
    }
3379

    
3380
    @Override
3381
    public FeatureProvider getFeatureProviderFromFeature(Feature feature) {
3382
        return ((DefaultFeature) feature).getData();
3383
    }
3384

    
3385
    @Override
3386
    public DataStore getStore() {
3387
        return this;
3388
    }
3389

    
3390
    @Override
3391
    public FeatureStore getFeatureStore() {
3392
        return this;
3393
    }
3394

    
3395
    @Override
3396
    public void createCache(String name, DynObject parameters)
3397
            throws DataException {
3398
        cache = dataManager.createFeatureCacheProvider(name, parameters);
3399
        if (cache == null) {
3400
            throw new CreateException("FeaureCacheProvider", null);
3401
        }
3402
        cache.apply(this, provider);
3403
        provider = cache;
3404

    
3405
        featureCount = null;
3406
    }
3407

    
3408
    @Override
3409
    public FeatureCache getCache() {
3410
        return cache;
3411
    }
3412

    
3413
    @Override
3414
    public void clear() {
3415
        if (metadata != null) {
3416
            metadata.clear();
3417
        }
3418
    }
3419

    
3420
    @Override
3421
    public String getName() {
3422
        if (this.provider != null) {
3423
            return this.provider.getName();
3424
        }
3425
        if (this.parameters instanceof HasAFile) {
3426
            return FilenameUtils.getName(((HasAFile) this.parameters).getFile().getName());
3427
        }
3428
        return "unknow";
3429
    }
3430

    
3431
    @Override
3432
    public String getFullName() {
3433
        try {
3434
            if (this.provider != null) {
3435
                return this.provider.getFullName();
3436
            }
3437
            if (this.parameters instanceof HasAFile) {
3438
                return (((HasAFile) this.parameters).getFile().getAbsolutePath());
3439
            }
3440
            return null;
3441
        } catch (Throwable th) {
3442
            return null;
3443
        }
3444
    }
3445

    
3446
    @Override
3447
    public String getProviderName() {
3448
        if (this.provider != null) {
3449
            return this.provider.getProviderName();
3450
        }
3451
        if (this.parameters != null) {
3452
            return this.parameters.getDataStoreName();
3453
        }
3454
        return null;
3455

    
3456
    }
3457

    
3458
    @Override
3459
    public boolean isKnownEnvelope() {
3460
        return this.provider.isKnownEnvelope();
3461
    }
3462

    
3463
    @Override
3464
    public boolean hasRetrievedFeaturesLimit() {
3465
        return this.provider.hasRetrievedFeaturesLimit();
3466
    }
3467

    
3468
    @Override
3469
    public int getRetrievedFeaturesLimit() {
3470
        return this.provider.getRetrievedFeaturesLimit();
3471
    }
3472

    
3473
    @Override
3474
    public Interval getInterval() {
3475
        if (this.timeSupport != null) {
3476
            return this.timeSupport.getInterval();
3477
        }
3478
        try {
3479
            FeatureType type = this.getDefaultFeatureType();
3480
            FeatureAttributeDescriptor attr = type.getDefaultTimeAttribute();
3481
            if (attr != null) {
3482
                Interval interval = attr.getInterval();
3483
                if (interval != null) {
3484
                    return interval;
3485
                }
3486
            }
3487
        } catch (DataException ex) {
3488
        }
3489
        return this.provider.getInterval();
3490
    }
3491

    
3492
    @Override
3493
    public Collection getTimes() {
3494
        if (this.timeSupport != null) {
3495
            return this.timeSupport.getTimes();
3496
        }
3497
        return this.provider.getTimes();
3498
    }
3499

    
3500
    @Override
3501
    public Collection getTimes(Interval interval) {
3502
        if (this.timeSupport != null) {
3503
            return this.timeSupport.getTimes(interval);
3504
        }
3505
        return this.provider.getTimes(interval);
3506
    }
3507

    
3508
    public void setTimeSupport(FeatureStoreTimeSupport timeSupport) {
3509
        if (this.isEditing()) {
3510
            throw new RuntimeException("Can't add time support over attribute '" + timeSupport.getAttributeName() + "' while store is editing.");
3511
        }
3512
        if (!this.transforms.isEmpty()) {
3513
            throw new RuntimeException("Can't add time support over attribute '" + timeSupport.getAttributeName() + "' if has transforms.");
3514
        }
3515
        FeatureType ft = this.defaultFeatureType;
3516
        FeatureAttributeDescriptor attr = ft.getAttributeDescriptor(timeSupport.getRequiredFieldNames()[0]);
3517
        if (attr == null) {
3518
            throw new RuntimeException("Can't add time support over attribute '" + timeSupport.getAttributeName() + "', this attribute don't exists.");
3519
        }
3520
        EditableFeatureType eft = ft.getEditable();
3521
        attr = eft.getAttributeDescriptor(timeSupport.getAttributeName());
3522
        if (attr != null) {
3523
            if (!(attr.getFeatureAttributeEmulator() instanceof FeatureStoreTimeSupport)) {
3524
                throw new RuntimeException("Can't add time support, attribute '" + timeSupport.getAttributeName() + "'already exists.");
3525
            }
3526
            eft.remove(attr.getName());
3527
        }
3528
        EditableFeatureAttributeDescriptor attrTime = eft.add(
3529
                timeSupport.getAttributeName(),
3530
                timeSupport.getDataType()
3531
        );
3532
        attrTime.setIsTime(true);
3533
        attrTime.setFeatureAttributeEmulator(timeSupport);
3534
        eft.setDefaultTimeAttributeName(timeSupport.getAttributeName());
3535
        this.defaultFeatureType = eft.getNotEditableCopy();
3536

    
3537
        this.timeSupport = timeSupport;
3538
    }
3539

    
3540
    @Override
3541
    @SuppressWarnings("CloneDoesntCallSuperClone")
3542
    public Object clone() throws CloneNotSupportedException {
3543

    
3544
        DataStoreParameters dsp = getParameters();
3545

    
3546
        DefaultFeatureStore cloned_store = null;
3547

    
3548
        try {
3549
            cloned_store = (DefaultFeatureStore) DALLocator.getDataManager().
3550
                    openStore(this.getProviderName(), dsp);
3551
            if (transforms != null) {
3552
                cloned_store.transforms = (DefaultFeatureStoreTransforms) transforms.clone();
3553
                cloned_store.transforms.setStoreForClone(cloned_store);
3554
            }
3555
        } catch (Exception e) {
3556
            throw new CloneException(e);
3557
        }
3558
        return cloned_store;
3559

    
3560
    }
3561

    
3562
    @Override
3563
    public Feature getFeature(DynObject dynobject) {
3564
        if (dynobject instanceof DynObjectFeatureFacade) {
3565
            Feature f = ((DynObjectFeatureFacade) dynobject).getFeature();
3566
            return f;
3567
        }
3568
        return null;
3569
    }
3570

    
3571
    @Override
3572
    public Iterator iterator() {
3573
        FeatureSet fset = null;
3574
        try {
3575
            fset = this.getFeatureSet();
3576
            return fset.fastIterator();
3577
        } catch (DataException ex) {
3578
            throw new RuntimeException(ex);
3579
        } finally {
3580
            DisposeUtils.disposeQuietly(fset);
3581
        }
3582
    }
3583

    
3584
    @Override
3585
    public long size64() {
3586
        FeatureSet fset = null;
3587
        try {
3588
            fset = this.getFeatureSet();
3589
            return fset.getSize();
3590
        } catch (DataException ex) {
3591
            throw new RuntimeException(ex);
3592
        } finally {
3593
            DisposeUtils.disposeQuietly(fset);
3594
        }
3595
    }
3596

    
3597
    @Override
3598
    public ExpressionBuilder createExpressionBuilder() {
3599
        ExpressionBuilder builder = GeometryExpressionUtils.createExpressionBuilder();
3600
        return builder;
3601
    }
3602

    
3603
    @Override
3604
    public ExpressionBuilder createExpression() {
3605
        return createExpressionBuilder();
3606
    }
3607

    
3608
    public FeatureSet features() throws DataException {
3609
        // This is to avoid jython to create a property with this name
3610
        // to access method getFeatures.
3611
        return this.getFeatureSet();
3612
    }
3613

    
3614
    @Override
3615
    public DataStoreProviderFactory getProviderFactory() {
3616
        DataStoreProviderFactory factory = dataManager.getStoreProviderFactory(parameters.getDataStoreName());
3617
        return factory;
3618
    }
3619

    
3620
    @Override
3621
    public void useCache(String providerName, DynObject parameters) throws DataException {
3622
        throw new UnsupportedOperationException();
3623
    }
3624

    
3625
    @Override
3626
    public boolean isBroken() {
3627
        return this.state.isBroken();
3628
    }
3629

    
3630
    @Override
3631
    public Throwable getBreakingsCause() {
3632
        return this.state.getBreakingsCause();
3633
    }
3634

    
3635
    @Override
3636
    public SpatialIndex wrapSpatialIndex(SpatialIndex index) {
3637
        FeatureStoreProviderFactory factory = (FeatureStoreProviderFactory) this.getProviderFactory();
3638
        if (!factory.supportNumericOID()) {
3639
            return null;
3640
        }
3641
        SpatialIndex wrappedIndex = new WrappedSpatialIndex(index, this);
3642
        return wrappedIndex;
3643
    }
3644

    
3645
    @Override
3646
    public FeatureReference getFeatureReference(String code) {
3647
        FeatureReference featureReference = FeatureReferenceFactory.createFromCode(this, code);
3648
        return featureReference;
3649
    }
3650

    
3651
    @Override
3652
    public long getPendingChangesCount() {
3653
        if (this.featureManager == null) {
3654
            return 0;
3655
        }
3656
        return this.featureManager.getPendingChangesCount();
3657
    }
3658

    
3659
    private ResourcesStorage resourcesStorage;
3660

    
3661
    @Override
3662
    public ResourcesStorage getResourcesStorage() {
3663
        if (this.resourcesStorage != null) {
3664
            DisposeUtils.bind(this.resourcesStorage);
3665
            return this.resourcesStorage;
3666
        }
3667
        ResourcesStorage theResourcesStorage;
3668
        try {
3669
            theResourcesStorage = this.provider.getResourcesStorage();
3670
            if (theResourcesStorage != null) {
3671
                this.resourcesStorage = theResourcesStorage;
3672
                DisposeUtils.bind(this.resourcesStorage);
3673
                return theResourcesStorage;
3674
            }
3675
        } catch (Throwable th) {
3676

    
3677
        }
3678
        try {
3679
            DataServerExplorer explorer = this.getExplorer();
3680
            if (explorer == null) {
3681
                return null;
3682
            }
3683
            theResourcesStorage = explorer.getResourcesStorage(this);
3684
            explorer.dispose();
3685
            this.resourcesStorage = theResourcesStorage;
3686
            DisposeUtils.bind(this.resourcesStorage);
3687
            return theResourcesStorage;
3688
        } catch (Exception ex) {
3689
            LOGGER.trace("Can't create resources storage", ex);
3690
            return null;
3691
        }
3692
    }
3693

    
3694
    @Override
3695
    public StoresRepository getStoresRepository() {
3696
        final StoresRepository mainRepository = this.dataManager.getStoresRepository();
3697
        StoresRepository localRepository = this.provider.getStoresRepository();
3698
        if (localRepository == null) {
3699
            return mainRepository;
3700
        }
3701
        StoresRepository repository = new BaseStoresRepository(this.getName());
3702
        repository.addRepository(localRepository);
3703
        repository.addRepository(mainRepository);
3704
        return repository;
3705
    }
3706

    
3707
    @Override
3708
    public Feature getSampleFeature() {
3709
        if( sampleFeatureCache==null )  {
3710
            this.sampleFeatureCache = new CachedValue<Feature>(sample_feature_cache_timeout_ms) {
3711
                @Override
3712
                protected void reload() {
3713
                    Feature sampleFeature;
3714
                    long t1 = System.currentTimeMillis();
3715
                    try {                        
3716
                        FeatureSelection theSelection = getFeatureSelection();
3717
                        if (theSelection != null && !theSelection.isEmpty()) {
3718
                            sampleFeature = theSelection.first();
3719
                        } else {
3720
                            sampleFeature = first();
3721
                        }
3722
                        if (sampleFeature == null) {
3723
                            sampleFeature = createNewFeature();
3724
                        }
3725
                    } catch (DataException ex) {
3726
                        sampleFeature = null;
3727
                    }
3728
                    long t2 = System.currentTimeMillis();
3729
                    if( (t2 - t1)>5000 ) {
3730
                        // Mas de 5 seg es muy costoso, subimos mucho el tiempo de cache.
3731
                        this.setExpireTime(((60*60)*2)*1000); // 2h
3732
                    }
3733
                    this.setValue(sampleFeature);
3734
                }
3735
            };
3736
        }
3737
        return this.sampleFeatureCache.get();
3738
    }
3739

    
3740
    @Override
3741
    public boolean supportReferences() {
3742
        try {
3743
            return this.getDefaultFeatureType().supportReferences();
3744
        } catch (Exception ex) {
3745
            return false;
3746
        }
3747
    }
3748

    
3749
    private Boolean temporary = null;
3750
    
3751
    @Override
3752
    public boolean isTemporary() {
3753
        if(temporary != null) {
3754
            return this.temporary;
3755
        }
3756
        if (this.provider == null) {
3757
            return true;
3758
        }
3759
        return this.provider.isTemporary();
3760
    }
3761
    
3762
    @Override
3763
    public void setTemporary(Boolean temporary){
3764
        this.temporary = temporary;
3765
    }
3766

    
3767
    public FeatureType getOriginalFeatureType(FeatureType featureType) {
3768
        // FIXME this don't work for Store.fType.size() > 1
3769
        FeatureTypeManager manager = this.featureTypeManager;
3770
        if (manager == null) {
3771
            return null;
3772
        }
3773
        FeatureType originalFeatureType = manager.getOriginalFeatureType();
3774
        if (originalFeatureType == null) {
3775
            return null;
3776
        }
3777
        return originalFeatureType.getCopy();
3778
    }
3779

    
3780
    @Override
3781
    public Object getProperty(String name) {
3782
        if (this.propertiesSupportHelper == null) {
3783
            return null;
3784
        }
3785
        return this.propertiesSupportHelper.getProperty(name);
3786
    }
3787

    
3788
    @Override
3789
    public void setProperty(String name, Object value) {
3790
        if (this.propertiesSupportHelper == null) {
3791
            this.propertiesSupportHelper = new PropertiesSupportHelper();
3792
        }
3793
        this.propertiesSupportHelper.setProperty(name, value);
3794
    }
3795

    
3796
    @Override
3797
    public Map<String, Object> getProperties() {
3798
        if (this.propertiesSupportHelper == null) {
3799
            return Collections.EMPTY_MAP;
3800
        }
3801
        return this.propertiesSupportHelper.getProperties();
3802
    }
3803

    
3804
    @Override
3805
    public Feature getOriginalFeature(FeatureReference id) {
3806
        if (this.featureManager == null) {
3807
            return null;
3808
        }
3809
        return featureManager.getOriginal(id);
3810
    }
3811

    
3812
    @Override
3813
    public Feature getOriginalFeature(Feature feature) {
3814
        if (feature == null) {
3815
            return null;
3816
        }
3817
        return getOriginalFeature(feature.getReference());
3818
    }
3819

    
3820
    @Override
3821
    public boolean isFeatureModified(FeatureReference id) {
3822
        if (this.featureManager == null) {
3823
            return false;
3824
        }
3825
        return featureManager.isFeatureModified(id);
3826
    }
3827

    
3828
    @Override
3829
    public boolean isFeatureModified(Feature feature) {
3830
        if (feature == null) {
3831
            return false;
3832
        }
3833
        return isFeatureModified(feature.getReference());
3834
    }
3835

    
3836
    @Override
3837
    public void setTransaction(DataTransaction transaction) {
3838
        this.transaction = transaction;
3839
        if (transaction instanceof DataTransactionServices) {
3840
            this.provider.setTransaction((DataTransactionServices) transaction);
3841
        }
3842
    }
3843

    
3844
    @Override
3845
    public DataTransaction getTransaction() {
3846
        return transaction;
3847
    }
3848

    
3849
    @Override
3850
    public String toString() {
3851
        try {
3852
            return String.format("%s %x %s", this.getClass().getSimpleName(), this.hashCode(), this.getFullName());
3853
        } catch (Exception e) {
3854
            return super.toString();
3855
        }
3856
    }
3857

    
3858
    public String createUniqueID() {
3859
        UUID x = UUID.randomUUID();
3860
        String s = x.toString();
3861
        return s;
3862
    }
3863

    
3864
    @Override
3865
    public List<FeatureReference> getEditedFeatures() {
3866
        if( this.featureManager == null ) {
3867
            return Collections.EMPTY_LIST;
3868
        }
3869
        List<FeatureReference> references = this.featureManager.getAddedAndUpdatedFeatures();
3870
        if( references==null ) {
3871
            return Collections.EMPTY_LIST;
3872
        }
3873
        return references;
3874
    }
3875
    
3876
    public List<FeatureReference> getEditedFeaturesNotValidated() {
3877

    
3878
        try {
3879
            if (this.featureManager == null) {
3880
                return Collections.EMPTY_LIST;
3881
            }
3882
            
3883
            FeatureType type = this.getDefaultFeatureTypeQuietly();
3884
            DefaultFeatureRules rules = (DefaultFeatureRules) this.getDefaultFeatureType().getRules();
3885
            
3886
//            int checks = type.isCheckFeaturesAtFinishEditing() ? CHECK_REQUIREDS | CHECK_BASIC : 0;
3887
            int checks = CHECK_REQUIREDS | CHECK_BASIC;
3888
            if(type.isCheckFeaturesAtFinishEditing()){
3889
                checks = rules.isEmpty() ? checks : checks | CHECK_RULES_AT_FINISH;
3890
            }
3891
            if (checks == 0) {
3892
                return Collections.EMPTY_LIST;
3893
            }
3894
            List<FeatureReference> references = this.featureManager
3895
                    .getAddedAndUpdatedFeaturesNotValidated(rules, checks);
3896
            if (references == null) {
3897
                return Collections.EMPTY_LIST;
3898
            }
3899
            return references;
3900
        } catch (DataException ex) {
3901
            return null;
3902
        }
3903

    
3904
    }
3905

    
3906
    @Override
3907
    public Iterator<Feature> getFeaturesIterator(Iterator<FeatureReference> references) {
3908
        return new FeatureReferenceIteratorToFeatureIterator(this, references);
3909
    }
3910

    
3911
    @Override
3912
    public Iterable<Feature> getFeaturesIterable(Iterator<FeatureReference> references) {
3913
        return () -> new FeatureReferenceIteratorToFeatureIterator(this, references);
3914
    }
3915

    
3916
    @Override
3917
    public boolean isFeatureSelectionAvailable() {
3918
        try {
3919
            FeatureType type = this.getDefaultFeatureType();
3920
            return type.supportReferences();
3921
        } catch (DataException ex) {
3922
            return false;
3923
        }
3924
    }
3925
}