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

History | View | Annotate | Download (42.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 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.Closeable;
26
import java.io.File;
27
import java.io.IOException;
28
import java.io.InputStreamReader;
29
import java.net.URI;
30
import java.net.URL;
31
import java.nio.charset.StandardCharsets;
32
import java.util.ArrayList;
33
import java.util.HashMap;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.Objects;
37
import org.apache.commons.io.FileUtils;
38

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

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

    
114
    private static final Logger LOGGER = LoggerFactory.getLogger(CSVStoreProvider.class);
115

    
116
    public static final String NAME = DataStore.CSV_PROVIDER_NAME;
117
    public static final String DESCRIPTION = "CSV file";
118

    
119
    public static final String METADATA_DEFINITION_NAME = NAME;
120

    
121
    private final ResourceProvider resource;
122

    
123
    private long counterNewsOIDs = 0;
124
    private Envelope envelope;
125
    private boolean need_calculate_envelope = false;
126
    private final SimpleTaskStatus taskStatus;
127
    private final CSVFeatureWriter writer;
128
    private FeatureType featureType;
129
    private GetItemWithSize64<List<String>> virtualrows;
130
    private RowToFeatureTranslator rowToFeatureTranslator;
131
    private SpatialIndex spatialIndex;
132
    
133
    @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"})
134
    public CSVStoreProvider(
135
            CSVStoreParameters parameters,
136
            DataStoreProviderServices storeServices
137
        ) throws InitializeException {
138
        super(
139
                parameters,
140
                storeServices,
141
                FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME)
142
        );
143
        this.writer = new CSVFeatureWriter();
144
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
145
        this.taskStatus = manager.createDefaultSimpleTaskStatus("CSV");
146
        this.taskStatus.setAutoremove(true);
147

    
148
        counterNewsOIDs = 0;
149

    
150
        File file = getCSVParameters().getFile();
151
        resource = this.createResource(
152
                FileResource.NAME,
153
                new Object[]{file.getAbsolutePath()}
154
        );
155

    
156
        resource.addConsumer(this);
157

    
158
        initializeFeatureTypes();
159
    }
160

    
161
    private CSVStoreParameters getCSVParameters() {
162
        return (CSVStoreParameters) this.getParameters();
163
    }
164

    
165
    @Override
166
    public String getProviderName() {
167
        return NAME;
168
    }
169

    
170
    @Override
171
    public boolean allowWrite() {
172
        return true;
173
    }
174

    
175
    private String getFullFileName() {
176
        // Usar solo para mostrar mensajes en el logger.
177
        String s;
178
        try {
179
            s = getCSVParameters().getFile().getAbsolutePath();
180
        } catch (Exception e2) {
181
            s = "(unknow)";
182
        }
183
        return s;
184
    }
185

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

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

    
218
    }
219

    
220
    @Override
221
    @SuppressWarnings("Convert2Lambda")
