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

History | View | Annotate | Download (48.5 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.task.SimpleTaskStatus;
93
import org.gvsig.tools.task.TaskStatusManager;
94
import org.gvsig.tools.util.UnmodifiableBasicMap;
95
import org.gvsig.tools.util.UnmodifiableBasicMapToMapAdapter;
96
import org.gvsig.tools.util.UnmodifiableBasicSet;
97
import org.gvsig.tools.visitor.VisitCanceledException;
98
import org.gvsig.tools.visitor.Visitor;
99
import org.slf4j.Logger;
100
import org.slf4j.LoggerFactory;
101

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

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

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

    
143
        public ReaderData() {
144
            this.needCalculateEnvelope = false;
145
            this.features = new ArrayList<>();
146
            this.OIDcounter = 0;
147
            this.store = null;
148
        }
149

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

    
180
        public String getName() {
181
            return name;
182
        }
183

    
184
        public void setName(String name) {
185
            this.name = name;
186
        }
187

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

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

    
227
        public void setStoresRepository(StoresRepository storesRepository) {
228
            this.storesRepository = storesRepository;
229
        }
230

    
231
        public StoresRepository getStoresRepository() {
232
            return storesRepository;
233
        }
234

    
235
        public void setParameters(OpenFeatureStoreParameters parameters) {
236
            if( parameters==null ) {
237
                LOGGER.warn("Can't set parameters to null");
238
                return;
239
            }
240
            this.parameters = parameters;
241
        }
242

    
243
        public OpenFeatureStoreParameters getParameters() {
244
            return parameters;
245
        }
246

    
247
        public String getAlias() {
248
            return this.alias;
249
        }
250

    
251
        public void setAlias(String alias) {
252
            this.alias = alias;
253
        }
254
        
255
        
256
    }
257
    
258
    class Children implements UnmodifiableBasicMap<String, DataStore> {
259
        // Con esta clase se pospone la creacion de los stores hasta que se 
260
        // pide cada uno de ellos, y no cuando se pide el Map de estos, ya
261
        // que el map se puede pedir solo para saber si hay o cuantos hay.
262

    
263
        @Override
264
        public DataStore get(String key) {
265
            for (ReaderData child : childrenData) {
266
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
267
                    return child.getStore();
268
                }
269
            }
270
            return null;
271
        }
272

    
273
        @Override
274
        public boolean isEmpty() {
275
            return childrenData.isEmpty();
276
        }
277

    
278
        @Override
279
        public boolean containsKey(String key) {
280
            for (ReaderData child : childrenData) {
281
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
282
                    return true;
283
                }
284
            }
285
            return false;
286
        }
287

    
288
        @Override
289
        public Map<String, DataStore> toMap() {
290
            return new UnmodifiableBasicMapToMapAdapter<>(this);
291
        }
292

    
293
        @Override
294
        public int size() {
295
            return childrenData.size();
296
        }
297

    
298
        @Override
299
        public UnmodifiableBasicSet<String> keySet() {
300
            return new UnmodifiableBasicSet<String>() {
301

    
302
                @Override
303
                public boolean isEmpty() {
304
                    return childrenData.isEmpty();
305
                }
306

    
307
                @Override
308
                public int size() {
309
                    return childrenData.size();
310
                }
311

    
312
                @Override
313
                public Iterator<String> iterator() {
314
                    final Iterator<ReaderData> it = childrenData.iterator();
315
                    return new Iterator<String>() {
316
                        @Override
317
                        public boolean hasNext() {
318
                            return it.hasNext();
319
                        }
320

    
321
                        @Override
322
                        public String next() {
323
                            ReaderData theReaderData = it.next();
324
                            return theReaderData.getName();
325
                        }
326
                    };
327
                }
328
            };
329
        }
330

    
331
        @Override
332
        public Iterator<DataStore> iterator() {
333
            final Iterator<String> it = this.keySet().iterator();
334
            return new Iterator<DataStore>() {
335
                @Override
336
                public boolean hasNext() {
337
                    return it.hasNext();
338
                }
339

    
340
                @Override
341
                public DataStore next() {
342
                    String name = it.next();
343
                    return get(name);
344
                }
345
            };
346
        }
