Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.file / org.gvsig.fmap.dal.file.lib / src / main / java / org / gvsig / fmap / dal / feature / spi / simpleprovider / SimpleSequentialReaderStoreProvider.java @ 44331

History | View | Annotate | Download (49.4 KB)

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

    
26
import java.io.File;
27
import java.io.IOException;
28
import java.util.ArrayList;
29
import java.util.HashMap;
30
import java.util.Iterator;
31
import java.util.List;
32
import java.util.Locale;
33
import java.util.Map;
34

    
35
import org.apache.commons.lang3.StringUtils;
36
import org.cresques.cts.IProjection;
37
import org.gvsig.fmap.dal.BaseStoresRepository;
38
import org.gvsig.fmap.dal.DALLocator;
39
import org.gvsig.fmap.dal.DataManager;
40
import org.gvsig.fmap.dal.DataServerExplorer;
41
import org.gvsig.fmap.dal.DataStore;
42
import org.gvsig.fmap.dal.DataStoreNotification;
43
import org.gvsig.fmap.dal.DataTypes;
44
import org.gvsig.fmap.dal.FileHelper;
45
import org.gvsig.fmap.dal.StoresRepository;
46
import org.gvsig.fmap.dal.exception.DataException;
47
import org.gvsig.fmap.dal.exception.InitializeException;
48
import org.gvsig.fmap.dal.exception.OpenException;
49
import org.gvsig.fmap.dal.exception.ReadException;
50
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
51
import org.gvsig.fmap.dal.feature.EditableFeature;
52
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
53
import org.gvsig.fmap.dal.feature.EditableFeatureType;
54
import org.gvsig.fmap.dal.feature.Feature;
55
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
56
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
57
import org.gvsig.fmap.dal.feature.FeatureStore;
58
import org.gvsig.fmap.dal.feature.FeatureType;
59
import org.gvsig.fmap.dal.feature.OpenFeatureStoreParameters;
60
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
61
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
62
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
63
import org.gvsig.fmap.dal.feature.spi.memory.AbstractMemoryStoreProvider;
64
import org.gvsig.fmap.dal.resource.file.FileResource;
65
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
66
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
67
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorer;
68
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorerParameters;
69
import org.gvsig.fmap.dal.spi.DALSPILocator;
70
import org.gvsig.fmap.dal.spi.DataManagerProviderServices;
71
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
72
import org.gvsig.fmap.geom.Geometry;
73
import org.gvsig.fmap.geom.GeometryLocator;
74
import org.gvsig.fmap.geom.GeometryManager;
75
import org.gvsig.fmap.geom.GeometryUtils;
76
import org.gvsig.fmap.geom.aggregate.MultiPoint;
77
import org.gvsig.fmap.geom.primitive.Envelope;
78
import org.gvsig.fmap.geom.primitive.Point;
79
import org.gvsig.fmap.geom.type.GeometryType;
80
import org.gvsig.timesupport.Interval;
81
import org.gvsig.tools.ToolsLocator;
82
import org.gvsig.tools.dataTypes.CoercionException;
83
import org.gvsig.tools.dataTypes.DataTypeUtils;
84
import org.gvsig.tools.dataTypes.DataTypesManager;
85
import org.gvsig.tools.dataTypes.DataTypesManager.Coercion;
86
import org.gvsig.tools.dataTypes.DataTypesManager.CoercionWithLocale;
87
import org.gvsig.tools.dispose.Disposable;
88
import org.gvsig.tools.dispose.DisposeUtils;
89
import org.gvsig.tools.dynobject.Tags;
90
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
91
import org.gvsig.tools.exception.BaseException;
92
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
93
import org.gvsig.tools.task.SimpleTaskStatus;
94
import org.gvsig.tools.task.TaskStatusManager;
95
import org.gvsig.tools.util.UnmodifiableBasicMap;
96
import org.gvsig.tools.util.UnmodifiableBasicMapToMapAdapter;
97
import org.gvsig.tools.util.UnmodifiableBasicSet;
98
import org.gvsig.tools.visitor.VisitCanceledException;
99
import org.gvsig.tools.visitor.Visitor;
100
import org.slf4j.Logger;
101
import org.slf4j.LoggerFactory;
102

    
103
@SuppressWarnings("UseSpecificCatch")
104
public class SimpleSequentialReaderStoreProvider extends AbstractMemoryStoreProvider implements
105
        ResourceConsumer {
106

    
107
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSequentialReaderStoreProvider.class);
108

    
109
    private class StoresRepositoryWithChildren extends BaseStoresRepository {
110
        
111
        public StoresRepositoryWithChildren() {
112
            super(SimpleSequentialReaderStoreProvider.this.getName());
113
        }
114
        
115
        @Override
116
        public DataStore getStore(String name) {
117
            if( StringUtils.equalsIgnoreCase(name, readerData.getName()) ||
118
                StringUtils.equalsIgnoreCase(name, readerData.getAlias()) ) {
119
                return readerData.getStore();
120
            }
121
            for (ReaderData child : childrenData) {
122
                if( StringUtils.equalsIgnoreCase(name, child.getName()) ||
123
                    StringUtils.equalsIgnoreCase(name, child.getAlias()) ) {
124
                    return child.getStore();
125
                }
126
            }
127
            return super.getStore(name);
128
        }
129
        
130
    }
131
    