222
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
223

    
224
        try {
225
            I18nManager i18n = ToolsLocator.getI18nManager();
226
            this.taskStatus.add();
227
            taskStatus.message(i18n.getTranslation("_Preparing_writing"));
228
            this.getResource().closeRequest();
229
            getResource().execute(new ResourceAction() {
230
                @Override
231
                public Object run() throws Exception {
232
                    FeatureSet features = null;
233
                    DisposableIterator it = null;
234
                    try {
235
                        File file = (File) resource.get();
236
                        features = getStoreServices().getFeatureStore().getFeatureSet();
237

    
238
                        taskStatus.message(i18n.getTranslation("_Writing_records"));
239
                        if (virtualrows == null) {
240
                            List<FeatureProvider> newdata = new ArrayList<>();
241
                            FeatureType ftype = getStoreServices().getDefaultFeatureType();
242
                            writer.initialize(getCSVParameters(), file, ftype, getCSVPreferences());
243
                            writer.begin();
244
                            it = features.fastIterator();
245
                            taskStatus.setRangeOfValues(0, features.getSize());
246
                            taskStatus.setCurValue(0);
247
                            while (it.hasNext()) {
248
                                taskStatus.incrementCurrentValue();
249
                                FeatureProvider feature = getStoreServices().getFeatureProviderFromFeature(
250
                                        (org.gvsig.fmap.dal.feature.Feature) it.next());
251
                                writer.add(feature);
252
                                if (feature.getOID() == null) {
253
                                    LOGGER.warn("feature without OID");
254
                                    feature.setOID(createNewOID());
255
                                }
256
                                newdata.add(feature);
257
                            }
258
                            data = newdata;
259
                            if (writer.getEnvelope() != null) {
260
                                envelope = writer.getEnvelope();
261
                            } else {
262
                                envelope = null;
263
                            }
264
                            resource.notifyChanges();
265
                            writer.end();
266
                            bboxFileSave(envelope);
267
                        } else {
268

    
269
                            CSVStoreParameters csvParams = getCSVParameters();
270
                            CSVStoreParameters tmpParams = (CSVStoreParameters) csvParams.getCopy();
271

    
272
                            String tmpBase = File.createTempFile("tmp_" + System.currentTimeMillis(), null).getAbsolutePath();
273
                            File tmpFile = new File(tmpBase + ".csv");
274
                            tmpParams.setFile(tmpFile);
275

    
276
                            FeatureType ftype = getStoreServices().getDefaultFeatureType();
277
                            writer.initialize(tmpParams, tmpFile, ftype, getCSVPreferences(),spatialIndex);
278
                            writer.begin();
279
                            it = features.fastIterator();
280
                            taskStatus.setIndeterminate();
281
                            taskStatus.setCurValue(0);
282
                            while (it.hasNext()) {
283
                                taskStatus.incrementCurrentValue();
284
                                if( taskStatus.isCancellationRequested() ) {
285
                                    taskStatus.cancel();
286
                                    LOGGER.info("CSV writing canceled ("+getFullFileName()+")");
287
                                    break;
288
                                }
289
                                FeatureProvider feature = getStoreServices().getFeatureProviderFromFeature(
290
                                        (org.gvsig.fmap.dal.feature.Feature) it.next());
291
                                writer.add(feature);
292
                                if (feature.getOID() == null) {
293
                                    LOGGER.warn("feature without OID");
294
                                    feature.setOID(createNewOID());
295
                                }
296
                            }
297
                            if (writer.getEnvelope() != null) {
298
                                envelope = writer.getEnvelope();
299
                            } else {
300
                                envelope = null;
301
                            }
302
                            resource.notifyChanges();
303
                            writer.end();
304
                            if( !taskStatus.isCancelled() ) {
305
                                if (!csvParams.getFile().delete()) {
306
                                    LOGGER.debug("Can't delete csv file '" + csvParams.getFile() + "'.");
307
                                    throw new IOException("Can't delete csv '"
308
                                            + FilenameUtils.getBaseName(csvParams.getFile().getAbsolutePath())
309
                                            + "' file to replace with the new csv.\nThe new csv is in temporary file '" + tmpFile.getAbsolutePath()
310
                                            + "'");
311
                                }
312

    
313
                                File csvFile = csvParams.getFile();
314
                                FileUtils.moveFile(tmpParams.getFile(), csvFile);
315
                                FileUtils.delete(new File(FilenameUtils.removeExtension(csvFile.getAbsolutePath())+".idx"));
316

    
317
                                bboxFileSave(envelope);
318

    
319
                                loadFeatures();
320
                            }
321
                        }
322
                    } finally {
323
                        if (it != null) {
324
                            it.dispose();
325
                        }
326
                        if (features != null) {
327
                            features.dispose();
328
                        }
329
                    }
330
                    return null;
331
                }
332

    
333
            });
334
            this.taskStatus.terminate();
335
        } catch (Exception e) {
336
            this.taskStatus.abort();
337
            throw new PerformEditingException(getResource().toString(), e);
338
        }
339
    }
340

    
341
    private CsvPreference getCSVPreferences() {
342
        CSVReaderSuperCSV reader = new CSVReaderSuperCSV(getCSVParameters());
343
        return reader.getCSVPreferences();
344
    }
345

    
346
    @Override
347
    public boolean closeResourceRequested(ResourceProvider resource) {
348
        return true;
349
    }
350

    
351
    @Override
352
    public int getOIDType() {
353
        return DataTypes.LONG;
354
    }
355

    
356
    @Override
357
    public boolean supportsAppendMode() {
358
        return true;
359
    }
360

    
361
    @Override
362
    @SuppressWarnings("Convert2Lambda")
363
    public void append(final FeatureProvider featureProvider) {
364
        //todo
365
        getResource().execute(new ResourceAction() {
366
            @Override
367
            public Object run() throws Exception {
368
                //writer.append(getStoreServices().createFeature(featureProvider));
369
                writer.add(featureProvider);
370
                return null;
371
            }
372
        });
373
    }
