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

History | View | Annotate | Download (43.4 KB)

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

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

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

    
97
@SuppressWarnings("UseSpecificCatch")
98
public class SimpleSequentialReaderStoreProvider extends AbstractMemoryStoreProvider implements
99
        ResourceConsumer {
100

    
101
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSequentialReaderStoreProvider.class);
102

    
103
    private class ReaderData {
104
        private FeatureType defaultFeatureType;
105
        private List<FeatureType> featureTypes;
106
        private final List<FeatureProvider> features;
107
        private boolean needCalculateEnvelope;
108
        private String name;
109
        private long OIDcounter;
110
        private DataStore store;
111
        private StoresRepository storesRepository;
112

    
113
        public ReaderData() {
114
            this.needCalculateEnvelope = false;
115
            this.features = new ArrayList<>();
116
            this.OIDcounter = 0;
117
            this.store = null;
118
        }
119
        
120
        public void addFeatureProvider(FeatureProvider feature) {
121
                feature.setOID(this.OIDcounter++);
122
                this.features.add(feature);
123
        }
124
        
125
        public void setFeatureTypes(List<FeatureType> featureTypes, FeatureType defaultFeatureType) {
126
            this.featureTypes = featureTypes;
127
            this.defaultFeatureType = defaultFeatureType;
128
        }
129
        
130
        public void setNeedCalculateEnvelope(boolean needCalculateEnvelope) {
131
            this.needCalculateEnvelope = needCalculateEnvelope;
132
        }
133
        
134
        public boolean getNeedCalculateEnvelope() {
135
            return this.needCalculateEnvelope;
136
        }
137

    
138
        public String getName() {
139
            return name;
140
        }
141

    
142
        public void setName(String name) {
143
            this.name = name;
144
        }
145

    
146
        public List<FeatureType> getFeatureTypes() {
147
            return this.featureTypes;
148
        }
149

    
150
        public FeatureType getDefaultFeatureType() {
151
            return this.defaultFeatureType;
152
        }
153
        
154
        public List<FeatureProvider> getFeatures() {
155
            return this.features;
156
        }
157
        
158
        public DataStore getStore() throws InitializeException {
159
            if( this.store == null ) {
160
                SimpleSequentialReaderStoreProvider provider = new SimpleSequentialReaderStoreProvider(
161
                        readerFactory,
162
                        getParameters(),
163
                        null,
164
                        childrenData,
165
                        this
166
                );
167
                DataManagerProviderServices manager = DALSPILocator.getDataManagerProviderServices();
168
                this.store = manager.openStore(
169
                        getParameters(), 
170
                        provider
171
                );
172
                provider.setStoreServices((FeatureStoreProviderServices) this.store);
173
                provider.name = this.name;
174
            }
175
            return this.store;
176
        }
177

    
178
        public void setStoresRepository(StoresRepository storesRepository) {
179
            this.storesRepository = storesRepository;
180
        }
181

    
182
        public StoresRepository getStoresRepository() {
183
            return storesRepository;
184
        }
185
        
186
    }
187
    
188
    class Children implements UnmodifiableBasicMap<String, DataStore> {
189
        // Con esta clase se pospone la creacion de los stores hasta que se 
190
        // pide cada uno de ellos, y no cuando se pide el Map de estos, ya
191
        // que el map se puede pedir solo para saber si hay o cuantos hay.
192

    
193
        @Override
194
        public DataStore get(String key) {
195
            for (ReaderData child : childrenData) {
196
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
197
                    try {
198
                        return child.getStore();
199
                    } catch (Exception ex) {
200
                        throw new RuntimeException(ex);
201
                    }
202
                }
203
            }
204
            return null;
205
        }
206

    
207
        @Override
208
        public boolean isEmpty() {
209
            return childrenData.isEmpty();
210
        }
211

    
212
        @Override
213
        public boolean containsKey(String key) {
214
            for (ReaderData child : childrenData) {
215
                if( StringUtils.equalsIgnoreCase(child.getName(), key) ) {
216
                    return true;
217
                }
218
            }
219
            return false;
220
        }
221

    
222
        @Override
223
        public Map<String, DataStore> toMap() {
224
            return new UnmodifiableBasicMapToMapAdapter<>(this);
225
        }
226

    
227
        @Override
228
        public int size() {
229
            return childrenData.size();
230
        }
231

    
232
        @Override
