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.csv / src / main / java / org / gvsig / fmap / dal / store / csv / CSVStoreProvider.java @ 45359

History | View | Annotate | Download (47.2 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 modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.dal.store.csv;
24

    
25
import java.io.File;
26
import java.io.FileInputStream;
27
import java.io.FileNotFoundException;
28
import java.io.FileReader;
29
import java.io.IOException;
30
import java.io.InputStreamReader;
31
import java.net.URI;
32
import java.net.URL;
33
import java.nio.charset.Charset;
34
import java.util.ArrayList;
35
import java.util.HashMap;
36
import java.util.Iterator;
37
import java.util.List;
38
import java.util.Locale;
39
import java.util.Map;
40

    
41
import org.apache.commons.io.FilenameUtils;
42
import org.apache.commons.io.IOUtils;
43
import org.apache.commons.lang3.StringUtils;
44
import org.cresques.cts.IProjection;
45
import org.gvsig.fmap.dal.DALLocator;
46
import org.gvsig.fmap.dal.DataManager;
47
import org.gvsig.fmap.dal.DataServerExplorer;
48
import org.gvsig.fmap.dal.DataStore;
49
import org.gvsig.fmap.dal.DataStoreNotification;
50
import org.gvsig.fmap.dal.DataTypes;
51
import org.gvsig.fmap.dal.FileHelper;
52
import org.gvsig.fmap.dal.exception.DataException;
53
import org.gvsig.fmap.dal.exception.InitializeException;
54
import org.gvsig.fmap.dal.exception.OpenException;
55
import org.gvsig.fmap.dal.exception.ReadException;
56
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
57
import org.gvsig.fmap.dal.feature.EditableFeature;
58
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
59
import org.gvsig.fmap.dal.feature.EditableFeatureType;
60
import org.gvsig.fmap.dal.feature.Feature;
61
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
62
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
63
import org.gvsig.fmap.dal.feature.FeatureSet;
64
import org.gvsig.fmap.dal.feature.FeatureStore;
65
import org.gvsig.fmap.dal.feature.FeatureType;
66
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
67
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
68
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
69
import org.gvsig.fmap.dal.feature.spi.memory.AbstractMemoryStoreProvider;
70
import org.gvsig.fmap.dal.resource.ResourceAction;
71
import org.gvsig.fmap.dal.resource.file.FileResource;
72
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
73
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
74
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorer;
75
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorerParameters;
76
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
77
import org.gvsig.fmap.dal.store.csv.AutomaticDetectionOfTypes.DetectedValue;
78
import org.gvsig.fmap.dal.store.csv.simplereaders.CSVReader;
79
import org.gvsig.fmap.dal.store.csv.simplereaders.FixedLenReader;
80
import org.gvsig.fmap.dal.store.csv.simplereaders.SimpleReader;
81
import org.gvsig.fmap.geom.Geometry;
82
import org.gvsig.fmap.geom.GeometryLocator;
83
import org.gvsig.fmap.geom.GeometryManager;
84
import org.gvsig.fmap.geom.GeometryUtils;
85
import org.gvsig.fmap.geom.aggregate.MultiPoint;
86
import org.gvsig.fmap.geom.primitive.Envelope;
87
import org.gvsig.fmap.geom.primitive.Point;
88
import org.gvsig.fmap.geom.type.GeometryType;
89
import org.gvsig.tools.ToolsLocator;
90
import org.gvsig.tools.dataTypes.DataType;
91
import org.gvsig.tools.dataTypes.DataTypesManager;
92
import org.gvsig.tools.dataTypes.Coercion;
93
import org.gvsig.tools.dispose.DisposableIterator;
94
import org.gvsig.tools.dynobject.Tags;
95
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
96
import org.gvsig.tools.evaluator.AbstractEvaluator;
97
import org.gvsig.tools.evaluator.EvaluatorData;
98
import org.gvsig.tools.evaluator.EvaluatorException;
99
import org.gvsig.tools.exception.BaseException;
100
import org.gvsig.tools.exception.NotYetImplemented;
101
import org.gvsig.tools.persistence.PersistentState;
102
import org.gvsig.tools.persistence.exception.PersistenceException;
103
import org.gvsig.tools.task.SimpleTaskStatus;
104
import org.gvsig.tools.task.TaskStatusManager;
105
import org.gvsig.tools.visitor.VisitCanceledException;
106
import org.gvsig.tools.visitor.Visitor;
107
import org.slf4j.Logger;
108
import org.slf4j.LoggerFactory;
109
import org.supercsv.prefs.CsvPreference;
110
import org.gvsig.tools.dataTypes.CoercionContext;
111

    
112
@SuppressWarnings("UseSpecificCatch")
113
public class CSVStoreProvider extends AbstractMemoryStoreProvider implements
114
        ResourceConsumer {
115

    
116
    private static final Logger LOGGER = LoggerFactory.getLogger(CSVStoreProvider.class);
117

    
118
    public static final String NAME = DataStore.CSV_PROVIDER_NAME;
119
    public static final String DESCRIPTION = "CSV file";
120

    
121
    public static final String METADATA_DEFINITION_NAME = NAME;
122

    
123
    private ResourceProvider resource;
124

    
125
    private long counterNewsOIDs = 0;
126
    private Envelope envelope;
127
    private boolean need_calculate_envelope = false;
128
    private final SimpleTaskStatus taskStatus;
129
    private final CSVFeatureWriter writer;
130
    private FeatureType featureType;
131

    
132
    public CSVStoreProvider(CSVStoreParameters parameters,
133
            DataStoreProviderServices storeServices) throws InitializeException {
134
        super(
135
                parameters,
136
                storeServices,
137
                FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME)
138
        );
139
        this.writer = new CSVFeatureWriter();
140
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
141
        this.taskStatus = manager.createDefaultSimpleTaskStatus("CSV");
142

    
143
        counterNewsOIDs = 0;
144

    
145
        File file = getCSVParameters().getFile();
146
        resource = this.createResource(
147
                FileResource.NAME,
148
                new Object[]{file.getAbsolutePath()}
149
        );
150

    
151
        resource.addConsumer(this);
152

    
153
        initializeFeatureTypes();
154
    }
155

    
156
    private CSVStoreParameters getCSVParameters() {
157
        return (CSVStoreParameters) this.getParameters();
158
    }
159

    
160
    @Override
161
    public String getProviderName() {
162
        return NAME;
163
    }
164

    
165
    @Override
166
    public boolean allowWrite() {
167
        return true;
168
    }
169

    
170
    private String getFullFileName() {
171
        // Usar solo para mostrar mensajes en el logger.
172
        String s;
173
        try {
174
            s = getCSVParameters().getFile().getAbsolutePath();
175
        } catch (Exception e2) {
176
            s = "(unknow)";
177
        }
178
        return s;
179
    }
180

    
181
    @Override
182
    public void open() throws OpenException {
183
        if (this.data != null) {
184
            return;
185
        }
186
        this.data = new ArrayList<>();
187
        resource.setData(new HashMap());
188
        counterNewsOIDs = 0;
189
        try {
190
            loadFeatures();
191
        } catch (RuntimeException e) {
192
            LOGGER.debug("Can't load features from CSV '" + getFullFileName() + "'.", e);
193
            throw e;
194
        } catch (Exception e) {
195
            LOGGER.debug("Can't load features from CSV '" + getFullFileName() + "'.", e);
196
            throw new RuntimeException(e);
197
        }
198
    }
199

    
200
    @Override
201
    public DataServerExplorer getExplorer() throws ReadException {
202
        DataManager manager = DALLocator.getDataManager();
203
        FilesystemServerExplorerParameters params;
204
        try {
205
            params = (FilesystemServerExplorerParameters) manager
206
                    .createServerExplorerParameters(FilesystemServerExplorer.NAME);
207
            params.setRoot(this.getCSVParameters().getFile().getParent());
208
            return manager.openServerExplorer(FilesystemServerExplorer.NAME, params);
209
        } catch (DataException e) {
210
            throw new ReadException(this.getProviderName(), e);
211
        } catch (ValidateDataParametersException e) {
212
            throw new ReadException(this.getProviderName(), e);
213
        }
214

    
215
    }
216

    
217
    @Override
218
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
219

    
220
        try {
221
            this.taskStatus.add();
222
            taskStatus.message("_preparing");
223
            this.getResource().closeRequest();
224
            getResource().execute(new ResourceAction() {
225
                @Override
226
                public Object run() throws Exception {
227
                    FeatureSet features = null;
228
                    DisposableIterator it = null;
229
                    try {
230
                        File file = (File) resource.get();
231

    
232
                        features
233
                                = getStoreServices().getFeatureStore()
234
                                        .getFeatureSet();
235
                        List<FeatureProvider> newdata = new ArrayList<FeatureProvider>();
236
                        FeatureType ftype = getStoreServices().getDefaultFeatureType();
237
                        writer.initialize(getCSVParameters(), file, ftype, getCSVPreferences());
238
                        writer.begin();
239
                        it = features.fastIterator();
240
                        taskStatus.setRangeOfValues(0, 0);
241
                        long counter = 0;
242
                        while (it.hasNext()) {
243
                            taskStatus.setCurValue(counter++);
244
                            FeatureProvider feature = getStoreServices().getFeatureProviderFromFeature(
245
                                    (org.gvsig.fmap.dal.feature.Feature) it.next());
246
                            writer.add(feature);
247
                            if (feature.getOID() == null) {
248
                                LOGGER.warn("feature without OID");
249
                                feature.setOID(createNewOID());
250
                            }
251
                            newdata.add(feature);
252
                        }
253
                        data = newdata;
254
                        if (writer.getEnvelope() != null) {
255
                            envelope = writer.getEnvelope().getGeometry().getEnvelope();
256
                        }
257
                        resource.notifyChanges();
258
                        writer.end();
259
                    } finally {
260
                        if (it != null) {
261
                            it.dispose();
262
                        }
263
                        if (features != null) {
264
                            features.dispose();
265
                        }
266
                    }
267
                    return null;
268
                }
269

    
270
            });
271
            this.taskStatus.terminate();
272
        } catch (Exception e) {
273
            this.taskStatus.abort();
274
            throw new PerformEditingException(getResource().toString(), e);
275
        } finally {
276
            this.taskStatus.remove();
277
        }
278
    }