374

    
375
    @Override
376
    @SuppressWarnings("Convert2Lambda")
377
    public void beginAppend() throws DataException {
378
        getResource().execute(new ResourceAction() {
379
            @Override
380
            public Object run() throws Exception {
381
                writer.initialize(
382
                        getCSVParameters(),
383
                        getCSVParameters().getFile(),
384
                        getFeatureStore().getDefaultFeatureType(),
385
                        getCSVPreferences()
386
                );
387
                writer.beginAppend();
388
                return null;
389
            }
390
        });
391

    
392
    }
393

    
394
    @Override
395
    @SuppressWarnings("Convert2Lambda")
396
    public void endAppend() {
397
        try {
398
            getResource().execute(new ResourceAction() {
399
                @Override
400
                public Object run() throws Exception {
401
                    writer.end();
402
                    resource.notifyChanges(); //resourcesNotifyChanges();
403
                    counterNewsOIDs = -1;
404
                    return null;
405
                }
406
            });
407
            if (writer.getEnvelope() != null) {
408
                envelope = writer.getEnvelope();
409
            } else {
410
                envelope = null;
411
            }
412
            writer.end();
413
            this.close();
414
            bboxFileSave(envelope);            
415
            
416
        } catch (Exception ex) {
417
            LOGGER.warn("Not been able to end append '" + this.getFullName() + "'.", ex);
418
        }
419
    }
420

    
421
    public void saveToState(PersistentState state) throws PersistenceException {
422
        throw new NotYetImplemented();
423
    }
424

    
425
    public void loadFromState(PersistentState state) throws PersistenceException {
426
        throw new NotYetImplemented();
427
    }
428

    
429
    @Override
430
    public Object createNewOID() {
431
        return counterNewsOIDs++;
432
    }
433

    
434
    protected void initializeFeatureTypes() throws InitializeException {
435
        try {
436
            this.open();
437
        } catch (OpenException e) {
438
            throw new InitializeException(this.getProviderName(), e);
439
        }
440
    }
441

    
442
    @Override
443
    @SuppressWarnings("Convert2Lambda")
444
    public Envelope getEnvelope() throws DataException {
445
        this.open();
446
        if (this.envelope != null) {
447
            return this.envelope;
448
        }
449
        this.envelope = bboxFileLoad();
450
        if (this.envelope != null) {
451
            return this.envelope;
452
        }
453
        if (!this.need_calculate_envelope) {
454
            return null;
455
        }
456
        try {
457
            I18nManager i18n = ToolsLocator.getI18nManager();
458
            this.taskStatus.add();
459
            this.taskStatus.message(i18n.getTranslation("_Calculating_envelope"));
460
            FeatureStore fs = this.getFeatureStore();
461
            FeatureType ft = fs.getDefaultFeatureType();
462
            FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
463
            this.taskStatus.setRangeOfValues(0, fs.getFeatureCount());
464
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
465
            fs.accept(new Visitor() {
466
                @Override
467
                public void visit(Object obj) throws VisitCanceledException, BaseException {
468
                    taskStatus.incrementCurrentValue();
469
                    if(taskStatus.isCancellationRequested()){
470
                        taskStatus.cancel();
471
                        throw new VisitCanceledException();
472
                    }
473
                    Feature f = (Feature) obj;
474
                    Geometry geom = f.getDefaultGeometry();
475
                    if (geom != null) {
476
                        try {
477
                            Envelope env = geom.getEnvelope();
478
                            envelope.add(env);
479
                        } catch(Exception ex) {
480
                            LOGGER.warn("Can't calculate envelop of geometry in feature '"+Objects.toString(f.getReference())+"'.",ex);
481
                        }
482
                    }
483
                }
484
            });
485
            bboxFileSave(envelope);
486
            taskStatus.terminate();
487
        } catch (VisitCanceledException e) {
488
            return null;
489
        } catch (BaseException e) {
490
            taskStatus.abort();
491
            LOGGER.warn("Can't calculate the envelope of CSV file '" + this.getFullName() + "'.", e);
492
            return null;
493
        }
494

    
495
        this.need_calculate_envelope = false;
496
        return this.envelope;
497
    }
498

    
499
    @Override
500
    public Object getDynValue(String name) throws DynFieldNotFoundException {
501
        if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
502
            try {
503
                return this.getEnvelope();
504
            } catch (DataException e) {
505
                return null;
506
            }
507
        } else {
508
            if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
509
                IProjection pro = CSVStoreParameters.getCRS(this.getCSVParameters());
510
                if (pro != null) {
511
                    return pro;
512
                }
513
            }