132
    private class ReaderData implements Disposable {
133
        private FeatureType defaultFeatureType;
134
        private List<FeatureType> featureTypes;
135
        private List<FeatureProvider> features;
136
        private boolean needCalculateEnvelope;
137
        private String name;
138
        private String alias;
139
        private long OIDcounter;
140
        private DataStore store;
141
        private StoresRepository storesRepository;
142
        private OpenFeatureStoreParameters parameters;
143
        private ResourcesStorage resourcesStorage;
144
        
145
        public ReaderData() {
146
            this.needCalculateEnvelope = false;
147
            this.features = new ArrayList<>();
148
            this.OIDcounter = 0;
149
            this.store = null;
150
        }
151

    
152
        @Override
153
        public void dispose() {
154
            DisposeUtils.disposeQuietly(store);
155
            this.store = null;
156
            this.features = null;
157
            this.OIDcounter = 0;
158
            this.defaultFeatureType = null;
159
            this.featureTypes = null;
160
            this.parameters = null;
161
            this.storesRepository = null;
162
        }
163
        
164
        public void addFeatureProvider(FeatureProvider feature) {
165
                feature.setOID(this.OIDcounter++);
166
                this.features.add(feature);
167
        }
168
        
169
        public void setFeatureTypes(List<FeatureType> featureTypes, FeatureType defaultFeatureType) {
170
            this.featureTypes = featureTypes;
171
            this.defaultFeatureType = defaultFeatureType;
172
        }
173
        
174
        public void setNeedCalculateEnvelope(boolean needCalculateEnvelope) {
175
            this.needCalculateEnvelope = needCalculateEnvelope;
176
        }
177
        
178
        public boolean getNeedCalculateEnvelope() {
179
            return this.needCalculateEnvelope;
180
        }
181

    
182
        public String getName() {
183
            return name;
184
        }
185

    
186
        public void setName(String name) {
187
            this.name = name;
188
        }
189

    
190
        public List<FeatureType> getFeatureTypes() {
191
            return this.featureTypes;
192
        }
193

    
194
        public FeatureType getDefaultFeatureType() {
195
            return this.defaultFeatureType;
196
        }
197
        
198
        public List<FeatureProvider> getFeatures() {
199
            return this.features;
200
        }
201
        
202
        public DataStore getStore() {
203
            if( this.store == null ) {
204
                try {
205
                    SimpleSequentialReaderStoreProvider provider = new SimpleSequentialReaderStoreProvider(
206
                            readerFactory, 
207
                            (SimpleSequentialReaderStoreParameters) getParameters(),
208
                            null,
209
                            childrenData,
210
                            this
211
                    );
212
                    DataManagerProviderServices manager = DALSPILocator.getDataManagerProviderServices();
213
                    this.store = manager.openStore(
214
                            getParameters(), 
215
                            provider
216
                    );
217
                    provider.setStoreServices((FeatureStoreProviderServices) this.store);
218
                    provider.name = this.name;
219
                    DisposeUtils.bind(this.store);
220
                } catch(Exception ex) {
221
                   LOGGER.warn("Can't build store form child '"+name+"'.",ex);
222
                   return null; 
223
                }
224
            }
225
            DisposeUtils.bind(this.store);
226
            return this.store;
227
        }
228

    
229
        public void setStoresRepository(StoresRepository storesRepository) {
230
            this.storesRepository = storesRepository;
231
        }
232

    
233
        public StoresRepository getStoresRepository() {
234
            return storesRepository;
235
        }
236

    
237
        public ResourcesStorage getResourcesStorage() {
238
            return resourcesStorage;
239
        }
240

    
241
        public void setResourcesStorage(ResourcesStorage resourcesStorage) {
242
            this.resourcesStorage = resourcesStorage;
243
        }
244

    
245
        public void setParameters(OpenFeatureStoreParameters parameters) {
246
            if( parameters==null ) {
247
                LOGGER.warn("Can't set parameters to null");
248
                return;
249
            }
250
            this.parameters = parameters;
251
        }
252

    
253
        public OpenFeatureStoreParameters getParameters() {
254
            return parameters;
255
        }
256

    
257
        public String getAlias() {
258
            return this.alias;
259
        }
260

    
261
        public void setAlias(String alias) {
262
            this.alias = alias;
263
        }
264
        
265
        
266
    }
267
    
268
    class Children implements UnmodifiableBasicMap<String, DataStore> {
269
        // Con esta clase se pospone la creacion de los stores hasta que se 
270
        // pide cada uno de ellos, y no cuando se pide el Map de estos, ya
271
        // que el map se puede pedir solo para saber si hay o cuantos hay.
272

    
273
        @Override
274
        public DataStore get(String key) {
275
            for (ReaderData child : childrenData) {
276
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
277
                    return child.getStore();
278
                }
279
            }
280
            return null;
281
        }
282

    
283
        @Override
284
        public boolean isEmpty() {
285
            return childrenData.isEmpty();
286
        }
287

    
288
        @Override
289
        public boolean containsKey(String key) {
290
            for (ReaderData child : childrenData) {
291
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
292
                    return true;
293
                }
294
            }
295
            return false;
296
        }
297

    
298
        @Override
299
        public Map<String, DataStore> toMap() {
300
            return new UnmodifiableBasicMapToMapAdapter<>(this);
301
        }
302

    
303
        @Override
304
        public int size() {
305
            return childrenData.size();
306
        }
307

    
308
        @Override
309
        public UnmodifiableBasicSet<String> keySet() {
310
            return new UnmodifiableBasicSet<String>() {
311

    
312
                @Override
313
                public boolean isEmpty() {
314
                    return childrenData.isEmpty();
315
                }
316

    
317
                @Override
318
                public int size() {
319
                    return childrenData.size();
320
                }
321

    
322
                @Override
323
                public Iterator<String> iterator() {
324
                    final Iterator<ReaderData> it = childrenData.iterator();
325
                    return new Iterator<String>() {
326
                        @Override
327
                        public boolean hasNext() {
328
                            return it.hasNext();
329
                        }
330

    
331
                        @Override
332
                        public String next() {
333
                            ReaderData theReaderData = it.next();
334
                            return theReaderData.getName();
335
                        }
336
                    };
337
                }
338
            };
339
        }
340

    
341
        @Override
342
        public Iterator<DataStore> iterator() {
343
            final Iterator<String> it = this.keySet().iterator();
344
            return new Iterator<DataStore>() {
345
                @Override
346
                public boolean hasNext() {
347
                    return it.hasNext();
348
                }
349

    
350
                @Override
351
                public DataStore next() {
352
                    String name = it.next();
353
                    return get(name);
354
                }
355
            };
356
        }
