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

History | View | Annotate | Download (47.1 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.DefaultFeatureProvider;
62
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
63
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
64
import org.gvsig.fmap.dal.feature.spi.memory.AbstractMemoryStoreProvider;
65
import org.gvsig.fmap.dal.resource.file.FileResource;
66
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
67
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
68
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorer;
69
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorerParameters;
70
import org.gvsig.fmap.dal.spi.DALSPILocator;
71
import org.gvsig.fmap.dal.spi.DataManagerProviderServices;
72
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
73
import org.gvsig.fmap.geom.Geometry;
74
import org.gvsig.fmap.geom.GeometryLocator;
75
import org.gvsig.fmap.geom.GeometryManager;
76
import org.gvsig.fmap.geom.GeometryUtils;
77
import org.gvsig.fmap.geom.aggregate.MultiPoint;
78
import org.gvsig.fmap.geom.primitive.Envelope;
79
import org.gvsig.fmap.geom.primitive.Point;
80
import org.gvsig.fmap.geom.type.GeometryType;
81
import org.gvsig.tools.ToolsLocator;
82
import org.gvsig.tools.dataTypes.Coercion;
83
import org.gvsig.tools.dataTypes.CoercionContext;
84
import org.gvsig.tools.dataTypes.CoercionException;
85
import org.gvsig.tools.dataTypes.DataTypesManager;
86
import org.gvsig.tools.dispose.Disposable;
87
import org.gvsig.tools.dispose.DisposeUtils;
88
import org.gvsig.tools.dynobject.Tags;
89
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
90
import org.gvsig.tools.exception.BaseException;
91
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
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 static class SimpleSequentialReaderFeatureProvider extends DefaultFeatureProvider {
109

    
110
        public SimpleSequentialReaderFeatureProvider(FeatureType type) {
111
            super(type);
112
        }
113

    
114
        public SimpleSequentialReaderFeatureProvider(FeatureType featureType, Object createNewOID) {
115
            super(featureType, createNewOID);
116
        }
117

    
118
        @Override
119
        public FeatureType getType() {
120
            // Esta implementacion impide que se pueda modificar la extructura de 
121
            // esta tabla ya que siempre retorna el FeatureType del proveedor.
122
            //
123
            // Para permitir edicion de la estructura de la tabla deberia pedirle
124
            // el type al store services, que es quien controla que pasa cuando
125
            // estamos en edicion, pero con este proveedor, si el reader tiene varias
126
            // tablas no devuelbe el tipo corriente, a fecha de hoy (11/2019) siempre 
127
            // devuelbe el type de la tabla principal.
128
            return super.getType();
129
        }
130
    }
131
    
132
    private class StoresRepositoryWithChildren extends BaseStoresRepository {
133
        
134
        public StoresRepositoryWithChildren() {
135
            super(SimpleSequentialReaderStoreProvider.this.getName());
136
        }
137
        
138
        @Override
139
        public DataStore getStore(String name) {
140
            if( StringUtils.equalsIgnoreCase(name, readerData.getName()) ||
141
                StringUtils.equalsIgnoreCase(name, readerData.getAlias()) ) {
142
                return readerData.getStore();
143
            }
144
            for (ReaderData child : childrenData) {
145
                if( StringUtils.equalsIgnoreCase(name, child.getName()) ||
146
                    StringUtils.equalsIgnoreCase(name, child.getAlias()) ) {
147
                    return child.getStore();
148
                }
149
            }
150
            return super.getStore(name);
151
        }
152
        
153
    }
154
    
155
    private class ReaderData implements Disposable {
156
        private FeatureType defaultFeatureType;
157
        private List<FeatureType> featureTypes;
158
        private List<FeatureProvider> features;
159
        private boolean needCalculateEnvelope;
160
        private String name;
161
        private String alias;
162
        private long OIDcounter;
163
        private DataStore store;
164
        private StoresRepository storesRepository;
165
        private OpenFeatureStoreParameters parameters;
166
        private ResourcesStorage resourcesStorage;
167
        
168
        public ReaderData() {
169
            this.needCalculateEnvelope = false;
170
            this.features = new ArrayList<>();
171
            this.OIDcounter = 0;
172
            this.store = null;
173
        }
174

    
175
        @Override
176
        public void dispose() {
177
            DisposeUtils.disposeQuietly(store);
178
            this.store = null;
179
            this.features = null;
180
            this.OIDcounter = 0;
181
            this.defaultFeatureType = null;
182
            this.featureTypes = null;
183
            this.parameters = null;
184
            this.storesRepository = null;
185
        }
186
        
187
        public void addFeatureProvider(FeatureProvider feature) {
188
                feature.setOID(this.OIDcounter++);
189
                this.features.add(feature);
190
        }
191
        
192
        public void setFeatureTypes(List<FeatureType> featureTypes, FeatureType defaultFeatureType) {
193
            this.featureTypes = featureTypes;
194
            this.defaultFeatureType = defaultFeatureType;
195
        }
196
        
197
        public void setNeedCalculateEnvelope(boolean needCalculateEnvelope) {
198
            this.needCalculateEnvelope = needCalculateEnvelope;
199
        }
200
        
201
        public boolean getNeedCalculateEnvelope() {
202
            return this.needCalculateEnvelope;
203
        }
204

    
205
        public String getName() {
206
            return name;
207
        }
208

    
209
        public void setName(String name) {
210
            this.name = name;
211
        }
212

    
213
        public List<FeatureType> getFeatureTypes() {
214
            return this.featureTypes;
215
        }
216

    
217
        public FeatureType getDefaultFeatureType() {
218
            return this.defaultFeatureType;
219
        }
220
        
221
        public List<FeatureProvider> getFeatures() {
222
            return this.features;
223
        }
224
        
225
        public DataStore getStore() {
226
            if( this.store == null ) {
227
                try {
228
                    SimpleSequentialReaderStoreProvider provider = new SimpleSequentialReaderStoreProvider(
229
                            readerFactory, 
230
                            (SimpleSequentialReaderStoreParameters) getParameters(),
231
                            null,
232
                            childrenData,
233
                            this
234
                    );
235
                    DataManagerProviderServices manager = DALSPILocator.getDataManagerProviderServices();
236
                    this.store = manager.openStore(
237
                            getParameters(), 
238
                            provider
239
                    );
240
                    provider.setStoreServices((FeatureStoreProviderServices) this.store);
241
                    provider.name = this.name;
242
                    DisposeUtils.bind(this.store);
243
                } catch(Exception ex) {
244
                   LOGGER.warn("Can't build store form child '"+name+"'.",ex);
245
                   return null; 
246
                }
247
            }
248
            DisposeUtils.bind(this.store);
249
            return this.store;
250
        }
251

    
252
        public void setStoresRepository(StoresRepository storesRepository) {
253
            this.storesRepository = storesRepository;
254
        }
255

    
256
        public StoresRepository getStoresRepository() {
257
            return storesRepository;
258
        }
259

    
260
        public ResourcesStorage getResourcesStorage() {
261
            return resourcesStorage;
262
        }
263

    
264
        public void setResourcesStorage(ResourcesStorage resourcesStorage) {
265
            this.resourcesStorage = resourcesStorage;
266
        }
267

    
268
        public void setParameters(OpenFeatureStoreParameters parameters) {
269
            if( parameters==null ) {
270
                LOGGER.warn("Can't set parameters to null");
271
                return;
272
            }
273
            this.parameters = parameters;
274
        }
275

    
276
        public OpenFeatureStoreParameters getParameters() {
277
            return parameters;
278
        }
279

    
280
        public String getAlias() {
281
            return this.alias;
282
        }
283

    
284
        public void setAlias(String alias) {
285
            this.alias = alias;
286
        }
287
        
288
        
289
    }
290
    
291
    class Children implements UnmodifiableBasicMap<String, DataStore> {
292
        // Con esta clase se pospone la creacion de los stores hasta que se 
293
        // pide cada uno de ellos, y no cuando se pide el Map de estos, ya
294
        // que el map se puede pedir solo para saber si hay o cuantos hay.
295

    
296
        @Override
297
        public DataStore get(String key) {
298
            for (ReaderData child : childrenData) {
299
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
300
                    return child.getStore();
301
                }
302
            }
303
            return null;
304
        }
305

    
306
        @Override
307
        public boolean isEmpty() {
308
            return childrenData.isEmpty();
309
        }
310

    
311
        @Override
312
        public boolean containsKey(String key) {
313
            for (ReaderData child : childrenData) {
314
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
315
                    return true;
316
                }
317
            }
318
            return false;
319
        }