514
        }
515
        return super.getDynValue(name);
516
    }
517

    
518
    @Override
519
    public void resourceChanged(ResourceProvider resource) {
520
        this.getStoreServices().notifyChange(
521
                DataStoreNotification.RESOURCE_CHANGED,
522
                resource);
523
    }
524

    
525
    @Override
526
    public Object getSourceId() {
527
        return this.getCSVParameters().getFile();
528
    }
529

    
530
    @Override
531
    public String getName() {
532
        String name = this.getCSVParameters().getFile().getName();
533
        return FilenameUtils.getBaseName(name);
534
    }
535

    
536
    @Override
537
    public String getFullName() {
538
        return this.getCSVParameters().getFile().getAbsolutePath();
539
    }
540

    
541
    @Override
542
    public ResourceProvider getResource() {
543
        return resource;
544
    }
545

    
546
    private boolean isEmpty(String s) {
547
        if (s == null) {
548
            return true;
549
        }
550
        return s.trim().length() == 0;
551
    }
552

    
553
    private void init(CSVStoreParameters parameters, DataStoreProviderServices storeServices) {
554
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
555
    }
556

    
557
    static class ToPointEvaluaror extends AbstractEvaluator {
558

    
559
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
560

    
561
        private GeometryManager geommgr = null;
562
        private String xname = null;
563
        private String yname = null;
564
        private String zname = null;
565
        private final Coercion toDouble;
566
        private int errorcount = 0;
567

    
568
        ToPointEvaluaror(String[] pointDimensionNames) {
569
            this.xname = pointDimensionNames[0];
570
            this.yname = pointDimensionNames[1];
571
            if (pointDimensionNames.length > 2) {
572
                this.zname = pointDimensionNames[2];
573
            }
574
            this.geommgr = GeometryLocator.getGeometryManager();
575
            this.toDouble = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.DOUBLE);
576
        }
577

    
578
        @Override
579
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
580
            try {
581
                double x = ((Double) toDouble.coerce(data.getDataValue(xname)));
582
                double y = ((Double) toDouble.coerce(data.getDataValue(yname)));
583
                Point point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
584
                if (zname != null) {
585
                    double z = ((Double) toDouble.coerce(data.getDataValue(zname)));
586
                    point.setCoordinateAt(2, z);
587
                }
588
                return point;
589
            } catch (Exception ex) {
590
                if (++errorcount < 5) {
591
                    logger.warn("[" + errorcount + "] Can't create point in CSV provider. XNAME='"
592
                            + xname + "', YNAME='" + yname + "', ZNAME='" + zname + "', data=" + data.toString());
593
                }
594
                return null;
595
            }
596
        }
597

    
598
        @Override
599
        public String getName() {
600
            return "ToPointEvaluaror";
601
        }
602

    
603
    }