357
        
358
    }
359

    
360
    private final ResourceProvider resource;
361

    
362
    private Envelope envelope;
363
    private boolean need_calculate_envelope = false;
364
    private String name = "";
365
    private final SimpleSequentialReaderFactory readerFactory;
366
    private List<ReaderData> childrenData;
367
    private ReaderData readerData;
368
    private final boolean isAChild;
369

    
370
    public SimpleSequentialReaderStoreProvider(
371
            SimpleSequentialReaderFactory readerFactory,
372
            SimpleSequentialReaderStoreParameters parameters,
373
            DataStoreProviderServices storeServices
374
        ) throws InitializeException {
375
        this(readerFactory, parameters, storeServices, new ArrayList<ReaderData>(), null);
376
    }
377
    
378
    private SimpleSequentialReaderStoreProvider(
379
            SimpleSequentialReaderFactory readerFactory,
380
            SimpleSequentialReaderStoreParameters parameters,
381
            DataStoreProviderServices storeServices,
382
            List<ReaderData> childrenData,
383
            ReaderData readerData
384
            
385
        ) throws InitializeException {
386
        super(
387
                parameters,
388
                storeServices,
389
                FileHelper.newMetadataContainer(readerFactory.getName())
390
        );
391
        this.childrenData = childrenData;
392
        this.readerData = readerData;
393
        this.readerFactory = readerFactory;
394
        this.isAChild = this.readerData!=null;
395
        
396
        File file = getParameters().getFile();
397
        resource = this.createResource(
398
                FileResource.NAME,
399
                new Object[]{file.getAbsolutePath()}
400
        );
401

    
402
        resource.addConsumer(this);
403
        if( this.readerData!=null ) {
404
            this.name = this.readerData.getName();            
405
        }
406
        if( storeServices != null ) {
407
            initializeFeatureTypes();
408
        }
409
    }
410
    
411
    private boolean isChild() {
412
        return this.isAChild;
413
    }
414
    
415
    private void setStoreServices(FeatureStoreProviderServices storeServices) throws InitializeException {
416
        this.store = storeServices;
417
        initializeFeatureTypes();
418
    }
419

    
420
    @Override
421
    public FeatureStoreProviderServices getStoreServices() {
422
        if( this.store==null ) {
423
            // Por aqui no deberia de pasar.
424
            // Si pasa es que algo va mal.
425
            LOGGER.warn("Algo no anda bien, el store es null");
426
            DataStore theStore = this.readerData.getStore();
427
            if( theStore==null ) {
428
                return null;
429
            }
430
            this.store = (FeatureStoreProviderServices) theStore ;
431
        }
432
        return this.store;
433
    }
434
    
435
    @Override
436
    public SimpleSequentialReaderStoreParameters getParameters() {
437
        return (SimpleSequentialReaderStoreParameters) super.getParameters();
438
    }
439

    
440
    @Override
441
    public String getProviderName() {
442
        if( this.readerFactory==null ) {
443
            return "unknown";
444
        }
445
        return this.readerFactory.getName();
446
    }
447

    
448
    @Override
449
    public boolean allowWrite() {
450
        return false;
451
    }
452

    
453
    private String getFullFileName() {
454
        // Usar solo para mostrar mensajes en el logger.
455
        String s;
456
        try {
457
            s = getParameters().getFile().getAbsolutePath();
458
        } catch (Exception e2) {
459
            s = "(unknow)";
460
        }
461
        return s;
462
    }
463

    
464
    @Override
465
    public void open() throws OpenException {
466
        if ( this.data != null ) {
467
            return;
468
        }
469
        this.data = new ArrayList<>();
470
        resource.setData(new HashMap());
471
        try {
472
            loadFeatures();
473
        } catch (RuntimeException e) {
474
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
475
            throw e;
476
        } catch (Exception e) {
477
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
478
            throw new RuntimeException(e);
479
        }
480
    }
481

    
482
    @Override
483
    public DataServerExplorer getExplorer() throws ReadException {
484
        DataManager manager = DALLocator.getDataManager();
485
        FilesystemServerExplorerParameters params;
486
        try {
487
            params = (FilesystemServerExplorerParameters) manager
488
                    .createServerExplorerParameters(FilesystemServerExplorer.NAME);
489
            params.setRoot(this.getParameters().getFile().getParent());
490
            return manager.openServerExplorer(FilesystemServerExplorer.NAME, params);
491
        } catch (DataException | ValidateDataParametersException e) {
492
            throw new ReadException(this.getProviderName(), e);
493
        }
494

    
495
    }
496

    
497
    @Override
498
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
499
        throw new UnsupportedOperationException();
500
    }
501

    
502
    @Override
503
    public boolean closeResourceRequested(ResourceProvider resource) {
504
        return true;
505
    }
506

    
507
    @Override
508
    public int getOIDType() {
509
        return DataTypes.LONG;
510
    }
511

    
512
    @Override
513
    public boolean supportsAppendMode() {
514
        return false;
515
    }
516

    
517
    @Override
518
    public void append(FeatureProvider featureProvider) {
519
        throw new UnsupportedOperationException();
520
    }
521

    
522
    @Override
523
    public void beginAppend() {
524
        throw new UnsupportedOperationException();
525
    }
526

    
527
    @Override
528
    public void endAppend() {
529
        throw new UnsupportedOperationException();
530
    }
531

    
532
    @Override
533
    public Object createNewOID() {
534
        throw new UnsupportedOperationException();
535
    }
536

    
537
    @Override
538
    protected void doDispose() throws BaseException {
539
        for (ReaderData theReaderData : this.childrenData) {
540
            DisposeUtils.disposeQuietly(theReaderData);
541
        }
542
        this.childrenData = null;
543
        DisposeUtils.disposeQuietly(this.readerData);
544
        this.readerData = null;
545
//        this.resource = null;
546
//        this.readerFactory = null;
547
        this.envelope = null;
548
        super.doDispose();
549
    }