279

    
280
    private CsvPreference getCSVPreferences() {
281
        CSVReader reader = new CSVReader(getCSVParameters());
282
        return reader.getCSVPreferences();
283
    }
284

    
285
    @Override
286
    public boolean closeResourceRequested(ResourceProvider resource) {
287
        return true;
288
    }
289

    
290
    @Override
291
    public int getOIDType() {
292
        return DataTypes.LONG;
293
    }
294

    
295
    @Override
296
    public boolean supportsAppendMode() {
297
        return true;
298
    }
299

    
300
    @Override
301
    public void append(final FeatureProvider featureProvider) {
302
        //todo
303
        getResource().execute(new ResourceAction() {
304
            @Override
305
            public Object run() throws Exception {
306
                //writer.append(getStoreServices().createFeature(featureProvider));
307
                writer.add(featureProvider);
308
                return null;
309
            }
310
        });
311
    }
312

    
313
    @Override
314
    public void beginAppend() throws DataException {
315
        this.close();
316
        getResource().execute(new ResourceAction() {
317
            public Object run() throws Exception {
318
                writer.initialize(
319
                        getCSVParameters(),
320
                        getCSVParameters().getFile(),
321
                        getFeatureStore().getDefaultFeatureType(),
322
                        getCSVPreferences()
323
                );
324
                writer.beginAppend();
325
                return null;
326
            }
327
        });
328

    
329
    }