604

    
605
    public static class RowToFeatureTranslator {
606

    
607
        private Coercion coercion[];
608
        private CoercionContext coercionContext[];
609
        private int sizes[];
610
        private String[] names;
611
        private final boolean ignore_errors;
612
        private long count_errors;
613
        private FeatureType csvFeatureType;
614

    
615
        public RowToFeatureTranslator(boolean ignore_errors) {
616
            this.ignore_errors = ignore_errors;
617
            this.count_errors = 0;
618
        }
619

    
620
        public int getColumnSize(int column) {
621
            return this.sizes[column];
622
        }
623

    
624
        public void initialize(FeatureType ftype) {
625
            this.csvFeatureType = ftype;
626
            int columns = this.csvFeatureType.size();
627
            this.names = new String[columns];
628
            this.coercion = new Coercion[columns];
629
            this.coercionContext = new CoercionContext[columns];
630
            this.sizes = new int[columns];
631
            int index = 0;
632
            for (int i = 0; i < this.csvFeatureType.size(); i++) {
633
                FeatureAttributeDescriptor ad = this.csvFeatureType.getAttributeDescriptor(i);
634
                names[i] = null;
635
                if( ad.isComputed() ) {
636
                    continue;
637
                }
638
                names[index] = ad.getName();
639
                coercion[index] = ad.getCoercion();
640
                coercionContext[index] = ad.getCoercionContext();
641
                sizes[index] = ad.getSize();
642
                index++;
643
            }
644
        }
645

    
646
        public void translate(long rowindex, List<String> row, FeatureProvider feature) throws Exception {
647

    
648
            feature.setOID(rowindex);
649
            for (int i = 0; i < names.length; i++) {
650
                String name = names[i];
651
                if( name == null ) {
652
                    break;
653
                }
654
                Object rawvalue = row.get(i);
655
                try {
656
                    Object value = null;
657
                    if (coercion[i] != null) {
658
                        value = coercion[i].coerce(rawvalue, coercionContext[i]);
659
                    }
660
                    int findex = feature.getType().getIndex(name);
661
                    if( findex>=0 ) {
662
                        // Ojo que puede que se este filtrando el featuretype y no 
663
                        // tenga todos los atributos, por ejemplo al pintar la vista.
664
                        feature.set(findex, value);
665
                    }
666
                    if (sizes[i] >= 0
667
                            && (value instanceof String || value instanceof URL
668
                            || value instanceof URI || value instanceof File)) {
669
                        int x = value.toString().length();
670
                        if (sizes[i] < x) {
671
                            sizes[i] = x;
672
                        }
673
                    }
674
                } catch (Exception ex) {
675
                    if (!ignore_errors) {
676
                        throw ex;
677
                    }
678
                    if (count_errors++ < 10) {
679
                        LOGGER.warn("Can't load value of attribute " + name +"/" +i+" in row " + rowindex + ".", ex);
680
                    }
681
                    if (count_errors == 10) {
682
                        LOGGER.info("Too many errors, suppress messages.");
683
                    }
684
                }
685
            }
686
        }
687
    }
688

    
689
    private void loadFeatures() {
690
        InputStreamReader in = null;
691
        SimpleReader reader = null;
692
        try {
693
            taskStatus.setTitle("CSV "+this.getName());
694
            taskStatus.add();
695
//            boolean ignore_errors = CSVStoreParameters.getIgnoreErrors(getCSVParameters());
696

    
697
            // Initialize the feature types
698
            EditableFeatureType edftype = getStoreServices().createFeatureType(this.getName());
699
            CSVUtils.loadFeatureType(getCSVParameters(), edftype, true, taskStatus);
700
            FeatureType ftype = edftype.getNotEditableCopy();
701
            this.setFeatureType(ftype);
702

    
703
            in = CSVUtils.openFile(
704
                    this.getCSVParameters().getFile(),
705
                    CSVStoreParameters.getCharset(this.getCSVParameters())
706
            );
707
            reader = CSVUtils.getSimpleReader(getCSVParameters(), in);
708
            if (CSVStoreParameters.isFirstLineHeader(getCSVParameters())) {
709
                reader.getHeader(); // Skip and ignore the header of file
710
            }
711
            this.rowToFeatureTranslator = new RowToFeatureTranslator(
712
                    CSVStoreParameters.getIgnoreErrors(getCSVParameters())
713
            );
714
            this.rowToFeatureTranslator.initialize(ftype);
715
            if (ftype.getDefaultGeometryAttributeName() != null) {
716
                this.need_calculate_envelope = true;
717
            }
718
            I18nManager i18n = ToolsLocator.getI18nManager();
719
            taskStatus.message(i18n.getTranslation("_Loading"));
720

    
721
            if(this.virtualrows != null && this.virtualrows instanceof Closeable){
722
                IOUtils.closeQuietly((Closeable) this.virtualrows);
723
                this.virtualrows = null;
724
            }
725
            
726
            this.virtualrows = ((AbstractSimpleReader) reader).getVirtualRows(this.taskStatus);
727
            if (this.virtualrows == null) {
728

    
729
                List<String> row = reader.read();
730

    
731
                int skipLines = CSVStoreParameters.getSkipLines(getCSVParameters());
732
                if (skipLines > 0) {
733
                    row = reader.skip(skipLines);
734
                }
735
                int limit = CSVStoreParameters.getLimit(getCSVParameters());
736
                while (row != null) {
737
                    taskStatus.incrementCurrentValue();
738
                    if( taskStatus.isCancellationRequested() ) {
739
                        taskStatus.cancel();
740
                        break;
741
                    }
742
                    FeatureProvider feature = this.createFeatureProvider(ftype);
743
                    this.rowToFeatureTranslator.translate(reader.getLine(), row, feature);
744

    
745
                    this.addFeatureProvider(feature);
746
                    if (limit > 0) {
747
                        if (limit < this.data.size()) {
748
                            break;
749
                        }
750
                    }
751
                    row = reader.read();
752
                }
753
                for (int i = 0; i < ftype.size(); i++) {
754
                    if (this.rowToFeatureTranslator.getColumnSize(i) > 0) {
755
//                    if (sizes[i] > 0) {
756
                        EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
757
//                        efad.setSize(sizes[i]);
758
                        efad.setSize(this.rowToFeatureTranslator.getColumnSize(i));
759
                    }
760
                }
761
                // Volvemos a asignar al store el featuretype, ya que puede
762
                // haber cambiado.
763
                ftype = edftype.getNotEditableCopy();
764
                this.setFeatureType(ftype);
765
            } else {
766
                this.loadOrCreateSpatialIndex();
767
            }
768
            if( taskStatus.isRunning() ) {
769
                taskStatus.terminate();
770
            }
771
        } catch (Throwable ex) {
772
            taskStatus.abort();
773
            int lineno = 0;
774
            if (reader != null) {
775
                lineno = reader.getLine();
776
            }
777
            throw new RuntimeException("Problems reading file '" + getFullFileName() + "' near line " + lineno + ".", ex);
778

    
779
        } finally {
780
            if (reader != null) {
781
                try {
782
                    reader.close();
783
                } catch (Exception ex) {
784
                    // Do nothing
785
                }
786
//                reader = null;
787
            }
788
            if (in != null) {
789
                try {
790
                    in.close();
791
                } catch (Exception ex) {
792
                    // Do nothing
793
                }
794
//                in = null;
795
            }
796
            taskStatus.remove();
797
        }
798
    }