320

    
321
        @Override
322
        public Map<String, DataStore> toMap() {
323
            return new UnmodifiableBasicMapToMapAdapter<>(this);
324
        }
325

    
326
        @Override
327
        public int size() {
328
            return childrenData.size();
329
        }
330

    
331
        @Override
332
        public UnmodifiableBasicSet<String> keySet() {
333
            return new UnmodifiableBasicSet<String>() {
334

    
335
                @Override
336
                public boolean isEmpty() {
337
                    return childrenData.isEmpty();
338
                }
339

    
340
                @Override
341
                public int size() {
342
                    return childrenData.size();
343
                }
344

    
345
                @Override
346
                public Iterator<String> iterator() {
347
                    final Iterator<ReaderData> it = childrenData.iterator();
348
                    return new Iterator<String>() {
349
                        @Override
350
                        public boolean hasNext() {
351
                            return it.hasNext();
352
                        }
353

    
354
                        @Override
355
                        public String next() {
356
                            ReaderData theReaderData = it.next();
357
                            return theReaderData.getName();
358
                        }
359
                    };
360
                }
361
            };
362
        }
363

    
364
        @Override
365
        public Iterator<DataStore> iterator() {
366
            final Iterator<String> it = this.keySet().iterator();
367
            return new Iterator<DataStore>() {
368
                @Override
369
                public boolean hasNext() {
370
                    return it.hasNext();
371
                }
372

    
373
                @Override
374
                public DataStore next() {
375
                    String name = it.next();
376
                    return get(name);
377
                }
378
            };
379
        }
380
        
381
    }
382

    
383
    private final ResourceProvider resource;
384

    
385
    private Envelope envelope;
386
    private boolean need_calculate_envelope = false;
387
    private String name = "";
388
    private final SimpleSequentialReaderFactory readerFactory;
389
    private List<ReaderData> childrenData;
390
    private ReaderData readerData;
391
    private final boolean isAChild;