330

    
331
    @Override
332
    public void endAppend() {
333
        try {
334
            getResource().execute(new ResourceAction() {
335
                public Object run() throws Exception {
336
                    writer.end();
337
                    resource.notifyChanges(); //resourcesNotifyChanges();
338
                    counterNewsOIDs = -1;
339
                    return null;
340
                }
341
            });
342
            writer.end();
343
        } catch (PerformEditingException ex) {
344
            LOGGER.warn("Not been able to end append '" + this.getFullName() + "'.", ex);
345
        }
346
    }
347

    
348
    public void saveToState(PersistentState state) throws PersistenceException {
349
        throw new NotYetImplemented();
350
    }
351

    
352
    public void loadFromState(PersistentState state) throws PersistenceException {
353
        throw new NotYetImplemented();
354
    }
355

    
356
    @Override
357
    public Object createNewOID() {
358
        return counterNewsOIDs++;
359
    }
360

    
361
    protected void initializeFeatureTypes() throws InitializeException {
362
        try {
363
            this.open();
364
        } catch (OpenException e) {
365
            throw new InitializeException(this.getProviderName(), e);
366
        }
367
    }
368

    
369
    @Override
370
    public Envelope getEnvelope() throws DataException {
371
        this.open();
372
        if (this.envelope != null) {
373
            return this.envelope;
374
        }
375
        if (!this.need_calculate_envelope) {
376
            return null;
377
        }
378
        FeatureStore fs = this.getFeatureStore();
379
        FeatureType ft = fs.getDefaultFeatureType();
380
        FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
381

    
382
        try {
383
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
384
            fs.accept(new Visitor() {
385
                @Override
386
                public void visit(Object obj) throws VisitCanceledException, BaseException {
387
                    Feature f = (Feature) obj;
388
                    Geometry geom = f.getDefaultGeometry();
389
                    if (geom != null) {
390
                        envelope.add(geom.getEnvelope());
391
                    }
392
                }
393
            });
394
        } catch (BaseException e) {
395
            LOGGER.warn("Can't calculate the envelope of CSV file '" + this.getFullName() + "'.", e);
396
            this.envelope = null;
397
        }
398

    
399
        this.need_calculate_envelope = false;
400
        return this.envelope;
401
    }
402

    
403
    @Override
404
    public Object getDynValue(String name) throws DynFieldNotFoundException {
405
        if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
406
            try {
407
                return this.getEnvelope();
408
            } catch (DataException e) {
409
                return null;
410
            }
411
        } else {
412
            if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
413
                IProjection pro = CSVStoreParameters.getCRS(this.getCSVParameters());
414
                if (pro != null) {
415
                    return pro;
416
                }
417
            }
418
        }
419
        return super.getDynValue(name);
420
    }
421

    
422
    @Override
423
    public void resourceChanged(ResourceProvider resource) {
424
        this.getStoreServices().notifyChange(
425
                DataStoreNotification.RESOURCE_CHANGED,
426
                resource);
427
    }
428

    
429
    @Override
430
    public Object getSourceId() {
431
        return this.getCSVParameters().getFile();
432
    }
433

    
434
    @Override
435
    public String getName() {
436
        String name = this.getCSVParameters().getFile().getName();
437
        return FilenameUtils.getBaseName(name);
438
    }
439

    
440
    @Override
441
    public String getFullName() {
442
        return this.getCSVParameters().getFile().getAbsolutePath();
443
    }
444

    
445
    @Override
446
    public ResourceProvider getResource() {
447
        return resource;
448
    }
449

    
450
    private boolean isEmpty(String s) {
451
        if (s == null) {
452
            return true;
453
        }
454
        return s.trim().length() == 0;
455
    }
456

    
457
    private void init(CSVStoreParameters parameters, DataStoreProviderServices storeServices) {
458
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
459
    }
460
    
