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

History | View | Annotate | Download (113 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24

    
25
package org.gvsig.fmap.dal.feature.impl;
26

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

    
175
@SuppressWarnings("UseSpecificCatch")
176
public class DefaultFeatureStore extends AbstractDataStore implements
177
    DataStoreInitializer2, FeatureStoreProviderServices, FeatureStore, Observer {
178

    
179
    private static final String PERSISTENCE_DEFINITION_NAME = "FeatureStore";
180

    
181
    private DataStoreParameters parameters = null;
182
    private FeatureSelection selection;
183
    private FeatureLocks locks;
184

    
185
    private DelegateWeakReferencingObservable delegateObservable =
186
        new DelegateWeakReferencingObservable(this);
187

    
188
    private FeatureCommandsStack commands;
189
    
190
    /*
191
    TODO: Sustituir estos tres manager por un EditingManager
192
    */
193
    private FeatureTypeManager featureTypeManager;
194
    private FeatureManager featureManager;
195
    private SpatialManager spatialManager;
196

    
197
    private FeatureType defaultFeatureType = null;
198
    private List<FeatureType> featureTypes = new ArrayList<>();
199

    
200
    private int mode = MODE_QUERY;
201
    private long versionOfUpdate = 0;
202
    private boolean hasStrongChanges = true;
203
    private boolean hasInserts = true;
204

    
205
    private DefaultDataManager dataManager = null;
206

    
207
    private FeatureStoreProvider provider = null;
208

    
209
    private DefaultFeatureIndexes indexes;
210

    
211
    private DefaultFeatureStoreTransforms transforms;
212

    
213
    DelegatedDynObject metadata;
214

    
215
    private Set metadataChildren;
216

    
217
    private Long featureCount = null;
218

    
219
    private long temporalOid = 0;
220

    
221
    private FeatureCacheProvider cache;
222

    
223
    StateInformation state;
224

    
225
    FeatureStoreTimeSupport timeSupport;
226

    
227

    
228
    private class StateInformation extends HashMap<Object, Object> {
229

    
230
        private static final long serialVersionUID = 4109026189635185666L;
231

    
232
        private boolean broken;
233
        private Throwable breakingsCause;
234

    
235
        @SuppressWarnings("OverridableMethodCallInConstructor")
236
        public StateInformation() {
237
            this.clear();
238
        }
239

    
240
        @Override
241
        public void clear() {
242
            this.broken = false;
243
            this.breakingsCause = null;
244
            super.clear();
245
        }
246

    
247
        public boolean isBroken() {
248
            return this.broken;
249
        }
250

    
251
        public void broken() {
252
            this.broken = true;
253
        }
254

    
255
        public Throwable getBreakingsCause() {
256
            return this.breakingsCause;
257
        }
258

    
259
        public void setBreakingsCause(Throwable cause) {
260
            if( this.breakingsCause==null ) {
261
                this.breakingsCause = cause;
262
            }
263
            this.broken = true;
264
        }
265
    }
266

    
267

    
268

    
269
    /*
270
     * TODO:
271
     *
272
     * - Comprobar que solo se pueden a?adir reglas de validacion sobre un
273
     * EditableFeatureType. - Comprobar que solo se puede hacer un update con un
274
     * featureType al que se le han cambiado las reglas de validacion cuando
275
     * hasStrongChanges=false.
276
     */
277

    
278
    public DefaultFeatureStore() {
279
        this.state = new StateInformation();
280
    }
281
    
282
    @Override
283
    protected DataManager getDataManager() {
284
        return this.dataManager;
285
    }
286

    
287
    @Override
288
    public void intialize(DataManager dataManager,
289
        DataStoreParameters parameters) throws InitializeException {
290

    
291
        DynObjectManager dynManager = ToolsLocator.getDynObjectManager();
292

    
293
        this.metadata = (DelegatedDynObject) dynManager.createDynObject(
294
            FeatureStore.METADATA_DEFINITION_NAME,
295
            MetadataManager.METADATA_NAMESPACE
296
        );
297

    
298
        this.dataManager = (DefaultDataManager) dataManager;
299

    
300
        this.parameters = parameters;
301
        this.transforms = new DefaultFeatureStoreTransforms(this);
302
        try {
303
            indexes = new DefaultFeatureIndexes(this);
304
        } catch (DataException e) {
305
            throw new InitializeException(e);
306
        }
307

    
308
    }
309

    
310
    @Override
311
    public void setProvider(org.gvsig.fmap.dal.DataStoreProvider provider) {
312
        this.provider = (FeatureStoreProvider) provider;
313
        this.delegate((DynObject) provider);
314
        this.metadataChildren = new HashSet();
315
        this.metadataChildren.add(provider);
316
        loadDALFile();
317
        
318
        // Habria que crear un metodo en el proveedor para que de prioidad
319
        // a los parametros frente a lo que se a leido en el fichero dal
320
        // DataStoreProvider arreglalo segun los parametros del usuario
321
        // fixFeatureTypeFromParameters
322
        this.provider.fixFeatureTypeFromParameters();
323
        try {
324
            if( defaultFeatureType!=null ) {
325
                FeatureAttributeDescriptor attrGeom = defaultFeatureType.getDefaultGeometryAttribute();
326
                if (attrGeom!=null) {
327
                    DefaultFeatureAttributeDescriptor gattr = (DefaultFeatureAttributeDescriptor) attrGeom;
328
                    IProjection srs = (IProjection) this.getDynValue(METADATA_CRS);
329
                    if( srs!=null && srs!=gattr.getSRS() ) {
330
                        gattr.setSRSForced(srs);
331
                    }
332
                }
333
            }
334
        } catch(Throwable th) {
335
            LOGGER.warn("Can't patch DAL file",th);
336
        }
337
    }
338

    
339
    @Override
340
    public DataStoreParameters getParameters() {
341
        if( this.parameters==null ) {
342
            LOGGER.warn("Store parametes are null");
343
        }
344
        return parameters;
345
    }
346

    
347
    public int getMode() {
348
        return this.mode;
349
    }
350

    
351
    @Override
352
    public DataManager getManager() {
353
        return this.dataManager;
354
    }
355

    
356
    @Override
357
    public UnmodifiableBasicMap<String,DataStore> getChildren() {
358
        UnmodifiableBasicMap<String, DataStore> children = this.provider.getChildren();
359
        if( children == null ) {
360
            return UnmodifiableBasicMap.EMPTY_UNMODIFIABLEBASICMAP;
361
        }
362
        return children;
363
    }
364

    
365
    @Override
366
    public FeatureStoreProvider getProvider() {
367
        return this.provider;
368
    }
369

    
370
    public FeatureManager getFeatureManager() {
371
        return this.featureManager;
372
    }
373

    
374
    @Override
375
    public void setFeatureTypes(List types, FeatureType defaultType) {
376
        this.featureTypes = types;
377
        this.defaultFeatureType = defaultType;
378
    }
379

    
380
    public void open() throws OpenException {
381
        if (this.mode != MODE_QUERY) {
382
            // TODO: Se puede hacer un open estando en edicion ?
383
            try {
384
                throw new IllegalStateException();
385
            } catch(Exception ex) {
386
                LOGGER.warn("Opening a store in editing/append mode ("+this.getFullName()+").",ex);
387
            }
388
        }
389
        if( this.notifyChange(DataStoreNotification.BEFORE_OPEN).isCanceled() ) {
390
          return;
391
        }
392
        this.provider.open();
393
        this.notifyChange(DataStoreNotification.AFTER_OPEN);
394
    }
395

    
396
    @Override
397
    public void refresh() throws OpenException, InitializeException {
398
        if (this.mode != MODE_QUERY) {
399
            throw new IllegalStateException();
400
        }
401
        if( this.notifyChange(FeatureStoreNotification.BEFORE_REFRESH).isCanceled() ) {
402
          return;
403
        }
404
        if( state.isBroken() ) {
405
            this.load(state);
406
        } else {
407
            this.featureCount = null;
408
            this.provider.refresh();
409
        }
410
        this.notifyChange(FeatureStoreNotification.AFTER_REFRESH);
411
    }
412

    
413
    public void close() throws CloseException {
414
        if (this.mode != MODE_QUERY) {
415
            // TODO: Se puede hacer un close estando en edicion ?
416
            try {
417
                throw new IllegalStateException();
418
            } catch(Exception ex) {
419
                LOGGER.warn("Clossing a store in editing/append mode ("+this.getFullName()+").",ex);
420
            }
421
        }
422
        if( this.notifyChange(DataStoreNotification.BEFORE_CLOSE).isCanceled() ) {
423
          return;
424
        }
425
        this.featureCount = null;
426
        this.provider.close();
427
        this.notifyChange(DataStoreNotification.AFTER_CLOSE);
428
    }
429

    
430
    @Override
431
    protected void doDispose() throws BaseException {
432
        if (this.mode != MODE_QUERY) {
433
            // TODO: Se puede hacer un dispose estando en edicion ?
434
            try {
435
                throw new IllegalStateException();
436
            } catch(Exception ex) {
437
                LOGGER.warn("Dispossing a store in editing/append mode ("+this.getFullName()+").",ex);
438
            }
439
        }
440
        if( this.notifyChange(DataStoreNotification.BEFORE_DISPOSE).isCanceled() ) {
441
          return;
442
        }
443
        this.disposeIndexes();
444
        if( this.provider!=null ) {
445
            this.provider.dispose();
446
        }
447
        if (this.selection != null) {
448
            this.selection.dispose();
449
            this.selection = null;
450
        }
451
        this.commands = null;
452
        this.featureCount = null;
453
        if (this.locks != null) {
454
            // this.locks.dispose();
455
            this.locks = null;
456
        }
457

    
458
        if (this.featureTypeManager != null) {
459
            this.featureTypeManager.dispose();
460
            this.featureTypeManager = null;
461
        }
462

    
463
        this.featureManager = null;
464
        this.spatialManager = null;
465

    
466
        this.parameters = null;
467
        this.notifyChange(DataStoreNotification.AFTER_DISPOSE);
468
        if (delegateObservable != null) {
469
            this.delegateObservable.deleteObservers();
470
            this.delegateObservable = null;
471
        }
472
    }
473

    
474
    @Override
475
    public boolean allowWrite() {
476
        SimpleIdentityManager identityManager = ToolsLocator.getIdentityManager();
477
        if( ! identityManager.getCurrentIdentity().isAuthorized(DataManager.WRITE_STORE_AUTHORIZATION,this.getParameters(), this.getName()) ) {
478
            return false;
479
        }
480
        return this.provider.allowWrite();
481
    }
482

    
483
    @Override
484
    public boolean canWriteGeometry(int geometryType) throws DataException {
485
        return this.provider.canWriteGeometry(geometryType, 0);
486
    }
487

    
488
    @Override
489
    public DataServerExplorer getExplorer() throws ReadException,
490
        ValidateDataParametersException {
491
        if( this.state.isBroken() ) {
492
            try {
493
                return this.provider.getExplorer();
494
            } catch(Throwable th) {
495
                return null;
496
            }
497
        } else {
498
            return this.provider.getExplorer();
499
        }
500
    }
501

    
502
    /*
503
     * public Metadata getMetadata() throws MetadataNotFoundException {
504
     * // TODO:
505
     * // Si el provider devuelbe null habria que ver de construir aqui
506
     * // los metadatos basicos, como el Envelope y el SRS.
507
     *
508
     * // TODO: Estando en edicion el Envelope deberia de
509
     * // actualizarse usando el spatialManager
510
     * return this.provider.getMetadata();
511
     * }
512
     */
513

    
514
    @Override
515
    public Envelope getEnvelope() throws DataException {
516
        if (this.mode == MODE_FULLEDIT) {
517
                // Just in case another thread tries to write in the store
518
                synchronized (this) {
519
                        return this.spatialManager.getEnvelope();
520
                        }
521
        }
522
        if (hasDynValue(DataStore.METADATA_ENVELOPE)){
523
            return (Envelope)getDynValue(DataStore.METADATA_ENVELOPE);
524
        }
525
        Envelope envelope = this.provider.getEnvelope();
526
        if( envelope!=null ) {
527
            return envelope;
528
        }
529
        FeatureAttributeDescriptor attrdesc = this.getDefaultFeatureType().getDefaultGeometryAttribute();
530
        if( attrdesc == null || !attrdesc.isComputed() ) {
531
            return null;
532
        }
533
        final int index = attrdesc.getIndex();
534
        final MutableObject<Envelope> envelopeValue = new MutableObject<>();
535
        try {
536
            this.accept(new Visitor() {
537
                @Override
538
                public void visit(Object obj) throws VisitCanceledException, BaseException {
539
                    Feature f = (Feature) obj;
540
                    Geometry g =  (Geometry) f.get(index);
541
                    if( g == null ) {
542
                        return;
543
                    }
544
                    if( envelopeValue.getValue()==null ) {
545
                        envelopeValue.setValue(g.getEnvelope());
546
                    } else {
547
                        envelopeValue.getValue().add(g);
548
                    }
549
                }
550
            });
551
        } catch (Throwable th) {
552
            LOGGER.warn("Can't calculate envelope", th);
553
            return null;
554
        }
555
        return envelopeValue.getValue();
556
    }
557

    
558
    /**
559
     * @throws org.gvsig.fmap.dal.exception.DataException
560
     * @deprecated use getDefaultFeatureType().getDefaultSRS()
561
     */
562
    @Override
563
    public IProjection getSRSDefaultGeometry() throws DataException {
564
        return this.getDefaultFeatureType().getDefaultSRS();
565
    }
566

    
567
    @Override
568
    public FeatureSelection createDefaultFeatureSelection()
569
        throws DataException {
570
        return new DefaultFeatureSelection(this);
571
    }
572

    
573
    @Override
574
    public FeatureProvider createDefaultFeatureProvider(FeatureType type)
575
        throws DataException {
576
        if (type.hasOID()) {
577
            return new DefaultFeatureProvider(type,
578
                this.provider.createNewOID());
579
        }
580
        return new DefaultFeatureProvider(type);
581
    }
582

    
583
    @Override
584
    public void saveToState(PersistentState state) throws PersistenceException {
585
        /*if (this.mode != FeatureStore.MODE_QUERY) {
586
            throw new PersistenceException(new IllegalStateException(
587
                this.getName()));
588
        }*/
589
        state.set("dataStoreName", this.getName());
590
        state.set("parameters", this.parameters);
591
        state.set("selection", this.selection);
592
        state.set("transforms", this.transforms);
593
        // TODO locks persistence
594
        // state.set("locks", this.locks);
595
        // TODO indexes persistence
596
        // state.set("indexes", this.indexes);
597
        Map evaluatedAttr = new HashMap(1);
598
        Iterator iterType = featureTypes.iterator();
599
        Iterator iterAttr;
600
        FeatureType type;
601
        DefaultFeatureAttributeDescriptor attr;
602
        List attrs;
603
        while (iterType.hasNext()) {
604
            type = (FeatureType) iterType.next();
605
            attrs = new ArrayList();
606
            iterAttr = type.iterator();
607
            while (iterAttr.hasNext()) {
608
                attr = (DefaultFeatureAttributeDescriptor) iterAttr.next();
609
                if ((attr.getEvaluator() != null)
610
                    && (attr.getEvaluator() instanceof Persistent)) {
611
                    attrs.add(attr);
612
                }
613
            }
614
            if (!attrs.isEmpty()) {
615
                evaluatedAttr.put(type.getId(), attrs);
616
            }
617

    
618
        }
619

    
620
        if (evaluatedAttr.isEmpty()) {
621
            evaluatedAttr = null;
622
        }
623

    
624
        state.set("evaluatedAttributes", evaluatedAttr);
625
        state.set("defaultFeatureTypeId", defaultFeatureType.getId());
626

    
627
    }
628

    
629
    @Override
630
    public void loadFromState(final PersistentState persistentState)
631
        throws PersistenceException {
632
        if (this.provider != null) {
633
            throw new PersistenceStoreAlreadyLoadedException(this.getName());
634
        }
635
        if (this.getManager() == null) {
636
            this.dataManager = (DefaultDataManager) DALLocator.getDataManager();
637
        }
638
        state.clear();
639
        try {
640
            state.put("parameters", persistentState.get("parameters"));
641
        } catch(Throwable th) {
642
            state.setBreakingsCause(th);
643
        }
644
        try {
645
            state.put("selection", persistentState.get("selection"));
646
        } catch(Throwable th) {
647
            state.setBreakingsCause(th);
648
        }
649
        try {
650
            state.put("transforms",  persistentState.get("transforms"));
651
        } catch(Throwable th) {
652
            state.setBreakingsCause(th);
653
        }
654
        try {
655
            state.put("evaluatedAttributes",  persistentState.get("evaluatedAttributes"));
656
        } catch(Throwable th) {
657
            state.setBreakingsCause(th);
658
        }
659
        try {
660
            state.put("defaultFeatureTypeId", persistentState.getString("defaultFeatureTypeId"));
661
        } catch(Throwable th) {
662
            state.setBreakingsCause(th);
663
        }
664
        load(state);
665
    }
666

    
667
    private void load(StateInformation state) {
668
        this.featureTypes = new ArrayList();
669
        this.defaultFeatureType = null;
670
        this.featureCount = null;
671

    
672
        DataStoreParameters params = (DataStoreParameters) state.get("parameters");
673
        try {
674
            intialize(dataManager, params);
675
        } catch(Throwable th) {
676
            state.setBreakingsCause(th);
677
        }
678

    
679
        try {
680
            DataStoreProvider prov = dataManager.createProvider(
681
                getStoreProviderServices(),
682
                params
683
            );
684
            setProvider(prov);
685
        } catch(Throwable th) {
686
            LOGGER.warn("Can't load store from state.", th);
687
            state.setBreakingsCause(th);
688
        }
689
        try {
690
            selection = (FeatureSelection) state.get("selection");
691
        } catch(Throwable th) {
692
            state.setBreakingsCause(th);
693
        }
694

    
695
        try {
696
            this.transforms = (DefaultFeatureStoreTransforms) state.get("transforms");
697
            this.transforms.setFeatureStore(this);
698
            for( FeatureStoreTransform transform : this.transforms ) {
699
                try {
700
                    transform.setUp();
701
                } catch(Throwable th) {
702
                    state.setBreakingsCause(th);
703
                }
704
            }
705
        } catch(Throwable th) {
706
            state.setBreakingsCause(th);
707
        }
708

    
709
        try {
710
            Map evaluatedAttributes = (Map) state.get("evaluatedAttributes");
711
            if ((evaluatedAttributes != null) && !evaluatedAttributes.isEmpty()) {
712
                    Iterator iterEntries = evaluatedAttributes.entrySet().iterator();
713
                    while (iterEntries.hasNext()) {
714
                            Entry entry = (Entry) iterEntries.next();
715
                            List attrs = (List) entry.getValue();
716
                            if (attrs.isEmpty()) {
717
                                    continue;
718
                            }
719
                            int fTypePos = -1;
720
                            DefaultFeatureType type = null;
721
                            for (int i = 0; i < featureTypes.size(); i++) {
722
                                    type = (DefaultFeatureType) featureTypes.get(i);
723
                                    if (type.getId().equals(entry.getKey())) {
724
                                            fTypePos = i;
725
                                            break;
726
                                    }
727
                            }
728
                            if (type == null) {
729
                                    throw new PersistenceCantFindFeatureTypeException(
730
                                            getName(), (String) entry.getKey());
731
                            }
732
                            DefaultEditableFeatureType eType = (DefaultEditableFeatureType) type.getEditable();
733
                            Iterator<FeatureAttributeDescriptor> iterAttr = attrs.iterator();
734
                            while (iterAttr.hasNext()) {
735
                                    FeatureAttributeDescriptor attr = iterAttr.next();
736
                                    eType.addLike(attr);
737
                            }
738
                            featureTypes.set(fTypePos, eType.getNotEditableCopy());
739

    
740
                    }
741

    
742
            }
743
        } catch(Throwable th) {
744
            state.setBreakingsCause(th);
745
        }
746

    
747

    
748
        try {
749
            String defaultFeatureTypeId = (String) state.get("defaultFeatureTypeId");
750
            FeatureType ftype;
751

    
752
            if (defaultFeatureType == null ||
753
                    defaultFeatureType.getId() == null ||
754
                    !defaultFeatureType.getId().equals(defaultFeatureTypeId)) {
755

    
756
                    ftype = getFeatureType(defaultFeatureTypeId);
757
                    if (ftype == null) {
758
                            /*
759
                             * Un error en el m?todo de PostgreSQL getName(), hace que
760
                             * el nombre del featureType sea valor retornado por el getProviderName()
761
                             * De momento se pone este parche para apa?arlo y poder mantener compatibilidad
762
                             * con proyectos antiguos (2.1 y 2.2)
763
                             */
764
                            ftype = getFeatureType(getName());
765
                            if(ftype == null ) {
766
                                    throw new RuntimeException("Can't locate feature type");
767
                            }
768
                    }
769
                    defaultFeatureType = ftype;
770
            }
771
        } catch(Throwable th) {
772
            state.setBreakingsCause(th);
773
        }
774

    
775
        LOGGER.info("load() broken:{}, {}, {}.",
776
                new Object[] { state.isBroken(), this.getProviderName(), params }
777
        );
778
    }
779

    
780
    public DataStoreProviderServices getStoreProviderServices() {
781
        return this;
782
    }
783

    
784
    public static void registerPersistenceDefinition() {
785
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
786
        if (manager.getDefinition(PERSISTENCE_DEFINITION_NAME) == null) {
787
            DynStruct definition =
788
                manager.addDefinition(DefaultFeatureStore.class,
789
                    PERSISTENCE_DEFINITION_NAME, PERSISTENCE_DEFINITION_NAME
790
                        + " Persistent definition", null, null);
791
            definition.addDynFieldString("dataStoreName").setMandatory(true)
792
                .setPersistent(true);
793

    
794
            definition.addDynFieldObject("parameters")
795
                .setClassOfValue(DynObject.class).setMandatory(true)
796
                .setPersistent(true);
797

    
798
            definition.addDynFieldObject("selection")
799
                .setClassOfValue(FeatureSelection.class).setMandatory(false)
800
                .setPersistent(true);
801

    
802
            definition.addDynFieldObject("transforms")
803
                .setClassOfValue(DefaultFeatureStoreTransforms.class)
804
                .setMandatory(true).setPersistent(true);
805

    
806
            definition.addDynFieldMap("evaluatedAttributes")
807
                .setClassOfItems(List.class) // List<DefaultFeatureAttributeDescriptor>
808
                .setMandatory(false).setPersistent(true);
809

    
810
            definition.addDynFieldString("defaultFeatureTypeId")
811
                .setMandatory(true).setPersistent(true);
812
        }
813
    }
814

    
815
    public static void registerMetadataDefinition() throws MetadataException {
816
        MetadataManager manager = MetadataLocator.getMetadataManager();
817
        if (manager.getDefinition(FeatureStore.METADATA_DEFINITION_NAME) == null) {
818
            DynStruct metadataDefinition =
819
                manager.addDefinition(FeatureStore.METADATA_DEFINITION_NAME, null);
820
            metadataDefinition.extend(manager
821
                .getDefinition(DataStore.METADATA_DEFINITION_NAME));
822
        }
823
    }
824

    
825
    //
826
    // ====================================================================
827
    // Gestion de la seleccion
828
    //
829

    
830
    @Override
831
    public void setSelection(DataSet selection) throws DataException {
832
        this.setSelection((FeatureSet) selection);
833
    }
834

    
835
    @Override
836
    public DataSet createSelection() throws DataException {
837
        return createFeatureSelection();
838
    }
839

    
840
    @Override
841
    public DataSet getSelection() throws DataException {
842
        return this.getFeatureSelection();
843
    }
844

    
845
    @Override
846
    public void setSelection(FeatureSet selection) throws DataException {
847
        setSelection(selection, true);
848
    }
849

    
850
    public void setSelection(FeatureSet selection, boolean undoable)
851
        throws DataException {
852
        if (selection == null) {
853
            if (undoable) {
854
                throw new SelectionNotAllowedException(getName());
855
            }
856

    
857
        } else {
858
            if (selection.equals(this.selection)) {
859
                return;
860
            }
861
            if (!selection.isFromStore(this)) {
862
                throw new SelectionNotAllowedException(getName());
863
            }
864
        }
865

    
866
        if (this.selection != null) {
867
            this.selection.deleteObserver(this);
868
        }
869
        if (selection == null) {
870
            if (this.selection != null) {
871
                this.selection.dispose();
872
            }
873
            this.selection = null;
874
            return;
875
        }
876
        if (selection instanceof FeatureSelection) {
877
            if (undoable && isEditing()) {
878
                commands.selectionSet(this, this.selection,
879
                    (FeatureSelection) selection);
880
            }
881
            if (this.selection != null) {
882
                this.selection.dispose();
883
            }
884
            this.selection = (FeatureSelection) selection;
885
        } else {
886
            if (undoable && isEditing()) {
887
                commands.startComplex("_selectionSet");
888
            }
889
            if (selection instanceof DefaultFeatureSelection) {
890
                DefaultFeatureSelection defSelection =
891
                    (DefaultFeatureSelection) selection;
892
                defSelection.deselectAll(undoable);
893
                defSelection.select(selection, undoable);
894
            } else {
895
                this.selection.deselectAll();
896
                this.selection.select(selection);
897
            }
898
            if (undoable && isEditing()) {
899
                commands.endComplex();
900
            }
901
        }
902
        this.selection.addObserver(this);
903

    
904
        this.notifyChange(DataStoreNotification.SELECTION_CHANGE);
905
    }
906

    
907
    @Override
908
    public FeatureSelection createFeatureSelection() throws DataException {
909
        return this.provider.createFeatureSelection();
910
    }
911

    
912
    @Override
913
    public FeatureSelection getFeatureSelection() throws DataException {
914
        if (selection == null) {
915
            this.selection = createFeatureSelection();
916
            this.selection.addObserver(this);
917
        }
918
        return selection;
919
    }
920

    
921
    //
922
    // ====================================================================
923
    // Gestion de notificaciones
924
    //
925

    
926
    @Override
927
    public FeatureStoreNotification notifyChange(FeatureStoreNotification storeNotification) {
928
        if (delegateObservable != null) {
929
          try {
930
              delegateObservable.notifyObservers(storeNotification);
931
          } catch (Throwable ex) {
932
              LOGGER.warn("Problems notifying changes in the store '"+this.getName()+" ("+storeNotification.getType()+").",ex);
933
          }
934
        }
935
        return storeNotification;
936
    }
937

    
938
    @Override
939
    public FeatureStoreNotification notifyChange(String notification) {
940
      return notifyChange(new DefaultFeatureStoreNotification(this, notification));
941
    }
942
    
943
    public FeatureStoreNotification notifyChange(String notification,
944
      Iterator<FeatureReference> deleteds, 
945
      Iterator<Feature> inserteds, 
946
      Iterator<Feature> updateds, 
947
      Iterator<FeatureTypeChanged> featureTypesChanged, 
948
      boolean isSelectionCompromised) {
949
        return notifyChange(new DefaultFeatureStoreNotification(this, notification,
950
            deleteds, inserteds, updateds, featureTypesChanged, isSelectionCompromised));
951
    }
952

    
953
    @Override
954
    public FeatureStoreNotification notifyChange(String notification, FeatureProvider data) {
955
        Feature f = null;
956
        if( data !=null ) {
957
          try {
958
              f = createFeature(data);
959
          } catch (Throwable ex) {
960
              LOGGER.warn("Problems creating a feature to notifying changes in the store '"+this.getName()+" ("+notification+").",ex);
961
          }
962
        }
963
        return notifyChange(notification, f);
964
    }
965

    
966
    public FeatureStoreNotification notifyChange(String notification, Feature feature) {
967
        return notifyChange(new DefaultFeatureStoreNotification(this, notification,
968
            feature));
969
    }
970

    
971
    public FeatureStoreNotification notifyChange(String notification, Command command) {
972
        return notifyChange(new DefaultFeatureStoreNotification(this, notification,
973
            command));
974
    }
975

    
976
    public FeatureStoreNotification notifyChange(String notification, EditableFeatureType type) {
977
        return notifyChange(new DefaultFeatureStoreNotification(this, notification,
978
            type));
979
    }
980

    
981
    @Override
982
    public FeatureStoreNotification notifyChange(String notification, Resource resource) {
983
        return notifyChange(new DefaultFeatureStoreNotification(this,
984
            DataStoreNotification.RESOURCE_CHANGED));
985
    }
986

    
987
    //
988
    // ====================================================================
989
    // Gestion de bloqueos
990
    //
991

    
992
    @Override
993
    public boolean isLocksSupported() {
994
        return this.provider.isLocksSupported();
995
    }
996

    
997
    @Override
998
    public FeatureLocks getLocks() throws DataException {
999
        if (!this.provider.isLocksSupported()) {
1000
            LOGGER.warn("Locks not supported");
1001
            return null;
1002
        }
1003
        if (locks == null) {
1004
            this.locks = this.provider.createFeatureLocks();
1005
        }
1006
        return locks;
1007
    }
1008

    
1009
    //
1010
    // ====================================================================
1011
    // Interface Observable
1012
    //
1013

    
1014
    @Override
1015
    public void disableNotifications() {
1016
        this.delegateObservable.disableNotifications();
1017

    
1018
    }
1019

    
1020
    @Override
1021
    public void enableNotifications() {
1022
        this.delegateObservable.enableNotifications();
1023
    }
1024

    
1025
    @Override
1026
    public void beginComplexNotification() {
1027
        this.delegateObservable.beginComplexNotification();
1028

    
1029
    }
1030

    
1031
    @Override
1032
    public void endComplexNotification() {
1033
        this.delegateObservable.endComplexNotification();
1034

    
1035
    }
1036

    
1037
    @Override
1038
    public void addObserver(Observer observer) {
1039
        if (delegateObservable != null) {
1040
            this.delegateObservable.addObserver(observer);
1041
        }
1042
    }
1043

    
1044
    @Override
1045
    public void deleteObserver(Observer observer) {
1046
        if (delegateObservable != null) {
1047
            this.delegateObservable.deleteObserver(observer);
1048
        }
1049
    }
1050

    
1051
    @Override
1052
    public void deleteObservers() {
1053
        this.delegateObservable.deleteObservers();
1054

    
1055
    }
1056

    
1057
    //
1058
    // ====================================================================
1059
    // Interface Observer
1060
    //
1061
    // Usado para observar:
1062
    // - su seleccion
1063
    // - sus bloqueos
1064
    // - sus recursos
1065
    //
1066

    
1067
    @Override
1068
    public void update(Observable observable, Object notification) {
1069
        if (observable instanceof FeatureSet) {
1070
            if (observable == this.selection) {
1071
                this.notifyChange(DataStoreNotification.SELECTION_CHANGE);
1072
            } else if (observable == this.locks) {
1073
                this.notifyChange(FeatureStoreNotification.LOCKS_CHANGE);
1074
            }
1075

    
1076
        } else if (observable instanceof FeatureStoreProvider) {
1077
            if (observable == this.provider) {
1078

    
1079
            }
1080
        } else if (observable instanceof FeatureReferenceSelection) {
1081
            if(notification instanceof String){
1082
                    this.notifyChange((String)notification);
1083
            }
1084
        }
1085
    }
1086

    
1087
    //
1088
    // ====================================================================
1089
    // Edicion
1090
    //
1091

    
1092
    private void newVersionOfUpdate() {
1093
        this.versionOfUpdate++;
1094
    }
1095

    
1096
    private long currentVersionOfUpdate() {
1097
        return this.versionOfUpdate;
1098
    }
1099

    
1100
    private void checkInEditingMode() throws NeedEditingModeException {
1101
        if (mode != MODE_FULLEDIT) {
1102
            throw new NeedEditingModeException(this.getName());
1103
        }
1104
    }
1105

    
1106
    private void checkNotInAppendMode() throws IllegalStateException {
1107
        if (mode == MODE_APPEND) {
1108
                        throw new IllegalStateException("Error: store "
1109
                                        + this.getFullName() + " is in append mode");
1110
        }
1111
    }
1112

    
1113
    private void checkIsOwnFeature(Feature feature)
1114
        throws IllegalFeatureException {
1115
        if (((DefaultFeature) feature).getStore() != this) {
1116
            throw new IllegalFeatureException(this.getName());
1117
        }
1118
        // FIXME: fixFeatureType no vale para el checkIsOwnFeature
1119
        // fixFeatureType((DefaultFeatureType) feature.getType());
1120
    }
1121

    
1122
    private void exitEditingMode() {
1123
        if (commands != null) {
1124
            commands.clear();
1125
            commands = null;
1126
        }
1127

    
1128
        if (featureTypeManager != null) {
1129
            featureTypeManager.dispose();
1130
            featureTypeManager = null;
1131

    
1132
        }
1133

    
1134
        // TODO implementar un dispose para estos dos
1135
        featureManager = null;
1136
        spatialManager = null;
1137

    
1138
        featureCount = null;
1139

    
1140
        mode = MODE_QUERY;
1141
        hasStrongChanges = true; // Lo deja a true por si las moscas
1142
        hasInserts = true;
1143
    }
1144

    
1145
    @Override
1146
    synchronized public void edit() throws DataException {
1147
        edit(MODE_FULLEDIT);
1148
    }
1149

    
1150
    @Override
1151
    synchronized public void edit(int mode) throws DataException {
1152
        LOGGER.debug("Starting editing in mode: {}", mode);
1153
        try {
1154
            if (this.mode != MODE_QUERY) {
1155
                throw new AlreadyEditingException(this.getName());
1156
            }
1157
            if (!this.provider.supportsAppendMode()) {
1158
                mode = MODE_FULLEDIT;
1159
            }
1160
            switch (mode) {
1161
            case MODE_QUERY:
1162
                throw new IllegalStateException(this.getName());
1163

    
1164
            case MODE_FULLEDIT:
1165
                if (!this.transforms.isEmpty()) {
1166
                    throw new IllegalStateException(this.getName());
1167
                }
1168
                if( notifyChange(FeatureStoreNotification.BEFORE_STARTEDITING).isCanceled() ) {
1169
                  return;
1170
                }
1171
                invalidateIndexes();
1172
                featureManager = new FeatureManager();
1173
                featureTypeManager = new FeatureTypeManager(this);
1174
                spatialManager = new SpatialManager(this, provider.getEnvelope());
1175

    
1176
                commands = new DefaultFeatureCommandsStack(
1177
                        this, featureManager,
1178
                        spatialManager, featureTypeManager);
1179
                this.mode = MODE_FULLEDIT;
1180
                hasStrongChanges = false;
1181
                hasInserts = false;
1182
                notifyChange(FeatureStoreNotification.AFTER_STARTEDITING);
1183
                break;
1184
            case MODE_APPEND:
1185
                if (!this.transforms.isEmpty()) {
1186
                    throw new IllegalStateException(this.getName());
1187
                }
1188
                if( notifyChange(FeatureStoreNotification.BEFORE_STARTEDITING).isCanceled() ) {
1189
                  return;
1190
                }
1191
                invalidateIndexes();
1192
                this.provider.beginAppend();
1193
                this.mode = MODE_APPEND;
1194
                hasInserts = false;
1195
                notifyChange(FeatureStoreNotification.AFTER_STARTEDITING);
1196
                break;
1197
            }
1198
        } catch (Exception e) {
1199
            throw new StoreEditException(e, this.getName());
1200
        }
1201
    }
1202

    
1203
    private void invalidateIndexes() {
1204
        setIndexesValidStatus(false);
1205
    }
1206

    
1207
    private void setIndexesValidStatus(boolean valid) {
1208
        FeatureIndexes theIndexes = getIndexes();
1209
        LOGGER.debug("Setting the store indexes to valid status {}: {}", (valid
1210
            ? Boolean.TRUE : Boolean.FALSE), theIndexes);
1211
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1212
            FeatureIndex index = (FeatureIndex) iterator.next();
1213
            if (index instanceof FeatureIndexProviderServices) {
1214
                FeatureIndexProviderServices indexServices =
1215
                    (FeatureIndexProviderServices) index;
1216
                indexServices.setValid(valid);
1217
            }
1218
        }
1219
    }
1220

    
1221
    private void updateIndexes() throws FeatureIndexException {
1222
        FeatureIndexes theIndexes = getIndexes();
1223
        LOGGER.debug("Refilling indexes: {}", theIndexes);
1224
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1225
            FeatureIndex index = (FeatureIndex) iterator.next();
1226
            if (index instanceof FeatureIndexProviderServices) {
1227
                FeatureIndexProviderServices indexServices =
1228
                    (FeatureIndexProviderServices) index;
1229
                indexServices.fill(true, null);
1230
            }
1231
        }
1232
    }