392

    
393
    public SimpleSequentialReaderStoreProvider(
394
            SimpleSequentialReaderFactory readerFactory,
395
            SimpleSequentialReaderStoreParameters parameters,
396
            DataStoreProviderServices storeServices
397
        ) throws InitializeException {
398
        this(readerFactory, parameters, storeServices, new ArrayList<ReaderData>(), null);
399
    }
400
    
401
    private SimpleSequentialReaderStoreProvider(
402
            SimpleSequentialReaderFactory readerFactory,
403
            SimpleSequentialReaderStoreParameters parameters,
404
            DataStoreProviderServices storeServices,
405
            List<ReaderData> childrenData,
406
            ReaderData readerData
407
            
408
        ) throws InitializeException {
409
        super(
410
                parameters,
411
                storeServices,
412
                FileHelper.newMetadataContainer(readerFactory.getName())
413
        );
414
        this.childrenData = childrenData;
415
        this.readerData = readerData;
416
        this.readerFactory = readerFactory;
417
        this.isAChild = this.readerData!=null;
418
        
419
        File file = getParameters().getFile();
420
        resource = this.createResource(
421
                FileResource.NAME,
422
                new Object[]{file.getAbsolutePath()}
423
        );
424

    
425
        resource.addConsumer(this);
426
        if( this.readerData!=null ) {
427
            this.name = this.readerData.getName();            
428
        }
429
        if( storeServices != null ) {
430
            initializeFeatureTypes();
431
        }
432
    }
433
    
434
    private boolean isChild() {
435
        return this.isAChild;
436
    }
437
    
438
    private void setStoreServices(FeatureStoreProviderServices storeServices) throws InitializeException {
439
        this.store = storeServices;
440
        initializeFeatureTypes();
441
    }
442

    
443
    @Override
444
    public FeatureStoreProviderServices getStoreServices() {
445
        if( this.store==null ) {
446
            // Por aqui no deberia de pasar.
447
            // Si pasa es que algo va mal.
448
            LOGGER.warn("Algo no anda bien, el store es null");
449
            DataStore theStore = this.readerData.getStore();
450
            if( theStore==null ) {
451
                return null;
452
            }
453
            this.store = (FeatureStoreProviderServices) theStore ;
454
        }
455
        return this.store;
456
    }
457
    
458
    @Override
459
    public SimpleSequentialReaderStoreParameters getParameters() {
460
        return (SimpleSequentialReaderStoreParameters) super.getParameters();
461
    }
462

    
463
    @Override
464
    public String getProviderName() {
465
        if( this.readerFactory==null ) {
466
            return "unknown";
467
        }
468
        return this.readerFactory.getName();
469
    }
470

    
471
    @Override
472
    public boolean allowWrite() {
473
        return false;
474
    }
475

    
476
    private String getFullFileName() {
477
        // Usar solo para mostrar mensajes en el logger.
478
        String s;
479
        try {
480
            s = getParameters().getFile().getAbsolutePath();
481
        } catch (Exception e2) {
482
            s = "(unknow)";
483
        }
484
        return s;
485
    }
486

    
487
    @Override
488
    public void open() throws OpenException {
489
        if ( this.data != null ) {
490
            return;
491
        }
492
        this.data = new ArrayList<>();
493
        resource.setData(new HashMap());
494
        try {
495
            loadFeatures();
496
        } catch (RuntimeException e) {
497
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
498
            throw e;
499
        } catch (Exception e) {
500
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
501
            throw new RuntimeException(e);
502
        }
503
    }
504

    
505
    @Override
506
    public DataServerExplorer getExplorer() throws ReadException {
507
        DataManager manager = DALLocator.getDataManager();
508
        FilesystemServerExplorerParameters params;
509
        try {
510
            params = (FilesystemServerExplorerParameters) manager
511
                    .createServerExplorerParameters(FilesystemServerExplorer.NAME);
512
            params.setRoot(this.getParameters().getFile().getParent());
513
            return manager.openServerExplorer(FilesystemServerExplorer.NAME, params);
514
        } catch (DataException | ValidateDataParametersException e) {
515
            throw new ReadException(this.getProviderName(), e);
516
        }
517

    
518
    }
519

    
520
    @Override
521
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
522
        throw new UnsupportedOperationException();
523
    }
524

    
525
    @Override
526
    public boolean closeResourceRequested(ResourceProvider resource) {
527
        return true;
528
    }
529

    
530
    @Override
531
    public int getOIDType() {
532
        return DataTypes.LONG;
533
    }
534

    
535
    @Override
536
    public boolean supportsAppendMode() {
537
        return false;
538
    }
539

    
540
    @Override
541
    public void append(FeatureProvider featureProvider) {
542
        throw new UnsupportedOperationException();
543
    }
544

    
545
    @Override
546
    public void beginAppend() {
547
        throw new UnsupportedOperationException();
548
    }
549

    
550
    @Override
551
    public void endAppend() {
552
        throw new UnsupportedOperationException();
553
    }
554

    
555
    @Override
556
    public Object createNewOID() {
557
        throw new UnsupportedOperationException();
558
    }
559

    
560
    @Override
561
    public FeatureProvider createFeatureProvider(FeatureType featureType)throws DataException  {
562
            return new SimpleSequentialReaderFeatureProvider(featureType);
563
    }
564

    
565
    @Override