799

    
800
    @Override
801
    public void fixFeatureTypeFromParameters() {
802
        if(mustFixFeatureType() && featureType != null){
803
            this.setFeatureType(featureType);
804
        }
805
    }
806
    
807
    private boolean mustFixFeatureType() {
808
        try {
809
            String param_types_def = CSVStoreParameters.getRawFieldTypes(this.getParameters());
810
            String[] pointDimensionNames = CSVStoreParameters.getPointDimensionNames(this.getParameters());
811
            String geometry_column = CSVStoreParameters.getGeometryColumn(this.getParameters());
812
            
813
            
814
            FeatureType dalFeatureType = this.getStoreServices().getDefaultFeatureType();
815
            if (StringUtils.isNotBlank(geometry_column)){
816
                FeatureAttributeDescriptor attr = dalFeatureType.getAttributeDescriptor(geometry_column);
817
                if(attr == null || attr.getType() !=  DataTypes.GEOMETRY){
818
                    return true;
819
                }
820
            }
821
            if (StringUtils.isNotBlank(param_types_def)
822
                    || pointDimensionNames != null) {
823
                for (FeatureAttributeDescriptor dalAttr : dalFeatureType) {
824
                    if(dalAttr.isComputed()){
825
                        continue;
826
                    }
827
                    FeatureAttributeDescriptor csvAttr = featureType.getAttributeDescriptor(dalAttr.getName());
828
                    if(csvAttr == null){
829
                        return true;
830
                    }
831
                    if(!dalAttr.get("all").equals(csvAttr.get("all"))){
832
                        return true;
833
                    }
834
                }
835
            }
836
        } catch (DataException ex) {
837
            LOGGER.warn("Can't check if should fix the feature type", ex);
838
        }
839
        return false;
840
    }
841

    
842
    private void setFeatureType(FeatureType ftype) {
843
        try {
844
            List<FeatureType> ftypes = new ArrayList<>();
845
            ftypes.add(ftype);
846
            this.featureType = ftype;
847
            if(this.getStoreServices().getDefaultFeatureType() == null){
848
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
849
                return;
850
            }
851
            if(mustFixFeatureType()){
852
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
853
            }
854
        } catch (DataException ex) {
855
            LOGGER.warn("Cant set feature type", ex);
856
        }
857
    }
858

    
859
    @Override
860
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType providerFeatureType, FeatureType featureType)
861
            throws DataException {
862
        this.open();
863
        if (this.virtualrows == null) {
864
            return super.createSet(query, providerFeatureType, featureType);
865
        }
866
        return new CSVSetProvider(this, query, providerFeatureType, featureType);
867
    }
868

    
869
    @Override
870
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType featureType)
871
            throws DataException {
872
        this.open();
873
        if (this.virtualrows == null) {
874
            return super.createSet(query, featureType);
875
        }
876
        return new CSVSetProvider(this, query, featureType, featureType);
877
    }