347
        
348
    }
349

    
350
    private final ResourceProvider resource;
351

    
352
    private Envelope envelope;
353
    private boolean need_calculate_envelope = false;
354
    private String name = "";
355
    private final SimpleSequentialReaderFactory readerFactory;
356
    private List<ReaderData> childrenData;
357
    private ReaderData readerData;
358
    private final boolean isAChild;
359

    
360
    public SimpleSequentialReaderStoreProvider(
361
            SimpleSequentialReaderFactory readerFactory,
362
            SimpleSequentialReaderStoreParameters parameters,
363
            DataStoreProviderServices storeServices
364
        ) throws InitializeException {
365
        this(readerFactory, parameters, storeServices, new ArrayList<ReaderData>(), null);
366
    }
367
    
368
    private SimpleSequentialReaderStoreProvider(
369
            SimpleSequentialReaderFactory readerFactory,
370
            SimpleSequentialReaderStoreParameters parameters,
371
            DataStoreProviderServices storeServices,
372
            List<ReaderData> childrenData,
373
            ReaderData readerData
374
            
375
        ) throws InitializeException {
376
        super(
377
                parameters,
378
                storeServices,
379
                FileHelper.newMetadataContainer(readerFactory.getName())
380
        );
381
        this.childrenData = childrenData;
382
        this.readerData = readerData;
383
        this.readerFactory = readerFactory;
384
        this.isAChild = this.readerData!=null;
385
        
386
        File file = getParameters().getFile();
387
        resource = this.createResource(
388
                FileResource.NAME,
389
                new Object[]{file.getAbsolutePath()}
390
        );
391

    
392
        resource.addConsumer(this);
393
        if( this.readerData!=null ) {
394
            this.name = this.readerData.getName();            
395
        }
396
        if( storeServices != null ) {
397
            initializeFeatureTypes();
398
        }
399
    }
400
    
401
    private boolean isChild() {
402
        return this.isAChild;
403
    }
404
    
405
    private void setStoreServices(FeatureStoreProviderServices storeServices) throws InitializeException {
406
        this.store = storeServices;
407
        initializeFeatureTypes();
408
    }
409

    
410
    @Override
411
    public FeatureStoreProviderServices getStoreServices() {
412
        if( this.store==null ) {
413
            // Por aqui no deberia de pasar.
414
            // Si pasa es que algo va mal.
415
            LOGGER.warn("Algo no anda bien, el store es null");
416
            DataStore theStore = this.readerData.getStore();
417
            if( theStore==null ) {
418
                return null;
419
            }
420
            this.store = (FeatureStoreProviderServices) theStore ;
421
        }
422
        return this.store;
423
    }
424
    
425
    @Override
426
    public SimpleSequentialReaderStoreParameters getParameters() {
427
        return (SimpleSequentialReaderStoreParameters) super.getParameters();
428
    }
429

    
430
    @Override
431
    public String getProviderName() {
432
        if( this.readerFactory==null ) {
433
            return "unknown";
434
        }
435
        return this.readerFactory.getName();
436
    }
437

    
438
    @Override
439
    public boolean allowWrite() {
440
        return false;
441
    }
442

    
443
    private String getFullFileName() {
444
        // Usar solo para mostrar mensajes en el logger.
445
        String s;
446
        try {
447
            s = getParameters().getFile().getAbsolutePath();
448
        } catch (Exception e2) {
449
            s = "(unknow)";
450
        }
451
        return s;
452
    }
453

    
454
    @Override
455
    public void open() throws OpenException {
456
        if ( this.data != null ) {
457
            return;
458
        }
459
        this.data = new ArrayList<>();
460
        resource.setData(new HashMap());
461
        try {
462
            loadFeatures();
463
        } catch (RuntimeException e) {
464
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
465
            throw e;
466
        } catch (Exception e) {
467
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
468
            throw new RuntimeException(e);
469
        }
470
    }
471

    
472
    @Override