1233

    
1234
    private void waitForIndexes() {
1235
        FeatureIndexes theIndexes = getIndexes();
1236
        LOGGER.debug("Waiting for indexes to finish filling: {}", theIndexes);
1237
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1238
            FeatureIndex index = (FeatureIndex) iterator.next();
1239
            if (index instanceof FeatureIndexProviderServices) {
1240
                FeatureIndexProviderServices indexServices =
1241
                    (FeatureIndexProviderServices) index;
1242
                indexServices.waitForIndex();
1243
            }
1244
        }
1245
    }
1246

    
1247
    private void disposeIndexes() {
1248
        FeatureIndexes theIndexes = getIndexes();
1249
        LOGGER.debug("Disposing indexes: {}", theIndexes);
1250
        if( theIndexes==null ) {
1251
            return;
1252
        }
1253
        for (Iterator iterator = theIndexes.iterator(); iterator.hasNext();) {
1254
            FeatureIndex index = (FeatureIndex) iterator.next();
1255
            if (index instanceof FeatureIndexProviderServices) {
1256
                FeatureIndexProviderServices indexServices =
1257
                    (FeatureIndexProviderServices) index;
1258
                indexServices.dispose();
1259
            }
1260
        }
1261
    }
1262

    
1263
    @Override
1264
    public boolean isEditing() {
1265
        return mode == MODE_FULLEDIT;
1266
    }