233
        public UnmodifiableBasicSet<String> keySet() {
234
            return new UnmodifiableBasicSet<String>() {
235

    
236
                @Override
237
                public boolean isEmpty() {
238
                    return childrenData.isEmpty();
239
                }
240

    
241
                @Override
242
                public int size() {
243
                    return childrenData.size();
244
                }
245

    
246
                @Override
247
                public Iterator<String> iterator() {
248
                    final Iterator<ReaderData> it = childrenData.iterator();
249
                    return new Iterator<String>() {
250
                        @Override
251
                        public boolean hasNext() {
252
                            return it.hasNext();
253
                        }
254

    
255
                        @Override
256
                        public String next() {
257
                            ReaderData theReaderData = it.next();
258
                            return theReaderData.getName();
259
                        }
260
                    };
261
                }
262
            };
263
        }
264

    
265
        @Override
266
        public Iterator<DataStore> iterator() {
267
            final Iterator<String> it = this.keySet().iterator();
268
            return new Iterator<DataStore>() {
269
                @Override
270
                public boolean hasNext() {
271
                    return it.hasNext();
272
                }
273

    
274
                @Override
275
                public DataStore next() {
276
                    String name = it.next();
277
                    return get(name);
278
                }
279
            };
280
        }
281
        
282
    }
283

    
284
    private final ResourceProvider resource;
285

    
286
    private Envelope envelope;
287
    private boolean need_calculate_envelope = false;
288
    private String name = "";
289
    private final SimpleSequentialReaderFactory readerFactory;
290
    private List<ReaderData> childrenData;
291
    private ReaderData readerData;
292

    
293
    public SimpleSequentialReaderStoreProvider(
294
            SimpleSequentialReaderFactory readerFactory,
295
            SimpleSequentialReaderStoreParameters parameters,
296
            DataStoreProviderServices storeServices
297
        ) throws InitializeException {
298
        this(readerFactory, parameters, storeServices, new ArrayList<ReaderData>(), null);
299
    }
300
    
301
    private SimpleSequentialReaderStoreProvider(
302
            SimpleSequentialReaderFactory readerFactory,
303
            SimpleSequentialReaderStoreParameters parameters,
304
            DataStoreProviderServices storeServices,
305
            List<ReaderData> childrenData,
306
            ReaderData readerData
307
            
308
        ) throws InitializeException {
309
        super(
310
                parameters,
311
                storeServices,
312
                FileHelper.newMetadataContainer(readerFactory.getName())
313
        );
314
        this.childrenData = childrenData;
315
        this.readerData = readerData;
316
        this.readerFactory = readerFactory;
317

    
318
        File file = getParameters().getFile();
319
        resource = this.createResource(
320
                FileResource.NAME,
321
                new Object[]{file.getAbsolutePath()}
322
        );
323

    
324
        resource.addConsumer(this);
325
        if( this.readerData!=null ) {
326
            this.name = this.readerData.getName();            
327
        }
328
        if( storeServices != null ) {
329
            initializeFeatureTypes();
330
        }
331
    }
332

    
333
    private void setStoreServices(FeatureStoreProviderServices storeServices) throws InitializeException {
334
        this.store = storeServices;
335
        initializeFeatureTypes();
336
    }
337
    
338
    @Override
339
    public SimpleSequentialReaderStoreParameters getParameters() {
340
        return (SimpleSequentialReaderStoreParameters) super.getParameters();
341
    }
342

    
343
    @Override
344
    public String getProviderName() {
345
        if( this.readerFactory==null ) {
346
            return "unknown";
347
        }
348
        return this.readerFactory.getName();
349
    }
350

    
351
    @Override
352
    public boolean allowWrite() {
353
        return false;
354
    }
355

    
356
    private String getFullFileName() {
357
        // Usar solo para mostrar mensajes en el logger.
358
        String s;
359
        try {
360
            s = getParameters().getFile().getAbsolutePath();
361
        } catch (Exception e2) {
362
            s = "(unknow)";
363
        }
364
        return s;
365
    }
366

    
367
    @Override
368
    public void open() throws OpenException {
369
        if ( this.data != null ) {
370
            return;
371
        }
372
        this.data = new ArrayList<>();
373
        resource.setData(new HashMap());
374
        try {
375
            loadFeatures();
376
        } catch (RuntimeException e) {
377
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
378
            throw e;
379
        } catch (Exception e) {
380
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
381
            throw new RuntimeException(e);
382
        }
383
    }
384

    
385
    @Override
386
    public DataServerExplorer getExplorer() throws ReadException {
387
        DataManager manager = DALLocator.getDataManager();
388
        FilesystemServerExplorerParameters params;
389
        try {
390
            params = (FilesystemServerExplorerParameters) manager
391
                    .createServerExplorerParameters(FilesystemServerExplorer.NAME);
392
            params.setRoot(this.getParameters().getFile().getParent());
393
            return manager.openServerExplorer(FilesystemServerExplorer.NAME, params);
394
        } catch (DataException | ValidateDataParametersException e) {
395
            throw new ReadException(this.getProviderName(), e);
396
        }
397

    
398
    }