461
    public static class FieldTypeParser {
462

    
463
        public String name = null;
464
        public int type = DataTypes.STRING;
465
        public int size = 0;
466
        public int geomType = Geometry.TYPES.GEOMETRY;
467
        public int geomSubtype = Geometry.SUBTYPES.GEOM2D;
468
        public Map<String,String> tags = new HashMap<>();
469
        public Map<String,String> assignments = new HashMap<>();
470
        
471
        // Valores obtenidos desde la deteccion automatica desde los datos
472
        public DetectedValue detectedValue = null ;
473

    
474
        private String typename = "string";
475

    
476
        private final String fullFileName;
477
        private final String providerName;
478
        
479
        FieldTypeParser(String providerName, String fullFileName) {
480
            this.providerName = providerName;
481
            this.fullFileName = fullFileName;
482
        }
483

    
484
        public String getProviderName() {
485
            return this.providerName;
486
        }
487
        
488
        public String getFullFileName() {
489
            return this.fullFileName;
490
        }
491
        
492
        public void clear() {
493
            this.name = null;
494
            this.type = DataTypes.STRING;
495
            this.size = 0;
496
            this.tags = new HashMap<>();
497
            this.assignments = new HashMap<>();
498
        }
499

    
500
        public void copyFrom(FieldTypeParser other) {
501
            this.name = other.name;
502
            this.type = other.type;
503
            this.size = other.size;
504
            this.tags = new HashMap<>();
505
            this.tags.putAll(other.tags);
506
            this.assignments = new HashMap<>();
507
            this.assignments.putAll(other.assignments);
508
        }
509

    
510
        private int getType(String value) {
511
            DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
512
            return dataTypesManager.getType(typename);
513
        }
514

    
515
        private String[] split(String value, String separators) {
516
            int firstSeparatorPosition = 1000000;
517
            Character sep = null;
518
            for (char ch : separators.toCharArray()) {
519
                int pos = value.indexOf(ch);
520
                if( pos>0 && pos<firstSeparatorPosition ) {
521
                    sep = ch;
522
                    firstSeparatorPosition = pos;
523
                }
524
            }
525
            if( sep == null ) {
526
                return new String[] { value };
527
            }
528
            return value.split("["+sep+"]");
529
        }
530

    
531
        @SuppressWarnings("UseSpecificCatch")
532
        public boolean parse(String value) {
533
            String[] args;
534
            if ( value.contains("__") ) {
535
                args = value.split("__");
536
            } else {
537
                args = split(value, ":/#@!;-");
538
                if( args.length == 1 ) {
539
                    this.name = value;
540
                    return true;
541
                }
542
            }                        
543
            int n = 0;
544
            this.name = StringUtils.trim(args[n++]);
545
            if( n >= args.length ) {
546
                return true;
547
            }
548
            this.typename = StringUtils.trim(args[n++]);
549
            this.type = this.getType(this.typename);
550
            if ( this.type == DataTypes.INVALID ) {
551
                this.geomType = GeometryUtils.getGeometryType(this.typename);
552
                if( this.geomType==Geometry.TYPES.UNKNOWN )  {
553
                    this.type = DataTypes.STRING;
554
                    LOGGER.info("Type '" + this.typename + "' not valid for attribute '" + value + "' in '"+getProviderName()+"' file '" + getFullFileName() + "'.");
555
                } else {
556
                    this.typename = "GEOMETRY";
557
                    this.type = DataTypes.GEOMETRY;
558
                }
559
            }
560
            this.size = 0;
561

    
562
            while (n < args.length) {
563
                String option = StringUtils.trim(args[n++].toLowerCase());
564
                switch (option) {
565
                    case "size":
566
                        try {
567
                            this.size = Integer.parseInt(args[n++]);
568
                        } catch (Exception ex) {
569
                            LOGGER.warn("Ignore incorrect field size for field " + value + " in '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
570
                        }
571
                        break;
572
                    case "tag": {
573
                            String x = StringUtils.trim(args[n++]);
574
                            int pos = x.indexOf("=");
575
                            if( pos < 0 ) {
576
                                this.tags.put(x, null);
577
                            } else {
578
                                this.tags.put( 
579
                                        StringUtils.trim(StringUtils.substring(x, 0, pos)),
580
                                        StringUtils.trim(StringUtils.substring(x, pos+1))
581
                                );
582
                            }
583
                            break;
584
                        }
585
                    case "set": {
586
                            String x = StringUtils.trim(args[n++]);
587
                            int pos = x.indexOf("=");
588
                            if( pos < 0 ) {
589
                                this.assignments.put(x, null);
590
                            } else {
591
                                this.assignments.put( 
592
                                        StringUtils.trim(StringUtils.substring(x, 0, pos)),
593
                                        StringUtils.trim(StringUtils.substring(x, pos+1))
594
                                );
595
                            }
596
                            break;
597
                        }
598
                    default:
599
                        if( StringUtils.isNumeric(option) && this.size == 0 ) {
600
                          try {
601
                              this.size = Integer.parseInt(option);
602
                          } catch (Exception ex) {
603
                              LOGGER.warn("Ignore incorrect field size for field " + value + " in '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
604
                          }
605
                          break;
606
                        }
607
                        LOGGER.warn("Illegal argumente '"+option+"' for field '"+this.name+"' in '"+getProviderName()+"' file '" + getFullFileName() + "' ("+value+").");
608
                }
609
            }
610
            return true;
611
        }
612

    
613
    }
614

    
615
    private EditableFeatureType getFeatureType(String headers[], AutomaticDetectionOfTypes.DetectedValue automaticTypes[]) {
616
        EditableFeatureType fType = getStoreServices().createFeatureType(this.getName());
617
        fType.setHasOID(true);
618

    
619
        FieldTypeParser[] fieldTypes = new FieldTypeParser[headers.length];
620
        //
621
        // Calculamos cuales pueden ser los tipos de datos
622
        //
623
        for (int i = 0; i < fieldTypes.length; i++) {
624
            fieldTypes[i] = new FieldTypeParser(getProviderName(), getFullFileName());
625
        }
626

    
627
        // Asuminos los tipos pasados por parametro, que se supone
628
        // son los detectados automaticamente.
629
        if (automaticTypes != null) {
630
            for (int i = 0; i < fieldTypes.length && i < automaticTypes.length; i++) {
631
                fieldTypes[i].detectedValue = automaticTypes[i];
632
                fieldTypes[i].type = automaticTypes[i].getType();
633
            }
634
        }
635
        // Luego probamos con lo que diga las cabezeras del CVS, sobreescribiendo
636
        // los tipos anteriores en caso de definirse en la cabezara.
637
        for (int i = 0; i < fieldTypes.length; i++) {
638
            if (!fieldTypes[i].parse(headers[i])) {
639
                continue;
640
            }
641

    
642
        }
643

    
644
        // Y por ultimo hacemos caso a lo que se haya especificado en los parametros
645
        // de apertura del CSV, teniendo esto prioridad sobre todo.
646
        String param_types_def = CSVStoreParameters.getRawFieldTypes(this.getParameters());
647
        if (StringUtils.isNotBlank(param_types_def)) {
648
            String sep = CSVStoreParameters.getDelimiter(param_types_def);
649
            if (StringUtils.isNotBlank(sep)) {
650
                String[] param_types = param_types_def.split(sep);
651
                FieldTypeParser parser = new FieldTypeParser(getProviderName(), getFullFileName());
652
                for (String param_type : param_types) {
653
                    parser.clear();
654
                    parser.parse(param_type);
655
                    for (FieldTypeParser fieldType : fieldTypes) {
656
                        if (StringUtils.equalsIgnoreCase(fieldType.name, parser.name)) {
657
                            fieldType.copyFrom(parser);
658
                            break;
659
                        }
660
                    }
661
                }
662
            }
663
        }
664
        //
665
        // Una vez ya sabemos los tipos de datos rellenamos el feature-type
666
        //
667
        for (FieldTypeParser fieldType : fieldTypes) {
668
            EditableFeatureAttributeDescriptor fad = fType.add(fieldType.name, fieldType.type);
669
            if( fieldType.detectedValue!=null ) {
670
                fad.setDisplaySize(Math.max(fieldType.detectedValue.getDisplaySize(), fieldType.size));
671
                fad.setSize(Math.max(fieldType.detectedValue.getDisplaySize(), fieldType.size));
672
                if( fad.getPrecision()<fieldType.detectedValue.getPrecision() ) {
673
                    fad.setPrecision(fieldType.detectedValue.getPrecision());
674
                }
675
                if( fad.getScale()<fieldType.detectedValue.getScale()) {
676
                    fad.setScale(fieldType.detectedValue.getScale());
677
                }
678
            } else {
679
                fad.setDisplaySize(fieldType.size);
680
            }
681
            if (fieldType.type == DataTypes.GEOMETRY ) {
682
                fad.setGeometryType(fieldType.geomType, fieldType.geomSubtype);
683
                if( fType.getDefaultGeometryAttributeName() == null ) {
684
                    fType.setDefaultGeometryAttributeName(fieldType.name);
685
                }
686
            }
687
            Locale locale = CSVStoreParameters.getLocale(this.getParameters());
688
            fad.setLocale(locale);
689
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
690
                try {
691
                    switch(entry.getKey().toLowerCase()) {
692
                        case "expression":
693
                            // Los campos calculados los procesamos en una segunda
694
                            // pasada, cuando ya estan definidos el resto de los campos
695
                            // ya que pueden requerir campos que aun no se han definido.
696
                            break;
697
                        default:
698
                                fad.set(entry.getKey(), entry.getValue());
699
                            }
700
                } catch (Exception ex) {
701
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' of '"+fad.getName()+"'.", ex);
702
                }
703
            }            
704
            Tags tags = fad.getTags();
705
            for (Map.Entry<String, String> entry : fieldType.tags.entrySet()) {
706
                tags.set(entry.getKey(), entry.getValue());
707
            }
708
        }
709
        // Processamos ahora los campos calculados
710
        for (FieldTypeParser fieldType : fieldTypes) {
711
            EditableFeatureAttributeDescriptor fad = fType.getEditableAttributeDescriptor(fieldType.name);
712
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
713
                try {
714
                    switch(entry.getKey().toLowerCase()) {
715
                        case "expression":
716
                            fad.set(entry.getKey(), entry.getValue());
717
                            break;
718
                    }
719
                } catch (Exception ex) {
720
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' in '"+fad.getName()+"' of '"+getFullFileName()+"'.", ex);
721
                }
722
            }
723
        }
724
        String[] pointDimensionNames = CSVStoreParameters.getPointDimensionNames(this.getParameters());
725
        if ( pointDimensionNames != null ) {
726
            PointAttributeEmulator emulator = new PointAttributeEmulator(pointDimensionNames);
727
            String columnName = CSVStoreParameters.getPointColumnName(this.getParameters());
728
            if( StringUtils.isBlank(columnName) ) {
729
                columnName = "the_geom";
730
            }
731
            EditableFeatureAttributeDescriptor attr = fType.add(columnName, DataTypes.GEOMETRY, emulator);
732
            GeometryManager geommgr = GeometryLocator.getGeometryManager();
733
            GeometryType gt;
734
            try {
735
                if ( emulator.fieldNames != null && emulator.fieldNames.length <= 2 ) {
736
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
737
                } else {
738
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
739
                }
740
                attr.setGeometryType(gt);
741
            } catch (Exception e) {
742
                LOGGER.warn("Can't set geometry type for the calculated field in '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
743
            }
744
        }        
745
        
746
        String geometry_column = CSVStoreParameters.getGeometryColumn(this.getParameters());
747
        if (!StringUtils.isEmpty(geometry_column)) {
748
            EditableFeatureAttributeDescriptor attr = (EditableFeatureAttributeDescriptor) fType.get(geometry_column);
749
            if (attr != null && attr.getType() != DataTypes.GEOMETRY) {
750
                attr.setDataType(DataTypes.GEOMETRY);
751
                GeometryManager geommgr = GeometryLocator.getGeometryManager();
752
                GeometryType gt;
753
                try {
754
                    gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.UNKNOWN);
755
                    attr.setGeometryType(gt);
756
                } catch (Exception e) {
757
                    LOGGER.warn("Can't set geometry type for the calculated field in CSV file '" + getFullFileName() + "'.", e);
758
                }
759
                fType.setDefaultGeometryAttributeName(geometry_column);
760
            }
761
        }
762
        return fType;
763
    }