566
    protected void doDispose() throws BaseException {
567
        for (ReaderData theReaderData : this.childrenData) {
568
            DisposeUtils.disposeQuietly(theReaderData);
569
        }
570
        this.childrenData = null;
571
        DisposeUtils.disposeQuietly(this.readerData);
572
        this.readerData = null;
573
//        this.resource = null;
574
//        this.readerFactory = null;
575
        this.envelope = null;
576
        super.doDispose();
577
    }
578

    
579
    
580
    private void initializeFeatureTypes() throws InitializeException {
581
        try {
582
            this.open();
583
        } catch (OpenException e) {
584
            throw new InitializeException(this.getProviderName(), e);
585
        }
586
    }
587

    
588
    @Override
589
    public Envelope getEnvelope() throws DataException {
590
        this.open();
591
        if ( this.envelope != null ) {
592
            return this.envelope;
593
        }
594
        if ( !this.need_calculate_envelope ) {
595
            return null;
596
        }
597
        FeatureStore fs = this.getFeatureStore();
598
        FeatureType ft = fs.getDefaultFeatureType();
599
        FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
600

    
601
        try {
602
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
603
            fs.accept(new Visitor() {
604
                @Override
605
                public void visit(Object obj) throws VisitCanceledException, BaseException {
606
                    Feature f = (Feature) obj;
607
                    Geometry geom = f.getDefaultGeometry();
608
                    if ( geom != null ) {
609
                        envelope.add(geom.getEnvelope());
610
                    }
611
                }
612
            });
613
        } catch (BaseException e) {
614
            LOGGER.warn("Can't calculate the envelope of '"+getProviderName()+"' file '" + this.getFullName() + "'.", e);
615
            this.envelope = null;
616
        }
617

    
618
        this.need_calculate_envelope = false;
619
        return this.envelope;
620
    }
621

    
622
    @Override
623
    public Object getDynValue(String name) throws DynFieldNotFoundException {
624
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
625
            try {
626
                return this.getEnvelope();
627
            } catch (DataException e) {
628
                return null;
629
            }
630
        } else {
631
            if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
632
                IProjection pro = this.getParameters().getCRS();
633
                if ( pro != null ) {
634
                    return pro;
635
                }
636
                FeatureType type;
637
                try {
638
                    type = this.getStoreServices().getDefaultFeatureType();
639
                    pro = type.getDefaultSRS();
640
                    if( pro!=null ) {
641
                        return pro;
642
                    }
643
                } catch (DataException ex) {
644
                }
645
            }
646
        }
647
        return super.getDynValue(name);
648
    }
649

    
650
    @Override
651
    public void resourceChanged(ResourceProvider resource) {
652
        this.getStoreServices().notifyChange(
653
                DataStoreNotification.RESOURCE_CHANGED,
654
                resource);
655
    }
656

    
657
    @Override
658
    public Object getSourceId() {
659
        return this.getParameters().getFile();
660
    }
661

    
662
    @Override
663
    public String getName() {
664
        return this.name;
665
    }
666

    
667
    @Override
668
    public String getFullName() {
669
        return this.getParameters().getFile().getAbsolutePath();
670
    }
671

    
672
    @Override
673
    public ResourceProvider getResource() {
674
        return resource;
675
    }
676

    
677
    private String[] split(String value, String separators) {
678
        int firstSeparatorPosition = 1000000;
679
        Character sep = null;
680
        for (char ch : separators.toCharArray()) {
681
            int pos = value.indexOf(ch);
682
            if( pos>0 && pos<firstSeparatorPosition ) {
683
                sep = ch;
684
                firstSeparatorPosition = pos;
685
            }
686
        }
687
        if( sep == null ) {
688
            return new String[] { value };
689
        }
690
        return value.split("["+sep+"]");
691
    }
692
    