550

    
551
    
552
    private void initializeFeatureTypes() throws InitializeException {
553
        try {
554
            this.open();
555
        } catch (OpenException e) {
556
            throw new InitializeException(this.getProviderName(), e);
557
        }
558
    }
559

    
560
    @Override
561
    public Envelope getEnvelope() throws DataException {
562
        this.open();
563
        if ( this.envelope != null ) {
564
            return this.envelope;
565
        }
566
        if ( !this.need_calculate_envelope ) {
567
            return null;
568
        }
569
        FeatureStore fs = this.getFeatureStore();
570
        FeatureType ft = fs.getDefaultFeatureType();
571
        FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
572

    
573
        try {
574
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
575
            fs.accept(new Visitor() {
576
                @Override
577
                public void visit(Object obj) throws VisitCanceledException, BaseException {
578
                    Feature f = (Feature) obj;
579
                    Geometry geom = f.getDefaultGeometry();
580
                    if ( geom != null ) {
581
                        envelope.add(geom.getEnvelope());
582
                    }
583
                }
584
            });
585
        } catch (BaseException e) {
586
            LOGGER.warn("Can't calculate the envelope of '"+getProviderName()+"' file '" + this.getFullName() + "'.", e);
587
            this.envelope = null;
588
        }
589

    
590
        this.need_calculate_envelope = false;
591
        return this.envelope;
592
    }
593

    
594
    @Override
595
    public Object getDynValue(String name) throws DynFieldNotFoundException {
596
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
597
            try {
598
                return this.getEnvelope();
599
            } catch (DataException e) {
600
                return null;
601
            }
602
        } else {
603
            if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
604
                IProjection pro = this.getParameters().getCRS();
605
                if ( pro != null ) {
606
                    return pro;
607
                }
608
                FeatureType type;
609
                try {
610
                    type = this.getStoreServices().getDefaultFeatureType();
611
                    pro = type.getDefaultSRS();
612
                    if( pro!=null ) {
613
                        return pro;
614
                    }
615
                } catch (DataException ex) {
616
                }
617
            }
618
        }
619
        return super.getDynValue(name);
620
    }
621

    
622
    @Override
623
    public void resourceChanged(ResourceProvider resource) {
624
        this.getStoreServices().notifyChange(
625
                DataStoreNotification.RESOURCE_CHANGED,
626
                resource);
627
    }
628

    
629
    @Override
630
    public Object getSourceId() {
631
        return this.getParameters().getFile();
632
    }
633

    
634
    @Override
635
    public String getName() {
636
        return this.name;
637
    }
638

    
639
    @Override
640
    public String getFullName() {
641
        return this.getParameters().getFile().getAbsolutePath();
642
    }
643

    
644
    @Override
645
    public ResourceProvider getResource() {
646
        return resource;
647
    }
648

    
649
    private String[] split(String value, String separators) {
650
        int firstSeparatorPosition = 1000000;
651
        Character sep = null;
652
        for (char ch : separators.toCharArray()) {
653
            int pos = value.indexOf(ch);
654
            if( pos>0 && pos<firstSeparatorPosition ) {
655
                sep = ch;
656
                firstSeparatorPosition = pos;
657
            }
658
        }
659
        if( sep == null ) {
660
            return new String[] { value };
661
        }
662
        return value.split("["+sep+"]");
663
    }
664
    