1267

    
1268
    @Override
1269
    public boolean isAppending() {
1270
        return mode == MODE_APPEND;
1271
    }
1272

    
1273
    @Override
1274
    synchronized public void update(EditableFeatureType type)
1275
        throws DataException {
1276
        try {
1277
            if (type == null) {
1278
                throw new NullFeatureTypeException(getName());
1279
            }
1280
            if (mode == MODE_QUERY && type.hasOnlyMetadataChanges(this.defaultFeatureType)) {
1281
                if( notifyChange(FeatureStoreNotification.BEFORE_UPDATE_TYPE, type).isCanceled() ) {
1282
                  return;
1283
                }
1284
                FeatureType theType = type.getNotEditableCopy();
1285
                if( defaultFeatureType.getId().equals(theType.getId()) ) {
1286
                    defaultFeatureType = theType;
1287
                }
1288
                List newtypes = new ArrayList();
1289
                for (FeatureType featureType : this.featureTypes) {
1290
                    if( featureType.getId().equals(theType.getId()) ) {
1291
                        newtypes.add(theType);
1292
                    } else {
1293
                        newtypes.add(featureType);
1294
                    }                    
1295
                }
1296
                this.featureTypes = newtypes;
1297
                saveDALFile();
1298
                notifyChange(FeatureStoreNotification.AFTER_UPDATE_TYPE, type);
1299
                return ;
1300
            }
1301
            boolean typehasStrongChanges = ((DefaultEditableFeatureType) type).hasStrongChanges();
1302
            if (typehasStrongChanges) {
1303
                checkInEditingMode();
1304
            }  else if(this.isAppending()) {
1305
                throw new NeedEditingModeException(this.getName());
1306
            }
1307
            // FIXME: Comprobar que es un featureType aceptable.
1308
            if( notifyChange(FeatureStoreNotification.BEFORE_UPDATE_TYPE, type).isCanceled() ) {
1309
              return;
1310
            }
1311
            newVersionOfUpdate();
1312
            
1313
            FeatureType oldt = type.getSource().getCopy();
1314
            FeatureType newt = type.getCopy();
1315
            commands.update(newt, oldt);
1316
            if (typehasStrongChanges) { 
1317
                hasStrongChanges = true;
1318
            }
1319
            notifyChange(FeatureStoreNotification.AFTER_UPDATE_TYPE, type);
1320
        } catch (Exception e) {
1321
            throw new StoreUpdateFeatureTypeException(e, this.getName());
1322
        }
1323
    }
1324

    
1325
    @Override
1326
    public void delete(Feature feature) throws DataException {
1327
        this.commands.delete(feature);
1328
    }
1329

    
1330
    synchronized public void doDelete(Feature feature) throws DataException {
1331
        try {
1332
            checkInEditingMode();
1333
            checkIsOwnFeature(feature);
1334
            if (feature instanceof EditableFeature) {
1335
                throw new StoreDeleteEditableFeatureException(getName());
1336
            }
1337
            if( notifyChange(FeatureStoreNotification.BEFORE_DELETE, feature).isCanceled() ) {
1338
              return;
1339
            }
1340

    
1341
            //Update the featureManager and the spatialManager
1342
            featureManager.delete(feature.getReference());
1343
            spatialManager.deleteFeature(feature);
1344

    
1345
            newVersionOfUpdate();
1346
            hasStrongChanges = true;
1347
            notifyChange(FeatureStoreNotification.AFTER_DELETE, feature);
1348
        } catch (Exception e) {
1349
            throw new StoreDeleteFeatureException(e, this.getName());
1350
        }
1351
    }
1352

    
1353
    public synchronized void insert(FeatureSet set) throws DataException {
1354
        switch (mode) {
1355
        case MODE_QUERY:
1356
            throw new NeedEditingModeException(this.getName());
1357

    
1358
        case MODE_APPEND:
1359
        case MODE_FULLEDIT:
1360
            break;
1361
        }
1362
        try {
1363
            set.accept((Object obj) -> {
1364
                EditableFeature ef = createNewFeature((Feature) obj);
1365
                insert(ef);
1366
            });
1367
        } catch (BaseException ex) {
1368
            throw new StoreInsertFeatureException(ex, this.getName());
1369
        }
1370
    }
