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 / simplereader / SimpleReaderStoreProvider.java @ 47634

History | View | Annotate | Download (42.6 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.simplereader;
24

    
25
import org.gvsig.fmap.dal.store.csv.*;
26
import java.io.Closeable;
27
import java.io.File;
28
import java.io.IOException;
29
import java.io.InputStreamReader;
30
import java.io.Reader;
31
import java.net.URI;
32
import java.net.URL;
33
import java.nio.charset.StandardCharsets;
34
import java.util.ArrayList;
35
import java.util.HashMap;
36
import java.util.Iterator;
37
import java.util.List;
38
import java.util.Objects;
39
import org.apache.commons.io.FileUtils;
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.CloseException;
53
import org.gvsig.fmap.dal.exception.DataException;
54
import org.gvsig.fmap.dal.exception.InitializeException;
55
import org.gvsig.fmap.dal.exception.OpenException;
56
import org.gvsig.fmap.dal.exception.ReadException;
57
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
58
import org.gvsig.fmap.dal.feature.EditableFeatureType;
59
import org.gvsig.fmap.dal.feature.Feature;
60
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
61
import org.gvsig.fmap.dal.feature.FeatureQuery;
62
import org.gvsig.fmap.dal.feature.FeatureSet;
63
import org.gvsig.fmap.dal.feature.FeatureStore;
64
import org.gvsig.fmap.dal.feature.FeatureType;
65
import org.gvsig.fmap.dal.feature.exception.CreateFeatureException;
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.FeatureReferenceProviderServices;
69
import org.gvsig.fmap.dal.feature.spi.FeatureSetProvider;
70
import org.gvsig.fmap.dal.feature.spi.memory.AbstractMemoryStoreProvider;
71
import org.gvsig.fmap.dal.resource.ResourceAction;
72
import org.gvsig.fmap.dal.resource.file.FileResource;
73
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
74
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
75
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorer;
76
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorerParameters;
77
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
78
import org.gvsig.fmap.dal.store.csv.simplereaders.AbstractSimpleReader;
79
import org.gvsig.fmap.dal.store.csv.simplereaders.CSVReaderSuperCSV;
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.SpatialIndex;
85
import org.gvsig.fmap.geom.SpatialIndexFactory;
86
import org.gvsig.fmap.geom.primitive.Envelope;
87
import org.gvsig.fmap.geom.primitive.Point;
88
import org.gvsig.tools.ToolsLocator;
89
import org.gvsig.tools.dataTypes.Coercion;
90
import org.gvsig.tools.dispose.DisposableIterator;
91
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
92
import org.gvsig.tools.evaluator.AbstractEvaluator;
93
import org.gvsig.tools.evaluator.EvaluatorData;
94
import org.gvsig.tools.evaluator.EvaluatorException;
95
import org.gvsig.tools.exception.BaseException;
96
import org.gvsig.tools.exception.NotYetImplemented;
97
import org.gvsig.tools.persistence.PersistentState;
98
import org.gvsig.tools.persistence.exception.PersistenceException;
99
import org.gvsig.tools.task.SimpleTaskStatus;
100
import org.gvsig.tools.task.TaskStatusManager;
101
import org.gvsig.tools.visitor.VisitCanceledException;
102
import org.gvsig.tools.visitor.Visitor;
103
import org.slf4j.Logger;
104
import org.slf4j.LoggerFactory;
105
import org.supercsv.prefs.CsvPreference;
106
import org.gvsig.tools.dataTypes.CoercionContext;
107
import org.gvsig.tools.dispose.DisposeUtils;
108
import org.gvsig.tools.dynobject.DynObject;
109
import org.gvsig.tools.i18n.I18nManager;
110
import org.gvsig.tools.util.GetItemWithSize64;
111

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

    
116
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleReaderStoreProvider.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 final 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
    private GetItemWithSize64<List<String>> virtualrows;
132
    private RowToFeatureTranslator rowToFeatureTranslator;
133
    private SpatialIndex spatialIndex;
134
    
135
    @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"})
136
    public SimpleReaderStoreProvider(
137
            CSVStoreParameters parameters,
138
            DataStoreProviderServices storeServices
139
        ) throws InitializeException {
140
        super(
141
                parameters,
142
                storeServices,
143
                FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME)
144
        );
145
        this.writer = new CSVFeatureWriter();
146
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
147
        this.taskStatus = manager.createDefaultSimpleTaskStatus("CSV");
148
        this.taskStatus.setAutoremove(true);
149

    
150
        counterNewsOIDs = 0;
151

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

    
158
        resource.addConsumer(this);
159

    
160
        initializeFeatureTypes();
161
    }
162

    
163
    private CSVStoreParameters getCSVParameters() {
164
        return (CSVStoreParameters) this.getParameters();
165
    }
166

    
167
    @Override
168
    public String getProviderName() {
169
        return NAME;
170
    }
171

    
172
    @Override
173
    public boolean allowWrite() {
174
        return true;
175
    }
176

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

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

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

    
220
    }
221

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

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

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

    
271
                            CSVStoreParameters csvParams = getCSVParameters();
272
                            CSVStoreParameters tmpParams = (CSVStoreParameters) csvParams.getCopy();
273

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

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

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

    
319
                                bboxFileSave(envelope);
320

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

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

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

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

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

    
358
    @Override
359
    public boolean supportsAppendMode() {
360
        return true;
361
    }
362

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

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

    
394
    }
395

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

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

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

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

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

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

    
497
        this.need_calculate_envelope = false;
498
        return this.envelope;
499
    }
500

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

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

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

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

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

    
543
    @Override
544
    public ResourceProvider getResource() {
545
        return resource;
546
    }
547

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

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

    
559
    static class ToPointEvaluaror extends AbstractEvaluator {
560

    
561
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
562

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

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

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

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

    
605
    }
606

    
607
    public static class RowToFeatureTranslator {
608

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

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

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

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

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

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

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

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

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

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

    
731
                List<String> row = reader.read();
732

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1060
    public SpatialIndex getSpatialIndex() {
1061
        return spatialIndex;
1062
    }
1063

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