665
    private class FieldTypeParser {
666

    
667
        public String name = null;
668
        public int type = DataTypes.STRING;
669
        public int size = 0;
670
        public int geomType = Geometry.TYPES.GEOMETRY;
671
        public int geomSubtype = Geometry.SUBTYPES.GEOM2D;
672
        public Map<String,String> tags = new HashMap<>();
673
        public Map<String,String> assignments = new HashMap<>();
674

    
675
        private String typename = "string";
676

    
677
        FieldTypeParser() {
678
        }
679

    
680
        private int getType(String value) {
681
            DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
682
            return dataTypesManager.getType(typename);
683
        }
684

    
685
        @SuppressWarnings("UseSpecificCatch")
686
        public boolean parse(String value) {
687
            String[] args;
688
            if ( value.contains("__") ) {
689
                args = value.split("__");
690
            } else {
691
                args = split(value, ":/#@!;-");
692
                if( args.length == 1 ) {
693
                    this.name = value;
694
                    return true;
695
                }
696
            }                        
697
            int n = 0;
698
            this.name = args[n++];
699
            if( n >= args.length ) {
700
                return true;
701
            }
702
            this.typename = args[n++];
703
            this.type = this.getType(this.typename);
704
            if ( this.type == DataTypes.INVALID ) {
705
                this.geomType = GeometryUtils.getGeometryType(this.typename);
706
                if( this.geomType==Geometry.TYPES.UNKNOWN )  {
707
                    this.type = DataTypes.STRING;
708
                    LOGGER.info("Type '" + this.typename + "' not valid for attribute '" + value + "' in '"+getProviderName()+"' file '" + getFullFileName() + "'.");
709
                } else {
710
                    this.typename = "GEOMETRY";
711
                    this.type = DataTypes.GEOMETRY;
712
                }
713
            }
714
            switch(this.type) {
715
                case DataTypes.STRING:
716
                    this.size = 50;
717
                    break;
718
                case DataTypes.INT:
719
                    this.size = 10;
720
                    break;
721
                case DataTypes.LONG:
722
                    this.size = 20;
723
                    break;
724
                case DataTypes.FLOAT:
725
                    this.size = 10;
726
                    break;
727
                case DataTypes.DOUBLE:
728
                    this.size = 20;
729
                    break;
730
                default:
731
                    this.size = 0;
732
            }
733
            while (n < args.length) {
734
                String option = args[n++].toLowerCase();
735
                switch (option) {
736
                    case "size":
737
                        try {
738
                            this.size = Integer.parseInt(args[n++]);
739
                        } catch (Exception ex) {
740
                            LOGGER.warn("Ignore incorrect field size for field " + value + " in '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
741
                        }
742
                        break;
743
                    case "tag": {
744
                            String x = args[n++];
745
                            int pos = x.indexOf("=");
746
                            if( pos < 0 ) {
747
                                this.tags.put(x, null);
748
                            } else {
749
                                this.tags.put( 
750
                                        StringUtils.substring(x, 0, pos),
751
                                        StringUtils.substring(x, pos+1)
752
                                );
753
                            }
754
                            break;
755
                        }
756
                    case "set": {
757
                            String x = args[n++];
758
                            int pos = x.indexOf("=");
759
                            if( pos < 0 ) {
760
                                this.assignments.put(x, null);
761
                            } else {
762
                                this.assignments.put( 
763
                                        StringUtils.substring(x, 0, pos),
764
                                        StringUtils.substring(x, pos+1)
765
                                );
766
                            }
767
                            break;
768
                        }
769
                    default:
770
                        LOGGER.warn("Illegal argumente '"+option+"' for field '"+this.name+"' in '"+getProviderName()+"' file '" + getFullFileName() + "' ("+value+").");
771
                }
772
            }
773
            return true;
774
        }
775

    
776
    }
777

    
778
    private EditableFeatureType getFeatureType(List<String> headers, int automaticTypes[]) {
779
        EditableFeatureType fType = getStoreServices().createFeatureType(this.getName());
780
        fType.setHasOID(true);
781
//        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
782

    
783
        FieldTypeParser[] fieldTypes = new FieldTypeParser[headers.size()];
784
        //
785
        // Calculamos cuales pueden ser los tipos de datos
786
        //
787
        for ( int i = 0; i < fieldTypes.length; i++ ) {
788
            fieldTypes[i] = new FieldTypeParser();
789
        }
790

    
791
        // Asuminos los tipos pasados por parametro, que se supone
792
        // son los detectados automaticamente.
793
        if ( automaticTypes != null ) {
794
            for ( int i = 0; i < fieldTypes.length && i < automaticTypes.length; i++ ) {
795
                fieldTypes[i].type = automaticTypes[i];
796
            }
797
        }
798
        // Luego probamos con lo que diga las cabezeras, sobreescribiendo
799
        // los tipos anteriores en caso de definirse en la cabezara.
800
        for ( int i = 0; i < fieldTypes.length; i++ ) {
801
            fieldTypes[i].parse(headers.get(i));
802
        }
803

    
804
        // Y por ultimo hacemos caso a lo que se haya especificado en los parametros
805
        // de apertura del reader, teniendo esto prioridad sobre todo.
806
        int[] param_types = this.getParameters().getFieldTypes();
807
        if ( param_types != null ) {
808
            for ( int i = 0; i < fieldTypes.length && i < param_types.length; i++ ) {
809
                fieldTypes[i].type = param_types[i];
810
            }
811
        }
812

    
813
        int[] param_sizes = this.getParameters().getFieldSizes();
814
        if ( param_sizes != null ) {
815
            for ( int i = 0; i < param_sizes.length; i++ ) {
816
                if ( param_sizes[i] > 0 ) {
817
                    fieldTypes[i].size = param_sizes[i];
818
                }
819
            }
820
        }
821
        //
822
        // Una vez ya sabemos los tipos de datos rellenamos el feature-type
823
        //
824
        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
825
        for (FieldTypeParser fieldType : fieldTypes) {
826
            EditableFeatureAttributeDescriptor fad = fType.add(fieldType.name, fieldType.type);
827
            fad.setSize(fieldType.size);
828
            if (fieldType.type == DataTypes.GEOMETRY ) {
829
                fad.setGeometryType(fieldType.geomType, fieldType.geomSubtype);
830
                if( fType.getDefaultGeometryAttributeName() == null ) {
831
                    fType.setDefaultGeometryAttributeName(fieldType.name);
832
                }
833
            }
834
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
835
                try {
836
                    switch(entry.getKey().toLowerCase()) {
837
                        case "hidden":
838
                            fad.setHidden(DataTypeUtils.toBoolean(entry.getValue(), false));
839
                            break;
840
                        case "allownull":
841
                            fad.setAllowNull(DataTypeUtils.toBoolean(entry.getValue(), false));
842
                            break;
843
                        case "pk":
844
                        case "ispk":
845
                        case "primarykey":
846
                        case "isprimarykey":
847
                            fad.setIsPrimaryKey(DataTypeUtils.toBoolean(entry.getValue(), false));
848
                            break;
849
                        case "isautomatic":
850
                        case "automatic":
851
                            fad.setIsAutomatic(DataTypeUtils.toBoolean(entry.getValue(), false));
852
                            break;
853
                        case "time":
854
                        case "istime":
855
                            fad.setIsTime(DataTypeUtils.toBoolean(entry.getValue(), false));
856
                            break;
857
                        case "profile":
858
                            fad.setDataProfileName(DataTypeUtils.toString(entry.getValue(), ""));
859
                            break;
860
                        case "group":
861
                            fad.setGroup(DataTypeUtils.toString(entry.getValue(), ""));
862
                            break;
863
                        case "description":
864
                            fad.setDescription(DataTypeUtils.toString(entry.getValue(), ""));
865
                            break;
866
                        case "label":
867
                            fad.setLabel(DataTypeUtils.toString(entry.getValue(), ""));
868
                            break;
869
                        case "expression":
870
                            // Los campos calculados los procesamos en una segunda
871
                            // pasada, cuando ya estan definidos el resto de los campos
872
                            // ya que pueden requerir campos que aun no se han definido.
873
                            break;
874
                        case "size":
875
                            fad.setSize(DataTypeUtils.toInteger(entry.getValue(), 50));
876
                            break;
877
                        case "precision":
878
                            fad.setPrecision(DataTypeUtils.toInteger(entry.getValue(), 10));
879
                            break;
880
                        case "order":
881
                            fad.setOrder(DataTypeUtils.toInteger(entry.getValue(), 0));
882
                            break;
883
                        case "foreingkey":
884
                            fad.getForeingKey().setForeingKey(DataTypeUtils.toBoolean(entry.getValue(), false));
885
                            break;
886
                        case "foreingkey.code":
887
                            fad.getForeingKey().setCodeName(DataTypeUtils.toString(entry.getValue(), ""));
888
                            break;
889
                        case "foreingkey.label":
890
                            fad.getForeingKey().setLabelFormula(DataTypeUtils.toString(entry.getValue(), ""));
891
                            break;
892
                        case "foreingkey.selectable":
893
                            fad.getForeingKey().setSelectable(DataTypeUtils.toBoolean(entry.getValue(), false));
894
                            break;
895
                        case "foreingkey.table":
896
                            fad.getForeingKey().setTableName(DataTypeUtils.toString(entry.getValue(), ""));
897
                            break;
898
                        case "interval":
899
                            fad.setInterval((Interval) dataTypesManager.coerce(DataTypes.INTERVAL, entry.getValue()));
900
                            break;
901
                        case "geomtype":
902
                        case "geometrytype":
903
                            fad.setGeometryType(entry.getValue());
904
                            break;
905
                        case "srs":
906
                            fad.setSRS(entry.getValue());
907
                            break;
908
                    }
909
                } catch (Exception ex) {
910
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' of '"+fad.getName()+"'.", ex);
911
                }
912
            }
913
            Tags tags = fad.getTags();
914
            for (Map.Entry<String, String> entry : fieldType.tags.entrySet()) {
915
                tags.set(entry.getKey(), entry.getValue());
916
            }
917
        }
918
        // Processamos ahora los campos calculados
919
        for (FieldTypeParser fieldType : fieldTypes) {
920
            EditableFeatureAttributeDescriptor fad = fType.getEditableAttributeDescriptor(fieldType.name);
921
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
922
                try {
923
                    switch(entry.getKey().toLowerCase()) {
924
                        case "expression":
925
                            fad.setFeatureAttributeEmulator((String) dataTypesManager.coerce(DataTypes.STRING, entry.getValue()));
926
                            break;
927
                    }
928
                } catch (Exception ex) {
929
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' in '"+fad.getName()+"' of '"+getFullFileName()+"'.", ex);
930
                }
931
            }
932
        }