764

    
765
    static class PointAttributeEmulator implements FeatureAttributeEmulator {
766

    
767
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
768

    
769
        private static final int XNAME = 0;
770
        private static final int YNAME = 1;
771
        private static final int ZNAME = 2;
772

    
773
        private final GeometryManager geommgr;
774
        private final String[] fieldNames;
775
        private final Coercion toDouble;
776
        private final DataType dataType;
777
        private int errorcount = 0;
778

    
779
        public PointAttributeEmulator(String[] pointDimensionNames) {
780
            if (pointDimensionNames.length > 2) {
781
                this.fieldNames = new String[3];
782
                this.fieldNames[ZNAME] = pointDimensionNames[2];
783
            } else {
784
                this.fieldNames = new String[2];
785
            }
786
            this.fieldNames[XNAME] = pointDimensionNames[0];
787
            this.fieldNames[YNAME] = pointDimensionNames[1];
788
            this.geommgr = GeometryLocator.getGeometryManager();
789
            DataTypesManager datatypeManager = ToolsLocator.getDataTypesManager();
790

    
791
            this.toDouble = datatypeManager.getCoercion(DataTypes.DOUBLE);
792
            this.dataType = datatypeManager.get(DataTypes.GEOMETRY);
793
        }
794

    
795
        @Override
796
        public Object get(Feature feature) {
797
            try {
798
                Object valueX = feature.get(this.fieldNames[XNAME]);
799
                valueX = toDouble.coerce(valueX);
800
                if (valueX == null) {
801
                    return null;
802
                }
803
                Object valueY = feature.get(this.fieldNames[YNAME]);
804
                valueY = toDouble.coerce(valueY);
805
                if (valueY == null) {
806
                    return null;
807
                }
808
                Object valueZ = null;
809
                if (this.fieldNames.length > 2) {
810
                    valueZ = toDouble.coerce(feature.get(this.fieldNames[ZNAME]));
811
                    if (valueZ == null) {
812
                        return null;
813
                    }
814
                }
815

    
816
                double x = ((Double) valueX);
817
                double y = ((Double) valueY);
818
                Point point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
819
                if (this.fieldNames.length > 2) {
820
                    double z = ((Double) valueZ);
821
                    point.setCoordinateAt(2, z);
822
                }
823
                return point;
824
            } catch (Exception ex) {
825
                if (++errorcount < 5) {
826
                    logger.warn("[" + errorcount + "] Can't create point in CSV provider. XNAME='"
827
                            + this.fieldNames[XNAME] + "', YNAME='" + this.fieldNames[XNAME] + "' feature=" + feature.toString(), ex);
828
                }
829
                return null;
830
            }
831
        }
832

    
833
        public void set(EditableFeature feature, Object value) {
834
            if (value == null) {
835
                return;
836
            }
837
            Point point = null;
838
            if (value instanceof MultiPoint) {
839
                point = (Point) ((MultiPoint) value).getPrimitiveAt(0);
840
            } else {
841
                point = (Point) value;
842
            }
843
            feature.set(this.fieldNames[XNAME], point.getX());
844
            feature.set(this.fieldNames[YNAME], point.getY());
845
            if (this.fieldNames.length > 2) {
846
                feature.set(this.fieldNames[ZNAME], point.getCoordinateAt(2));
847
            }
848
        }
849

    
850
        @Override
851
        public boolean allowSetting() {
852
            return true;
853
        }
854

    
855
        @Override
856
        public String[] getRequiredFieldNames() {
857
            return this.fieldNames;
858
        }
859

    
860
    }