473
    public DataServerExplorer getExplorer() throws ReadException {
474
        DataManager manager = DALLocator.getDataManager();
475
        FilesystemServerExplorerParameters params;
476
        try {
477
            params = (FilesystemServerExplorerParameters) manager
478
                    .createServerExplorerParameters(FilesystemServerExplorer.NAME);
479
            params.setRoot(this.getParameters().getFile().getParent());
480
            return manager.openServerExplorer(FilesystemServerExplorer.NAME, params);
481
        } catch (DataException | ValidateDataParametersException e) {
482
            throw new ReadException(this.getProviderName(), e);
483
        }
484

    
485
    }
486

    
487
    @Override
488
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
489
        throw new UnsupportedOperationException();
490
    }
491

    
492
    @Override
493
    public boolean closeResourceRequested(ResourceProvider resource) {
494
        return true;
495
    }
496

    
497
    @Override
498
    public int getOIDType() {
499
        return DataTypes.LONG;
500
    }
501

    
502
    @Override
503
    public boolean supportsAppendMode() {
504
        return false;
505
    }
506

    
507
    @Override
508
    public void append(FeatureProvider featureProvider) {
509
        throw new UnsupportedOperationException();
510
    }
511

    
512
    @Override
513
    public void beginAppend() {
514
        throw new UnsupportedOperationException();
515
    }
516

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

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

    
527
    @Override
528
    protected void doDispose() throws BaseException {
529
        for (ReaderData theReaderData : this.childrenData) {
530
            DisposeUtils.disposeQuietly(theReaderData);
531
        }
532
        this.childrenData = null;
533
        DisposeUtils.disposeQuietly(this.readerData);
534
        this.readerData = null;
535
//        this.resource = null;
536
//        this.readerFactory = null;
537
        this.envelope = null;
538
        super.doDispose();
539
    }
540

    
541
    
542
    private void initializeFeatureTypes() throws InitializeException {
543
        try {
544
            this.open();
545
        } catch (OpenException e) {
546
            throw new InitializeException(this.getProviderName(), e);
547
        }
548
    }
549

    
550
    @Override
551
    public Envelope getEnvelope() throws DataException {
552
        this.open();
553
        if ( this.envelope != null ) {
554
            return this.envelope;
555
        }
556
        if ( !this.need_calculate_envelope ) {
557
            return null;
558
        }
559
        FeatureStore fs = this.getFeatureStore();
560
        FeatureType ft = fs.getDefaultFeatureType();
561
        FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
562

    
563
        try {
564
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
565
            fs.accept(new Visitor() {
566
                @Override
567
                public void visit(Object obj) throws VisitCanceledException, BaseException {
568
                    Feature f = (Feature) obj;
569
                    Geometry geom = f.getDefaultGeometry();
570
                    if ( geom != null ) {
571
                        envelope.add(geom.getEnvelope());
572
                    }
573
                }
574
            });
575
        } catch (BaseException e) {
576
            LOGGER.warn("Can't calculate the envelope of '"+getProviderName()+"' file '" + this.getFullName() + "'.", e);
577
            this.envelope = null;
578
        }
579

    
580
        this.need_calculate_envelope = false;
581
        return this.envelope;
582
    }
583

    
584
    @Override
585
    public Object getDynValue(String name) throws DynFieldNotFoundException {
586
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
587
            try {
588
                return this.getEnvelope();
589
            } catch (DataException e) {
590
                return null;
591
            }
592
        } else {
593
            if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
594
                IProjection pro = this.getParameters().getCRS();
595
                if ( pro != null ) {
596
                    return pro;
597
                }
598
                FeatureType type;
599
                try {
600
                    type = this.getStoreServices().getDefaultFeatureType();
601
                    pro = type.getDefaultSRS();
602
                    if( pro!=null ) {
603
                        return pro;
604
                    }
605
                } catch (DataException ex) {
606
                }
607
            }
608
        }
609
        return super.getDynValue(name);
610
    }
611

    
612
    @Override
613
    public void resourceChanged(ResourceProvider resource) {
614
        this.getStoreServices().notifyChange(
615
                DataStoreNotification.RESOURCE_CHANGED,
616
                resource);
617
    }
618

    
619
    @Override
620
    public Object getSourceId() {
621
        return this.getParameters().getFile();
622
    }
623

    
624
    @Override