1371
    
1372
    private static EditableFeature lastChangedFeature = null;
1373

    
1374
    @Override
1375
    public synchronized void insert(EditableFeature feature)
1376
        throws DataException {
1377
        LOGGER.debug("In editing mode {}, insert feature: {}", mode, feature);
1378
        try {
1379
            switch (mode) {
1380
            case MODE_QUERY:
1381
                throw new NeedEditingModeException(this.getName());
1382

    
1383
            case MODE_APPEND:
1384
                checkIsOwnFeature(feature);
1385
                if (feature.getSource() != null) {
1386
                    throw new NoNewFeatureInsertException(this.getName());
1387
                }
1388
                if( notifyChange(FeatureStoreNotification.BEFORE_INSERT, feature).isCanceled() ) {
1389
                  return;
1390
                }
1391
                this.featureCount = null;
1392
                feature.validate(Feature.UPDATE);
1393
                provider.append(((DefaultEditableFeature) feature).getData());
1394
                hasStrongChanges = true;
1395
                hasInserts = true;
1396
                notifyChange(FeatureStoreNotification.AFTER_INSERT, feature);
1397
                break;
1398

    
1399
            case MODE_FULLEDIT:
1400
                if (feature.getSource() != null) {
1401
                    throw new NoNewFeatureInsertException(this.getName());
1402
                }
1403
                feature.validate(Feature.UPDATE);
1404
                commands.insert(feature);
1405
            }
1406
        } catch (Exception e) {
1407
            throw new StoreInsertFeatureException(e, this.getName());
1408
        }
1409
    }
1410

    
1411
    synchronized public void doInsert(EditableFeature feature)
1412
        throws DataException {
1413
        checkIsOwnFeature(feature);
1414

    
1415
        waitForIndexes();
1416

    
1417
        if( notifyChange(FeatureStoreNotification.BEFORE_INSERT, feature).isCanceled() ) {
1418
          return;
1419
        }
1420
        newVersionOfUpdate();
1421
        if ((lastChangedFeature == null)
1422
            || (lastChangedFeature.getSource() != feature.getSource())) {
1423
            lastChangedFeature = feature;
1424
            feature.validate(Feature.UPDATE);
1425
            lastChangedFeature = null;
1426
        }
1427
        //Update the featureManager and the spatialManager
1428
        ((DefaultEditableFeature) feature).setInserted(true);
1429
        DefaultFeature newFeature = (DefaultFeature) feature.getNotEditableCopy();
1430

    
1431

    
1432
        featureManager.add(newFeature);
1433
        spatialManager.insertFeature(newFeature);
1434

    
1435
        hasStrongChanges = true;
1436
        hasInserts = true;
1437
        notifyChange(FeatureStoreNotification.AFTER_INSERT, feature);
1438
    }
1439

    
1440
    @Override
1441
    public void update(EditableFeature feature)
1442
    throws DataException {
1443
        if ((feature).getSource() == null) {
1444
            insert(feature);
1445
            return;
1446
        }
1447
        commands.update(feature, feature.getSource());
1448
    }
1449

    
1450
    synchronized public void doUpdate(EditableFeature feature, Feature oldFeature)
1451
        throws DataException {
1452
        try {
1453
            checkInEditingMode();
1454
            checkIsOwnFeature(feature);
1455
            if( notifyChange(FeatureStoreNotification.BEFORE_UPDATE, feature).isCanceled() ) {
1456
              return;
1457
            }
1458
            newVersionOfUpdate();
1459
            if ((lastChangedFeature == null)
1460
                || (lastChangedFeature.getSource() != feature.getSource())) {
1461
                lastChangedFeature = feature;
1462
                feature.validate(Feature.UPDATE);
1463
                lastChangedFeature = null;
1464
            }
1465

    
1466
            //Update the featureManager and the spatialManager
1467
            Feature newf = feature.getNotEditableCopy();
1468
            featureManager.update(newf, oldFeature);
1469
            spatialManager.updateFeature(newf, oldFeature);
1470

    
1471
            hasStrongChanges = true;
1472
            notifyChange(FeatureStoreNotification.AFTER_UPDATE, feature);
1473
        } catch (Exception e) {
1474
            throw new StoreUpdateFeatureException(e, this.getName());
1475
        }
1476
    }
1477

    
1478
    @Override
1479
    synchronized public void redo() throws RedoException {
1480
        Command redo = commands.getNextRedoCommand();
1481
        try {
1482
            checkInEditingMode();
1483
        } catch (NeedEditingModeException ex) {
1484
            throw new RedoException(redo, ex);
1485
        }
1486
        if( notifyChange(FeatureStoreNotification.BEFORE_REDO, redo).isCanceled() ) {
1487
          return;
1488
        }
1489
        newVersionOfUpdate();
1490
        commands.redo();
1491
        hasStrongChanges = true;
1492
        notifyChange(FeatureStoreNotification.AFTER_REDO, redo);
1493
    }
1494

    
1495
    @Override
1496
    synchronized public void undo() throws UndoException {
1497
        Command undo = commands.getNextUndoCommand();
1498
        try {
1499
            checkInEditingMode();
1500
        } catch (NeedEditingModeException ex) {
1501
            throw new UndoException(undo, ex);
1502
        }
1503
        if( notifyChange(FeatureStoreNotification.BEFORE_UNDO, undo).isCanceled() ) {
1504
          return;
1505
        }
1506
        newVersionOfUpdate();
1507
        commands.undo();
1508
        hasStrongChanges = true;
1509
        notifyChange(FeatureStoreNotification.AFTER_UNDO, undo);
1510
    }
1511

    
1512
    @Override
1513
    public List getRedoInfos() {
1514
        if (isEditing() && (commands != null)) {
1515
            return commands.getRedoInfos();
1516
        } else {
1517
            return null;
1518
        }
1519
    }
1520

    
1521
    @Override
1522
    public List getUndoInfos() {
1523
        if (isEditing() && (commands != null)) {
1524
            return commands.getUndoInfos();
1525
        } else {
1526
            return null;
1527
        }
1528
    }
1529

    
1530
    public synchronized FeatureCommandsStack getCommandsStack()
1531
        throws DataException {
1532
        checkInEditingMode();
1533
        return commands;
1534
    }
1535

    
1536
    @Override
1537
    synchronized public void cancelEditing() throws DataException {
1538
        if( spatialManager!=null ) {
1539
            spatialManager.cancelModifies();
1540
        }
1541
        try {
1542
            switch (mode) {
1543
            case MODE_QUERY:
1544
                throw new NeedEditingModeException(this.getName());
1545

    
1546
            case MODE_APPEND:
1547
                if( notifyChange(FeatureStoreNotification.BEFORE_CANCELEDITING).isCanceled() ) {
1548
                  return;
1549
                }
1550
                provider.abortAppend();
1551
                exitEditingMode();
1552
                ((FeatureSelection) this.getSelection()).deselectAll();
1553
                updateIndexes();
1554
                notifyChange(FeatureStoreNotification.AFTER_CANCELEDITING);   
1555

    
1556
            case MODE_FULLEDIT:
1557
                boolean clearSelection = this.hasStrongChanges;
1558
                if (this.selection instanceof FeatureReferenceSelection) {
1559
                    clearSelection = this.hasInserts;
1560
                }
1561
                if( notifyChange(FeatureStoreNotification.BEFORE_CANCELEDITING).isCanceled() ) {
1562
                  return;
1563
                }
1564
                exitEditingMode();
1565
                if (clearSelection) {
1566
                    ((FeatureSelection) this.getSelection()).deselectAll();
1567
                }
1568
                updateIndexes();
1569
                notifyChange(FeatureStoreNotification.AFTER_CANCELEDITING);   
1570
            }
1571
        } catch (Exception e) {
1572
            throw new StoreCancelEditingException(e, this.getName());
1573
        }
1574
    }
1575

    
1576
    @Override
1577
    synchronized public void finishEditing() throws DataException {
1578
        LOGGER.debug("finish editing of mode: {}", mode);
1579
        try {
1580

    
1581
            /*
1582
             * Selection needs to be cleared when editing stops
1583
             * to prevent conflicts with selection remaining from
1584
             * editing mode.
1585
             */
1586
//            ((FeatureSelection) this.getSelection()).deselectAll();
1587
            Map<String,List<FeatureAttributeDescriptor>> computedFields = this.getComputedFields();
1588
            switch (mode) {
1589
            case MODE_QUERY:
1590
                throw new NeedEditingModeException(this.getName());
1591

    
1592
            case MODE_APPEND:
1593
                if( selection!=null ) {
1594
                    selection = null;
1595
                }
1596
                if( notifyChange(FeatureStoreNotification.BEFORE_FINISHEDITING).isCanceled() ) {
1597
                  return;
1598
                }
1599
                saveDALFile();
1600
                provider.endAppend();
1601
                exitEditingMode();
1602
                this.updateComputedFields(computedFields);
1603
                loadDALFile();
1604
                updateIndexes();
1605
                notifyChange(FeatureStoreNotification.AFTER_FINISHEDITING);
1606
                break;
1607

    
1608
            case MODE_FULLEDIT:
1609
                if (hasStrongChanges && !this.allowWrite()) {
1610
                    throw new WriteNotAllowedException(getName());
1611
                }
1612
                if( notifyChange(FeatureStoreNotification.BEFORE_FINISHEDITING, 
1613
                        featureManager.getDeleted(),
1614
                        featureManager.getInsertedFeatures(),
1615
                        featureManager.getUpdatedFeatures(),
1616
                        featureTypeManager.getFeatureTypesChanged().iterator(),
1617
                        featureManager.isSelectionCompromised()).isCanceled() ) {
1618
                  return;
1619
                }
1620
                saveDALFile();
1621
                if(featureManager.isSelectionCompromised() && selection!=null ) {
1622
                    selection = null;
1623
                }
1624
                if (hasStrongChanges) {
1625
                    validateFeatures(Feature.FINISH_EDITING);
1626

    
1627
                    /*
1628
                     * This will throw a PerformEditingExceptionif the provider
1629
                     * does not accept the changes (for example, an invalid field name)
1630
                     */
1631
                    provider.performChanges(featureManager.getDeleted(),
1632
                        featureManager.getInserted(),
1633
                        featureManager.getUpdated(),
1634
                        removeCalculatedAttributes(featureTypeManager.getFeatureTypesChanged()).iterator());
1635
                    
1636
                }  
1637
                this.updateComputedFields(computedFields);
1638
                exitEditingMode();
1639
                loadDALFile();
1640
                updateIndexes();
1641
                notifyChange(FeatureStoreNotification.AFTER_FINISHEDITING);
1642
                break;
1643
            }
1644
        } catch (PerformEditingException pee) {
1645
            throw new WriteException(provider.getSourceId().toString(), pee);
1646
        } catch (Exception e) {
1647
            throw new FinishEditingException(e);
1648
        }
1649
    }
1650
    private Map<String,List<FeatureAttributeDescriptor>> getComputedFields() throws DataException {
1651
        Map<String,List<FeatureAttributeDescriptor>> r = new HashMap<>();
1652
        
1653
        List<FeatureType> theTypes = new ArrayList<>();
1654
        theTypes.addAll(this.getFeatureTypes());
1655
        theTypes.add(this.getDefaultFeatureType());
1656
        for( int n=0; n<theTypes.size(); n++ ) {
1657
            FeatureType type = theTypes.get(n);
1658
                for (FeatureAttributeDescriptor attrdesc : type) {
1659
                    FeatureAttributeEmulator emulator = attrdesc.getFeatureAttributeEmulator();
1660
                    if( emulator!= null) {
1661
                        List<FeatureAttributeDescriptor> l = r.get(type.getId());
1662
                        if (l==null) {
1663
                            l = new ArrayList<>();
1664
                            r.put(type.getId(), l);
1665
                        }
1666
                        l.add(attrdesc);
1667
                    }
1668
            }
1669
        }
1670
        return r;
1671
    }
1672
    private void updateComputedFields(Map<String,List<FeatureAttributeDescriptor>> computedFields) throws DataException {
1673

    
1674
        List<FeatureType> theTypes = new ArrayList<>();
1675
        theTypes.addAll(this.getFeatureTypes());
1676
        theTypes.add(this.getDefaultFeatureType());
1677
        for( int n=0; n<theTypes.size(); n++ ) {
1678
            DefaultFeatureType type = (DefaultFeatureType) theTypes.get(n);
1679
            List<FeatureAttributeDescriptor> x = computedFields.get(type.getId());
1680
            if(x!=null && !x.isEmpty()) {
1681
                for (FeatureAttributeDescriptor attrdesc : x) {
1682
                    if (type.get(attrdesc.getName())==null) {
1683
                        type.add(attrdesc);
1684
                    }
1685
                }
1686
            }
1687
        }
1688
        
1689
    }
1690
    private List<FeatureTypeChanged> removeCalculatedAttributes(List<FeatureTypeChanged> ftypes) {
1691
        // FIXME: Falta por implementar
1692
//        for (FeatureStoreProvider.FeatureTypeChanged ftype : ftypes) {
1693
//            EditableFeatureType target = (EditableFeatureType) ftype.getTarget();
1694
//            for (FeatureAttributeDescriptor attributeDescriptor : ftype.getSource().getAttributeDescriptors()) {
1695
//                if (attributeDescriptor.isComputed()) {
1696
//                    target.remove(attributeDescriptor.getName());
1697
//                }
1698
//            }
1699
//        }
1700
        return ftypes;
1701
    }
1702
    
1703

    
1704
    private void saveDALFile() {       
1705
        org.gvsig.tools.resourcesstorage.ResourcesStorage.Resource resource = null;
1706
        try {
1707
            ResourcesStorage resourcesStorage = this.getResourcesStorage();
1708
            if( resourcesStorage == null || resourcesStorage.isReadOnly() ) {
1709
                return;
1710
            }
1711
            resource = resourcesStorage.getResource("dal");
1712
            if( resource == null || resource.isReadOnly() ) {
1713
                return;
1714
            }
1715
            DALFile dalFile = DALFile.getDALFile();
1716
            dalFile.setStore(this);
1717
            if( !dalFile.isEmpty() ) {
1718
                dalFile.write(resource);
1719
            }
1720
        } catch (Throwable ex) {
1721
            LOGGER.warn("Can't save DAL resource", ex);
1722
        } finally {
1723
            IOUtils.closeQuietly(resource);
1724
        }
1725
    }
