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

History | View | Annotate | Download (46 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 "expression":
838
                            // Los campos calculados los procesamos en una segunda
839
                            // pasada, cuando ya estan definidos el resto de los campos
840
                            // ya que pueden requerir campos que aun no se han definido.
841
                            break;
842
                        default:
843
                            fad.set(entry.getKey(), entry.getValue());
844
                    }
845
                } catch (Exception ex) {
846
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' of '"+fad.getName()+"'.", ex);
847
                }
848
            }
849
            Tags tags = fad.getTags();
850
            for (Map.Entry<String, String> entry : fieldType.tags.entrySet()) {
851
                tags.set(entry.getKey(), entry.getValue());
852
            }
853
        }
854
        // Processamos ahora los campos calculados
855
        for (FieldTypeParser fieldType : fieldTypes) {
856
            EditableFeatureAttributeDescriptor fad = fType.getEditableAttributeDescriptor(fieldType.name);
857
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
858
                try {
859
                    switch(entry.getKey().toLowerCase()) {
860
                        case "expression":
861
                            fad.set(entry.getKey(), entry.getValue());
862
                            break;
863
                    }
864
                } catch (Exception ex) {
865
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' in '"+fad.getName()+"' of '"+getFullFileName()+"'.", ex);
866
                }
867
            }
868
        }
869

    
870
        String[] pointDimensionNames = this.getParameters().getPointDimensionNames();
871
        if ( pointDimensionNames != null ) {
872
            PointAttributeEmulator emulator = new PointAttributeEmulator(pointDimensionNames);
873
            EditableFeatureAttributeDescriptor attr = fType.add("the_geom", DataTypes.GEOMETRY, emulator);
874
            GeometryManager geommgr = GeometryLocator.getGeometryManager();
875
            GeometryType gt;
876
            try {
877
                if ( emulator.fieldNames != null && emulator.fieldNames.length <= 2 ) {
878
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
879
                } else {
880
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
881
                }
882
                attr.setGeometryType(gt);
883
            } catch (Exception e) {
884
                LOGGER.warn("Can't set geometry type for the calculated field in '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
885
            }
886
        }
887
        return fType;
888
    }
889

    
890
    class PointAttributeEmulator implements FeatureAttributeEmulator {
891

    
892
        private static final int XNAME = 0;
893
        private static final int YNAME = 1;
894
        private static final int ZNAME = 2;
895

    
896
        private final GeometryManager geommgr;
897
        private final String[] fieldNames;
898
        private final Coercion toDouble;
899
        private int errorcount = 0;
900

    
901
        public PointAttributeEmulator(String[] pointDimensionNames) {
902
            if ( pointDimensionNames.length > 2 ) {
903
                this.fieldNames = new String[3];
904
                this.fieldNames[ZNAME] = pointDimensionNames[2];
905
            } else {
906
                this.fieldNames = new String[2];
907
            }
908
            this.fieldNames[XNAME] = pointDimensionNames[0];
909
            this.fieldNames[YNAME] = pointDimensionNames[1];
910
            this.geommgr = GeometryLocator.getGeometryManager();
911
            DataTypesManager datatypeManager = ToolsLocator.getDataTypesManager();
912

    
913
            this.toDouble = datatypeManager.getCoercion(DataTypes.DOUBLE);
914
        }
915

    
916
        @Override
917
        @SuppressWarnings("UseSpecificCatch")
918
        public Object get(Feature feature) {
919
            try {
920
                Object valueX = feature.get(this.fieldNames[XNAME]);
921
                valueX = toDouble.coerce(valueX);
922
                if ( valueX == null ) {
923
                    return null;
924
                }
925
                Object valueY = feature.get(this.fieldNames[YNAME]);
926
                valueY = toDouble.coerce(valueY);
927
                if ( valueY == null ) {
928
                    return null;
929
                }
930
                Object valueZ = null;
931
                if ( this.fieldNames.length > 2 ) {
932
                    valueZ = toDouble.coerce(feature.get(this.fieldNames[ZNAME]));
933
                    if ( valueZ == null ) {
934
                        return null;
935
                    }
936
                }
937

    
938
                double x = ((Double) valueX);
939
                double y = ((Double) valueY);
940
                Point point;
941
                if ( this.fieldNames.length > 2 ) {
942
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
943
                    double z = ((Double) valueZ);
944
                    point.setCoordinateAt(2, z);
945
                } else {
946
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM2D);
947
                }
948
                return point;
949
            } catch (Exception ex) {
950
                if ( ++errorcount < 5 ) {
951
                    LOGGER.warn("[" + errorcount + "] Can't create point from '"+getProviderName()+"' file."+
952
                            "XNAME='" + this.fieldNames[XNAME] + 
953
                            "', YNAME='" + this.fieldNames[YNAME] + 
954
                            "', ZNAME='" + ((this.fieldNames.length > 2)? this.fieldNames[ZNAME]: "(2D)") +
955
                            "' feature=" + feature.toString(), ex);
956
                }
957
                return null;
958
            }
959
        }