625
    public String getName() {
626
        return this.name;
627
    }
628

    
629
    @Override
630
    public String getFullName() {
631
        return this.getParameters().getFile().getAbsolutePath();
632
    }
633

    
634
    @Override
635
    public ResourceProvider getResource() {
636
        return resource;
637
    }
638

    
639
    private String[] split(String value, String separators) {
640
        int firstSeparatorPosition = 1000000;
641
        Character sep = null;
642
        for (char ch : separators.toCharArray()) {
643
            int pos = value.indexOf(ch);
644
            if( pos>0 && pos<firstSeparatorPosition ) {
645
                sep = ch;
646
                firstSeparatorPosition = pos;
647
            }
648
        }
649
        if( sep == null ) {
650
            return new String[] { value };
651
        }
652
        return value.split("["+sep+"]");
653
    }
654
    
655
    private class FieldTypeParser {
656

    
657
        public String name = null;
658
        public int type = DataTypes.STRING;
659
        public int size = 0;
660
        public int geomType = Geometry.TYPES.GEOMETRY;
661
        public int geomSubtype = Geometry.SUBTYPES.GEOM2D;
662
        public Map<String,String> tags = new HashMap<>();
663
        public Map<String,String> assignments = new HashMap<>();
664

    
665
        private String typename = "string";
666

    
667
        FieldTypeParser() {
668
        }
669

    
670
        private int getType(String value) {
671
            DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
672
            return dataTypesManager.getType(typename);
673
        }
674

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

    
766
    }