861

    
862
    static class ToPointEvaluaror extends AbstractEvaluator {
863

    
864
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
865

    
866
        private GeometryManager geommgr = null;
867
        private String xname = null;
868
        private String yname = null;
869
        private String zname = null;
870
        private Coercion toDouble;
871
        private int errorcount = 0;
872

    
873
        ToPointEvaluaror(String[] pointDimensionNames) {
874
            this.xname = pointDimensionNames[0];
875
            this.yname = pointDimensionNames[1];
876
            if (pointDimensionNames.length > 2) {
877
                this.zname = pointDimensionNames[2];
878
            }
879
            this.geommgr = GeometryLocator.getGeometryManager();
880
            this.toDouble = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.DOUBLE);
881
        }
882

    
883
        @Override
884
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
885
            try {
886
                double x = ((Double) toDouble.coerce(data.getDataValue(xname))).doubleValue();
887
                double y = ((Double) toDouble.coerce(data.getDataValue(yname))).doubleValue();
888
                Point point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
889
                if (zname != null) {
890
                    double z = ((Double) toDouble.coerce(data.getDataValue(zname))).doubleValue();
891
                    point.setCoordinateAt(2, z);
892
                }
893
                return point;
894
            } catch (Exception ex) {
895
                if (++errorcount < 5) {
896
                    logger.warn("[" + errorcount + "] Can't create point in CSV provider. XNAME='"
897
                            + xname + "', YNAME='" + yname + "', ZNAME='" + zname + "', data=" + data.toString());
898
                }
899
                return null;
900
            }
901
        }
902

    
903
        @Override
904
        public String getName() {
905
            return "ToPointEvaluaror";
906
        }
907

    
908
    }
909

    
910
    private SimpleReader getSimpleReader(InputStreamReader in) {
911
        SimpleReader reader;
912
        if (CSVStoreParameters.getRawFieldsDefinition(getCSVParameters()) != null) {
913
            reader = new FixedLenReader(in, getCSVParameters());
914
        } else {
915
            reader = new CSVReader(in, getCSVParameters());
916
        }
917
        return reader;
918
    }