399

    
400
    @Override
401
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
402
        throw new UnsupportedOperationException();
403
    }
404

    
405
    @Override
406
    public boolean closeResourceRequested(ResourceProvider resource) {
407
        return true;
408
    }
409

    
410
    @Override
411
    public int getOIDType() {
412
        return DataTypes.LONG;
413
    }
414

    
415
    @Override
416
    public boolean supportsAppendMode() {
417
        return false;
418
    }
419

    
420
    @Override
421
    public void append(FeatureProvider featureProvider) {
422
        throw new UnsupportedOperationException();
423
    }
424

    
425
    @Override
426
    public void beginAppend() {
427
        throw new UnsupportedOperationException();
428
    }
429

    
430
    @Override
431
    public void endAppend() {
432
        throw new UnsupportedOperationException();
433
    }
434

    
435
    @Override
436
    public Object createNewOID() {
437
        throw new UnsupportedOperationException();
438
    }
439

    
440
    protected void initializeFeatureTypes() throws InitializeException {
441
        try {
442
            this.open();
443
        } catch (OpenException e) {
444
            throw new InitializeException(this.getProviderName(), e);
445
        }
446
    }
447

    
448
    @Override
449
    public Envelope getEnvelope() throws DataException {
450
        this.open();
451
        if ( this.envelope != null ) {
452
            return this.envelope;
453
        }
454
        if ( !this.need_calculate_envelope ) {
455
            return null;
456
        }
457
        FeatureStore fs = this.getFeatureStore();
458
        FeatureType ft = fs.getDefaultFeatureType();
459
        FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
460

    
461
        try {
462
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
463
            fs.accept(new Visitor() {
464
                @Override
465
                public void visit(Object obj) throws VisitCanceledException, BaseException {
466
                    Feature f = (Feature) obj;
467
                    Geometry geom = f.getDefaultGeometry();
468
                    if ( geom != null ) {
469
                        envelope.add(geom.getEnvelope());
470
                    }
471
                }
472
            });
473
        } catch (BaseException e) {
474
            LOGGER.warn("Can't calculate the envelope of '"+getProviderName()+"' file '" + this.getFullName() + "'.", e);
475
            this.envelope = null;
476
        }
477

    
478
        this.need_calculate_envelope = false;
479
        return this.envelope;
480
    }
481

    
482
    @Override
483
    public Object getDynValue(String name) throws DynFieldNotFoundException {
484
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
485
            try {
486
                return this.getEnvelope();
487
            } catch (DataException e) {
488
                return null;
489
            }
490
        } else {
491
            if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
492
                IProjection pro = this.getParameters().getCRS();
493
                if ( pro != null ) {
494
                    return pro;
495
                }
496
                FeatureType type;
497
                try {
498
                    type = this.getStoreServices().getDefaultFeatureType();
499
                    pro = type.getDefaultSRS();
500
                    if( pro!=null ) {
501
                        return pro;
502
                    }
503
                } catch (DataException ex) {
504
                }
505
            }
506
        }
507
        return super.getDynValue(name);
508
    }
509

    
510
    @Override
511
    public void resourceChanged(ResourceProvider resource) {
512
        this.getStoreServices().notifyChange(
513
                DataStoreNotification.RESOURCE_CHANGED,
514
                resource);
515
    }
516

    
517
    @Override
518
    public Object getSourceId() {
519
        return this.getParameters().getFile();
520
    }
521

    
522
    @Override
523
    public String getName() {
524
        return this.name;
525
    }
526

    
527
    @Override
528
    public String getFullName() {
529
        return this.getParameters().getFile().getAbsolutePath();
530
    }
531

    
532
    @Override
533
    public ResourceProvider getResource() {
534
        return resource;
535
    }
536

    
537
    private String[] split(String value, String separators) {
538
        int firstSeparatorPosition = 1000000;
539
        Character sep = null;
540
        for (char ch : separators.toCharArray()) {
541
            int pos = value.indexOf(ch);
542
            if( pos>0 && pos<firstSeparatorPosition ) {
543
                sep = ch;
544
                firstSeparatorPosition = pos;
545
            }
546
        }
547
        if( sep == null ) {
548
            return new String[] { value };
549
        }
550
        return value.split("["+sep+"]");
551
    }
552
    