693
    private class FieldTypeParser {
694

    
695
        public String name = null;
696
        public int type = DataTypes.STRING;
697
        public int size = 0;
698
        public int geomType = Geometry.TYPES.GEOMETRY;
699
        public int geomSubtype = Geometry.SUBTYPES.GEOM2D;
700
        public Map<String,String> tags = new HashMap<>();
701
        public Map<String,String> assignments = new HashMap<>();
702

    
703
        private String typename = "string";
704

    
705
        FieldTypeParser() {
706
        }
707

    
708
        private int getType(String value) {
709
            DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
710
            return dataTypesManager.getType(typename);
711
        }
712

    
713
        @SuppressWarnings("UseSpecificCatch")
714
        public boolean parse(String value) {
715
            String[] args;
716
            if ( value.contains("__") ) {
717
                args = value.split("__");
718
            } else {
719
                args = split(value, ":/#@!;-");
720
                if( args.length == 1 ) {
721
                    this.name = value;
722
                    return true;
723
                }
724
            }                        
725
            int n = 0;
726
            this.name = args[n++];
727
            if( n >= args.length ) {
728
                return true;
729
            }
730
            this.typename = args[n++];
731
            this.type = this.getType(this.typename);
732
            if ( this.type == DataTypes.INVALID ) {
733
                this.geomType = GeometryUtils.getGeometryType(this.typename);
734
                if( this.geomType==Geometry.TYPES.UNKNOWN )  {
735
                    this.type = DataTypes.STRING;
736
                    LOGGER.info("Type '" + this.typename + "' not valid for attribute '" + value + "' in '"+getProviderName()+"' file '" + getFullFileName() + "'.");
737
                } else {
738
                    this.typename = "GEOMETRY";
739
                    this.type = DataTypes.GEOMETRY;
740
                }
741
            }
742
            switch(this.type) {
743
                case DataTypes.STRING:
744
                    this.size = 50;
745
                    break;
746
                case DataTypes.INT:
747
                    this.size = 10;
748
                    break;
749
                case DataTypes.LONG:
750
                    this.size = 20;
751
                    break;
752
                case DataTypes.FLOAT:
753
                    this.size = 10;
754
                    break;
755
                case DataTypes.DOUBLE:
756
                    this.size = 20;
757
                    break;
758
                default:
759
                    this.size = 0;
760
            }
761
            while (n < args.length) {
762
                String option = args[n++].toLowerCase();
763
                switch (option) {
764
                    case "size":
765
                        try {
766
                            this.size = Integer.parseInt(args[n++]);
767
                        } catch (Exception ex) {
768
                            LOGGER.warn("Ignore incorrect field size for field " + value + " in '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
769
                        }
770
                        break;
771
                    case "tag": {
772
                            String x = args[n++];
773
                            int pos = x.indexOf("=");
774
                            if( pos < 0 ) {
775
                                this.tags.put(x, null);
776
                            } else {
777
                                this.tags.put( 
778
                                        StringUtils.substring(x, 0, pos),
779
                                        StringUtils.substring(x, pos+1)
780
                                );
781
                            }
782
                            break;
783
                        }
784
                    case "set": {
785
                            String x = args[n++];
786
                            int pos = x.indexOf("=");
787
                            if( pos < 0 ) {
788
                                this.assignments.put(x, null);
789
                            } else {
790
                                this.assignments.put( 
791
                                        StringUtils.substring(x, 0, pos),
792
                                        StringUtils.substring(x, pos+1)
793
                                );
794
                            }
795
                            break;
796
                        }
797
                    default:
798
                        LOGGER.warn("Illegal argumente '"+option+"' for field '"+this.name+"' in '"+getProviderName()+"' file '" + getFullFileName() + "' ("+value+").");
799
                }
800
            }
801
            return true;
802
        }
803

    
804
    }
805

    
806
    private EditableFeatureType getFeatureType(List<String> headers, int automaticTypes[]) {
807
        EditableFeatureType fType = getStoreServices().createFeatureType(this.getName());
808
        fType.setHasOID(true);
809
//        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
810

    
811
        FieldTypeParser[] fieldTypes = new FieldTypeParser[headers.size()];
812
        //
813
        // Calculamos cuales pueden ser los tipos de datos
814
        //
815
        for ( int i = 0; i < fieldTypes.length; i++ ) {
816
            fieldTypes[i] = new FieldTypeParser();
817
        }
818

    
819
        // Asuminos los tipos pasados por parametro, que se supone
820
        // son los detectados automaticamente.
821
        if ( automaticTypes != null ) {
822
            for ( int i = 0; i < fieldTypes.length && i < automaticTypes.length; i++ ) {
823
                fieldTypes[i].type = automaticTypes[i];
824
            }
825
        }
826
        // Luego probamos con lo que diga las cabezeras, sobreescribiendo
827
        // los tipos anteriores en caso de definirse en la cabezara.
828
        for ( int i = 0; i < fieldTypes.length; i++ ) {
829
            fieldTypes[i].parse(headers.get(i));
830
        }
831

    
832
        // Y por ultimo hacemos caso a lo que se haya especificado en los parametros
833
        // de apertura del reader, teniendo esto prioridad sobre todo.
834
        int[] param_types = this.getParameters().getFieldTypes();
835
        if ( param_types != null ) {
836
            for ( int i = 0; i < fieldTypes.length && i < param_types.length; i++ ) {
837
                fieldTypes[i].type = param_types[i];
838
            }
839
        }
840

    
841
        int[] param_sizes = this.getParameters().getFieldSizes();
842
        if ( param_sizes != null ) {
843
            for ( int i = 0; i < param_sizes.length; i++ ) {
844
                if ( param_sizes[i] > 0 ) {
845
                    fieldTypes[i].size = param_sizes[i];
846
                }
847
            }
848
        }
849
        //
850
        // Una vez ya sabemos los tipos de datos rellenamos el feature-type
851
        //
852
        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
853
        for (FieldTypeParser fieldType : fieldTypes) {
854
            EditableFeatureAttributeDescriptor fad = fType.add(fieldType.name, fieldType.type);
855
            fad.setSize(fieldType.size);
856
            if (fieldType.type == DataTypes.GEOMETRY ) {
857
                fad.setGeometryType(fieldType.geomType, fieldType.geomSubtype);
858
                if( fType.getDefaultGeometryAttributeName() == null ) {
859
                    fType.setDefaultGeometryAttributeName(fieldType.name);
860
                }
861
            }
862
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
863
                try {
864
                    switch(entry.getKey().toLowerCase()) {
865
                        case "expression":
866
                            // Los campos calculados los procesamos en una segunda
867
                            // pasada, cuando ya estan definidos el resto de los campos
868
                            // ya que pueden requerir campos que aun no se han definido.
869
                            break;
870
                        default:
871
                            fad.set(entry.getKey(), entry.getValue());
872
                    }
873
                } catch (Exception ex) {
874
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' of '"+fad.getName()+"'.", ex);
875
                }
876
            }
877
            Tags tags = fad.getTags();
878
            for (Map.Entry<String, String> entry : fieldType.tags.entrySet()) {
879
                tags.set(entry.getKey(), entry.getValue());
880
            }
881
        }
882
        // Processamos ahora los campos calculados
883
        for (FieldTypeParser fieldType : fieldTypes) {
884
            EditableFeatureAttributeDescriptor fad = fType.getEditableAttributeDescriptor(fieldType.name);
885
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
886
                try {
887
                    switch(entry.getKey().toLowerCase()) {
888
                        case "expression":
889
                            fad.set(entry.getKey(), entry.getValue());
890
                            break;
891
                    }
892
                } catch (Exception ex) {
893
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' in '"+fad.getName()+"' of '"+getFullFileName()+"'.", ex);
894
                }
895
            }
896
        }