1726
    
1727
    private void loadDALFile() {
1728
        org.gvsig.tools.resourcesstorage.ResourcesStorage.Resource resource = null;
1729
        try {
1730
            ResourcesStorage resourcesStorage = this.getResourcesStorage();
1731
            if( resourcesStorage == null ) {
1732
                return;
1733
            }
1734
            resource = resourcesStorage.getResource("dal");
1735
            if( resource == null || !resource.exists() ) {
1736
                return;
1737
            }
1738
            DALFile dalFile = DALFile.getDALFile(resource);
1739
            if( !dalFile.isEmpty() ) {
1740
                dalFile.updateStore(this);
1741
            }
1742
        } catch (Throwable ex) {
1743
            LOGGER.warn("Can't load DAL resource", ex);
1744
        } finally {
1745
            IOUtils.closeQuietly(resource);
1746
        }
1747
    }
1748
    
1749
    /**
1750
     * Save changes in the provider without leaving the edit mode.
1751
     * Do not call observers to communicate a change of ediding mode.
1752
     * The operation's history is eliminated to prevent inconsistencies
1753
     * in the data.
1754
     *
1755
     * @throws DataException
1756
     */
1757
    @Override
1758
    synchronized public void commitChanges() throws DataException {
1759
      LOGGER.debug("commitChanges of mode: {}", mode);
1760
      if( !canCommitChanges() ) {
1761
              throw new WriteNotAllowedException(getName());
1762
      }
1763
      try {
1764
        switch (mode) {
1765
        case MODE_QUERY:
1766
          throw new NeedEditingModeException(this.getName());
1767

    
1768
        case MODE_APPEND:
1769
          this.provider.endAppend();
1770
          exitEditingMode();
1771
          invalidateIndexes();
1772
          this.provider.beginAppend();
1773
          hasInserts = false;
1774
          break;
1775

    
1776
        case MODE_FULLEDIT:
1777
          if (hasStrongChanges && !this.allowWrite()) {
1778
            throw new WriteNotAllowedException(getName());
1779
          }
1780
          if (hasStrongChanges) {
1781
            validateFeatures(Feature.FINISH_EDITING);
1782
            provider.performChanges(featureManager.getDeleted(),
1783
              featureManager.getInserted(),
1784
              featureManager.getUpdated(),
1785
              removeCalculatedAttributes(featureTypeManager.getFeatureTypesChanged()).iterator());
1786
          }
1787
          invalidateIndexes();
1788
          featureManager = new FeatureManager();
1789
          featureTypeManager = new FeatureTypeManager(this);
1790
          spatialManager = new SpatialManager(this, provider.getEnvelope());
1791

    
1792
          commands =
1793
            new DefaultFeatureCommandsStack(this, featureManager,
1794
              spatialManager, featureTypeManager);
1795
          featureCount = null;
1796
          hasStrongChanges = false;
1797
          hasInserts = false;
1798
          break;
1799
        }
1800
      } catch (Exception e) {
1801
        throw new FinishEditingException(e);
1802
      }
1803
    }
1804

    
1805
    @Override
1806
    synchronized public boolean canCommitChanges() throws DataException {
1807
        if ( !this.allowWrite()) {
1808
                return false;
1809
        }
1810
            switch (mode) {
1811
            default:
1812
        case MODE_QUERY:
1813
                return false;
1814

    
1815
        case MODE_APPEND:
1816
                return true;
1817

    
1818
        case MODE_FULLEDIT:
1819
            List types = this.getFeatureTypes();
1820
            for( int i=0; i<types.size(); i++ ) {
1821
                    Object type = types.get(i);
1822
                    if( type instanceof DefaultEditableFeatureType ) {
1823
                            if( ((DefaultEditableFeatureType)type).hasStrongChanges() ) {
1824
                                    return false;
1825
                            }
1826
                    }
1827
            }
1828
            return true;
1829
            }
1830
    }
1831

    
1832
    @Override
1833
    public void beginEditingGroup(String description)
1834
        throws NeedEditingModeException {
1835
        checkInEditingMode();
1836
        commands.startComplex(description);
1837
    }
1838

    
1839
    @Override
1840
    public void endEditingGroup() throws NeedEditingModeException {
1841
        checkInEditingMode();
1842
        commands.endComplex();
1843
    }
1844

    
1845
    @Override
1846
    public boolean isAppendModeSupported() {
1847
        return this.provider.supportsAppendMode();
1848
    }
1849

    
1850
    @Override
1851
    public void export(DataServerExplorer explorer, String provider,
1852
        NewFeatureStoreParameters params) throws DataException {
1853

    
1854
        if (this.getFeatureTypes().size() != 1) {
1855
            throw new NotYetImplemented(
1856
                "export whith more than one type not yet implemented");
1857
        }
1858
        FeatureSelection featureSelection = (FeatureSelection) getSelection();
1859
        FeatureStore target = null;
1860
        FeatureSet features = null;
1861
        DisposableIterator iterator = null;
1862
        try {
1863
            FeatureType type = this.getDefaultFeatureType();
1864
            if ((params.getDefaultFeatureType() == null)
1865
                || (params.getDefaultFeatureType().size() == 0)) {
1866
                params.setDefaultFeatureType(type.getEditable());
1867

    
1868
            }
1869
            explorer.add(provider, params, true);
1870

    
1871
            DataManager manager = DALLocator.getDataManager();
1872
            target = (FeatureStore) manager.openStore(provider, params);
1873
            FeatureType targetType = target.getDefaultFeatureType();
1874

    
1875
            target.edit(MODE_APPEND);
1876
            FeatureAttributeDescriptor[] pkattrs = type.getPrimaryKey();
1877
            if (featureSelection.getSize() > 0) {
1878
                features = this.getFeatureSelection();
1879
            } else {
1880
                if ((pkattrs != null) && (pkattrs.length > 0)) {
1881
                    FeatureQuery query = createFeatureQuery();
1882
                    for (FeatureAttributeDescriptor pkattr : pkattrs) {
1883
                        query.getOrder().add(pkattr.getName(), true);
1884
                    }
1885
                    features = this.getFeatureSet(query);
1886
                } else {
1887
                    features = this.getFeatureSet();
1888
                }
1889
            }
1890
            iterator = features.fastIterator();
1891
            while (iterator.hasNext()) {
1892
                DefaultFeature feature = (DefaultFeature) iterator.next();
1893
                target.insert(target.createNewFeature(targetType, feature));
1894
            }
1895
            target.finishEditing();
1896
            target.dispose();
1897
        } catch (Exception e) {
1898
            throw new DataExportException(e, params.toString());
1899
        } finally {
1900
            dispose(iterator);
1901
            dispose(features);
1902
            dispose(target);
1903
        }
1904
    }
1905

    
1906
    public void copyTo(final FeatureStore target) {
1907
        boolean finishEditingAtEnd = false;
1908
        try {
1909
            if( !target.isEditing() && !target.isAppending() ) {
1910
                finishEditingAtEnd = true;
1911
                target.edit(MODE_APPEND);
1912
            }
1913
            this.accept(new Visitor() {
1914
                @Override
1915
                public void visit(Object obj) throws VisitCanceledException, BaseException {
1916
                    Feature f_src = (Feature) obj;
1917
                    EditableFeature f_dst = target.createNewFeature(f_src);
1918
                    target.insert(f_dst);
1919
                }
1920
            });
1921
            if( finishEditingAtEnd ) {
1922
                target.finishEditing();
1923
            }
1924
            
1925
        } catch(Exception ex) {
1926
            try {
1927
                if( finishEditingAtEnd ) {
1928
                    target.cancelEditing();
1929
                }
1930
            } catch (Exception ex1) {
1931
            }
1932
            throw new RuntimeException("Can't copy store.",ex);
1933
        }
1934
            
1935
    }
1936
    
1937
    //
1938
    // ====================================================================
1939
    // Obtencion de datos
1940
    // getDataCollection, getFeatureCollection
1941
    //
1942

    
1943
    @Override
1944
    public DataSet getDataSet() throws DataException {
1945
        checkNotInAppendMode();
1946
        FeatureQuery query =
1947
            new DefaultFeatureQuery(this.getDefaultFeatureType());
1948
        return new DefaultFeatureSet(this, query);
1949
    }
1950

    
1951
    @Override
1952
    public DataSet getDataSet(DataQuery dataQuery) throws DataException {
1953
        checkNotInAppendMode();
1954
        return new DefaultFeatureSet(this, (FeatureQuery) dataQuery);
1955
    }
1956

    
1957
    @Override
1958
    public void getDataSet(Observer observer) throws DataException {
1959
        checkNotInAppendMode();
1960
        this.getFeatureSet(null, observer);
1961
    }
1962

    
1963
    @Override
1964
    public void getDataSet(DataQuery dataQuery, Observer observer)
1965
        throws DataException {
1966
        checkNotInAppendMode();
1967
        this.getFeatureSet((FeatureQuery) dataQuery, observer);
1968
    }
1969

    
1970
    @Override
1971
    public FeatureSet getFeatureSet() throws DataException {
1972
        return this.getFeatureSet((FeatureQuery)null);
1973
    }
1974

    
1975
    @Override
1976
    public FeatureSet getFeatureSet(FeatureQuery featureQuery)
1977
        throws DataException {
1978
        checkNotInAppendMode();
1979
        if( featureQuery==null ) {
1980
            featureQuery = new DefaultFeatureQuery(this.getDefaultFeatureType());
1981
        }
1982
        return new DefaultFeatureSet(this, featureQuery);
1983
    }
1984

    
1985
    @Override
1986
    public FeatureSet getFeatureSet(String filter) throws DataException {
1987
        return this.getFeatureSet(filter, null, true);
1988
    }
1989

    
1990
    @Override
1991
    public FeatureSet getFeatureSet(String filter, String sortBy) throws DataException {
1992
        return this.getFeatureSet(filter, sortBy, true);
1993
    }
1994

    
1995
    @Override
1996
    public FeatureSet getFeatureSet(Expression filter) throws DataException {
1997
        return this.getFeatureSet(filter, null, true);
1998
    }
1999
    
2000
    @Override
2001
    public FeatureSet getFeatureSet(Expression filter, String sortBy) throws DataException {
2002
        return this.getFeatureSet(filter, sortBy, true);
2003
    }
2004

    
2005
    @Override
2006
    public FeatureSet getFeatureSet(Expression filter, String sortBy, boolean asc) throws DataException {
2007
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2008
        return this.getFeatureSet(query);
2009
    }
2010
    
2011
    @Override
2012
    public FeatureSet getFeatureSet(String filter, String sortBy, boolean asc) throws DataException {
2013
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2014
        return this.getFeatureSet(query);
2015
    }
2016
    
2017
    @Override
2018
    public List<Feature> getFeatures(String filter)  {
2019
        return this.getFeatures(filter, null, true);
2020
    }
2021

    
2022
    @Override
2023
    public List<Feature> getFeatures(String filter, String sortBy)  {
2024
        return this.getFeatures(filter, sortBy, true);
2025
    }
2026

    
2027
    @Override
2028
    public List<Feature> getFeatures(String filter, String sortBy, boolean asc)  {
2029
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2030
        return this.getFeatures(query, 0);
2031
    }
2032
    
2033
    @Override
2034
    public List<Feature> getFeatures(Expression filter)  {
2035
        return this.getFeatures(filter, null, true);
2036
    }
2037

    
2038
    @Override
2039
    public List<Feature> getFeatures(Expression filter, String sortBy)  {
2040
        return this.getFeatures(filter, sortBy, true);
2041
    }
2042

    
2043
    @Override
2044
    public List<Feature> getFeatures(Expression filter, String sortBy, boolean asc)  {
2045
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2046
        return this.getFeatures(query, 0);
2047
    }
2048
    
2049
    @Override
2050
    public List<Feature> getFeatures(FeatureQuery query)  {
2051
        return this.getFeatures(query, 0);
2052
    }
2053
    
2054
    @Override
2055
    public List<Feature> getFeatures(FeatureQuery query, int pageSize)  {
2056
        try {
2057
            if( pageSize<=0 ) {
2058
                pageSize = 100;
2059
            }
2060
            FeaturePagingHelper pager = this.dataManager.createFeaturePagingHelper(this, query, pageSize);
2061
            return pager.asList();
2062
        } catch (BaseException ex) {
2063
            throw new RuntimeException("Can't create the list of features.", ex);
2064
        }
2065
    }
2066

    
2067
    @Override
2068
    public List<Feature> getFeatures() {
2069
        return this.getFeatures(null, 0);
2070
    }
2071

    
2072
    @Override
2073
    public Feature first() throws DataException {
2074
        return this.findFirst((FeatureQuery)null);
2075
    }
2076
    
2077
    @Override
2078
    public Feature findFirst(String filter) throws DataException {
2079
        return this.findFirst(filter, (String)null, true);
2080
    }
2081

    
2082
    @Override
2083
    public Feature findFirst(String filter, String sortBy) throws DataException {
2084
        return this.findFirst(filter, sortBy, true);
2085
    }
2086

    
2087
    @Override
2088
    public Feature findFirst(String filter, String sortBy, boolean asc) throws DataException {
2089
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2090
        return findFirst(query);
2091
    }
2092

    
2093
    @Override
2094
    public Feature findFirst(String filter, Expression sortBy, boolean asc) throws DataException {
2095
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2096
        return findFirst(query);
2097
    }
2098
    
2099
    @Override
2100
    public Feature findFirst(Expression filter) throws DataException {
2101
        return this.findFirst(filter, (String)null, true);
2102
    }
2103

    
2104
    @Override
2105
    public Feature findFirst(Expression filter, String sortBy) throws DataException {
2106
        return this.findFirst(filter, sortBy, true);
2107
    }
2108

    
2109
    @Override