960

    
961
        @Override
962
        public void set(EditableFeature feature, Object value) {
963
            if ( value == null ) {
964
                return;
965
            }
966
            Point point;
967
            if ( value instanceof MultiPoint ) {
968
                point = (Point) ((MultiPoint) value).getPrimitiveAt(0);
969
            } else {
970
                point = (Point) value;
971
            }
972
            feature.set(this.fieldNames[XNAME], point.getX());
973
            feature.set(this.fieldNames[YNAME], point.getY());
974
            if ( this.fieldNames.length > 2 ) {
975
                feature.set(this.fieldNames[ZNAME], point.getCoordinateAt(2));
976
            }
977
        }
978

    
979
        @Override
980
        public boolean allowSetting() {
981
            return true;
982
        }
983

    
984
        @Override
985
        public String[] getRequiredFieldNames() {
986
            return this.fieldNames;
987
        }
988

    
989
    }
990

    
991
    private void loadFeatures() throws IOException, DataException,
992
            CoercionException, CloneNotSupportedException {
993

    
994
        ReaderData theReaderData = this.readerData;
995
        if( theReaderData==null ) {
996
            theReaderData = new ReaderData();
997
            SimpleSequentialReader reader = this.readerFactory.createReader(this.getParameters());
998
            try {
999
                loadFeatures(reader, theReaderData);
1000
                StoresRepositoryWithChildren repoWithChildren = new StoresRepositoryWithChildren();
1001
                StoresRepository repo = reader.getStoresRepository();
1002
                if( repo!=null ) {
1003
                    repoWithChildren.addRepository(repo);
1004
                }        
1005
                this.childrenData = new ArrayList<>();
1006
                List<SimpleSequentialReader> children = reader.getChildren();
1007
                for(SimpleSequentialReader childReader : children ) {
1008
                    ReaderData childData = new ReaderData();
1009
                    loadFeatures(childReader, childData);
1010
                    this.childrenData.add(childData);
1011
                    repoWithChildren.add(childReader.getName(), childData.getParameters());
1012
                    if( !StringUtils.isBlank(childReader.getAlias()) ) {
1013
                        repoWithChildren.add(childReader.getAlias(), childData.getParameters());
1014
                    }
1015
                    childData.setStoresRepository(repoWithChildren);
1016
                }
1017
                repoWithChildren.add(reader.getName(), theReaderData.getParameters());
1018
                if( !StringUtils.isBlank(reader.getAlias()) ) {
1019
                    repoWithChildren.add(reader.getAlias(), theReaderData.getParameters());
1020
                }
1021
                theReaderData.setStoresRepository(repoWithChildren);
1022
                this.readerData = theReaderData;
1023
            } finally {
1024
                reader.close();
1025
            }
1026
        }
1027
        this.name = theReaderData.getName();
1028
        FeatureStoreProviderServices theStore = this.getStoreServices();
1029
        theStore.setFeatureTypes(theReaderData.getFeatureTypes(), theReaderData.getDefaultFeatureType());
1030
        this.need_calculate_envelope = theReaderData.getNeedCalculateEnvelope();
1031
        this.data = theReaderData.getFeatures();
1032

    
1033
    }
1034
    