553
    private class FieldTypeParser {
554

    
555
        public String name = null;
556
        public int type = DataTypes.STRING;
557
        public int size = 0;
558
        public int geomType = Geometry.TYPES.GEOMETRY;
559
        public int geomSubtype = Geometry.SUBTYPES.GEOM2D;
560
        public Map<String,String> tags = new HashMap<>();
561
        public Map<String,String> assignments = new HashMap<>();
562

    
563
        private String typename = "string";
564

    
565
        FieldTypeParser() {
566
        }
567

    
568
        private int getType(String value) {
569
            DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
570
            return dataTypesManager.getType(typename);
571
        }
572

    
573
        @SuppressWarnings("UseSpecificCatch")
574
        public boolean parse(String value) {
575
            String[] args;
576
            if ( value.contains("__") ) {
577
                args = value.split("__");
578
            } else {
579
                args = split(value, ":/#@!;-");
580
                if( args.length == 1 ) {
581
                    this.name = value;
582
                    return true;
583
                }
584
            }                        
585
            int n = 0;
586
            this.name = args[n++];
587
            if( n >= args.length ) {
588
                return true;
589
            }
590
            this.typename = args[n++];
591
            this.type = this.getType(this.typename);
592
            if ( this.type == DataTypes.INVALID ) {
593
                this.geomType = GeometryUtils.getGeometryType(this.typename);
594
                if( this.geomType==Geometry.TYPES.UNKNOWN )  {
595
                    this.type = DataTypes.STRING;
596
                    LOGGER.info("Type '" + this.typename + "' not valid for attribute '" + value + "' in '"+getProviderName()+"' file '" + getFullFileName() + "'.");
597
                } else {
598
                    this.typename = "GEOMETRY";
599
                    this.type = DataTypes.GEOMETRY;
600
                }
601
            }
602
            switch(this.type) {
603
                case DataTypes.STRING:
604
                    this.size = 50;
605
                    break;
606
                case DataTypes.INT:
607
                    this.size = 10;
608
                    break;
609
                case DataTypes.LONG:
610
                    this.size = 20;
611
                    break;
612
                case DataTypes.FLOAT:
613
                    this.size = 10;
614
                    break;
615
                case DataTypes.DOUBLE:
616
                    this.size = 20;
617
                    break;
618
                default:
619
                    this.size = 0;
620
            }
621
            while (n < args.length) {
622
                String option = args[n++].toLowerCase();
623
                switch (option) {
624
                    case "size":
625
                        try {
626
                            this.size = Integer.parseInt(args[n++]);
627
                        } catch (Exception ex) {
628
                            LOGGER.warn("Ignore incorrect field size for field " + value + " in '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
629
                        }
630
                        break;
631
                    case "tag": {
632
                            String x = args[n++];
633
                            int pos = x.indexOf("=");
634
                            if( pos < 0 ) {
635
                                this.tags.put(x, null);
636
                            } else {
637
                                this.tags.put( 
638
                                        StringUtils.substring(x, 0, pos),
639
                                        StringUtils.substring(x, pos+1)
640
                                );
641
                            }
642
                            break;
643
                        }
644
                    case "set": {
645
                            String x = args[n++];
646
                            int pos = x.indexOf("=");
647
                            if( pos < 0 ) {
648
                                this.assignments.put(x, null);
649
                            } else {
650
                                this.assignments.put( 
651
                                        StringUtils.substring(x, 0, pos),
652
                                        StringUtils.substring(x, pos+1)
653
                                );
654
                            }
655
                            break;
656
                        }
657
                    default:
658
                        LOGGER.warn("Illegal argumente '"+option+"' for field '"+this.name+"' in '"+getProviderName()+"' file '" + getFullFileName() + "' ("+value+").");
659
                }
660
            }
661
            return true;
662
        }
663

    
664
    }
665

    
666
    private EditableFeatureType getFeatureType(List<String> headers, int automaticTypes[]) {
667
        EditableFeatureType fType = getStoreServices().createFeatureType(this.getName());
668
        fType.setHasOID(true);
669
//        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
670

    
671
        FieldTypeParser[] fieldTypes = new FieldTypeParser[headers.size()];
672
        //
673
        // Calculamos cuales pueden ser los tipos de datos
674
        //
675
        for ( int i = 0; i < fieldTypes.length; i++ ) {
676
            fieldTypes[i] = new FieldTypeParser();
677
        }
678

    
679
        // Asuminos los tipos pasados por parametro, que se supone
680
        // son los detectados automaticamente.
681
        if ( automaticTypes != null ) {
682
            for ( int i = 0; i < fieldTypes.length && i < automaticTypes.length; i++ ) {
683
                fieldTypes[i].type = automaticTypes[i];
684
            }
685
        }
686
        // Luego probamos con lo que diga las cabezeras, sobreescribiendo
687
        // los tipos anteriores en caso de definirse en la cabezara.
688
        for ( int i = 0; i < fieldTypes.length; i++ ) {
689
            fieldTypes[i].parse(headers.get(i));
690
        }
691

    
692
        // Y por ultimo hacemos caso a lo que se haya especificado en los parametros
693
        // de apertura del reader, teniendo esto prioridad sobre todo.
694
        int[] param_types = this.getParameters().getFieldTypes();
695
        if ( param_types != null ) {
696
            for ( int i = 0; i < fieldTypes.length && i < param_types.length; i++ ) {
697
                fieldTypes[i].type = param_types[i];
698
            }
699
        }
700

    
701
        int[] param_sizes = this.getParameters().getFieldSizes();
702
        if ( param_sizes != null ) {
703
            for ( int i = 0; i < param_sizes.length; i++ ) {
704
                if ( param_sizes[i] > 0 ) {
705
                    fieldTypes[i].size = param_sizes[i];
706
                }
707
            }
708
        }
709
        //
710
        // Una vez ya sabemos los tipos de datos rellenamos el feature-type
711
        //
712
        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
713
        for (FieldTypeParser fieldType : fieldTypes) {
714
            EditableFeatureAttributeDescriptor fad = fType.add(fieldType.name, fieldType.type);
715
            fad.setSize(fieldType.size);
716
            if (fieldType.type == DataTypes.GEOMETRY ) {
717
                fad.setGeometryType(fieldType.geomType, fieldType.geomSubtype);
718
                if( fType.getDefaultGeometryAttributeName() == null ) {
719
                    fType.setDefaultGeometryAttributeName(fieldType.name);
720
                }
721
            }
722
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
723
                try {
724
                    switch(entry.getKey().toLowerCase()) {
725
                        case "hidden":
726
                            fad.setHidden((boolean) dataTypesManager.coerce(DataTypes.BOOLEAN, entry.getValue()));
727
                            break;
728
                        case "allownull":
729
                            fad.setAllowNull((boolean) dataTypesManager.coerce(DataTypes.BOOLEAN, entry.getValue()));
730
                            break;
731
                        case "pk":
732
                        case "ispk":
733
                        case "primarykey":
734
                        case "isprimarykey":
735
                            fad.setIsPrimaryKey((boolean) dataTypesManager.coerce(DataTypes.BOOLEAN, entry.getValue()));
736
                            break;
737
                        case "isautomatic":
738
                        case "automatic":
739
                            fad.setIsAutomatic((boolean) dataTypesManager.coerce(DataTypes.BOOLEAN, entry.getValue()));
740
                            break;
741
                        case "time":
742
                        case "istime":
743
                            fad.setIsTime((boolean) dataTypesManager.coerce(DataTypes.BOOLEAN, entry.getValue()));
744
                            break;
745
                        case "profile":
746
                            fad.setDataProfileName((String) dataTypesManager.coerce(DataTypes.STRING, entry.getValue()));
747
                            break;
748
                        case "group":
749
                            fad.setGroup((String) dataTypesManager.coerce(DataTypes.STRING, entry.getValue()));
750
                            break;
751
                        case "description":
752
                            fad.setDescription((String) dataTypesManager.coerce(DataTypes.STRING, entry.getValue()));
753
                            break;
754
                        case "label":
755
                            fad.setLabel((String) dataTypesManager.coerce(DataTypes.STRING, entry.getValue()));
756
                            break;
757
                        case "expression":
758
                            // Los campos calculados los procesamos en una segunda
759
                            // pasada, cuando ya estan definidos el resto de los campos
760
                            // ya que pueden requerir campos que aun no se han definido.
761
                            break;
762
                        case "size":
763
                            fad.setSize((int) dataTypesManager.coerce(DataTypes.INT, entry.getValue()));
764
                            break;
765
                        case "precision":
766
                            fad.setPrecision((int) dataTypesManager.coerce(DataTypes.INT, entry.getValue()));
767
                            break;
768
                        case "order":
769
                            fad.setOrder((int) dataTypesManager.coerce(DataTypes.INT, entry.getValue()));
770
                            break;
771
                        case "interval":
772
                            fad.setInterval((Interval) dataTypesManager.coerce(DataTypes.INTERVAL, entry.getValue()));
773
                            break;
774
                        case "geomtype":
775
                        case "geometrytype":
776
                            fad.setGeometryType(entry.getValue());
777
                            break;
778
                        case "srs":
779
                            fad.setSRS(entry.getValue());
780
                            break;
781
                    }
782
                } catch (Exception ex) {
783
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' of '"+fad.getName()+"'.", ex);
784
                }
785
            }
786
            Tags tags = fad.getTags();
787
            for (Map.Entry<String, String> entry : fieldType.tags.entrySet()) {
788
                tags.set(entry.getKey(), entry.getValue());
789
            }
790
        }
791
        // Processamos ahora los campos calculados
792
        for (FieldTypeParser fieldType : fieldTypes) {
793
            EditableFeatureAttributeDescriptor fad = fType.getEditableAttributeDescriptor(fieldType.name);
794
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
795
                try {
796
                    switch(entry.getKey().toLowerCase()) {
797
                        case "expression":
798
                            fad.setFeatureAttributeEmulator((String) dataTypesManager.coerce(DataTypes.STRING, entry.getValue()));
799
                            break;
800
                    }
801
                } catch (Exception ex) {
802
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' in '"+fad.getName()+"' of '"+getFullFileName()+"'.", ex);
803
                }
804
            }
805
        }