767

    
768
    private EditableFeatureType getFeatureType(List<String> headers, int automaticTypes[]) {
769
        EditableFeatureType fType = getStoreServices().createFeatureType(this.getName());
770
        fType.setHasOID(true);
771
//        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
772

    
773
        FieldTypeParser[] fieldTypes = new FieldTypeParser[headers.size()];
774
        //
775
        // Calculamos cuales pueden ser los tipos de datos
776
        //
777
        for ( int i = 0; i < fieldTypes.length; i++ ) {
778
            fieldTypes[i] = new FieldTypeParser();
779
        }
780

    
781
        // Asuminos los tipos pasados por parametro, que se supone
782
        // son los detectados automaticamente.
783
        if ( automaticTypes != null ) {
784
            for ( int i = 0; i < fieldTypes.length && i < automaticTypes.length; i++ ) {
785
                fieldTypes[i].type = automaticTypes[i];
786
            }
787
        }
788
        // Luego probamos con lo que diga las cabezeras, sobreescribiendo
789
        // los tipos anteriores en caso de definirse en la cabezara.
790
        for ( int i = 0; i < fieldTypes.length; i++ ) {
791
            fieldTypes[i].parse(headers.get(i));
792
        }
793

    
794
        // Y por ultimo hacemos caso a lo que se haya especificado en los parametros
795
        // de apertura del reader, teniendo esto prioridad sobre todo.
796
        int[] param_types = this.getParameters().getFieldTypes();
797
        if ( param_types != null ) {
798
            for ( int i = 0; i < fieldTypes.length && i < param_types.length; i++ ) {
799
                fieldTypes[i].type = param_types[i];
800
            }
801
        }
802

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

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

    
944
    class PointAttributeEmulator implements FeatureAttributeEmulator {
945

    
946
        private static final int XNAME = 0;
947
        private static final int YNAME = 1;
948
        private static final int ZNAME = 2;
949

    
950
        private final GeometryManager geommgr;
951
        private final String[] fieldNames;
952
        private final Coercion toDouble;
953
        private int errorcount = 0;
954

    
955
        public PointAttributeEmulator(String[] pointDimensionNames) {
956
            if ( pointDimensionNames.length > 2 ) {
957
                this.fieldNames = new String[3];
958
                this.fieldNames[ZNAME] = pointDimensionNames[2];
959
            } else {
960
                this.fieldNames = new String[2];
961
            }
962
            this.fieldNames[XNAME] = pointDimensionNames[0];
963
            this.fieldNames[YNAME] = pointDimensionNames[1];
964
            this.geommgr = GeometryLocator.getGeometryManager();
965
            DataTypesManager datatypeManager = ToolsLocator.getDataTypesManager();
966

    
967
            this.toDouble = datatypeManager.getCoercion(DataTypes.DOUBLE);
968
        }
969

    
970
        @Override
971
        @SuppressWarnings("UseSpecificCatch")
972
        public Object get(Feature feature) {
973
            try {
974
                Object valueX = feature.get(this.fieldNames[XNAME]);
975
                valueX = toDouble.coerce(valueX);
976
                if ( valueX == null ) {
977
                    return null;
978
                }
979
                Object valueY = feature.get(this.fieldNames[YNAME]);
980
                valueY = toDouble.coerce(valueY);
981
                if ( valueY == null ) {
982
                    return null;
983
                }
984
                Object valueZ = null;
985
                if ( this.fieldNames.length > 2 ) {
986
                    valueZ = toDouble.coerce(feature.get(this.fieldNames[ZNAME]));
987
                    if ( valueZ == null ) {
988
                        return null;
989
                    }
990
                }
991

    
992
                double x = ((Double) valueX);
993
                double y = ((Double) valueY);
994
                Point point;
995
                if ( this.fieldNames.length > 2 ) {
996
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
997
                    double z = ((Double) valueZ);
998
                    point.setCoordinateAt(2, z);
999
                } else {
1000
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM2D);
1001
                }
1002
                return point;
1003
            } catch (Exception ex) {
1004
                if ( ++errorcount < 5 ) {
1005
                    LOGGER.warn("[" + errorcount + "] Can't create point from '"+getProviderName()+"' file."+
1006
                            "XNAME='" + this.fieldNames[XNAME] + 
1007
                            "', YNAME='" + this.fieldNames[YNAME] + 
1008
                            "', ZNAME='" + ((this.fieldNames.length > 2)? this.fieldNames[ZNAME]: "(2D)") +
1009
                            "' feature=" + feature.toString(), ex);
1010
                }
1011
                return null;
1012
            }
1013
        }
1014

    
1015
        @Override
1016
        public void set(EditableFeature feature, Object value) {
1017
            if ( value == null ) {
1018
                return;
1019
            }
1020
            Point point;
1021
            if ( value instanceof MultiPoint ) {
1022
                point = (Point) ((MultiPoint) value).getPrimitiveAt(0);
1023
            } else {
1024
                point = (Point) value;
1025
            }
1026
            feature.set(this.fieldNames[XNAME], point.getX());
1027
            feature.set(this.fieldNames[YNAME], point.getY());
1028
            if ( this.fieldNames.length > 2 ) {
1029
                feature.set(this.fieldNames[ZNAME], point.getCoordinateAt(2));
1030
            }
1031
        }
1032

    
1033
        @Override
1034
        public boolean allowSetting() {
1035
            return true;
1036
        }
1037

    
1038
        @Override
1039
        public String[] getRequiredFieldNames() {
1040
            return this.fieldNames;
1041
        }
1042

    
1043
    }
1044

    
1045
    private void loadFeatures() throws IOException, DataException,
1046
            CoercionException, CloneNotSupportedException {
1047

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

    
1087
    }
1088
    
1089
    private void loadFeatures(SimpleSequentialReader reader, ReaderData readerData) throws IOException, DataException,