878

    
879
    public List<String> getRowByIndex(long index) {
880
        try {
881
            this.open();
882
        } catch(Exception ex) {
883
            throw new RuntimeException("Can't get row by index", ex);
884
        }
885
        if (this.virtualrows == null) {
886
            return null;
887
        }
888
        List<String> line = this.virtualrows.get64(index);
889
        if( line!=null ) {
890
            for (int i = 0; i < line.size(); i++) {
891
                String s = line.get(i);
892
                line.set(i, CSVReaderSuperCSV.unescapeCRLF(s));
893
            }
894
        }      
895
        return line;
896
    }
897

    
898
    public RowToFeatureTranslator getRowToFeatureTranslator() {
899
        if (this.rowToFeatureTranslator == null) {
900
            boolean ignore_errors = CSVStoreParameters.getIgnoreErrors(getCSVParameters());
901
            this.rowToFeatureTranslator = new RowToFeatureTranslator(ignore_errors);
902
            this.rowToFeatureTranslator.initialize(featureType);
903
        }
904
        return this.rowToFeatureTranslator;
905
    }
906

    
907
    @Override
908
    public long getFeatureCount() throws DataException {
909
        this.open();
910
        if (this.virtualrows == null) {
911
            return super.getFeatureCount();
912
        }
913
        return this.virtualrows.size64();
914
    }
915

    
916
    @Override
917
    public long getDataSize() throws DataException {
918
        this.open();
919
        if (this.virtualrows == null) {
920
            return super.getDataSize();
921
        }
922
        return this.virtualrows.size64();
923
    }
924

    
925
    @Override
926
    protected FeatureProvider internalGetFeatureProviderByReference(
927
            FeatureReferenceProviderServices reference) throws DataException {
928
        this.open();
929
        if (this.virtualrows == null) {
930
            return super.internalGetFeatureProviderByReference(reference);
931
        }
932
        int oid = ((Long) reference.getOID()).intValue();
933
        RowToFeatureTranslator translator = getRowToFeatureTranslator();
934
        FeatureProvider feature = this.createFeatureProvider(this.featureType);
935
        try {
936
            translator.translate(oid, this.virtualrows.get64(oid), feature);
937
        } catch (Exception ex) {
938
            throw new CreateFeatureException(ex, this.getName());
939
        }
940
        return feature;
941
    }
942

    
943
    @Override
944
    protected void doDispose() throws BaseException {
945
        super.doDispose();
946
        if (this.virtualrows != null && this.virtualrows instanceof Closeable) {
947
            IOUtils.closeQuietly((Closeable) this.virtualrows);
948
            this.virtualrows = null;
949
        }
950
    }
951
    
952
    @Override
953
     public void refresh() throws OpenException {
954
        try {
955
            this.close();
956
        } catch (CloseException e) {
957
            throw new OpenException(this.getProviderName(), e);
958
        }
959
        this.open();
960
    }
961

    
962
    @Override
963
    public void close() throws CloseException {
964
        super.close(); //To change body of generated methods, choose Tools | Templates.
965
        this.data = null;
966
        if(this.virtualrows != null && this.virtualrows instanceof Closeable){
967
            IOUtils.closeQuietly((Closeable) this.virtualrows);
968
            this.virtualrows = null;
969
            this.envelope = null;
970
            this.spatialIndex = null;
971
        }
972
        
973
    }
974
     
975
    