806

    
807
        String[] pointDimensionNames = this.getParameters().getPointDimensionNames();
808
        if ( pointDimensionNames != null ) {
809
            PointAttributeEmulator emulator = new PointAttributeEmulator(pointDimensionNames);
810
            EditableFeatureAttributeDescriptor attr = fType.add("the_geom", DataTypes.GEOMETRY, emulator);
811
            GeometryManager geommgr = GeometryLocator.getGeometryManager();
812
            GeometryType gt;
813
            try {
814
                if ( emulator.fieldNames != null && emulator.fieldNames.length <= 2 ) {
815
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
816
                } else {
817
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
818
                }
819
                attr.setGeometryType(gt);
820
            } catch (Exception e) {
821
                LOGGER.warn("Can't set geometry type for the calculated field in '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
822
            }
823
        }
824
        return fType;
825
    }
826

    
827
    class PointAttributeEmulator implements FeatureAttributeEmulator {
828

    
829
        private static final int XNAME = 0;
830
        private static final int YNAME = 1;
831
        private static final int ZNAME = 2;
832

    
833
        private final GeometryManager geommgr;
834
        private final String[] fieldNames;
835
        private final Coercion toDouble;
836
        private int errorcount = 0;
837

    
838
        public PointAttributeEmulator(String[] pointDimensionNames) {
839
            if ( pointDimensionNames.length > 2 ) {
840
                this.fieldNames = new String[3];
841
                this.fieldNames[ZNAME] = pointDimensionNames[2];
842
            } else {
843
                this.fieldNames = new String[2];
844
            }
845
            this.fieldNames[XNAME] = pointDimensionNames[0];
846
            this.fieldNames[YNAME] = pointDimensionNames[1];
847
            this.geommgr = GeometryLocator.getGeometryManager();
848
            DataTypesManager datatypeManager = ToolsLocator.getDataTypesManager();
849

    
850
            this.toDouble = datatypeManager.getCoercion(DataTypes.DOUBLE);
851
        }
852

    
853
        @Override
854
        @SuppressWarnings("UseSpecificCatch")
855
        public Object get(Feature feature) {
856
            try {
857
                Object valueX = feature.get(this.fieldNames[XNAME]);
858
                valueX = toDouble.coerce(valueX);
859
                if ( valueX == null ) {
860
                    return null;
861
                }
862
                Object valueY = feature.get(this.fieldNames[YNAME]);
863
                valueY = toDouble.coerce(valueY);
864
                if ( valueY == null ) {
865
                    return null;
866
                }
867
                Object valueZ = null;
868
                if ( this.fieldNames.length > 2 ) {
869
                    valueZ = toDouble.coerce(feature.get(this.fieldNames[ZNAME]));
870
                    if ( valueZ == null ) {
871
                        return null;
872
                    }
873
                }
874

    
875
                double x = ((Double) valueX);
876
                double y = ((Double) valueY);
877
                Point point;
878
                if ( this.fieldNames.length > 2 ) {
879
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
880
                    double z = ((Double) valueZ);
881
                    point.setCoordinateAt(2, z);
882
                } else {
883
                    point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM2D);
884
                }
885
                return point;
886
            } catch (Exception ex) {
887
                if ( ++errorcount < 5 ) {
888
                    LOGGER.warn("[" + errorcount + "] Can't create point from '"+getProviderName()+"' file."+
889
                            "XNAME='" + this.fieldNames[XNAME] + 
890
                            "', YNAME='" + this.fieldNames[YNAME] + 
891
                            "', ZNAME='" + ((this.fieldNames.length > 2)? this.fieldNames[ZNAME]: "(2D)") +
892
                            "' feature=" + feature.toString(), ex);
893
                }
894
                return null;
895
            }
896
        }