1035
    private void loadFeatures(SimpleSequentialReader reader, ReaderData readerData) throws IOException, DataException,
1036
            CoercionException, CloneNotSupportedException {
1037

    
1038
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
1039
        SimpleTaskStatus taskStatus = manager.createDefaultSimpleTaskStatus(reader.getName());
1040
        taskStatus.setAutoremove(true);
1041
        taskStatus.setIndeterminate();
1042
        taskStatus.add();
1043
        try {
1044
            List<String> headers;
1045

    
1046
            taskStatus.message("_preparing");
1047
            readerData.setName(reader.getName());
1048
            readerData.setStoresRepository(reader.getStoresRepository());
1049
            readerData.setResourcesStorage(reader.getResourcesStorage());
1050
            readerData.setParameters(reader.getParameters());
1051
            readerData.setAlias(reader.getAlias());
1052
            
1053
            boolean ignore_errors = getParameters().getIgnoreErrors();
1054

    
1055
            headers = getParameters().getFieldNames();
1056
            if ( headers == null ) {
1057
                // Esto es por si el getFieldNames devuelbe algo que no es
1058
                // un String pero su toString hace lo que toca.
1059
                headers = new ArrayList<>();
1060
                for (Object fielddesc : reader.getFieldNames()) {
1061
                    headers.add(fielddesc.toString());
1062
                }
1063
            }
1064

    
1065
            // Initialize the feature types
1066
            EditableFeatureType edftype = this.getFeatureType(headers, automaticDetectionOfTypes(reader));
1067

    
1068
            edftype.setLabel(reader.getLabel());
1069
            edftype.setDescription(reader.getDescription());
1070
            Map<String, String> tagsReader = reader.getTags();
1071
            if( tagsReader!=null ) {
1072
                Tags tagsType = edftype.getTags();
1073
                for (Map.Entry<String, String> tag : tagsReader.entrySet()) {
1074
                    tagsType.set(tag.getKey(), tag.getValue());
1075
                }
1076
            }
1077
            
1078
            FeatureType ftype = edftype.getNotEditableCopy();
1079
            List<FeatureType> ftypes = new ArrayList<>();
1080
            ftypes.add(ftype);
1081
            readerData.setFeatureTypes(ftypes, ftype);
1082

    
1083
            Coercion coercion[] = new Coercion[ftype.size()];
1084
            int sizes[] = new int[ftype.size()];
1085
            for ( int i = 0; i < ftype.size(); i++ ) {
1086
                sizes[i] = -1;
1087
                FeatureAttributeDescriptor ad = ftype.getAttributeDescriptor(i);
1088
                coercion[i] = ad.getDataType().getCoercion();
1089
                if ( ad.getDataType().getType() == DataTypes.STRING ) {
1090
                    if ( ad.getSize() == 0 ) {
1091
                        // Es un string y no tiene un size asignado.
1092
                        // Lo ponemos a cero para calcularlo.
1093
                        sizes[i] = 0;
1094
                    }
1095
                }
1096
            }
1097
           
1098
            if ( ftype.getDefaultGeometryAttributeName() != null ) {
1099
                readerData.setNeedCalculateEnvelope(true);
1100
            }
1101

    
1102
            Locale locale = getParameters().getLocale();
1103
            taskStatus.message("_loading");
1104
            long rowCount = reader.getRowCount();
1105
            if( rowCount >0 ) {
1106
                taskStatus.setRangeOfValues(0, rowCount);
1107
            }
1108

    
1109
            int count = 0;
1110

    
1111
            int count_errors = 0;
1112
            
1113
            reader.rewind();
1114
            List<Object> row = reader.read();
1115

    
1116
            Object rawvalue;
1117
            while ( row != null ) {
1118
                taskStatus.setCurValue(++count);
1119
                FeatureProvider feature = this.createFeatureProvider(ftype);
1120
                for ( int i = 0; i < row.size(); i++ ) {
1121
                    try {
1122
                        if( ftype.get(i).isComputed() ) {
1123
                            continue;
1124
                        }
1125
                        rawvalue = row.get(i);
1126
                        if( rawvalue instanceof String && StringUtils.isBlank((String)rawvalue) ) {
1127
                            rawvalue = null;
1128
                        }
1129
                        Object value;
1130
                        if ( locale != null && coercion[i] instanceof CoercionWithLocale ) {
1131
                            value = ((CoercionWithLocale) (coercion[i])).coerce(rawvalue, locale);
1132
                        } else {
1133
                            value = coercion[i].coerce(rawvalue);
1134
                        }
1135
                        feature.set(i, value);
1136
                        if ( sizes[i] >= 0 && value != null ) {
1137
                            int x = ((String) value).length();
1138
                            if ( sizes[i] < x ) {
1139
                                sizes[i] = x;
1140
                            }
1141
                        }
1142
                    } catch (Exception ex) {
1143
                        if ( !ignore_errors ) {
1144
                            throw ex;
1145
                        }
1146
                        if ( count_errors++ < 10 ) {
1147
                            LOGGER.warn("Can't load value of attribute " + i+"/"+row.size()+"/"+ftype.size()+ " in row " + count + " in " +readerData.getName()+ ".", ex);
1148
                        }
1149
                        if ( count_errors == 10 ) {
1150
                            LOGGER.info("Too many errors, suppress messages.");
1151
                        }
1152
                    }
1153
                }
1154
                readerData.addFeatureProvider(feature);
1155
                row = reader.read();
1156
            }
1157
            for ( int i = 0; i < ftype.size(); i++ ) {
1158
                if ( sizes[i] > 0 ) {
1159
                    EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
1160
                    if( efad.getSize()<sizes[i] ) {
1161
                        efad.setSize(sizes[i]);
1162
                    }
1163
                }
1164
            }
1165
            // Volvemos a asignar al store el featuretype, ya que puede
1166
            // haber cambiado.
1167
            ftype = edftype.getNotEditableCopy();
1168
            ftypes = new ArrayList<>();
1169
            ftypes.add(ftype);
1170
            readerData.setFeatureTypes(ftypes, ftype);
1171

    
1172
            taskStatus.terminate();
1173
        } catch(IOException ex) {
1174
            taskStatus.abort();
1175
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1176
            throw ex;
1177
        } catch (Exception ex) {
1178
            taskStatus.abort();
1179
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1180
        }
1181
    }