919

    
920
    private String getFixedHeader(int column) {
921
        char[] header = new char[3];
922

    
923
        String s = String.format("%03d", column);
924
        header[0] = (char) (s.charAt(0) + 17);
925
        header[1] = (char) (s.charAt(1) + 17);
926
        header[2] = (char) (s.charAt(2) + 17);
927
        return String.valueOf(header);
928
    }
929

    
930
    private String[] getFixedHeaders(int count) {
931
        String[] headers = new String[count];
932
        for (int i = 0; i < headers.length; i++) {
933
            headers[i] = getFixedHeader(i);
934
        }
935
        return headers;
936
    }
937

    
938
    private InputStreamReader openFile(File f, String charsetName) throws FileNotFoundException {
939
        Charset charset = Charset.defaultCharset();
940
        FileInputStream fis = new FileInputStream(f);
941
        if (!StringUtils.isEmpty(charsetName)) {
942
            if (Charset.isSupported(charsetName)) {
943
                try {
944
                    charset = Charset.forName(charsetName);
945
                } catch (Throwable th) {
946
                    LOGGER.warn("Can't use charset '" + charsetName + "' for read csv '" + this.getFullFileName() + "'.", th);
947
                }
948
            } else {
949
                LOGGER.warn("charset '" + charsetName + "' not supported for read csv '" + this.getFullFileName() + "'.");
950
            }
951
        }
952
        InputStreamReader isr = new InputStreamReader(fis, charset);
953
        return isr;
954
    }
955

    
956
    private void loadFeatures() {
957
        InputStreamReader in = null;
958
        SimpleReader reader = null;
959
        try {
960
            String headers[] = null;
961
            FeatureStoreProviderServices store = this.getStoreServices();
962

    
963
            boolean ignore_errors = CSVStoreParameters.getIgnoreErrors(getCSVParameters());
964

    
965
            in = openFile(
966
                    this.getCSVParameters().getFile(),
967
                    CSVStoreParameters.getCharset(this.getCSVParameters())
968
            );
969

    
970
            reader = getSimpleReader(in);
971

    
972
            headers = CSVStoreParameters.getHeaders(getCSVParameters());
973
            if (headers == null) {
974
                if (CSVStoreParameters.isFirstLineHeader(getCSVParameters())) {
975
                    headers = reader.getHeader();
976
                    if (headers == null) {
977
                        if (CSVStoreParameters.getIgnoreErrors(getCSVParameters())) {
978
                            headers = getFixedHeaders(reader.getColumnsCount());
979
                        } else {
980
                            String msg = "Can't retrieve header from csv file '"
981
                                    + this.getCSVParameters().getFile()
982
                                            .getAbsolutePath()
983
                                    + "' and not specified in the parameters.";
984
                            LOGGER.warn(msg);
985
                            throw new RuntimeException(msg);
986
                        }
987
                    }
988
                } else {
989
                    headers = getFixedHeaders(reader.getColumnsCount());
990
                }
991
            } else {
992
                if (CSVStoreParameters.isFirstLineHeader(getCSVParameters())) {
993
                    reader.getHeader(); // Skip and ignore the header of file
994
                }
995
            }
996

    
997
            AutomaticDetectionOfTypes.DetectedValue[] detectedTypes = automaticDetectionOfTypes(headers);
998
            if (detectedTypes != null && detectedTypes.length > headers.length) {
999
                // Se han detectado mas columnas que las que hay en la cabezera,
1000
                // a?adimos mas columnas a la cabezera.
1001
                String[] headers2 = new String[detectedTypes.length];
1002
                for (int i = 0; i < headers2.length; i++) {
1003
                    if (i < headers.length) {
1004
                        headers2[i] = headers[i];
1005
                    } else {
1006
                        headers2[i] = getFixedHeader(i);
1007
                    }
1008
                }
1009
                headers = headers2;
1010
            }
1011
            for (int i = 0; i < headers.length; i++) {
1012
                if (StringUtils.isEmpty(headers[i])) {
1013
                    headers[i] = getFixedHeader(i);
1014
                }
1015
            }
1016
            // Initialize the feature types
1017
            EditableFeatureType edftype = this.getFeatureType(headers, detectedTypes);
1018
            FeatureType ftype = edftype.getNotEditableCopy();
1019
            this.setFeatureType(ftype);
1020

    
1021
            Coercion coercion[] = new Coercion[ftype.size()];
1022
            CoercionContext coercionContext[] = new CoercionContext[ftype.size()];
1023
            int sizes[] = new int[ftype.size()];
1024
            for (int i = 0; i < ftype.size(); i++) {
1025
                sizes[i] = -1;
1026
                FeatureAttributeDescriptor ad = ftype.getAttributeDescriptor(i);
1027
                coercion[i] = ad.getCoercion();
1028
                coercionContext[i] = ad.getCoercionContext();
1029
                switch (ad.getDataType().getType()) {
1030
                    case DataTypes.INT:
1031
                        sizes[i] = 0;
1032
                        break;
1033
                    case DataTypes.LONG:
1034
                        sizes[i] = 0;
1035
                        break;
1036
                    case DataTypes.STRING:
1037
                        if (ad.getSize() == 0) {
1038
                            // Es un string y no tiene un size asignado.
1039
                            // Lo ponemos a cero para calcularlo.
1040
                            sizes[i] = 0;
1041
                        }
1042
                        break;
1043
                    case DataTypes.URL:
1044
                    case DataTypes.URI:
1045
                    case DataTypes.FILE:
1046
                        sizes[i] = 0;
1047
                }
1048
            }
1049
            if (ftype.getDefaultGeometryAttributeName() != null) {
1050
                this.need_calculate_envelope = true;
1051
            }
1052

    
1053
            Locale locale = CSVStoreParameters.getLocale(getCSVParameters());
1054
            taskStatus.message("_loading");
1055
            int count = 0;
1056

    
1057
            int count_errors = 0;
1058

    
1059
            List<String> row = reader.read();
1060

    
1061
            int skipLines = CSVStoreParameters.getSkipLines(getCSVParameters());
1062
            if (skipLines > 0) {
1063
                row = reader.skip(skipLines);
1064
            }
1065
            int limit = CSVStoreParameters.getLimit(getCSVParameters());
1066
            while (row != null) {
1067
                taskStatus.setCurValue(++count);
1068
                FeatureProvider feature = this.createFeatureProvider(ftype);
1069
                for (int i = 0; i < row.size(); i++) {
1070
                    Object rawvalue = row.get(i);
1071
                    try {
1072
                        Object value = null;
1073
                        if( coercion[i]!=null ) {
1074
                          value = coercion[i].coerce(rawvalue, coercionContext[i]);
1075
                        }
1076
                        feature.set(i, value);
1077
                        if (sizes[i] >= 0
1078
                                && (value instanceof String || value instanceof URL
1079
                                || value instanceof URI || value instanceof File)) {
1080
                            int x = value.toString().length();
1081
                            if (sizes[i] < x) {
1082
                                sizes[i] = x;
1083
                            }
1084
                        }
1085
                    } catch (Exception ex) {
1086
                        if (!ignore_errors) {
1087
                            throw ex;
1088
                        }
1089
                        if (count_errors++ < 10) {
1090
                            LOGGER.warn("Can't load value of attribute " + i + " in row " + count + ".", ex);
1091
                        }
1092
                        if (count_errors == 10) {
1093
                            LOGGER.info("Too many errors, suppress messages.");
1094
                        }
1095
                    }
1096
                }
1097
                this.addFeatureProvider(feature);
1098
                if (limit > 0) {
1099
                    if (limit < this.data.size()) {
1100
                        break;
1101
                    }
1102
                }
1103
                row = reader.read();
1104
            }
1105
            for (int i = 0; i < ftype.size(); i++) {
1106
                if (sizes[i] > 0) {
1107
                    EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
1108
                    efad.setSize(sizes[i]);
1109
                }
1110
            }
1111
            // Volvemos a asignar al store el featuretype, ya que puede
1112
            // haber cambiado.
1113
            ftype = edftype.getNotEditableCopy();
1114
            this.setFeatureType(ftype);
1115
            
1116

    
1117
            taskStatus.terminate();
1118
        } catch (Throwable ex) {
1119
            int lineno = 0;
1120
            if (reader != null) {
1121
                lineno = reader.getLine();
1122
            }
1123
            throw new RuntimeException("Problems reading file '" + getFullFileName() + "' near line " + lineno + ".", ex);
1124

    
1125
        } finally {
1126
            if (reader != null) {
1127
                try {
1128
                    reader.close();
1129
                } catch (Exception ex) {
1130
                    // Do nothing
1131
                }
1132
                reader = null;
1133
            }
1134
            if (in != null) {
1135
                try {
1136
                    in.close();
1137
                } catch (Exception ex) {
1138
                    // Do nothing
1139
                }
1140
                in = null;
1141
            }
1142
        }
1143
    }