897

    
898
        String[] pointDimensionNames = this.getParameters().getPointDimensionNames();
899
        if ( pointDimensionNames != null ) {
900
            PointAttributeEmulator emulator = new PointAttributeEmulator(pointDimensionNames);
901
            EditableFeatureAttributeDescriptor attr = fType.add("the_geom", DataTypes.GEOMETRY, emulator);
902
            GeometryManager geommgr = GeometryLocator.getGeometryManager();
903
            GeometryType gt;
904
            try {
905
                if ( emulator.fieldNames != null && emulator.fieldNames.length <= 2 ) {
906
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
907
                } else {
908
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
909
                }
910
                attr.setGeometryType(gt);
911
            } catch (Exception e) {
912
                LOGGER.warn("Can't set geometry type for the calculated field in '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
913
            }
914
        }
915
        return fType;
916
    }
917

    
918
    class PointAttributeEmulator implements FeatureAttributeEmulator {
919

    
920
        private static final int XNAME = 0;
921
        private static final int YNAME = 1;
922
        private static final int ZNAME = 2;
923

    
924
        private final GeometryManager geommgr;
925
        private final String[] fieldNames;
926
        private final Coercion toDouble;
927
        private int errorcount = 0;
928

    
929
        public PointAttributeEmulator(String[] pointDimensionNames) {
930
            if ( pointDimensionNames.length > 2 ) {
931
                this.fieldNames = new String[3];
932
                this.fieldNames[ZNAME] = pointDimensionNames[2];
933
            } else {
934
                this.fieldNames = new String[2];
935
            }
936
            this.fieldNames[XNAME] = pointDimensionNames[0];
937
            this.fieldNames[YNAME] = pointDimensionNames[1];
938
            this.geommgr = GeometryLocator.getGeometryManager();
939
            DataTypesManager datatypeManager = ToolsLocator.getDataTypesManager();
940

    
941
            this.toDouble = datatypeManager.getCoercion(DataTypes.DOUBLE);
942
        }
943

    
944
        @Override
945
        @SuppressWarnings("UseSpecificCatch")
946
        public Object get(Feature feature) {
947
            try {
948
                Object valueX = feature.get(this.fieldNames[XNAME]);
949
                valueX = toDouble.coerce(valueX);
950
                if ( valueX == null ) {
951
                    return null;
952
                }
953
                Object valueY = feature.get(this.fieldNames[YNAME]);
954
                valueY = toDouble.coerce(valueY);
955
                if ( valueY == null ) {
956
                    return null;
957
                }
958
                Object valueZ = null;
959
                if ( this.fieldNames.length > 2 ) {
960
                    valueZ = toDouble.coerce(feature.get(this.fieldNames[ZNAME]));
961
                    if ( valueZ == null ) {
962
                        return null;
963
                    }
964
                }
965

    
966
                double x = ((Double) valueX);
967
                double y = ((Double) valueY);
968
                Point point;
969
                if ( this.fieldNames.length > 2 ) {
970
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
971
                    double z = ((Double) valueZ);
972
                    point.setCoordinateAt(2, z);
973
                } else {
974
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM2D);
975
                }
976
                return point;
977
            } catch (Exception ex) {
978
                if ( ++errorcount < 5 ) {
979
                    LOGGER.warn("[" + errorcount + "] Can't create point from '"+getProviderName()+"' file."+
980
                            "XNAME='" + this.fieldNames[XNAME] + 
981
                            "', YNAME='" + this.fieldNames[YNAME] + 
982
                            "', ZNAME='" + ((this.fieldNames.length > 2)? this.fieldNames[ZNAME]: "(2D)") +
983
                            "' feature=" + feature.toString(), ex);
984
                }
985
                return null;
986
            }
987
        }
988

    
989
        @Override
990
        public void set(EditableFeature feature, Object value) {
991
            if ( value == null ) {
992
                return;
993
            }
994
            Point point;
995
            if ( value instanceof MultiPoint ) {
996
                point = (Point) ((MultiPoint) value).getPrimitiveAt(0);
997
            } else {
998
                point = (Point) value;
999
            }
1000
            feature.set(this.fieldNames[XNAME], point.getX());
1001
            feature.set(this.fieldNames[YNAME], point.getY());
1002
            if ( this.fieldNames.length > 2 ) {
1003
                feature.set(this.fieldNames[ZNAME], point.getCoordinateAt(2));
1004
            }
1005
        }
1006

    
1007
        @Override
1008
        public boolean allowSetting() {
1009
            return true;
1010
        }