1182
    
1183
    private int[] automaticDetectionOfTypes(SimpleSequentialReader reader) throws IOException {
1184
        boolean automatic_types_detection = getParameters().getAutomaticTypesDetection();
1185
        if ( !automatic_types_detection ) {
1186
            return null;
1187
        }
1188
        int[] types = null;
1189
        try {
1190
            reader.rewind();
1191
            List<String> fieldNames = reader.getFieldNames();
1192
            if ( fieldNames == null ) {
1193
                fieldNames = getParameters().getFieldNames();
1194
            }
1195

    
1196
            AutomaticDetectionOfTypes x = new AutomaticDetectionOfTypes(
1197
                    this.getFullFileName()
1198
            );
1199
            types = x.detect(
1200
                    fieldNames.size(), 
1201
                    reader, 
1202
                    false, 
1203
                    getParameters().getLocale()
1204
            );
1205
        } catch (Exception ex) {
1206
            throw new RuntimeException("Problems reading '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1207
        }
1208
        return types;
1209
    }
1210

    
1211
    @Override
1212
    public boolean hasDynValue(String name) {
1213
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
1214
            return true;
1215
        } else if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
1216
            return true;
1217
        }
1218
        return super.hasDynValue(name);
1219
    }
1220

    
1221
    @Override
1222
    public UnmodifiableBasicMap<String, DataStore> getChildren() {
1223
        if( this.isChild() ) {
1224
            return null;
1225
        }
1226
        return new Children();
1227
    }
1228

    
1229
    @Override
1230
    public StoresRepository getStoresRepository() {
1231
        StoresRepository repo = this.readerData.getStoresRepository();
1232
        return repo;
1233
    }
1234
    
1235
    @Override
1236
    public ResourcesStorage getResourcesStorage() {
1237
        ResourcesStorage storage = this.readerData.getResourcesStorage();
1238
        return storage;
1239
    }
1240
}