2110
    public Feature findFirst(Expression filter, String sortBy, boolean asc) throws DataException {
2111
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2112
        return findFirst(query);
2113
    }
2114
    
2115
    @Override
2116
    public Feature findFirst(Expression filter, Expression sortBy, boolean asc) throws DataException {
2117
        FeatureQuery query = this.createFeatureQuery(filter, sortBy, asc);
2118
        return findFirst(query);
2119
    }
2120
    
2121
    @Override
2122
    public Feature findFirst(FeatureQuery query) throws DataException {
2123
        if( query == null ) {
2124
            query = this.createFeatureQuery();
2125
        } else {
2126
            query = query.getCopy();
2127
        }
2128
        query.setLimit(1);
2129
        final MutableObject<Feature> feature = new MutableObject<>();
2130
        try {
2131
            this.accept(new Visitor() {
2132
                @Override
2133
                public void visit(Object obj) throws VisitCanceledException, BaseException {
2134
                    feature.setValue((Feature) obj);
2135
                    throw new VisitCanceledException();
2136
                }
2137
            }, query);
2138
        } catch(VisitCanceledException ex) {
2139

    
2140
        } catch(DataException ex) {
2141
            throw ex;
2142
        } catch(Exception ex) {
2143
            throw new RuntimeException("", ex);
2144
        }
2145
        return feature.getValue();
2146
    }
2147

    
2148
    @Override
2149
    public void accept(Visitor visitor) throws BaseException {
2150
        this.accept(visitor, null);
2151
    }
2152

    
2153
    @Override
2154
    public void accept(Visitor visitor, DataQuery dataQuery)
2155
        throws BaseException {
2156
        FeatureSet set = getFeatureSet((FeatureQuery) dataQuery);
2157
        try {
2158
            set.accept(visitor);
2159
        } finally {
2160
            set.dispose();
2161
        }
2162
    }
2163

    
2164
    public FeatureType getFeatureType(FeatureQuery featureQuery)
2165
        throws DataException {
2166
        DefaultFeatureType fType =
2167
            (DefaultFeatureType) this.getFeatureType(featureQuery
2168
                .getFeatureTypeId());
2169
        if( featureQuery.hasAttributeNames() || 
2170
            featureQuery.hasConstantsAttributeNames() ||
2171
            fType.hasRequiredFields()    
2172
            ) {
2173
            if( featureQuery.hasGroupByColumns()) {
2174
                return fType.getSubtype(featureQuery.getAttributeNames(), featureQuery.getConstantsAttributeNames(), false );
2175
            } else {
2176
                return fType.getSubtype(featureQuery.getAttributeNames(), featureQuery.getConstantsAttributeNames());
2177
            }
2178
        }
2179
        return fType;
2180
    }
2181

    
2182
    @Override
2183
    public void getFeatureSet(Observer observer) throws DataException {
2184
        checkNotInAppendMode();
2185
        this.getFeatureSet(null, observer);
2186
    }
2187

    
2188
    @Override
2189
    public void getFeatureSet(FeatureQuery query, Observer observer)
2190
        throws DataException {
2191
        class LoadInBackGround implements Runnable {
2192

    
2193
            private final FeatureStore store;
2194
            private final FeatureQuery query;
2195
            private final Observer observer;
2196

    
2197
            public LoadInBackGround(FeatureStore store, FeatureQuery query,
2198
                Observer observer) {
2199
                this.store = store;
2200
                this.query = query;
2201
                this.observer = observer;
2202
            }
2203

    
2204
            void notify(FeatureStoreNotification theNotification) {
2205
                observer.update(store, theNotification);
2206
            }
2207

    
2208
            @Override
2209
            public void run() {
2210
                FeatureSet set = null;
2211
                try {
2212
                    set = store.getFeatureSet(query);
2213
                    notify(new DefaultFeatureStoreNotification(store,
2214
                        FeatureStoreNotification.LOAD_FINISHED, set));
2215
                } catch (Exception e) {
2216
                    notify(new DefaultFeatureStoreNotification(store,
2217
                        FeatureStoreNotification.LOAD_FINISHED, e));
2218
                } finally {
2219
                    dispose(set);
2220
                }
2221
            }
2222
        }
2223

    
2224
        checkNotInAppendMode();
2225
        if (query == null) {
2226
            query = new DefaultFeatureQuery(this.getDefaultFeatureType());
2227
        }
2228
        LoadInBackGround task = new LoadInBackGround(this, query, observer);
2229
        Thread thread = new Thread(task, "Load Feature Set in background");
2230
        thread.start();
2231
    }
2232

    
2233
    @Override
2234
    public Feature getFeatureByReference(FeatureReference reference)
2235
        throws DataException {
2236
        checkNotInAppendMode();
2237
        DefaultFeatureReference ref = (DefaultFeatureReference) reference;
2238
        FeatureType featureType;
2239
        if (ref.getFeatureTypeId() == null) {
2240
            featureType = this.getDefaultFeatureType();
2241
        } else {
2242
            featureType = this.getFeatureType(ref.getFeatureTypeId());
2243
        }
2244
        return this.getFeatureByReference(reference, featureType);
2245
    }
2246

    
2247
    @Override
2248
    public Feature getFeatureByReference(FeatureReference reference,
2249
        FeatureType featureType) throws DataException {
2250
        checkNotInAppendMode();
2251
        featureType = fixFeatureType((DefaultFeatureType) featureType);
2252
        if (this.mode == MODE_FULLEDIT) {
2253
            Feature f = featureManager.get(reference, this, featureType);
2254
            if (f != null) {
2255
                return f;
2256
            }
2257
        }
2258

    
2259
        FeatureType sourceFeatureType = featureType;
2260
        if (!this.transforms.isEmpty()) {
2261
            sourceFeatureType = this.transforms.getSourceFeatureTypeFrom(featureType);
2262
        }
2263
        // TODO comprobar que el id es de este store
2264

    
2265
        DefaultFeature feature =
2266
            new DefaultFeature(this,
2267
                this.provider.getFeatureProviderByReference(
2268
                    (FeatureReferenceProviderServices) reference, sourceFeatureType));
2269

    
2270
        if (!this.transforms.isEmpty()) {
2271
            return this.transforms.applyTransform(feature, featureType);
2272
        }
2273
        return feature;
2274
    }
2275

    
2276
    //
2277
    // ====================================================================
2278
    // Gestion de features
2279
    //
2280

    
2281
    private FeatureType fixFeatureType(DefaultFeatureType type)
2282
        throws DataException {
2283
        FeatureType original = this.getDefaultFeatureType();
2284

    
2285
        if ((type == null) || type.equals(original)) {
2286
            return original;
2287
        } else {
2288
            if (!type.isSubtypeOf(original)) {
2289
                Iterator iter = this.getFeatureTypes().iterator();
2290
                FeatureType tmpType;
2291
                boolean found = false;
2292
                while (iter.hasNext()) {
2293
                    tmpType = (FeatureType) iter.next();
2294
                    if (type.equals(tmpType)) {
2295
                        return type;
2296

    
2297
                    } else
2298
                        if (type.isSubtypeOf(tmpType)) {
2299
                            found = true;
2300
                            original = tmpType;
2301
                            break;
2302
                        }
2303

    
2304
                }
2305
                if (!found) {
2306
                    throw new IllegalFeatureTypeException(getName());
2307
                }
2308
            }
2309
        }
2310

    
2311
        // Checks that type has all fields of pk
2312
        // else add the missing attributes at the end.
2313
        if (!original.hasOID()) {
2314
            // Gets original pk attributes
2315
            DefaultEditableFeatureType edOriginal =
2316
                (DefaultEditableFeatureType) original.getEditable();
2317
            FeatureAttributeDescriptor orgAttr;
2318
            Iterator edOriginalIter = edOriginal.iterator();
2319
            while (edOriginalIter.hasNext()) {
2320
                orgAttr = (FeatureAttributeDescriptor) edOriginalIter.next();
2321
                if (!orgAttr.isPrimaryKey()) {
2322
                    edOriginalIter.remove();
2323
                }
2324
            }
2325

    
2326
            // Checks if all pk attributes are in type
2327
            Iterator typeIterator;
2328
            edOriginalIter = edOriginal.iterator();
2329
            FeatureAttributeDescriptor attr;
2330
            while (edOriginalIter.hasNext()) {
2331
                orgAttr = (FeatureAttributeDescriptor) edOriginalIter.next();
2332
                typeIterator = type.iterator();
2333
                while (typeIterator.hasNext()) {
2334
                    attr = (FeatureAttributeDescriptor) typeIterator.next();
2335
                    if (attr.getName().equals(orgAttr.getName())) {
2336
                        edOriginalIter.remove();
2337
                        break;
2338
                    }
2339
                }
2340
            }
2341

    
2342
            // add missing pk attributes if any
2343
            if (edOriginal.size() > 0) {
2344
                boolean isEditable = type instanceof DefaultEditableFeatureType;
2345
                DefaultEditableFeatureType edType =
2346
                    (DefaultEditableFeatureType) original.getEditable();
2347
                edType.clear();
2348
                edType.addAll(type);
2349
                edType.addAll(edOriginal);
2350
                if (!isEditable) {
2351
                    type = (DefaultFeatureType) edType.getNotEditableCopy();
2352
                }
2353
            }
2354

    
2355
        }
2356

    
2357
        return type;
2358
    }
2359

    
2360
    @Override
2361
    public void validateFeatures(int mode) throws DataException {
2362
        FeatureSet collection = null;
2363
        DisposableIterator iter = null;
2364
        try {
2365
            FeatureRules rules = this.getDefaultFeatureType().getRules();
2366
            if( rules==null || rules.isEmpty() ) {
2367
                return;
2368
            }
2369
            checkNotInAppendMode();
2370
            collection = this.getFeatureSet();
2371
            iter = collection.fastIterator();
2372
            long previousVersionOfUpdate = currentVersionOfUpdate();
2373
            while (iter.hasNext()) {
2374
                ((DefaultFeature) iter.next()).validate(mode);
2375
                if (previousVersionOfUpdate != currentVersionOfUpdate()) {
2376
                    throw new ConcurrentDataModificationException(getName());
2377
                }
2378
            }
2379
        } catch (Exception e) {
2380
            throw new ValidateFeaturesException(e, getName());
2381
        } finally {
2382
            DisposeUtils.disposeQuietly(iter);
2383
            DisposeUtils.disposeQuietly(collection);
2384
        }
2385
    }
2386

    
2387
    @Override
2388
    public FeatureType getDefaultFeatureType() throws DataException {
2389
        try {
2390

    
2391
            if (isEditing()) {
2392
                FeatureType auxFeatureType =
2393
                    featureTypeManager.getType(defaultFeatureType.getId());
2394
                if (auxFeatureType != null) {
2395
                    return avoidEditable(auxFeatureType);
2396
                }
2397
            }
2398
            FeatureType type = this.transforms.getDefaultFeatureType();
2399
                if (type != null) {
2400
                return avoidEditable(type);
2401
                }
2402

    
2403
            return avoidEditable(defaultFeatureType);
2404

    
2405
        } catch (Exception e) {
2406
            throw new GetFeatureTypeException(e, getName());
2407
        }
2408
    }
2409

    
2410
    @Override
2411
    public FeatureType getDefaultFeatureTypeQuietly() {
2412
      try {
2413
        return this.getDefaultFeatureType();
2414
      } catch(Exception ex) {
2415
        return null;
2416
      }
2417
    }
2418
    
2419
    private FeatureType avoidEditable(FeatureType ft) {
2420
        if (ft instanceof EditableFeatureType) {
2421
            return ((EditableFeatureType) ft).getNotEditableCopy();
2422
        } else {
2423
            return ft;
2424
        }
2425
    }
2426

    
2427
    @Override
2428
    public FeatureType getFeatureType(String featureTypeId)
2429
        throws DataException {
2430
        if (featureTypeId == null) {
2431
            return this.getDefaultFeatureType();
2432
        }
2433
        try {
2434
            if (isEditing()) {
2435
                FeatureType auxFeatureType =
2436
                    featureTypeManager.getType(featureTypeId);
2437
                if (auxFeatureType != null) {
2438
                    return auxFeatureType;
2439
                }
2440
            }
2441
            FeatureType type = this.transforms.getFeatureType(featureTypeId);
2442
            if (type != null) {
2443
                return type;
2444
            }
2445
            Iterator iter = this.featureTypes.iterator();
2446
            while (iter.hasNext()) {
2447
                type = (FeatureType) iter.next();
2448
                if (type.getId().equals(featureTypeId)) {
2449
                    return type;
2450
                }
2451
            }
2452
            return null;
2453
        } catch (Exception e) {
2454
            throw new GetFeatureTypeException(e, getName());
2455
        }
2456
    }
2457

    
2458
    public FeatureType getProviderDefaultFeatureType() {
2459
        return defaultFeatureType;
2460
    }
2461

    
2462
    @Override
2463
    public List getFeatureTypes() throws DataException {
2464
        try {
2465
            List types;
2466
            if (isEditing()) {
2467
                types = new ArrayList();
2468
                Iterator it = featureTypes.iterator();
2469
                while (it.hasNext()) {
2470
                    FeatureType type = (FeatureType) it.next();
2471
                    FeatureType typeaux =
2472
                        featureTypeManager.getType(type.getId());
2473
                    if (typeaux != null) {
2474
                        types.add(typeaux);
2475
                    } else {
2476
                        types.add(type);
2477
                    }
2478
                }
2479
                it = featureTypeManager.newsIterator();
2480
                while (it.hasNext()) {
2481
                    FeatureType type = (FeatureType) it.next();
2482
                    types.add(type);
2483
                }
2484
            } else {
2485
                types = this.transforms.getFeatureTypes();
2486
                if (types == null) {
2487
                    types = featureTypes;
2488
                }
2489
            }
2490
            return Collections.unmodifiableList(types);
2491
        } catch (Exception e) {
2492
            throw new GetFeatureTypeException(e, getName());
2493
        }
2494
    }
2495

    
2496
    public List getProviderFeatureTypes() throws DataException {
2497
        return Collections.unmodifiableList(this.featureTypes);
2498
    }
2499

    
2500
    @Override