1011

    
1012
        @Override
1013
        public String[] getRequiredFieldNames() {
1014
            return this.fieldNames;
1015
        }
1016

    
1017
    }
1018

    
1019
    private void loadFeatures() throws IOException, DataException,
1020
            CoercionException, CloneNotSupportedException {
1021

    
1022
        ReaderData theReaderData = this.readerData;
1023
        if( theReaderData==null ) {
1024
            theReaderData = new ReaderData();
1025
            SimpleSequentialReader reader = this.readerFactory.createReader(this.getParameters());
1026
            try {
1027
                loadFeatures(reader, theReaderData);
1028
                StoresRepositoryWithChildren repoWithChildren = new StoresRepositoryWithChildren();
1029
                StoresRepository repo = reader.getStoresRepository();
1030
                if( repo!=null ) {
1031
                    repoWithChildren.addRepository(repo);
1032
                }        
1033
                this.childrenData = new ArrayList<>();
1034
                List<SimpleSequentialReader> children = reader.getChildren();
1035
                for(SimpleSequentialReader childReader : children ) {
1036
                    ReaderData childData = new ReaderData();
1037
                    loadFeatures(childReader, childData);
1038
                    this.childrenData.add(childData);
1039
                    repoWithChildren.add(childReader.getName(), childData.getParameters());
1040
                    if( !StringUtils.isBlank(childReader.getAlias()) ) {
1041
                        repoWithChildren.add(childReader.getAlias(), childData.getParameters());
1042
                    }
1043
                    childData.setStoresRepository(repoWithChildren);
1044
                }
1045
                repoWithChildren.add(reader.getName(), theReaderData.getParameters());
1046
                if( !StringUtils.isBlank(reader.getAlias()) ) {
1047
                    repoWithChildren.add(reader.getAlias(), theReaderData.getParameters());
1048
                }
1049
                theReaderData.setStoresRepository(repoWithChildren);
1050
                this.readerData = theReaderData;
1051
            } finally {
1052
                reader.close();
1053
            }
1054
        }
1055
        this.name = theReaderData.getName();
1056
        FeatureStoreProviderServices theStore = this.getStoreServices();
1057
        theStore.setFeatureTypes(theReaderData.getFeatureTypes(), theReaderData.getDefaultFeatureType());
1058
        this.need_calculate_envelope = theReaderData.getNeedCalculateEnvelope();
1059
        this.data = theReaderData.getFeatures();
1060

    
1061
    }
1062
    
1063
    private void loadFeatures(SimpleSequentialReader reader, ReaderData readerData) throws IOException, DataException,