933

    
934
        String[] pointDimensionNames = this.getParameters().getPointDimensionNames();
935
        if ( pointDimensionNames != null ) {
936
            PointAttributeEmulator emulator = new PointAttributeEmulator(pointDimensionNames);
937
            EditableFeatureAttributeDescriptor attr = fType.add("the_geom", DataTypes.GEOMETRY, emulator);
938
            GeometryManager geommgr = GeometryLocator.getGeometryManager();
939
            GeometryType gt;
940
            try {
941
                if ( emulator.fieldNames != null && emulator.fieldNames.length <= 2 ) {
942
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
943
                } else {
944
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
945
                }
946
                attr.setGeometryType(gt);
947
            } catch (Exception e) {
948
                LOGGER.warn("Can't set geometry type for the calculated field in '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
949
            }
950
        }
951
        return fType;
952
    }
953

    
954
    class PointAttributeEmulator implements FeatureAttributeEmulator {
955

    
956
        private static final int XNAME = 0;
957
        private static final int YNAME = 1;
958
        private static final int ZNAME = 2;
959

    
960
        private final GeometryManager geommgr;
961
        private final String[] fieldNames;
962
        private final Coercion toDouble;
963
        private int errorcount = 0;
964

    
965
        public PointAttributeEmulator(String[] pointDimensionNames) {
966
            if ( pointDimensionNames.length > 2 ) {
967
                this.fieldNames = new String[3];
968
                this.fieldNames[ZNAME] = pointDimensionNames[2];
969
            } else {
970
                this.fieldNames = new String[2];
971
            }
972
            this.fieldNames[XNAME] = pointDimensionNames[0];
973
            this.fieldNames[YNAME] = pointDimensionNames[1];
974
            this.geommgr = GeometryLocator.getGeometryManager();
975
            DataTypesManager datatypeManager = ToolsLocator.getDataTypesManager();
976

    
977
            this.toDouble = datatypeManager.getCoercion(DataTypes.DOUBLE);
978
        }
979

    
980
        @Override
981
        @SuppressWarnings("UseSpecificCatch")
982
        public Object get(Feature feature) {
983
            try {
984
                Object valueX = feature.get(this.fieldNames[XNAME]);
985
                valueX = toDouble.coerce(valueX);
986
                if ( valueX == null ) {
987
                    return null;
988
                }
989
                Object valueY = feature.get(this.fieldNames[YNAME]);
990
                valueY = toDouble.coerce(valueY);
991
                if ( valueY == null ) {
992
                    return null;
993
                }
994
                Object valueZ = null;
995
                if ( this.fieldNames.length > 2 ) {
996
                    valueZ = toDouble.coerce(feature.get(this.fieldNames[ZNAME]));
997
                    if ( valueZ == null ) {
998
                        return null;
999
                    }
1000
                }
1001

    
1002
                double x = ((Double) valueX);
1003
                double y = ((Double) valueY);
1004
                Point point;
1005
                if ( this.fieldNames.length > 2 ) {
1006
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
1007
                    double z = ((Double) valueZ);
1008
                    point.setCoordinateAt(2, z);
1009
                } else {
1010
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM2D);
1011
                }
1012
                return point;
1013
            } catch (Exception ex) {
1014
                if ( ++errorcount < 5 ) {
1015
                    LOGGER.warn("[" + errorcount + "] Can't create point from '"+getProviderName()+"' file."+
1016
                            "XNAME='" + this.fieldNames[XNAME] + 
1017
                            "', YNAME='" + this.fieldNames[YNAME] + 
1018
                            "', ZNAME='" + ((this.fieldNames.length > 2)? this.fieldNames[ZNAME]: "(2D)") +
1019
                            "' feature=" + feature.toString(), ex);
1020
                }
1021
                return null;
1022
            }
1023
        }