2501
    public Feature createFeature(FeatureProvider data) throws DataException {
2502
        DefaultFeature feature = new DefaultFeature(this, data);
2503
        return feature;
2504
    }
2505

    
2506
    public Feature createFeature(FeatureProvider data, FeatureType type)
2507
        throws DataException {
2508
        // FIXME: falta por implementar
2509
        // Comprobar si es un subtipo del feature de data
2510
        // y construir un feature usando el subtipo.
2511
        // Probablemente requiera generar una copia del data.
2512
        throw new NotYetImplemented();
2513
    }
2514

    
2515
    @Override
2516
    public EditableFeature createNewFeature(FeatureType type,
2517
        Feature defaultValues) throws DataException {
2518
        try {
2519
            FeatureProvider data = createNewFeatureProvider(type);
2520
            DefaultEditableFeature feature =
2521
                new DefaultEditableFeature(this, data);
2522
            feature.initializeValues(defaultValues);
2523
            data.setNew(true);
2524

    
2525
            return feature;
2526
        } catch (Exception e) {
2527
            throw new CreateFeatureException(e, getName());
2528
        }
2529
    }
2530

    
2531
    private FeatureProvider createNewFeatureProvider(FeatureType type)
2532
        throws DataException {
2533
        type = this.fixFeatureType((DefaultFeatureType) type);
2534
        FeatureProvider data = this.provider.createFeatureProvider(type);
2535
        data.setNew(true);
2536
        if (type.hasOID() && (data.getOID() == null)) {
2537
            data.setOID(this.provider.createNewOID());
2538
        } else {
2539
            data.setOID(this.getTemporalOID());
2540
        }
2541
        return data;
2542

    
2543
    }
2544

    
2545
    @Override
2546
    public EditableFeature createNewFeature(FeatureType type,
2547
        boolean defaultValues) throws DataException {
2548
        try {
2549
            FeatureProvider data = createNewFeatureProvider(type);
2550
            DefaultEditableFeature feature =
2551
                new DefaultEditableFeature(this, data);
2552
            if (defaultValues) {
2553
                feature.initializeValues();
2554
            }
2555
            return feature;
2556
        } catch (Exception e) {
2557
            throw new CreateFeatureException(e, getName());
2558
        }
2559
    }
2560

    
2561
    @Override
2562
    public EditableFeature createNewFeature(boolean defaultValues)
2563
        throws DataException {
2564
        return this.createNewFeature(this.getDefaultFeatureType(),
2565
            defaultValues);
2566
    }
2567

    
2568
    @Override
2569
    public EditableFeature createNewFeature() throws DataException {
2570
        return this.createNewFeature(this.getDefaultFeatureType(), true);
2571
    }
2572

    
2573
    @Override
2574
    public EditableFeature createNewFeature(Feature defaultValues) throws DataException {
2575
        FeatureType ft = this.getDefaultFeatureType();
2576
        EditableFeature f = this.createNewFeature(ft, false);
2577
        f.copyFrom(defaultValues);
2578
        return f;
2579
    }
2580

    
2581
    @Override
2582
    public EditableFeature createNewFeature(JsonObject defaultValues) throws DataException {
2583
        FeatureType ft = this.getDefaultFeatureType();
2584
        EditableFeature f = this.createNewFeature(ft, false);
2585
        f.copyFrom(defaultValues);
2586
        return f;
2587
    }
2588

    
2589
    @Override
2590
    public EditableFeatureType createFeatureType() {
2591
        EditableFeatureType ftype = new DefaultEditableFeatureType(this);
2592
        return ftype;
2593
    }
2594

    
2595
    @Override
2596
    public EditableFeatureType createFeatureType(String id) {
2597
        DefaultEditableFeatureType ftype = new DefaultEditableFeatureType(this, id);
2598
        return ftype;
2599
    }
2600

    
2601
    //
2602
    // ====================================================================
2603
    // Index related methods
2604
    //
2605

    
2606
    @Override
2607
    public FeatureIndexes getIndexes() {
2608
        return this.indexes;
2609
    }
2610

    
2611
    @Override
2612
    public FeatureIndex createIndex(FeatureType featureType,
2613
        String attributeName, String indexName) throws DataException {
2614
        return createIndex(null, featureType, attributeName, indexName);
2615
    }
2616

    
2617
    @Override
2618
    public FeatureIndex createIndex(String indexTypeName,
2619
        FeatureType featureType, String attributeName, String indexName)
2620
        throws DataException {
2621

    
2622
        return createIndex(indexTypeName, featureType, attributeName,
2623
            indexName, false, null);
2624
    }
2625

    
2626
    @Override
2627
    public FeatureIndex createIndex(FeatureType featureType,
2628
        String attributeName, String indexName, Observer observer)
2629
        throws DataException {
2630
        return createIndex(null, featureType, attributeName, indexName,
2631
            observer);
2632
    }
2633

    
2634
    @Override
2635
    public FeatureIndex createIndex(String indexTypeName,
2636
        FeatureType featureType, String attributeName, String indexName,
2637
        final Observer observer) throws DataException {
2638

    
2639
        return createIndex(indexTypeName, featureType, attributeName,
2640
            indexName, true, observer);
2641
    }
2642

    
2643
    private FeatureIndex createIndex(String indexTypeName,
2644
        FeatureType featureType, String attributeName, String indexName,
2645
        boolean background, final Observer observer) throws DataException {
2646

    
2647
        checkNotInAppendMode();
2648
        FeatureIndexProviderServices index;
2649
        index = dataManager.createFeatureIndexProvider(indexTypeName, this,
2650
                featureType, indexName,
2651
                featureType.getAttributeDescriptor(attributeName));
2652

    
2653
        try {
2654
            index.fill(background, observer);
2655
        } catch (FeatureIndexException e) {
2656
            throw new InitializeException(index.getName(), e);
2657
        }
2658

    
2659
        ((DefaultFeatureIndexes) getIndexes()).addIndex(index);
2660
        return index;
2661
    }
2662

    
2663
    //
2664
    // ====================================================================
2665
    // Transforms related methods
2666
    //
2667

    
2668
    @Override
2669
    public FeatureStoreTransforms getTransforms() {
2670
        return this.transforms;
2671
    }
2672

    
2673
    @Override
2674
    public FeatureQuery createFeatureQuery() {
2675
        return new DefaultFeatureQuery();
2676
    }
2677
    
2678
    @Override
2679
    public FeatureQuery createFeatureQuery(Expression filter, String sortBy, boolean asc) {
2680
        FeatureQuery query = null;
2681
        if( filter!=null ) {
2682
            query = this.createFeatureQuery();
2683
            query.setFilter(filter);
2684
        }
2685
        if( !StringUtils.isEmpty(sortBy) ) {
2686
            if( query == null ) {
2687
                query = this.createFeatureQuery();
2688
            }
2689
            if ( StringUtils.containsAny(sortBy, "(", ")") ) {
2690
                throw new IllegalArgumentException("Incorrect sortBy expression");
2691
            }
2692
            String[] attrnames;
2693
            if( sortBy.contains(",") ) {
2694
                attrnames = StringUtils.split(sortBy, ",");
2695
            } else {
2696
                attrnames = new String[] { sortBy };
2697
            }
2698
            for (String attrname : attrnames) {
2699
                attrname = attrname.trim();
2700
                if( attrname.startsWith("-") ) {
2701
                    query.getOrder().add(sortBy.substring(1).trim(), false);
2702
                } else if( attrname.endsWith("-") ) { 
2703
                    query.getOrder().add(sortBy.substring(0,sortBy.length()-1).trim(), false);
2704
                } else if( attrname.startsWith("+") ) {
2705
                    query.getOrder().add(sortBy.substring(1).trim(), true);
2706
                } else if( attrname.endsWith("-") ) { 
2707
                    query.getOrder().add(sortBy.substring(0,sortBy.length()-1).trim(), true);
2708
                } else {
2709
                    query.getOrder().add(sortBy, asc);
2710
                }
2711
            }
2712
        }
2713
        if( query != null ) {
2714
            query.retrievesAllAttributes();
2715
        }
2716
        return query;
2717
    }
2718
    
2719
    @Override
2720
    public FeatureQuery createFeatureQuery(String filter, String sortBy, boolean asc) {
2721
        if( StringUtils.isBlank(filter) ) {
2722
            return this.createFeatureQuery(
2723
                    (Expression)null, 
2724
                    sortBy, 
2725
                    asc
2726
            );
2727
        } else {
2728
            return this.createFeatureQuery(
2729
                    ExpressionUtils.createExpression(filter), 
2730
                    sortBy, 
2731
                    asc
2732
            );
2733
        }
2734
    }
2735
    
2736
    public FeatureQuery createFeatureQuery(Expression filter, Expression sortBy, boolean asc) {
2737
        FeatureQuery query = null;
2738
        if( filter != null ) {
2739
            query = this.createFeatureQuery();
2740
            query.setFilter(filter);
2741
        }
2742
        if( sortBy !=  null) {
2743
            if( query == null ) {
2744
                query = this.createFeatureQuery();
2745
            }
2746
            query.getOrder().add(sortBy, asc);
2747
        }
2748
        
2749
        if( query != null ) {
2750
            query.retrievesAllAttributes();
2751
        }
2752
        return query;
2753
    }
2754
    
2755
    public FeatureQuery createFeatureQuery(String filter, Expression sortBy, boolean asc) {
2756
        if( StringUtils.isBlank(filter) ) {
2757
            return this.createFeatureQuery(
2758
                    (Expression)null, 
2759
                    sortBy, 
2760
                    asc
2761
            );
2762
        } else {
2763
            return this.createFeatureQuery(
2764
                    ExpressionUtils.createExpression(filter), 
2765
                    sortBy, 
2766
                    asc
2767
            );
2768
        }
2769
    }
2770
    
2771
    @Override
2772
    public DataQuery createQuery() {
2773
        return createFeatureQuery();
2774
    }
2775

    
2776
    //
2777
    // ====================================================================
2778
    // UndoRedo related methods
2779
    //
2780

    
2781
    @Override
2782
    public boolean canRedo() {
2783
        return commands.canRedo();
2784
    }
2785

    
2786
    @Override
2787
    public boolean canUndo() {
2788
        return commands.canUndo();
2789
    }
2790

    
2791
    @Override
2792
    public void redo(int num) throws RedoException {
2793
        for (int i = 0; i < num; i++) {
2794
            redo();
2795
        }
2796
    }
2797

    
2798
    @Override
2799
    public void undo(int num) throws UndoException {
2800
        for (int i = 0; i < num; i++) {
2801
            undo();
2802
        }
2803
    }
2804

    
2805
    //
2806
    // ====================================================================
2807
    // Metadata related methods
2808
    //
2809

    
2810
    @Override
2811
    public Object getMetadataID() {
2812
        return this.provider.getSourceId();
2813
    }
2814

    
2815
    @Override
2816
    public void delegate(DynObject dynObject) {
2817
        this.metadata.delegate(dynObject);
2818
    }
2819

    
2820
    @Override
2821
    public DynClass getDynClass() {
2822
        return this.metadata.getDynClass();
2823
    }
2824

    
2825
    @Override
2826
    public Object getDynValue(String name) throws DynFieldNotFoundException {
2827
        try {
2828
            if (this.transforms.hasDynValue(name)) {
2829
                return this.transforms.getDynValue(name);
2830
            }
2831
            if (this.metadata.hasDynValue(name)) {
2832
                return this.metadata.getDynValue(name);
2833
            }
2834
            if (METADATA_PROVIDER.equalsIgnoreCase(name)) {
2835
                return this.provider.getProviderName();
2836
            } else if (METADATA_CONTAINERNAME.equalsIgnoreCase(name)) {
2837
                return this.provider.getSourceId();
2838
            } else if (METADATA_FEATURETYPE.equalsIgnoreCase(name)) {
2839
                try {
2840
                    return this.getDefaultFeatureType();
2841
                } catch (DataException e) {
2842
                    return null;
2843
                }
2844
            }
2845
            return this.metadata.getDynValue(name);
2846
        } catch(Exception ex ) {
2847
            LOGGER.debug("Can't retrieve the value of '"+name+"' in store '"+this.getName()+"'.",ex);
2848
            return null;
2849
        }
2850
    }
2851

    
2852
    @Override
2853
    public boolean hasDynValue(String name) {
2854
        if (this.transforms.hasDynValue(name)) {
2855
            return true;
2856
        }
2857
        return this.metadata.hasDynValue(name);
2858
    }
2859

    
2860
    @Override
2861
    public boolean hasDynMethod(String name) {
2862
        return ((DynObject_v2)this.metadata).hasDynMethod(name);
2863
    }
2864

    
2865
    @Override
2866
    public void implement(DynClass dynClass) {
2867
        this.metadata.implement(dynClass);
2868
    }
2869

    
2870
    @Override
2871
    public Object invokeDynMethod(String name, Object[] args)
2872
        throws DynMethodException {
2873
        return this.metadata.invokeDynMethod(this, name, args);
2874
    }
2875

    
2876
    @Override
2877
    public Object invokeDynMethod(int code, Object[] args)
2878
        throws DynMethodException {
2879
        return this.metadata.invokeDynMethod(this, code, args);
2880
    }
2881

    
2882
    @Override
2883
    public void setDynValue(String name, Object value)
2884
        throws DynFieldNotFoundException {
2885
                if( this.transforms.hasDynValue(name) ) {
2886
                        this.transforms.setDynValue(name, value);
2887
                        return;
2888
                }
2889
        this.metadata.setDynValue(name, value);
2890

    
2891
    }
2892

    
2893
    /*
2894
     * (non-Javadoc)
2895
     *
2896
     * @see org.gvsig.metadata.Metadata#getMetadataChildren()
2897
     */
2898
    @Override
2899
    public Set getMetadataChildren() {
2900
        return this.metadataChildren;
2901
    }
2902

    
2903
    /*
2904
     * (non-Javadoc)
2905
     *
2906
     * @see org.gvsig.metadata.Metadata#getMetadataName()
2907
     */
2908
    @Override
2909
    public String getMetadataName() {
2910
        return this.provider.getProviderName();
2911
    }