1064
            CoercionException, CloneNotSupportedException {
1065

    
1066
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
1067
        SimpleTaskStatus taskStatus = manager.createDefaultSimpleTaskStatus(reader.getName());
1068
        taskStatus.setAutoremove(true);
1069
        taskStatus.setIndeterminate();
1070
        taskStatus.add();
1071
        try {
1072
            List<String> headers;
1073

    
1074
            taskStatus.message("_preparing");
1075
            readerData.setName(reader.getName());
1076
            readerData.setStoresRepository(reader.getStoresRepository());
1077
            readerData.setResourcesStorage(reader.getResourcesStorage());
1078
            readerData.setParameters(reader.getParameters());
1079
            readerData.setAlias(reader.getAlias());
1080
            
1081
            boolean ignore_errors = getParameters().getIgnoreErrors();
1082

    
1083
            headers = getParameters().getFieldNames();
1084
            if ( headers == null ) {
1085
                // Esto es por si el getFieldNames devuelbe algo que no es
1086
                // un String pero su toString hace lo que toca.
1087
                headers = new ArrayList<>();
1088
                for (Object fielddesc : reader.getFieldNames()) {
1089
                    headers.add(fielddesc.toString());
1090
                }
1091
            }
1092

    
1093
            // Initialize the feature types
1094
            EditableFeatureType edftype = this.getFeatureType(headers, automaticDetectionOfTypes(reader));
1095

    
1096
            edftype.setLabel(reader.getLabel());
1097
            edftype.setDescription(reader.getDescription());
1098
            Map<String, String> tagsReader = reader.getTags();
1099
            if( tagsReader!=null ) {
1100
                Tags tagsType = edftype.getTags();
1101
                for (Map.Entry<String, String> tag : tagsReader.entrySet()) {
1102
                    tagsType.set(tag.getKey(), tag.getValue());
1103
                }
1104
            }
1105
            
1106
            FeatureType ftype = edftype.getNotEditableCopy();
1107
            List<FeatureType> ftypes = new ArrayList<>();
1108
            ftypes.add(ftype);
1109
            readerData.setFeatureTypes(ftypes, ftype);
1110

    
1111
            Coercion coercion[] = new Coercion[ftype.size()];
1112
            CoercionContext coercionContext[] = new CoercionContext[ftype.size()];
1113
            int sizes[] = new int[ftype.size()];
1114
            for ( int i = 0; i < ftype.size(); i++ ) {
1115
                sizes[i] = -1;
1116
                FeatureAttributeDescriptor ad = ftype.getAttributeDescriptor(i);
1117
                coercion[i] = ad.getCoercion();
1118
                coercionContext[i] = ad.getCoercionContext();
1119
                if ( ad.getDataType().getType() == DataTypes.STRING ) {
1120
                    if ( ad.getSize() == 0 ) {
1121
                        // Es un string y no tiene un size asignado.
1122
                        // Lo ponemos a cero para calcularlo.
1123
                        sizes[i] = 0;
1124
                    }
1125
                }
1126
            }
1127
           
1128
            if ( ftype.getDefaultGeometryAttributeName() != null ) {
1129
                readerData.setNeedCalculateEnvelope(true);
1130
            }
1131

    
1132
            Locale locale = getParameters().getLocale();
1133
            taskStatus.message("_loading");
1134
            long rowCount = reader.getRowCount();
1135
            if( rowCount >0 ) {
1136
                taskStatus.setRangeOfValues(0, rowCount);
1137
            }
1138

    
1139
            int count = 0;
1140

    
1141
            int count_errors = 0;
1142
            
1143
            reader.rewind();
1144
            List<Object> row = reader.read();
1145

    
1146
            Object rawvalue;
1147
            while ( row != null ) {
1148
                taskStatus.setCurValue(++count);
1149
                FeatureProvider feature = this.createFeatureProvider(ftype);
1150
                for ( int i = 0; i < row.size(); i++ ) {
1151
                    try {
1152
                        if( ftype.get(i).isComputed() ) {
1153
                            continue;
1154
                        }
1155
                        rawvalue = row.get(i);
1156
                        if( rawvalue instanceof String && StringUtils.isBlank((String)rawvalue) ) {
1157
                            rawvalue = null;
1158
                        }
1159
                        Object value = coercion[i].coerce(rawvalue, coercionContext[i]);
1160
                        feature.set(i, value);
1161
                        if ( sizes[i] >= 0 && value != null ) {
1162
                            int x = ((String) value).length();
1163
                            if ( sizes[i] < x ) {
1164
                                sizes[i] = x;
1165
                            }
1166
                        }
1167
                    } catch (Exception ex) {
1168
                        if ( !ignore_errors ) {
1169
                            throw ex;
1170
                        }
1171
                        if ( count_errors++ < 10 ) {
1172
                            LOGGER.warn("Can't load value of attribute " + i+" ("+ftype.getAttributeName(i)+") in row " + count + " in '" +readerData.getName()+ "'(row size "+row.size()+", type size "+ftype.size()+ ").", ex);
1173
                        }
1174
                        if ( count_errors == 10 ) {
1175
                            LOGGER.info("Too many errors, suppress messages.");
1176
                        }
1177
                    }
1178
                }
1179
                readerData.addFeatureProvider(feature);
1180
                row = reader.read();
1181
            }
1182
            for ( int i = 0; i < ftype.size(); i++ ) {
1183
                if ( sizes[i] > 0 ) {
1184
                    EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
1185
                    if( efad.getSize()<sizes[i] ) {
1186
                        efad.setSize(sizes[i]);
1187
                    }
1188
                }
1189
            }
1190
            // Volvemos a asignar al store el featuretype, ya que puede
1191
            // haber cambiado.
1192
            ftype = edftype.getNotEditableCopy();
1193
            ftypes = new ArrayList<>();
1194
            ftypes.add(ftype);
1195
            readerData.setFeatureTypes(ftypes, ftype);
1196

    
1197
            taskStatus.terminate();
1198
        } catch(IOException ex) {
1199
            taskStatus.abort();
1200
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1201
            throw ex;
1202
        } catch (Exception ex) {
1203
            taskStatus.abort();
1204
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1205
        }
1206
    }
1207
    
1208
    private int[] automaticDetectionOfTypes(SimpleSequentialReader reader) throws IOException {
1209
        boolean automatic_types_detection = getParameters().getAutomaticTypesDetection();
1210
        if ( !automatic_types_detection ) {
1211
            return null;
1212
        }
1213
        int[] types = null;
1214
        try {
1215
            reader.rewind();
1216
            List<String> fieldNames = reader.getFieldNames();
1217
            if ( fieldNames == null ) {
1218
                fieldNames = getParameters().getFieldNames();
1219
            }
1220

    
1221
            AutomaticDetectionOfTypes x = new AutomaticDetectionOfTypes(
1222
                    this.getFullFileName()
1223
            );
1224
            types = x.detect(
1225
                    fieldNames.size(), 
1226
                    reader, 
1227
                    false, 
1228
                    getParameters().getLocale()
1229
            );
1230
        } catch (Exception ex) {
1231
            throw new RuntimeException("Problems reading '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1232
        }
1233
        return types;
1234
    }
1235

    
1236
    @Override
1237
    public boolean hasDynValue(String name) {
1238
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
1239
            return true;
1240
        } else if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
1241
            return true;
1242
        }
1243
        return super.hasDynValue(name);
1244
    }
1245

    
1246
    @Override
1247
    public UnmodifiableBasicMap<String, DataStore> getChildren() {
1248
        if( this.isChild() ) {
1249
            return null;
1250
        }
1251
        return new Children();
1252
    }
1253

    
1254
    @Override
1255
    public StoresRepository getStoresRepository() {
1256
        StoresRepository repo = this.readerData.getStoresRepository();
1257
        return repo;
1258
    }
1259
    
1260
    @Override
1261
    public ResourcesStorage getResourcesStorage() {
1262
        ResourcesStorage storage = this.readerData.getResourcesStorage();
1263
        return storage;
1264
    }
1265
}