1144

    
1145
    private AutomaticDetectionOfTypes.DetectedValue[] automaticDetectionOfTypes(String[] headers) throws IOException {
1146
        boolean automatic_types_detection = CSVStoreParameters.getAutomaticTypesDetection(getCSVParameters());
1147
        if (!automatic_types_detection) {
1148
            return null;
1149
        }
1150
        AutomaticDetectionOfTypes.DetectedValue[] types = null;
1151

    
1152
        FileReader in = null;
1153
        SimpleReader reader = null;
1154

    
1155
        try {
1156
            in = new FileReader(this.getCSVParameters().getFile());
1157
            reader = getSimpleReader(in);
1158
            AutomaticDetectionOfTypes x = new AutomaticDetectionOfTypes(
1159
                    this.getFullFileName()
1160
            );
1161
            types = x.detect(
1162
                    headers.length,
1163
                    reader,
1164
                    CSVStoreParameters.isFirstLineHeader(getCSVParameters()),
1165
                    CSVStoreParameters.getLocale(getCSVParameters())
1166
            );
1167
        } catch (Exception ex) {
1168
            int lineno = 0;
1169
            if (reader != null) {
1170
                lineno = reader.getLine();
1171
            }
1172
            throw new RuntimeException("Problems reading file '" + getFullFileName() + "' near line " + lineno + ".", ex);
1173

    
1174
        } finally {
1175
            IOUtils.closeQuietly(reader);
1176
            IOUtils.closeQuietly(in);
1177
        }
1178
        return types;
1179
    }
1180
    
1181
    @Override
1182
    public void fixFeatureTypeFromParameters() {
1183
        String param_types_def = CSVStoreParameters.getRawFieldTypes(this.getParameters());
1184
        String[] pointDimensionNames = CSVStoreParameters.getPointDimensionNames(this.getParameters());
1185
        String geometry_column = CSVStoreParameters.getGeometryColumn(this.getParameters());
1186
        if (StringUtils.isNotBlank(param_types_def) || 
1187
                pointDimensionNames != null || 
1188
                !StringUtils.isBlank(geometry_column)) {
1189
            this.setFeatureType(featureType);
1190
        }
1191
    }
1192
    
1193
    private void setFeatureType(FeatureType ftype) {
1194
        FeatureStoreProviderServices store = this.getStoreServices();
1195
        List<FeatureType> ftypes = new ArrayList<>();
1196
        ftypes.add(ftype);
1197
        this.featureType = ftype;
1198
        store.setFeatureTypes(ftypes, ftype);
1199
    }
1200

    
1201
}