2912

    
2913
    public FeatureTypeManager getFeatureTypeManager() {
2914
        return this.featureTypeManager;
2915
    }
2916

    
2917
    @Override
2918
    public long getFeatureCount() throws DataException {
2919
        if (featureCount == null) {
2920
            featureCount = this.provider.getFeatureCount();
2921
        }
2922
        if (this.isEditing()) {
2923
            if(this.isAppending()) {
2924
                try{
2925
                    throw new IllegalStateException();
2926
                } catch(IllegalStateException e) {
2927
                    LOGGER.info("Call DefaultFeatureStore.getFeatureCount editing in mode APPEND",e);
2928
                }
2929
                return -1;
2930
            } else {
2931
                return featureCount
2932
                    + this.featureManager.getDeltaSize();
2933
            }
2934
        }
2935
        return featureCount;
2936
    }
2937

    
2938
    private Long getTemporalOID() {
2939
        return this.temporalOid++;
2940
    }
2941

    
2942
    @Override
2943
    public FeatureType getProviderFeatureType(String featureTypeId) {
2944
        if (featureTypeId == null) {
2945
            return this.defaultFeatureType;
2946
        }
2947
        FeatureType type;
2948
        Iterator iter = this.featureTypes.iterator();
2949
        while (iter.hasNext()) {
2950
            type = (FeatureType) iter.next();
2951
            if (type.getId().equals(featureTypeId)) {
2952
                return type;
2953
            }
2954
        }
2955
        return null;
2956
    }
2957

    
2958
    @Override
2959
    public FeatureProvider getFeatureProviderFromFeature(Feature feature) {
2960
        return ((DefaultFeature) feature).getData();
2961
    }
2962

    
2963
    @Override
2964
    public DataStore getStore() {
2965
        return this;
2966
    }
2967

    
2968
    @Override
2969
    public FeatureStore getFeatureStore() {
2970
        return this;
2971
    }
2972

    
2973
    @Override
2974
    public void createCache(String name, DynObject parameters)
2975
        throws DataException {
2976
        cache = dataManager.createFeatureCacheProvider(name, parameters);
2977
        if (cache == null) {
2978
            throw new CreateException("FeaureCacheProvider", null);
2979
        }
2980
        cache.apply(this, provider);
2981
        provider = cache;
2982

    
2983
        featureCount = null;
2984
    }
2985

    
2986
    @Override
2987
    public FeatureCache getCache() {
2988
        return cache;
2989
    }
2990

    
2991
    @Override
2992
    public void clear() {
2993
        if (metadata != null) {
2994
            metadata.clear();
2995
        }
2996
    }
2997

    
2998
    @Override
2999
    public String getName() {
3000
        if( this.provider != null ) {
3001
            return this.provider.getName();
3002
        }
3003
        if( this.parameters instanceof HasAFile ) {
3004
            return FilenameUtils.getName(((HasAFile)this.parameters).getFile().getName());
3005
        }
3006
        return "unknow";
3007
    }
3008

    
3009
    @Override
3010
    public String getFullName() {
3011
        try {
3012
            if( this.provider!=null ) {
3013
                return this.provider.getFullName();
3014
            }
3015
            if( this.parameters instanceof HasAFile ) {
3016
                return (((HasAFile)this.parameters).getFile().getAbsolutePath());
3017
            }
3018
            return null;
3019
        } catch(Throwable th) {
3020
            return null;
3021
        }
3022
    }
3023

    
3024
    @Override
3025
    public String getProviderName() {
3026
        if( this.provider!=null ) {
3027
            return this.provider.getProviderName();
3028
        }
3029
        if( this.parameters != null ) {
3030
            return this.parameters.getDataStoreName();
3031
        }
3032
        return null;
3033

    
3034
    }
3035

    
3036
    @Override
3037
    public boolean isKnownEnvelope() {
3038
        return this.provider.isKnownEnvelope();
3039
    }
3040

    
3041
    @Override
3042
    public boolean hasRetrievedFeaturesLimit() {
3043
        return this.provider.hasRetrievedFeaturesLimit();
3044
    }
3045

    
3046
    @Override
3047
    public int getRetrievedFeaturesLimit() {
3048
        return this.provider.getRetrievedFeaturesLimit();
3049
    }
3050

    
3051
    @Override
3052
    public Interval getInterval() {
3053
        if( this.timeSupport!=null ) {
3054
            return this.timeSupport.getInterval();
3055
        }
3056
        try {
3057
            FeatureType type = this.getDefaultFeatureType();
3058
            FeatureAttributeDescriptor attr = type.getDefaultTimeAttribute();
3059
            if( attr!=null ) {
3060
                Interval interval = attr.getInterval();
3061
                if( interval!=null ) {
3062
                    return interval;
3063
                }
3064
            }
3065
        } catch (DataException ex) {
3066
        }
3067
        return this.provider.getInterval();
3068
    }
3069

    
3070
    @Override
3071
    public Collection getTimes() {
3072
        if( this.timeSupport!=null ) {
3073
            return this.timeSupport.getTimes();
3074
        }
3075
        return this.provider.getTimes();
3076
    }
3077

    
3078
    @Override
3079
    public Collection getTimes(Interval interval) {
3080
        if( this.timeSupport!=null ) {
3081
            return this.timeSupport.getTimes(interval);
3082
        }
3083
        return this.provider.getTimes(interval);
3084
    }
3085

    
3086
    public void setTimeSupport(FeatureStoreTimeSupport timeSupport) {
3087
        if( this.isEditing() ) {
3088
            throw new RuntimeException("Can't add time support over attribute '"+timeSupport.getAttributeName()+"' while store is editing.");
3089
        }
3090
        if( !this.transforms.isEmpty() ) {
3091
            throw new RuntimeException("Can't add time support over attribute '"+timeSupport.getAttributeName()+"' if has transforms.");
3092
        }
3093
        FeatureType ft = this.defaultFeatureType;
3094
        FeatureAttributeDescriptor attr = ft.getAttributeDescriptor(timeSupport.getRequiredFieldNames()[0]);
3095
        if( attr == null ) {
3096
            throw new RuntimeException("Can't add time support over attribute '"+timeSupport.getAttributeName()+"', this attribute don't exists.");
3097
        }
3098
        EditableFeatureType eft = ft.getEditable();
3099
        attr = eft.getAttributeDescriptor(timeSupport.getAttributeName());
3100
        if( attr != null ) {
3101
            if( !(attr.getFeatureAttributeEmulator() instanceof FeatureStoreTimeSupport) ) {
3102
                throw new RuntimeException("Can't add time support, attribute '"+timeSupport.getAttributeName()+"'already exists.");
3103
            }
3104
            eft.remove(attr.getName());
3105
        }
3106
        EditableFeatureAttributeDescriptor attrTime = eft.add(
3107
            timeSupport.getAttributeName(), 
3108
            timeSupport.getDataType()
3109
        );
3110
        attrTime.setIsTime(true);
3111
        attrTime.setFeatureAttributeEmulator(timeSupport);
3112
        eft.setDefaultTimeAttributeName(timeSupport.getAttributeName());
3113
        this.defaultFeatureType = eft.getNotEditableCopy();
3114
        
3115
        this.timeSupport = timeSupport;
3116
    }
3117

    
3118
    @Override
3119
    @SuppressWarnings("CloneDoesntCallSuperClone")
3120
    public Object clone() throws CloneNotSupportedException {
3121

    
3122
        DataStoreParameters dsp = getParameters();
3123

    
3124
        DefaultFeatureStore cloned_store = null;
3125

    
3126
        try {
3127
            cloned_store = (DefaultFeatureStore) DALLocator.getDataManager().
3128
                openStore(this.getProviderName(), dsp);
3129
            if (transforms != null) {
3130
                cloned_store.transforms = (DefaultFeatureStoreTransforms) transforms.clone();
3131
                cloned_store.transforms.setStoreForClone(cloned_store);
3132
            }
3133
        } catch (Exception e) {
3134
            throw new CloneException(e);
3135
        }
3136
        return cloned_store;
3137

    
3138
    }
3139

    
3140
    @Override
3141
    public Feature getFeature(DynObject dynobject) {
3142
        if (dynobject instanceof DynObjectFeatureFacade){
3143
            Feature f = ((DynObjectFeatureFacade)dynobject).getFeature();
3144
            return f;
3145
        }
3146
        return null;
3147
    }
3148

    
3149
    @Override
3150
    public Iterator iterator() {
3151
        FeatureSet fset = null;
3152
        try {
3153
            fset  = this.getFeatureSet();
3154
            return fset.fastIterator();
3155
        } catch (DataException ex) {
3156
            throw new RuntimeException(ex);
3157
        } finally {
3158
            DisposeUtils.disposeQuietly(fset);
3159
        }
3160
    }
3161

    
3162
    @Override
3163
    public long size64() {
3164
        FeatureSet fset = null;
3165
        try {
3166
            fset  = this.getFeatureSet();
3167
            return fset.getSize();
3168
        } catch (DataException ex) {
3169
            throw new RuntimeException(ex);
3170
        } finally {
3171
            DisposeUtils.disposeQuietly(fset);
3172
        }
3173
    }
3174
   
3175
    @Override
3176
    public ExpressionBuilder createExpressionBuilder() {
3177
        ExpressionBuilder builder = GeometryExpressionUtils.createExpressionBuilder();
3178
        return builder;
3179
    }
3180

    
3181
    @Override
3182
    public ExpressionBuilder createExpression() {
3183
        return createExpressionBuilder();
3184
    }
3185

    
3186
    public FeatureSet features() throws DataException {
3187
        // This is to avoid jython to create a property with this name
3188
        // to access method getFeatures.
3189
        return this.getFeatureSet();
3190
    }
3191

    
3192
    @Override
3193
    public DataStoreProviderFactory getProviderFactory() {
3194
        DataStoreProviderFactory factory = dataManager.getStoreProviderFactory(parameters.getDataStoreName());
3195
        return factory;
3196
    }
3197

    
3198
    @Override
3199
    public void useCache(String providerName, DynObject parameters) throws DataException {
3200
        throw new UnsupportedOperationException();
3201
    }
3202

    
3203
    @Override
3204
    public boolean isBroken() {
3205
        return this.state.isBroken();
3206
    }
3207

    
3208
    @Override
3209
    public Throwable getBreakingsCause() {
3210
            return this.state.getBreakingsCause();
3211
    }
3212

    
3213
    @Override
3214
    public SpatialIndex wrapSpatialIndex(SpatialIndex index) {
3215
      FeatureStoreProviderFactory factory = (FeatureStoreProviderFactory) this.getProviderFactory();
3216
      if( !factory.supportNumericOID() ) {
3217
          return null;
3218
      }
3219
      SpatialIndex wrappedIndex = new WrappedSpatialIndex(index, this);
3220
      return wrappedIndex;
3221
  }
3222

    
3223
    @Override
3224
    public FeatureReference getFeatureReference(String code) {
3225
        FeatureReference featureReference = new DefaultFeatureReference(this, code);
3226
        return featureReference;
3227
    }
3228

    
3229
    @Override
3230
    public long getPendingChangesCount() {
3231
        if( this.featureManager==null ) {
3232
            return 0;
3233
        }
3234
        return this.featureManager.getPendingChangesCount();
3235
    }
3236

    
3237
    @Override
3238
    public ResourcesStorage getResourcesStorage() {
3239
        ResourcesStorage resourcesStorage;
3240
        try {
3241
            resourcesStorage = this.provider.getResourcesStorage();
3242
            if( resourcesStorage!=null ) {
3243
                return resourcesStorage;
3244
            }
3245
        } catch(Throwable th) {
3246
            
3247
        }
3248
        try {
3249
            DataServerExplorer explorer = this.getExplorer();
3250
            if( explorer==null ) {
3251
                return null;
3252
            }
3253
            resourcesStorage = explorer.getResourcesStorage(this);
3254
            explorer.dispose();
3255
            return resourcesStorage;
3256
        } catch (Exception ex) {
3257
            LOGGER.warn("Can't create resources storage",ex);
3258
            return null;
3259
        }
3260
    }
3261

    
3262
    @Override
3263
    public StoresRepository getStoresRepository() {
3264
        final StoresRepository mainRepository = this.dataManager.getStoresRepository();
3265
        StoresRepository localRepository = this.provider.getStoresRepository();
3266
        if( localRepository==null ) {
3267
            return mainRepository;
3268
        }
3269
        StoresRepository repository = new BaseStoresRepository(this.getName());
3270
        repository.addRepository(localRepository);
3271
        repository.addRepository(mainRepository);
3272
        return repository;
3273
    }
3274

    
3275
    @Override
3276
    public Feature getSampleFeature() {
3277
            Feature sampleFeature;
3278
            try {
3279
                FeatureSelection theSelection = this.getFeatureSelection();
3280
                if( theSelection!=null && !theSelection.isEmpty() ) {
3281
                    sampleFeature = theSelection.first();
3282
                } else {
3283
                    sampleFeature = this.first();
3284
                }
3285
                if( sampleFeature==null ) {
3286
                    sampleFeature = this.createNewFeature();
3287
                }
3288
            } catch (DataException ex) {
3289
                return null;
3290
            }
3291
            return sampleFeature;
3292
    }
3293

    
3294
    @Override
3295
    public boolean supportReferences() {
3296
        try {
3297
            return this.getDefaultFeatureType().supportReferences();
3298
        } catch (Exception ex) {
3299
            return false;
3300
        }
3301
    }
3302

    
3303
    @Override
3304
    public boolean isTemporary() {
3305
        if( this.provider==null ) {
3306
            return true;
3307
        }
3308
        return this.provider.isTemporary();
3309
    }
3310
    
3311
    public FeatureType getOriginalFeatureType(FeatureType featureType)  {
3312
        // FIXME this don't work for Store.fType.size() > 1
3313
        FeatureTypeManager manager = this.featureTypeManager;
3314
         if (manager==null) {
3315
             return null;
3316
         }
3317
         FeatureType originalFeatureType = manager.getOriginalFeatureType();
3318
         if (originalFeatureType==null) {
3319
             return null;
3320
         }
3321
         return originalFeatureType.getCopy();
3322
    }
3323
}