976
    private void loadOrCreateSpatialIndex() {
977
        FeatureSetProvider set = null;
978
        DisposableIterator<FeatureProvider> it = null;
979
        try {
980
            if( this.virtualrows == null ) {
981
                return;
982
            }
983
            FeatureAttributeDescriptor geomdesc = this.featureType.getDefaultGeometryAttribute();
984
            if( geomdesc == null ) {
985
                return;
986
            }
987
//            String indexTypeName = "MVRTree";
988
//            String extname = "mvtree";
989
            String indexTypeName = GeometryManager.SPATIALINDEX_DEFAULT_QUADTREE;
990
            String extname = "qtree";
991
            
992
            this.envelope = bboxFileLoad();
993
            File indexfile = this.getAuxFile(extname); 
994
            boolean createIndex = !indexfile.exists();
995

    
996
            GeometryManager geomManager = GeometryLocator.getGeometryManager();
997
            SpatialIndexFactory indexfactory = geomManager.getSpatialIndexFactory(indexTypeName);
998
            DynObject params = indexfactory.createParameters();
999
            params.setDynValue("file", indexfile);
1000
            SpatialIndex index = geomManager.createSpatialIndex(indexTypeName, params);
1001
            if( createIndex ) { 
1002
                I18nManager i18n = ToolsLocator.getI18nManager();
1003
                this.taskStatus.add();
1004
                taskStatus.message(i18n.getTranslation("_Creating_spatial_index"));
1005
                taskStatus.setRangeOfValues(0, this.virtualrows.size64());
1006
                taskStatus.setCurValue(0);
1007
                Envelope theEnvelope = geomManager.createEnvelope(Geometry.SUBTYPES.GEOM2D);
1008
                set = this.createSet(null, featureType);
1009
                it = set.fastIterator();
1010
                while( it.hasNext() ) {
1011
                    taskStatus.incrementCurrentValue();
1012
                    if( taskStatus.isCancellationRequested() ) {
1013
                        taskStatus.cancel();
1014
                        LOGGER.info("CSV spatial index creation canceled ("+getFullFileName()+")");
1015
                        break;
1016
                    }
1017
                    FeatureProvider f = it.next();
1018
                    if( f == null ) {
1019
                        continue;
1020
                    }
1021
                    Object oid = null;
1022
                    try {
1023
                        oid = f.getOID();
1024
                        Geometry geom = (Geometry) f.get(geomdesc.getName());
1025
                        index.insert(geom, oid);
1026
                        theEnvelope.add(geom);
1027
                    } catch(Exception ex) {
1028
                        LOGGER.debug("Can't insert feature '"+Objects.toString(oid)+"' in spatial index.",ex);
1029
                    }
1030
                }
1031
                taskStatus.message(i18n.getTranslation("_Saving_spatial_index"));
1032
                taskStatus.setIndeterminate();
1033
                index.flush();
1034
                bboxFileSave(theEnvelope);
1035
                taskStatus.terminate();
1036
                this.envelope = theEnvelope;
1037
            }
1038
            this.spatialIndex = index;
1039
        } catch (Exception ex) {
1040
            taskStatus.abort();
1041
            LOGGER.warn("Can't create spatial index.",ex);
1042
        } finally {
1043
            DisposeUtils.disposeQuietly(it);
1044
            DisposeUtils.disposeQuietly(set);
1045
            taskStatus.remove();
1046
        }
1047
    }
1048

    
1049
    public File getAuxFile(String extension) {
1050
        File data_file = CSVStoreParameters.getFile(this.getCSVParameters());
1051
        if (data_file == null){
1052
            return null;
1053
        }
1054
        File index_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + "." + extension);
1055
        return index_file;
1056
    }
1057

    
1058
    public SpatialIndex getSpatialIndex() {
1059
        return spatialIndex;
1060
    }
1061

    
1062
    private void bboxFileSave(Envelope envelope) {
1063
        File bboxfile = this.getAuxFile("bbox");
1064
        bboxFileSave(bboxfile, envelope);
1065
    }
1066
    
1067
    private void bboxFileSave(File bboxfile, Envelope envelope) {
1068
        if( envelope == null ) {
1069
            bboxfile.delete();
1070
            return;
1071
        }
1072
        try {
1073
            FileUtils.write(
1074
                    bboxfile, 
1075
                    envelope.getBox2D().convertToWKTQuietly(), 
1076
                    StandardCharsets.UTF_8
1077
            );
1078
        } catch(Exception ex) {
1079
            LOGGER.warn("Can't write bbox file '"+Objects.toString(bboxfile)+"'.",ex);
1080
        }
1081
    }
1082
    
1083
    private Envelope bboxFileLoad() {
1084
        File bboxfile = this.getAuxFile("bbox");
1085
        return bboxFileLoad(bboxfile);
1086
    }
1087
    
1088
    private Envelope bboxFileLoad(File bboxfile) {
1089
        try {
1090
            if( bboxfile.exists() ) {
1091
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
1092
                String wkt = FileUtils.readFileToString(bboxfile, StandardCharsets.UTF_8);                    
1093
                Geometry geom = geomManager.createFrom(wkt);
1094
                if( geom!=null ) {
1095
                    return geom.getEnvelope();
1096
                }
1097
            }
1098
        } catch(Exception ex) {
1099
            LOGGER.warn("Can't load bbox file",ex);
1100
        }
1101
        return null;
1102
    }
1103
    
1104
}