897

    
898
        @Override
899
        public void set(EditableFeature feature, Object value) {
900
            if ( value == null ) {
901
                return;
902
            }
903
            Point point;
904
            if ( value instanceof MultiPoint ) {
905
                point = (Point) ((MultiPoint) value).getPrimitiveAt(0);
906
            } else {
907
                point = (Point) value;
908
            }
909
            feature.set(this.fieldNames[XNAME], point.getX());
910
            feature.set(this.fieldNames[YNAME], point.getY());
911
            if ( this.fieldNames.length > 2 ) {
912
                feature.set(this.fieldNames[ZNAME], point.getCoordinateAt(2));
913
            }
914
        }
915

    
916
        @Override
917
        public boolean allowSetting() {
918
            return true;
919
        }
920

    
921
        @Override
922
        public String[] getRequiredFieldNames() {
923
            return this.fieldNames;
924
        }
925

    
926
    }
927

    
928
    private void loadFeatures() throws IOException, DataException,
929
            CoercionException, CloneNotSupportedException {
930

    
931
        ReaderData theReaderData = this.readerData;
932
        if( theReaderData==null ) {
933
            theReaderData = new ReaderData();
934
            SimpleSequentialReader reader = this.readerFactory.createReader(this.getParameters());
935
            try {
936
                loadFeatures(reader, theReaderData);
937
                this.childrenData = new ArrayList<>();
938
                for(SimpleSequentialReader childReader : reader.getChildren() ) {
939
                    ReaderData childData = new ReaderData();
940
                    loadFeatures(childReader, childData);
941
                    this.childrenData.add(childData);
942
                }
943
                this.readerData = theReaderData;
944
            } finally {
945
                reader.close();
946
            }
947
        }
948
        this.name = theReaderData.getName();
949
        FeatureStoreProviderServices theStore = this.getStoreServices();
950
        theStore.setFeatureTypes(theReaderData.getFeatureTypes(), theReaderData.getDefaultFeatureType());
951
        this.need_calculate_envelope = theReaderData.getNeedCalculateEnvelope();
952
        this.data = theReaderData.getFeatures();
953

    
954
    }