1024

    
1025
        @Override
1026
        public void set(EditableFeature feature, Object value) {
1027
            if ( value == null ) {
1028
                return;
1029
            }
1030
            Point point;
1031
            if ( value instanceof MultiPoint ) {
1032
                point = (Point) ((MultiPoint) value).getPrimitiveAt(0);
1033
            } else {
1034
                point = (Point) value;
1035
            }
1036
            feature.set(this.fieldNames[XNAME], point.getX());
1037
            feature.set(this.fieldNames[YNAME], point.getY());
1038
            if ( this.fieldNames.length > 2 ) {
1039
                feature.set(this.fieldNames[ZNAME], point.getCoordinateAt(2));
1040
            }
1041
        }
1042

    
1043
        @Override
1044
        public boolean allowSetting() {
1045
            return true;
1046
        }
1047

    
1048
        @Override
1049
        public String[] getRequiredFieldNames() {
1050
            return this.fieldNames;
1051
        }
1052

    
1053
    }
1054

    
1055
    private void loadFeatures() throws IOException, DataException,
1056
            CoercionException, CloneNotSupportedException {
1057

    
1058
        ReaderData theReaderData = this.readerData;
1059
        if( theReaderData==null ) {
1060
            theReaderData = new ReaderData();
1061
            SimpleSequentialReader reader = this.readerFactory.createReader(this.getParameters());
1062
            try {
1063
                loadFeatures(reader, theReaderData);
1064
                StoresRepositoryWithChildren repoWithChildren = new StoresRepositoryWithChildren();
1065
                StoresRepository repo = reader.getStoresRepository();
1066
                if( repo!=null ) {
1067
                    repoWithChildren.addRepository(repo);
1068
                }        
1069
                this.childrenData = new ArrayList<>();
1070
                List<SimpleSequentialReader> children = reader.getChildren();
1071
                for(SimpleSequentialReader childReader : children ) {
1072
                    ReaderData childData = new ReaderData();
1073
                    loadFeatures(childReader, childData);
1074
                    this.childrenData.add(childData);
1075
                    repoWithChildren.add(childReader.getName(), childData.getParameters());
1076
                    if( !StringUtils.isBlank(childReader.getAlias()) ) {
1077
                        repoWithChildren.add(childReader.getAlias(), childData.getParameters());
1078
                    }
1079
                    childData.setStoresRepository(repoWithChildren);
1080
                }
1081
                repoWithChildren.add(reader.getName(), theReaderData.getParameters());
1082
                if( !StringUtils.isBlank(reader.getAlias()) ) {
1083
                    repoWithChildren.add(reader.getAlias(), theReaderData.getParameters());
1084
                }
1085
                theReaderData.setStoresRepository(repoWithChildren);
1086
                this.readerData = theReaderData;
1087
            } finally {
1088
                reader.close();
1089
            }
1090
        }
1091
        this.name = theReaderData.getName();
1092
        FeatureStoreProviderServices theStore = this.getStoreServices();
1093
        theStore.setFeatureTypes(theReaderData.getFeatureTypes(), theReaderData.getDefaultFeatureType());
1094
        this.need_calculate_envelope = theReaderData.getNeedCalculateEnvelope();
1095
        this.data = theReaderData.getFeatures();
1096

    
1097
    }
1098
    
1099
    private void loadFeatures(SimpleSequentialReader reader, ReaderData readerData) throws IOException, DataException,
