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 @ 46301

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.featureset.DefaultFeatureSet;
133
import org.gvsig.fmap.dal.feature.impl.undo.DefaultFeatureCommandsStack;
134
import org.gvsig.fmap.dal.feature.impl.undo.FeatureCommandsStack;
135
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
136
import org.gvsig.fmap.dal.feature.spi.DefaultFeatureProvider;
137
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
138
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
139
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProvider;
140
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
141
import org.gvsig.fmap.dal.feature.spi.cache.FeatureCacheProvider;
142
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProviderServices;
143
import org.gvsig.fmap.dal.impl.DefaultDataManager;
144
import org.gvsig.fmap.dal.resource.Resource;
145
import org.gvsig.fmap.dal.spi.AbstractDataStore;
146
import org.gvsig.fmap.dal.spi.DataStoreInitializer2;
147
import org.gvsig.fmap.dal.spi.DataStoreProvider;
148
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
149
import org.gvsig.fmap.dal.spi.DataTransactionServices;
150
import org.gvsig.fmap.geom.Geometry;
151
import org.gvsig.fmap.geom.GeometryUtils;
152
import org.gvsig.fmap.geom.SpatialIndex;
153
import org.gvsig.fmap.geom.primitive.Envelope;
154
import org.gvsig.metadata.MetadataLocator;
155
import org.gvsig.metadata.MetadataManager;
156
import org.gvsig.metadata.exceptions.MetadataException;
157
import org.gvsig.timesupport.Interval;
158
import org.gvsig.tools.ToolsLocator;
159
import org.gvsig.tools.dispose.DisposableIterator;
160
import org.gvsig.tools.dispose.DisposeUtils;
161
import org.gvsig.tools.dynobject.DelegatedDynObject;
162
import org.gvsig.tools.dynobject.DynClass;
163
import org.gvsig.tools.dynobject.DynObject;
164
import org.gvsig.tools.dynobject.DynObjectManager;
165
import org.gvsig.tools.dynobject.DynObject_v2;
166
import org.gvsig.tools.dynobject.DynStruct;
167
import org.gvsig.tools.dynobject.Tags;
168
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
169
import org.gvsig.tools.dynobject.exception.DynMethodException;
170
import org.gvsig.tools.exception.BaseException;
171
import org.gvsig.tools.exception.NotYetImplemented;
172
import org.gvsig.tools.identitymanagement.SimpleIdentityManager;
173
import org.gvsig.tools.observer.Observable;
174
import org.gvsig.tools.observer.Observer;
175
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
176
import org.gvsig.tools.persistence.PersistenceManager;
177
import org.gvsig.tools.persistence.Persistent;
178
import org.gvsig.tools.persistence.PersistentState;
179
import org.gvsig.tools.persistence.exception.PersistenceException;
180
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
181
import org.gvsig.tools.undo.RedoException;
182
import org.gvsig.tools.undo.UndoException;
183
import org.gvsig.tools.undo.command.Command;
184
import org.gvsig.tools.util.CachedValue;
185
import org.gvsig.tools.util.ChainedIterator;
186
import org.gvsig.tools.util.GetItemWithSizeIsEmptyAndIterator64;
187
import org.gvsig.tools.util.HasAFile;
188
import org.gvsig.tools.util.PropertiesSupportHelper;
189
import org.gvsig.tools.util.UnmodifiableBasicMap;
190
import org.gvsig.tools.visitor.VisitCanceledException;
191
import org.gvsig.tools.visitor.Visitor;
192

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

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

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

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

    
209
    private FeatureCommandsStack commands;
210

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

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

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

    
226
    private DefaultDataManager dataManager = null;
227

    
228
    private FeatureStoreProvider provider = null;
229

    
230
    private DefaultFeatureIndexes indexes;
231

    
232
    private DefaultFeatureStoreTransforms transforms;
233

    
234
    /*friend*/ DelegatedDynObject metadata;
235

    
236
    private Set metadataChildren;
237

    
238
    private Long featureCount = null;
239

    
240
    private long temporalOid = 0;
241

    
242
    private FeatureCacheProvider cache;
243

    
244
    private final StateInformation state;
245

    
246
    private FeatureStoreTimeSupport timeSupport;
247

    
248
    private PropertiesSupportHelper propertiesSupportHelper;
249
    private DataTransaction transaction;
250

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

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

    
257
        private static final long serialVersionUID = 4109026189635185666L;
258

    
259
        private boolean broken;
260
        private Throwable breakingsCause;
261

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

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

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

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

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

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

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

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

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

    
316
        DynObjectManager dynManager = ToolsLocator.getDynObjectManager();
317

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

    
323
        this.dataManager = (DefaultDataManager) dataManager;
324

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

    
333
    }
334

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
657
        }
658

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

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

    
666
    }
667

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

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

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

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

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

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

    
780
                }
781

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
991
    }
992

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1118
    }
1119

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

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

    
1129
    }
1130

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

    
1135
    }
1136

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

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

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

    
1155
    }
1156

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

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

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

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

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

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

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

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

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

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

    
1234
        }
1235

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

    
1240
        featureCount = null;
1241

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

    
1246
        this.editingSessionCode = null;
1247
    }
1248

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1481
        }
1482
    }
1483

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

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

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

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

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

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

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

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

    
1584
    private static EditableFeature lastChangedFeature = null;
1585

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

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

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

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

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

    
1641
        waitForIndexes();
1642

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2058
    }
2059

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

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

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

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

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

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

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

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

    
2202
            case MODE_APPEND:
2203
                return true;
2204

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

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

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

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

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

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

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

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

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

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

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

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

    
2323
    }
2324

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2765
        }
2766

    
2767
        return type;
2768
    }
2769

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

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

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

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

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

    
2811
            return avoidEditable(defaultFeatureType);
2812

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

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

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

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

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

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

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

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

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

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

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

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

    
2948
    }
2949

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3312
    }
3313

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

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

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

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

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

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

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

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

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

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

    
3404
        featureCount = null;
3405
    }
3406

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

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

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

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

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

    
3455
    }
3456

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

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

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

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

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

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

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

    
3536
        this.timeSupport = timeSupport;
3537
    }
3538

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

    
3543
        DataStoreParameters dsp = getParameters();
3544

    
3545
        DefaultFeatureStore cloned_store = null;
3546

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

    
3559
    }
3560

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3658
    private ResourcesStorage resourcesStorage;
3659

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3903
    }
3904

    
3905
//    public Iterator<Feature> getFeaturesIterator(Iterator<FeatureReference> references) {
3906
//        return new FeatureReferenceIteratorToFeatureIterator(this, references);
3907
//    }
3908
//
3909
    public boolean isFeatureSelectionAvailable() {
3910
        try {
3911
            FeatureType type = this.getDefaultFeatureType();
3912
            return type.supportReferences();
3913
        } catch (DataException ex) {
3914
            return false;
3915
        }
3916
    }
3917
}