1090
            CoercionException, CloneNotSupportedException {
1091

    
1092
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
1093
        SimpleTaskStatus taskStatus = manager.createDefaultSimpleTaskStatus(reader.getName());
1094
        taskStatus.setAutoremove(true);
1095
        taskStatus.setIndeterminate();
1096
        taskStatus.add();
1097
        try {
1098
            List<String> headers;
1099

    
1100
            taskStatus.message("_preparing");
1101
            readerData.setName(reader.getName());
1102
            readerData.setStoresRepository(reader.getStoresRepository());
1103
            readerData.setParameters(reader.getParameters());
1104
            readerData.setAlias(reader.getAlias());
1105
            
1106
            boolean ignore_errors = getParameters().getIgnoreErrors();
1107

    
1108
            headers = getParameters().getFieldNames();
1109
            if ( headers == null ) {
1110
                headers = reader.getFieldNames();
1111
            }
1112

    
1113
            // Initialize the feature types
1114
            EditableFeatureType edftype = this.getFeatureType(headers, automaticDetectionOfTypes(reader));
1115

    
1116
            edftype.setLabel(reader.getLabel());
1117
            edftype.setDescription(reader.getDescription());
1118
            Map<String, String> tagsReader = reader.getTags();
1119
            if( tagsReader!=null ) {
1120
                Tags tagsType = edftype.getTags();
1121
                for (Map.Entry<String, String> tag : tagsReader.entrySet()) {
1122
                    tagsType.set(tag.getKey(), tag.getValue());
1123
                }
1124
            }
1125
            
1126
            FeatureType ftype = edftype.getNotEditableCopy();
1127
            List<FeatureType> ftypes = new ArrayList<>();
1128
            ftypes.add(ftype);
1129
            readerData.setFeatureTypes(ftypes, ftype);
1130

    
1131
            Coercion coercion[] = new Coercion[ftype.size()];
1132
            int sizes[] = new int[ftype.size()];
1133
            for ( int i = 0; i < ftype.size(); i++ ) {
1134
                sizes[i] = -1;
1135
                FeatureAttributeDescriptor ad = ftype.getAttributeDescriptor(i);
1136
                coercion[i] = ad.getDataType().getCoercion();
1137
                if ( ad.getDataType().getType() == DataTypes.STRING ) {
1138
                    if ( ad.getSize() == 0 ) {
1139
                        // Es un string y no tiene un size asignado.
1140
                        // Lo ponemos a cero para calcularlo.
1141
                        sizes[i] = 0;
1142
                    }
1143
                }
1144
            }
1145
           
1146
            if ( ftype.getDefaultGeometryAttributeName() != null ) {
1147
                readerData.setNeedCalculateEnvelope(true);
1148
            }
1149

    
1150
            Locale locale = getParameters().getLocale();
1151
            taskStatus.message("_loading");
1152
            long rowCount = reader.getRowCount();
1153
            if( rowCount >0 ) {
1154
                taskStatus.setRangeOfValues(0, rowCount);
1155
            }
1156

    
1157
            int count = 0;
1158

    
1159
            int count_errors = 0;
1160
            
1161
            reader.rewind();
1162
            List<Object> row = reader.read();
1163

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

    
1220
            taskStatus.terminate();
1221
        } catch (Exception ex) {
1222
            taskStatus.abort();
1223
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1224
        }
1225
    }
1226
    
1227
    private int[] automaticDetectionOfTypes(SimpleSequentialReader reader) throws IOException {
1228
        boolean automatic_types_detection = getParameters().getAutomaticTypesDetection();
1229
        if ( !automatic_types_detection ) {
1230
            return null;
1231
        }
1232
        int[] types = null;
1233
        try {
1234
            reader.rewind();
1235
            List<String> fieldNames = reader.getFieldNames();
1236
            if ( fieldNames == null ) {
1237
                fieldNames = getParameters().getFieldNames();
1238
            }
1239

    
1240
            AutomaticDetectionOfTypes x = new AutomaticDetectionOfTypes(
1241
                    this.getFullFileName()
1242
            );
1243
            types = x.detect(
1244
                    fieldNames.size(), 
1245
                    reader, 
1246
                    false, 
1247
                    getParameters().getLocale()
1248
            );
1249
        } catch (Exception ex) {
1250
            throw new RuntimeException("Problems reading '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1251
        }
1252
        return types;
1253
    }
1254

    
1255
    @Override
1256
    public boolean hasDynValue(String name) {
1257
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
1258
            return true;
1259
        } else if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
1260
            return true;
1261
        }
1262
        return super.hasDynValue(name);
1263
    }
1264

    
1265
    @Override
1266
    public UnmodifiableBasicMap<String, DataStore> getChildren() {
1267
        if( this.isChild() ) {
1268
            return null;
1269
        }
1270
        return new Children();
1271
    }
1272

    
1273
    @Override
1274
    public StoresRepository getStoresRepository() {
1275
        StoresRepository repo = this.readerData.getStoresRepository();
1276
        return repo;
1277
    }
1278
    
1279
}