1100
            CoercionException, CloneNotSupportedException {
1101

    
1102
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
1103
        SimpleTaskStatus taskStatus = manager.createDefaultSimpleTaskStatus(reader.getName());
1104
        taskStatus.setAutoremove(true);
1105
        taskStatus.setIndeterminate();
1106
        taskStatus.add();
1107
        try {
1108
            List<String> headers;
1109

    
1110
            taskStatus.message("_preparing");
1111
            readerData.setName(reader.getName());
1112
            readerData.setStoresRepository(reader.getStoresRepository());
1113
            readerData.setResourcesStorage(reader.getResourcesStorage());
1114
            readerData.setParameters(reader.getParameters());
1115
            readerData.setAlias(reader.getAlias());
1116
            
1117
            boolean ignore_errors = getParameters().getIgnoreErrors();
1118

    
1119
            headers = getParameters().getFieldNames();
1120
            if ( headers == null ) {
1121
                // Esto es por si el getFieldNames devuelbe algo que no es
1122
                // un String pero su toString hace lo que toca.
1123
                headers = new ArrayList<>();
1124
                for (Object fielddesc : reader.getFieldNames()) {
1125
                    headers.add(fielddesc.toString());
1126
                }
1127
            }
1128

    
1129
            // Initialize the feature types
1130
            EditableFeatureType edftype = this.getFeatureType(headers, automaticDetectionOfTypes(reader));
1131

    
1132
            edftype.setLabel(reader.getLabel());
1133
            edftype.setDescription(reader.getDescription());
1134
            Map<String, String> tagsReader = reader.getTags();
1135
            if( tagsReader!=null ) {
1136
                Tags tagsType = edftype.getTags();
1137
                for (Map.Entry<String, String> tag : tagsReader.entrySet()) {
1138
                    tagsType.set(tag.getKey(), tag.getValue());
1139
                }
1140
            }
1141
            
1142
            FeatureType ftype = edftype.getNotEditableCopy();
1143
            List<FeatureType> ftypes = new ArrayList<>();
1144
            ftypes.add(ftype);
1145
            readerData.setFeatureTypes(ftypes, ftype);
1146

    
1147
            Coercion coercion[] = new Coercion[ftype.size()];
1148
            int sizes[] = new int[ftype.size()];
1149
            for ( int i = 0; i < ftype.size(); i++ ) {
1150
                sizes[i] = -1;
1151
                FeatureAttributeDescriptor ad = ftype.getAttributeDescriptor(i);
1152
                coercion[i] = ad.getDataType().getCoercion();
1153
                if ( ad.getDataType().getType() == DataTypes.STRING ) {
1154
                    if ( ad.getSize() == 0 ) {
1155
                        // Es un string y no tiene un size asignado.
1156
                        // Lo ponemos a cero para calcularlo.
1157
                        sizes[i] = 0;
1158
                    }
1159
                }
1160
            }
1161
           
1162
            if ( ftype.getDefaultGeometryAttributeName() != null ) {
1163
                readerData.setNeedCalculateEnvelope(true);
1164
            }
1165

    
1166
            Locale locale = getParameters().getLocale();
1167
            taskStatus.message("_loading");
1168
            long rowCount = reader.getRowCount();
1169
            if( rowCount >0 ) {
1170
                taskStatus.setRangeOfValues(0, rowCount);
1171
            }
1172

    
1173
            int count = 0;
1174

    
1175
            int count_errors = 0;
1176
            
1177
            reader.rewind();
1178
            List<Object> row = reader.read();
1179

    
1180
            Object rawvalue;
1181
            while ( row != null ) {
1182
                taskStatus.setCurValue(++count);
1183
                FeatureProvider feature = this.createFeatureProvider(ftype);
1184
                for ( int i = 0; i < row.size(); i++ ) {
1185
                    try {
1186
                        if( ftype.get(i).isComputed() ) {
1187
                            continue;
1188
                        }
1189
                        rawvalue = row.get(i);
1190
                        if( rawvalue instanceof String && StringUtils.isBlank((String)rawvalue) ) {
1191
                            rawvalue = null;
1192
                        }
1193
                        Object value;
1194
                        if ( locale != null && coercion[i] instanceof CoercionWithLocale ) {
1195
                            value = ((CoercionWithLocale) (coercion[i])).coerce(rawvalue, locale);
1196
                        } else {
1197
                            value = coercion[i].coerce(rawvalue);
1198
                        }
1199
                        feature.set(i, value);
1200
                        if ( sizes[i] >= 0 && value != null ) {
1201
                            int x = ((String) value).length();
1202
                            if ( sizes[i] < x ) {
1203
                                sizes[i] = x;
1204
                            }
1205
                        }
1206
                    } catch (Exception ex) {
1207
                        if ( !ignore_errors ) {
1208
                            throw ex;
1209
                        }
1210
                        if ( count_errors++ < 10 ) {
1211
                            LOGGER.warn("Can't load value of attribute " + i+"/"+row.size()+"/"+ftype.size()+ " in row " + count + " in " +readerData.getName()+ ".", ex);
1212
                        }
1213
                        if ( count_errors == 10 ) {
1214
                            LOGGER.info("Too many errors, suppress messages.");
1215
                        }
1216
                    }
1217
                }
1218
                readerData.addFeatureProvider(feature);
1219
                row = reader.read();
1220
            }
1221
            for ( int i = 0; i < ftype.size(); i++ ) {
1222
                if ( sizes[i] > 0 ) {
1223
                    EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
1224
                    if( efad.getSize()<sizes[i] ) {
1225
                        efad.setSize(sizes[i]);
1226
                    }
1227
                }
1228
            }
1229
            // Volvemos a asignar al store el featuretype, ya que puede
1230
            // haber cambiado.
1231
            ftype = edftype.getNotEditableCopy();
1232
            ftypes = new ArrayList<>();
1233
            ftypes.add(ftype);
1234
            readerData.setFeatureTypes(ftypes, ftype);
1235

    
1236
            taskStatus.terminate();
1237
        } catch (Exception ex) {
1238
            taskStatus.abort();
1239
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1240
        }
1241
    }
1242
    
1243
    private int[] automaticDetectionOfTypes(SimpleSequentialReader reader) throws IOException {
1244
        boolean automatic_types_detection = getParameters().getAutomaticTypesDetection();
1245
        if ( !automatic_types_detection ) {
1246
            return null;
1247
        }
1248
        int[] types = null;
1249
        try {
1250
            reader.rewind();
1251
            List<String> fieldNames = reader.getFieldNames();
1252
            if ( fieldNames == null ) {
1253
                fieldNames = getParameters().getFieldNames();
1254
            }
1255

    
1256
            AutomaticDetectionOfTypes x = new AutomaticDetectionOfTypes(
1257
                    this.getFullFileName()
1258
            );
1259
            types = x.detect(
1260
                    fieldNames.size(), 
1261
                    reader, 
1262
                    false, 
1263
                    getParameters().getLocale()
1264
            );
1265
        } catch (Exception ex) {
1266
            throw new RuntimeException("Problems reading '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1267
        }
1268
        return types;
1269
    }
1270

    
1271
    @Override
1272
    public boolean hasDynValue(String name) {
1273
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
1274
            return true;
1275
        } else if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
1276
            return true;
1277
        }
1278
        return super.hasDynValue(name);
1279
    }
1280

    
1281
    @Override
1282
    public UnmodifiableBasicMap<String, DataStore> getChildren() {
1283
        if( this.isChild() ) {
1284
            return null;
1285
        }
1286
        return new Children();
1287
    }
1288

    
1289
    @Override
1290
    public StoresRepository getStoresRepository() {
1291
        StoresRepository repo = this.readerData.getStoresRepository();
1292
        return repo;
1293
    }
1294
    
1295
    @Override
1296
    public ResourcesStorage getResourcesStorage() {
1297
        ResourcesStorage storage = this.readerData.getResourcesStorage();
1298
        return storage;
1299
    }
1300
}