955
    
956
    private void loadFeatures(SimpleSequentialReader reader, ReaderData readerData) throws IOException, DataException,
957
            CoercionException, CloneNotSupportedException {
958

    
959
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
960
        SimpleTaskStatus taskStatus = manager.createDefaultSimpleTaskStatus(reader.getName());
961
        taskStatus.setAutoremove(true);
962
        taskStatus.setIndeterminate();
963
        taskStatus.add();
964
        try {
965
            List<String> headers;
966

    
967
            taskStatus.message("_preparing");
968
            readerData.setName(reader.getName());
969
            readerData.setStoresRepository(reader.getStoresRepository());
970
            
971
            boolean ignore_errors = getParameters().getIgnoreErrors();
972

    
973
            headers = getParameters().getFieldNames();
974
            if ( headers == null ) {
975
                headers = reader.getFieldNames();
976
            }
977

    
978
            // Initialize the feature types
979
            EditableFeatureType edftype = this.getFeatureType(headers, automaticDetectionOfTypes(reader));
980

    
981
            edftype.setLabel(reader.getLabel());
982
            edftype.setDescription(reader.getDescription());
983
            Map<String, String> tagsReader = reader.getTags();
984
            if( tagsReader!=null ) {
985
                Tags tagsType = edftype.getTags();
986
                for (Map.Entry<String, String> tag : tagsReader.entrySet()) {
987
                    tagsType.set(tag.getKey(), tag.getValue());
988
                }
989
            }
990
            
991
            FeatureType ftype = edftype.getNotEditableCopy();
992
            List<FeatureType> ftypes = new ArrayList<>();
993
            ftypes.add(ftype);
994
            readerData.setFeatureTypes(ftypes, ftype);
995

    
996
            Coercion coercion[] = new Coercion[ftype.size()];
997
            int sizes[] = new int[ftype.size()];
998
            for ( int i = 0; i < ftype.size(); i++ ) {
999
                sizes[i] = -1;
1000
                FeatureAttributeDescriptor ad = ftype.getAttributeDescriptor(i);
1001
                coercion[i] = ad.getDataType().getCoercion();
1002
                if ( ad.getDataType().getType() == DataTypes.STRING ) {
1003
                    if ( ad.getSize() == 0 ) {
1004
                        // Es un string y no tiene un size asignado.
1005
                        // Lo ponemos a cero para calcularlo.
1006
                        sizes[i] = 0;
1007
                    }
1008
                }
1009
            }
1010
           
1011
            if ( ftype.getDefaultGeometryAttributeName() != null ) {
1012
                readerData.setNeedCalculateEnvelope(true);
1013
            }
1014

    
1015
            Locale locale = getParameters().getLocale();
1016
            taskStatus.message("_loading");
1017
            long rowCount = reader.getRowCount();
1018
            if( rowCount >0 ) {
1019
                taskStatus.setRangeOfValues(0, rowCount);
1020
            }
1021

    
1022
            int count = 0;
1023

    
1024
            int count_errors = 0;
1025
            
1026
            reader.rewind();
1027
            List<Object> row = reader.read();
1028

    
1029
            Object rawvalue;
1030
            while ( row != null ) {
1031
                taskStatus.setCurValue(++count);
1032
                FeatureProvider feature = this.createFeatureProvider(ftype);
1033
                for ( int i = 0; i < row.size(); i++ ) {
1034
                    try {
1035
                        if( ftype.get(i).isComputed() ) {
1036
                            continue;
1037
                        }
1038
                        rawvalue = row.get(i);
1039
                        if( rawvalue instanceof String && StringUtils.isBlank((String)rawvalue) ) {
1040
                            rawvalue = null;
1041
                        }
1042
                        Object value;
1043
                        if ( locale != null && coercion[i] instanceof CoercionWithLocale ) {
1044
                            value = ((CoercionWithLocale) (coercion[i])).coerce(rawvalue, locale);
1045
                        } else {
1046
                            value = coercion[i].coerce(rawvalue);
1047
                        }
1048
                        feature.set(i, value);
1049
                        if ( sizes[i] >= 0 && value != null ) {
1050
                            int x = ((String) value).length();
1051
                            if ( sizes[i] < x ) {
1052
                                sizes[i] = x;
1053
                            }
1054
                        }
1055
                    } catch (Exception ex) {
1056
                        if ( !ignore_errors ) {
1057
                            throw ex;
1058
                        }
1059
                        if ( count_errors++ < 10 ) {
1060
                            LOGGER.warn("Can't load value of attribute " + i+"/"+row.size()+"/"+ftype.size()+ " in row " + count + " in " +readerData.getName()+ ".", ex);
1061
                        }
1062
                        if ( count_errors == 10 ) {
1063
                            LOGGER.info("Too many errors, suppress messages.");
1064
                        }
1065
                    }
1066
                }
1067
                readerData.addFeatureProvider(feature);
1068
                row = reader.read();
1069
            }
1070
            for ( int i = 0; i < ftype.size(); i++ ) {
1071
                if ( sizes[i] > 0 ) {
1072
                    EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
1073
                    if( efad.getSize()<sizes[i] ) {
1074
                        efad.setSize(sizes[i]);
1075
                    }
1076
                }
1077
            }
1078
            // Volvemos a asignar al store el featuretype, ya que puede
1079
            // haber cambiado.
1080
            ftype = edftype.getNotEditableCopy();
1081
            ftypes = new ArrayList<>();
1082
            ftypes.add(ftype);
1083
            readerData.setFeatureTypes(ftypes, ftype);
1084

    
1085
            taskStatus.terminate();
1086
        } catch (Exception ex) {
1087
            taskStatus.abort();
1088
            LOGGER.warn("Can't load features from '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1089
        }
1090
    }
1091
    
1092
    private int[] automaticDetectionOfTypes(SimpleSequentialReader reader) throws IOException {
1093
        boolean automatic_types_detection = getParameters().getAutomaticTypesDetection();
1094
        if ( !automatic_types_detection ) {
1095
            return null;
1096
        }
1097
        int[] types = null;
1098
        try {
1099
            reader.rewind();
1100
            List<String> fieldNames = reader.getFieldNames();
1101
            if ( fieldNames == null ) {
1102
                fieldNames = getParameters().getFieldNames();
1103
            }
1104

    
1105
            AutomaticDetectionOfTypes x = new AutomaticDetectionOfTypes(
1106
                    this.getFullFileName()
1107
            );
1108
            types = x.detect(
1109
                    fieldNames.size(), 
1110
                    reader, 
1111
                    false, 
1112
                    getParameters().getLocale()
1113
            );
1114
        } catch (Exception ex) {
1115
            throw new RuntimeException("Problems reading '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
1116
        }
1117
        return types;
1118
    }
1119

    
1120
    @Override
1121
    public boolean hasDynValue(String name) {
1122
        if ( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
1123
            return true;
1124
        } else if ( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
1125
            return true;
1126
        }
1127
        return super.hasDynValue(name);
1128
    }
1129

    
1130
    @Override
1131
    public UnmodifiableBasicMap<String, DataStore> getChildren() {
1132
        return new Children();
1133
    }
1134

    
1135
    @Override
1136
    public StoresRepository getStoresRepository() {
1137
        StoresRepository repo = this.readerData.getStoresRepository();
1138
        if( repo == null ) {
1139
            return super.getStoresRepository();
1140
        }
1141
        return repo;
1142
    }
1143
    
1144
}