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

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

    
25
import java.io.Closeable;
26
import java.io.File;
27
import java.io.IOException;
28
import java.io.InputStreamReader;
29
import java.io.Reader;
30
import java.net.URI;
31
import java.net.URL;
32
import java.nio.charset.StandardCharsets;
33
import java.util.ArrayList;
34
import java.util.HashMap;
35
import java.util.Iterator;
36
import java.util.List;
37
import java.util.Objects;
38
import org.apache.commons.io.FileUtils;
39
import org.apache.commons.io.FilenameUtils;
40
import org.apache.commons.io.IOUtils;
41
import org.cresques.cts.IProjection;
42
import org.gvsig.fmap.dal.DALLocator;
43
import org.gvsig.fmap.dal.DataManager;
44
import org.gvsig.fmap.dal.DataServerExplorer;
45
import org.gvsig.fmap.dal.DataStore;
46
import org.gvsig.fmap.dal.DataStoreNotification;
47
import org.gvsig.fmap.dal.DataTypes;
48
import org.gvsig.fmap.dal.exception.CloseException;
49
import org.gvsig.fmap.dal.exception.DataException;
50
import org.gvsig.fmap.dal.exception.InitializeException;
51
import org.gvsig.fmap.dal.exception.OpenException;
52
import org.gvsig.fmap.dal.exception.ReadException;
53
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
54
import org.gvsig.fmap.dal.feature.EditableFeatureType;
55
import org.gvsig.fmap.dal.feature.Feature;
56
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
57
import org.gvsig.fmap.dal.feature.FeatureQuery;
58
import org.gvsig.fmap.dal.feature.FeatureStore;
59
import org.gvsig.fmap.dal.feature.FeatureType;
60
import org.gvsig.fmap.dal.feature.exception.CreateFeatureException;
61
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
62
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
63
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
64
import org.gvsig.fmap.dal.feature.spi.FeatureSetProvider;
65
import org.gvsig.fmap.dal.feature.spi.memory.AbstractMemoryStoreProvider;
66
import org.gvsig.fmap.dal.resource.file.FileResource;
67
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
68
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
69
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorer;
70
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorerParameters;
71
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
72
import org.gvsig.fmap.dal.store.simplereader.simplereaders.AbstractSimpleReader;
73
import org.gvsig.fmap.dal.store.simplereader.simplereaders.SimpleReader;
74
import org.gvsig.fmap.geom.Geometry;
75
import org.gvsig.fmap.geom.GeometryLocator;
76
import org.gvsig.fmap.geom.GeometryManager;
77
import org.gvsig.fmap.geom.SpatialIndex;
78
import org.gvsig.fmap.geom.SpatialIndexFactory;
79
import org.gvsig.fmap.geom.primitive.Envelope;
80
import org.gvsig.fmap.geom.primitive.Point;
81
import org.gvsig.tools.ToolsLocator;
82
import org.gvsig.tools.dataTypes.Coercion;
83
import org.gvsig.tools.dataTypes.CoercionContext;
84
import org.gvsig.tools.dispose.DisposableIterator;
85
import org.gvsig.tools.dispose.DisposeUtils;
86
import org.gvsig.tools.dynobject.DynObject;
87
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
88
import org.gvsig.tools.evaluator.AbstractEvaluator;
89
import org.gvsig.tools.evaluator.EvaluatorData;
90
import org.gvsig.tools.evaluator.EvaluatorException;
91
import org.gvsig.tools.exception.BaseException;
92
import org.gvsig.tools.exception.NotYetImplemented;
93
import org.gvsig.tools.i18n.I18nManager;
94
import org.gvsig.tools.persistence.PersistentState;
95
import org.gvsig.tools.persistence.exception.PersistenceException;
96
import org.gvsig.tools.task.SimpleTaskStatus;
97
import org.gvsig.tools.task.TaskStatusManager;
98
import org.gvsig.tools.util.GetItemWithSize64;
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

    
104
@SuppressWarnings("UseSpecificCatch")
105
public abstract class SimpleReaderStoreProvider extends AbstractMemoryStoreProvider implements
106
        ResourceConsumer {
107

    
108
    protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleReaderStoreProvider.class);
109

    
110
//    public static final String NAME = DataStore.CSV_PROVIDER_NAME;
111
//    public static final String DESCRIPTION = "CSV file";
112
//
113
//    public static final String METADATA_DEFINITION_NAME = NAME;
114

    
115
    protected final ResourceProvider resource;
116

    
117
    protected long counterNewsOIDs = 0;
118
    protected Envelope envelope;
119
    protected boolean need_calculate_envelope = false;
120
    protected final SimpleTaskStatus taskStatus;
121
    protected FeatureType featureType;
122
    protected GetItemWithSize64<List<String>> virtualrows;
123
    protected RowToFeatureTranslator rowToFeatureTranslator;
124
    protected SpatialIndex spatialIndex;
125
    
126
    @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"})
127
    public SimpleReaderStoreProvider(
128
            SimpleReaderStoreParameters parameters,
129
            DataStoreProviderServices storeServices,
130
            DynObject metadata
131
        ) throws InitializeException {
132
        super(
133
                parameters,
134
                storeServices,
135
                metadata
136
        );
137
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
138
        this.taskStatus = manager.createDefaultSimpleTaskStatus("CSV");
139
        this.taskStatus.setAutoremove(true);
140

    
141
        counterNewsOIDs = 0;
142

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

    
149
        resource.addConsumer(this);
150

    
151
        initializeFeatureTypes();
152
    }
153

    
154
    private SimpleReaderStoreParameters getSimpleReaderParameters() {
155
        return (SimpleReaderStoreParameters) this.getParameters();
156
    }
157

    
158
    @Override
159
    public abstract String getProviderName();
160

    
161
    @Override
162
    public boolean allowWrite() {
163
        return false;
164
    }
165

    
166
    protected String getFullFileName() {
167
        // Usar solo para mostrar mensajes en el logger.
168
        String s;
169
        try {
170
            s = getSimpleReaderParameters().getFile().getAbsolutePath();
171
        } catch (Exception e2) {
172
            s = "(unknow)";
173
        }
174
        return s;
175
    }
176

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

    
196
    @Override
197
    public DataServerExplorer getExplorer() throws ReadException {
198
        DataManager manager = DALLocator.getDataManager();
199
        FilesystemServerExplorerParameters params;
200
        try {
201
            params = (FilesystemServerExplorerParameters) manager
202
                    .createServerExplorerParameters(FilesystemServerExplorer.NAME);
203
            params.setRoot(this.getSimpleReaderParameters().getFile().getParent());
204
            return manager.openServerExplorer(FilesystemServerExplorer.NAME, params);
205
        } catch (Exception e) {
206
            throw new ReadException(this.getProviderName(), e);
207
        }
208

    
209
    }
210

    
211
    @Override
212
    @SuppressWarnings("Convert2Lambda")
213
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
214
        throw new UnsupportedOperationException();
215
    }
216

    
217
    @Override
218
    public boolean closeResourceRequested(ResourceProvider resource) {
219
        return true;
220
    }
221

    
222
    @Override
223
    public int getOIDType() {
224
        return DataTypes.LONG;
225
    }
226

    
227
    @Override
228
    public boolean supportsAppendMode() {
229
        return false;
230
    }
231

    
232
    @Override
233
    @SuppressWarnings("Convert2Lambda")
234
    public void append(final FeatureProvider featureProvider) {
235
        throw new UnsupportedOperationException();
236
    }
237

    
238
    @Override
239
    @SuppressWarnings("Convert2Lambda")
240
    public void beginAppend() throws DataException {
241
        throw new UnsupportedOperationException();
242
    }
243

    
244
    @Override
245
    @SuppressWarnings("Convert2Lambda")
246
    public void endAppend() {
247
        throw new UnsupportedOperationException();
248
    }
249

    
250
    public void saveToState(PersistentState state) throws PersistenceException {
251
        throw new NotYetImplemented();
252
    }
253

    
254
    public void loadFromState(PersistentState state) throws PersistenceException {
255
        throw new NotYetImplemented();
256
    }
257

    
258
    @Override
259
    public Object createNewOID() {
260
        return counterNewsOIDs++;
261
    }
262

    
263
    protected void initializeFeatureTypes() throws InitializeException {
264
        try {
265
            this.open();
266
        } catch (OpenException e) {
267
            throw new InitializeException(this.getProviderName(), e);
268
        }
269
    }
270

    
271
    @Override
272
    @SuppressWarnings("Convert2Lambda")
273
    public Envelope getEnvelope() throws DataException {
274
        this.open();
275
        if (this.envelope != null) {
276
            return this.envelope;
277
        }
278
        this.envelope = bboxFileLoad();
279
        if (this.envelope != null) {
280
            return this.envelope;
281
        }
282
        if (!this.need_calculate_envelope) {
283
            return null;
284
        }
285
        try {
286
            I18nManager i18n = ToolsLocator.getI18nManager();
287
            this.taskStatus.add();
288
            this.taskStatus.message(i18n.getTranslation("_Calculating_envelope"));
289
            FeatureStore fs = this.getFeatureStore();
290
            FeatureType ft = fs.getDefaultFeatureType();
291
            FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
292
            this.taskStatus.setRangeOfValues(0, fs.getFeatureCount());
293
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
294
            fs.accept(new Visitor() {
295
                @Override
296
                public void visit(Object obj) throws VisitCanceledException, BaseException {
297
                    taskStatus.incrementCurrentValue();
298
                    if(taskStatus.isCancellationRequested()){
299
                        taskStatus.cancel();
300
                        throw new VisitCanceledException();
301
                    }
302
                    Feature f = (Feature) obj;
303
                    Geometry geom = f.getDefaultGeometry();
304
                    if (geom != null) {
305
                        try {
306
                            Envelope env = geom.getEnvelope();
307
                            envelope.add(env);
308
                        } catch(Exception ex) {
309
                            LOGGER.warn("Can't calculate envelop of geometry in feature '"+Objects.toString(f.getReference())+"'.",ex);
310
                        }
311
                    }
312
                }
313
            });
314
            bboxFileSave(envelope);
315
            taskStatus.terminate();
316
        } catch (VisitCanceledException e) {
317
            return null;
318
        } catch (BaseException e) {
319
            taskStatus.abort();
320
            LOGGER.warn("Can't calculate the envelope of CSV file '" + this.getFullName() + "'.", e);
321
            return null;
322
        }
323

    
324
        this.need_calculate_envelope = false;
325
        return this.envelope;
326
    }
327

    
328
    @Override
329
    public Object getDynValue(String name) throws DynFieldNotFoundException {
330
        if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
331
            try {
332
                return this.getEnvelope();
333
            } catch (DataException e) {
334
                return null;
335
            }
336
        } else {
337
            if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
338
                IProjection pro = SimpleReaderStoreParameters.getCRS(this.getSimpleReaderParameters());
339
                if (pro != null) {
340
                    return pro;
341
                }
342
            }
343
        }
344
        return super.getDynValue(name);
345
    }
346

    
347
    @Override
348
    public void resourceChanged(ResourceProvider resource) {
349
        this.getStoreServices().notifyChange(
350
                DataStoreNotification.RESOURCE_CHANGED,
351
                resource);
352
    }
353

    
354
    @Override
355
    public Object getSourceId() {
356
        return this.getSimpleReaderParameters().getFile();
357
    }
358

    
359
    @Override
360
    public String getName() {
361
        String name = this.getSimpleReaderParameters().getFile().getName();
362
        return FilenameUtils.getBaseName(name);
363
    }
364

    
365
    @Override
366
    public String getFullName() {
367
        return this.getSimpleReaderParameters().getFile().getAbsolutePath();
368
    }
369

    
370
    @Override
371
    public ResourceProvider getResource() {
372
        return resource;
373
    }
374

    
375
    private boolean isEmpty(String s) {
376
        if (s == null) {
377
            return true;
378
        }
379
        return s.trim().length() == 0;
380
    }
381

    
382
    private void init(SimpleReaderStoreParameters parameters, DataStoreProviderServices storeServices) {
383
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
384
    }
385

    
386
    static class ToPointEvaluaror extends AbstractEvaluator {
387

    
388
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
389

    
390
        private GeometryManager geommgr = null;
391
        private String xname = null;
392
        private String yname = null;
393
        private String zname = null;
394
        private final Coercion toDouble;
395
        private int errorcount = 0;
396

    
397
        ToPointEvaluaror(String[] pointDimensionNames) {
398
            this.xname = pointDimensionNames[0];
399
            this.yname = pointDimensionNames[1];
400
            if (pointDimensionNames.length > 2) {
401
                this.zname = pointDimensionNames[2];
402
            }
403
            this.geommgr = GeometryLocator.getGeometryManager();
404
            this.toDouble = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.DOUBLE);
405
        }
406

    
407
        @Override
408
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
409
            try {
410
                double x = ((Double) toDouble.coerce(data.getDataValue(xname)));
411
                double y = ((Double) toDouble.coerce(data.getDataValue(yname)));
412
                Point point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
413
                if (zname != null) {
414
                    double z = ((Double) toDouble.coerce(data.getDataValue(zname)));
415
                    point.setCoordinateAt(2, z);
416
                }
417
                return point;
418
            } catch (Exception ex) {
419
                if (++errorcount < 5) {
420
                    logger.warn("[" + errorcount + "] Can't create point in CSV provider. XNAME='"
421
                            + xname + "', YNAME='" + yname + "', ZNAME='" + zname + "', data=" + data.toString());
422
                }
423
                return null;
424
            }
425
        }
426

    
427
        @Override
428
        public String getName() {
429
            return "ToPointEvaluaror";
430
        }
431

    
432
    }
433

    
434
    public static class RowToFeatureTranslator {
435

    
436
        private Coercion coercion[];
437
        private CoercionContext coercionContext[];
438
        private int sizes[];
439
        private String[] names;
440
        private final boolean ignore_errors;
441
        private long count_errors;
442
        private FeatureType csvFeatureType;
443

    
444
        public RowToFeatureTranslator(boolean ignore_errors) {
445
            this.ignore_errors = ignore_errors;
446
            this.count_errors = 0;
447
        }
448

    
449
        public int getColumnSize(int column) {
450
            return this.sizes[column];
451
        }
452

    
453
        public void initialize(FeatureType ftype) {
454
            this.csvFeatureType = ftype;
455
            int columns = this.csvFeatureType.size();
456
            this.names = new String[columns];
457
            this.coercion = new Coercion[columns];
458
            this.coercionContext = new CoercionContext[columns];
459
            this.sizes = new int[columns];
460
            int index = 0;
461
            for (int i = 0; i < this.csvFeatureType.size(); i++) {
462
                FeatureAttributeDescriptor ad = this.csvFeatureType.getAttributeDescriptor(i);
463
                names[i] = null;
464
                if( ad.isComputed() ) {
465
                    continue;
466
                }
467
                names[index] = ad.getName();
468
                coercion[index] = ad.getCoercion();
469
                coercionContext[index] = ad.getCoercionContext();
470
                sizes[index] = ad.getSize();
471
                index++;
472
            }
473
        }
474

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

    
477
            feature.setOID(rowindex);
478
            for (int i = 0; i < names.length; i++) {
479
                String name = names[i];
480
                if( name == null ) {
481
                    break;
482
                }
483
                Object rawvalue = row.get(i);
484
                try {
485
                    Object value = null;
486
                    if (coercion[i] != null) {
487
                        value = coercion[i].coerce(rawvalue, coercionContext[i]);
488
                    }
489
                    int findex = feature.getType().getIndex(name);
490
                    if( findex>=0 ) {
491
                        // Ojo que puede que se este filtrando el featuretype y no 
492
                        // tenga todos los atributos, por ejemplo al pintar la vista.
493
                        feature.set(findex, value);
494
                    }
495
                    if (sizes[i] >= 0
496
                            && (value instanceof String || value instanceof URL
497
                            || value instanceof URI || value instanceof File)) {
498
                        int x = value.toString().length();
499
                        if (sizes[i] < x) {
500
                            sizes[i] = x;
501
                        }
502
                    }
503
                } catch (Exception ex) {
504
                    if (!ignore_errors) {
505
                        throw ex;
506
                    }
507
                    if (count_errors++ < 10) {
508
                        LOGGER.warn("Can't load value of attribute " + name +"/" +i+" in row " + rowindex + ".", ex);
509
                    }
510
                    if (count_errors == 10) {
511
                        LOGGER.info("Too many errors, suppress messages.");
512
                    }
513
                }
514
            }
515
        }
516
    }
517

    
518
    protected abstract SimpleReaderFeatureTypeLoader getFeatureTypeLoader();
519
    
520
    protected void loadFeatures() {
521
        InputStreamReader in = null;
522
        SimpleReader reader = null;
523
        try {
524
            taskStatus.setTitle("CSV "+this.getName());
525
            taskStatus.add();
526
//            boolean ignore_errors = CSVStoreParameters.getIgnoreErrors(getCSVParameters());
527

    
528
            // Initialize the feature types
529
            EditableFeatureType edftype = getStoreServices().createFeatureType(this.getName());
530
            SimpleReaderFeatureTypeLoader featureTypeLoader = getFeatureTypeLoader();
531
            featureTypeLoader.loadFeatureType(edftype, true, taskStatus);
532
            FeatureType ftype = edftype.getNotEditableCopy();
533
            this.setFeatureType(ftype);
534

    
535
            in = SimpleReaderUtils.openFile(
536
                    this.getSimpleReaderParameters().getFile(),
537
                    SimpleReaderStoreParameters.getCharset(this.getSimpleReaderParameters())
538
            );
539
            reader = getSimpleReader(getSimpleReaderParameters(), in);
540
            if (featureTypeLoader.isFirstLineHeader()) {
541
                reader.getHeader(); // Skip and ignore the header of file
542
            }
543
            this.rowToFeatureTranslator = new RowToFeatureTranslator(
544
                    SimpleReaderStoreParameters.getIgnoreErrors(getSimpleReaderParameters())
545
            );
546
            this.rowToFeatureTranslator.initialize(ftype);
547
            if (ftype.getDefaultGeometryAttributeName() != null) {
548
                this.need_calculate_envelope = true;
549
            }
550
            I18nManager i18n = ToolsLocator.getI18nManager();
551
            taskStatus.message(i18n.getTranslation("_Loading"));
552

    
553
            if(this.virtualrows != null && this.virtualrows instanceof Closeable){
554
                IOUtils.closeQuietly((Closeable) this.virtualrows);
555
                this.virtualrows = null;
556
            }
557
            
558
            this.virtualrows = ((AbstractSimpleReader) reader).getVirtualRows(this.taskStatus);
559
            if (this.virtualrows == null) {
560

    
561
                List<String> row = reader.read();
562

    
563
                int skipLines = SimpleReaderStoreParameters.getSkipLines(getSimpleReaderParameters());
564
                if (skipLines > 0) {
565
                    row = reader.skip(skipLines);
566
                }
567
                int limit = SimpleReaderStoreParameters.getLimit(getSimpleReaderParameters());
568
                while (row != null) {
569
                    taskStatus.incrementCurrentValue();
570
                    if( taskStatus.isCancellationRequested() ) {
571
                        taskStatus.cancel();
572
                        break;
573
                    }
574
                    FeatureProvider feature = this.createFeatureProvider(ftype);
575
                    this.rowToFeatureTranslator.translate(reader.getLine(), row, feature);
576

    
577
                    this.addFeatureProvider(feature);
578
                    if (limit > 0) {
579
                        if (limit < this.data.size()) {
580
                            break;
581
                        }
582
                    }
583
                    row = reader.read();
584
                }
585
                for (int i = 0; i < ftype.size(); i++) {
586
                    if (this.rowToFeatureTranslator.getColumnSize(i) > 0) {
587
//                    if (sizes[i] > 0) {
588
                        EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
589
//                        efad.setSize(sizes[i]);
590
                        efad.setSize(this.rowToFeatureTranslator.getColumnSize(i));
591
                    }
592
                }
593
                // Volvemos a asignar al store el featuretype, ya que puede
594
                // haber cambiado.
595
                ftype = edftype.getNotEditableCopy();
596
                this.setFeatureType(ftype);
597
            } else {
598
                this.loadOrCreateSpatialIndex();
599
            }
600
            if( taskStatus.isRunning() ) {
601
                taskStatus.terminate();
602
            }
603
        } catch (Throwable ex) {
604
            taskStatus.abort();
605
            int lineno = 0;
606
            if (reader != null) {
607
                lineno = reader.getLine();
608
            }
609
            throw new RuntimeException("Problems reading file '" + getFullFileName() + "' near line " + lineno + ".", ex);
610

    
611
        } finally {
612
            if (reader != null) {
613
                try {
614
                    reader.close();
615
                } catch (Exception ex) {
616
                    // Do nothing
617
                }
618
//                reader = null;
619
            }
620
            if (in != null) {
621
                try {
622
                    in.close();
623
                } catch (Exception ex) {
624
                    // Do nothing
625
                }
626
//                in = null;
627
            }
628
            taskStatus.remove();
629
        }
630
    }
631

    
632
    @Override
633
    public void fixFeatureTypeFromParameters() {
634
        if(mustFixFeatureType() && featureType != null){
635
            this.setFeatureType(featureType);
636
        }
637
    }
638
    
639
    protected boolean mustFixFeatureType() {
640
        return false;
641
    }
642

    
643
    private void setFeatureType(FeatureType ftype) {
644
        try {
645
            List<FeatureType> ftypes = new ArrayList<>();
646
            ftypes.add(ftype);
647
            this.featureType = ftype;
648
            if(this.getStoreServices().getDefaultFeatureType() == null){
649
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
650
                return;
651
            }
652
            if(mustFixFeatureType()){
653
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
654
            }
655
        } catch (DataException ex) {
656
            LOGGER.warn("Cant set feature type", ex);
657
        }
658
    }
659

    
660
    @Override
661
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType providerFeatureType, FeatureType featureType)
662
            throws DataException {
663
        this.open();
664
        if (this.virtualrows == null) {
665
            return super.createSet(query, providerFeatureType, featureType);
666
        }
667
        return new SimpleReaderSetProvider(this, query, providerFeatureType, featureType);
668
    }
669

    
670
    @Override
671
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType featureType)
672
            throws DataException {
673
        this.open();
674
        if (this.virtualrows == null) {
675
            return super.createSet(query, featureType);
676
        }
677
        return new SimpleReaderSetProvider(this, query, featureType, featureType);
678
    }
679

    
680
    public List<String> getRowByIndex(long index) {
681
        try {
682
            this.open();
683
        } catch(Exception ex) {
684
            throw new RuntimeException("Can't get row by index", ex);
685
        }
686
        if (this.virtualrows == null) {
687
            return null;
688
        }
689
        List<String> line = this.virtualrows.get64(index);
690
        return line;
691
    }
692

    
693
    public RowToFeatureTranslator getRowToFeatureTranslator() {
694
        if (this.rowToFeatureTranslator == null) {
695
            boolean ignore_errors = SimpleReaderStoreParameters.getIgnoreErrors(getSimpleReaderParameters());
696
            this.rowToFeatureTranslator = new RowToFeatureTranslator(ignore_errors);
697
            this.rowToFeatureTranslator.initialize(featureType);
698
        }
699
        return this.rowToFeatureTranslator;
700
    }
701

    
702
    @Override
703
    public long getFeatureCount() throws DataException {
704
        this.open();
705
        if (this.virtualrows == null) {
706
            return super.getFeatureCount();
707
        }
708
        return this.virtualrows.size64();
709
    }
710

    
711
    @Override
712
    public long getDataSize() throws DataException {
713
        this.open();
714
        if (this.virtualrows == null) {
715
            return super.getDataSize();
716
        }
717
        return this.virtualrows.size64();
718
    }
719

    
720
    @Override
721
    protected FeatureProvider internalGetFeatureProviderByReference(
722
            FeatureReferenceProviderServices reference) throws DataException {
723
        this.open();
724
        if (this.virtualrows == null) {
725
            return super.internalGetFeatureProviderByReference(reference);
726
        }
727
        int oid = ((Long) reference.getOID()).intValue();
728
        RowToFeatureTranslator translator = getRowToFeatureTranslator();
729
        FeatureProvider feature = this.createFeatureProvider(this.featureType);
730
        try {
731
            translator.translate(oid, this.virtualrows.get64(oid), feature);
732
        } catch (Exception ex) {
733
            throw new CreateFeatureException(ex, this.getName());
734
        }
735
        return feature;
736
    }
737

    
738
    @Override
739
    protected void doDispose() throws BaseException {
740
        super.doDispose();
741
        if (this.virtualrows != null && this.virtualrows instanceof Closeable) {
742
            IOUtils.closeQuietly((Closeable) this.virtualrows);
743
            this.virtualrows = null;
744
        }
745
    }
746
    
747
    @Override
748
     public void refresh() throws OpenException {
749
        try {
750
            this.close();
751
        } catch (CloseException e) {
752
            throw new OpenException(this.getProviderName(), e);
753
        }
754
        this.open();
755
    }
756

    
757
    @Override
758
    public void close() throws CloseException {
759
        super.close(); //To change body of generated methods, choose Tools | Templates.
760
        this.data = null;
761
        if(this.virtualrows != null && this.virtualrows instanceof Closeable){
762
            IOUtils.closeQuietly((Closeable) this.virtualrows);
763
            this.virtualrows = null;
764
            this.envelope = null;
765
            this.spatialIndex = null;
766
        }
767
        
768
    }
769
     
770
    
771
    private void loadOrCreateSpatialIndex() {
772
        FeatureSetProvider set = null;
773
        DisposableIterator<FeatureProvider> it = null;
774
        try {
775
            if( this.virtualrows == null ) {
776
                return;
777
            }
778
            FeatureAttributeDescriptor geomdesc = this.featureType.getDefaultGeometryAttribute();
779
            if( geomdesc == null ) {
780
                return;
781
            }
782
//            String indexTypeName = "MVRTree";
783
//            String extname = "mvtree";
784
            String indexTypeName = GeometryManager.SPATIALINDEX_DEFAULT_QUADTREE;
785
            String extname = "qtree";
786
            
787
            this.envelope = bboxFileLoad();
788
            File indexfile = this.getAuxFile(extname); 
789
            boolean createIndex = !indexfile.exists();
790

    
791
            GeometryManager geomManager = GeometryLocator.getGeometryManager();
792
            SpatialIndexFactory indexfactory = geomManager.getSpatialIndexFactory(indexTypeName);
793
            DynObject params = indexfactory.createParameters();
794
            params.setDynValue("file", indexfile);
795
            SpatialIndex index = geomManager.createSpatialIndex(indexTypeName, params);
796
            if( createIndex ) { 
797
                I18nManager i18n = ToolsLocator.getI18nManager();
798
                this.taskStatus.add();
799
                taskStatus.message(i18n.getTranslation("_Creating_spatial_index"));
800
                taskStatus.setRangeOfValues(0, this.virtualrows.size64());
801
                taskStatus.setCurValue(0);
802
                Envelope theEnvelope = geomManager.createEnvelope(Geometry.SUBTYPES.GEOM2D);
803
                set = this.createSet(null, featureType);
804
                it = set.fastIterator();
805
                while( it.hasNext() ) {
806
                    taskStatus.incrementCurrentValue();
807
                    if( taskStatus.isCancellationRequested() ) {
808
                        taskStatus.cancel();
809
                        LOGGER.info("CSV spatial index creation canceled ("+getFullFileName()+")");
810
                        break;
811
                    }
812
                    FeatureProvider f = it.next();
813
                    if( f == null ) {
814
                        continue;
815
                    }
816
                    Object oid = null;
817
                    try {
818
                        oid = f.getOID();
819
                        Geometry geom = (Geometry) f.get(geomdesc.getName());
820
                        index.insert(geom, oid);
821
                        theEnvelope.add(geom);
822
                    } catch(Exception ex) {
823
                        LOGGER.debug("Can't insert feature '"+Objects.toString(oid)+"' in spatial index.",ex);
824
                    }
825
                }
826
                taskStatus.message(i18n.getTranslation("_Saving_spatial_index"));
827
                taskStatus.setIndeterminate();
828
                index.flush();
829
                bboxFileSave(theEnvelope);
830
                taskStatus.terminate();
831
                this.envelope = theEnvelope;
832
            }
833
            this.spatialIndex = index;
834
        } catch (Exception ex) {
835
            taskStatus.abort();
836
            LOGGER.warn("Can't create spatial index.",ex);
837
        } finally {
838
            DisposeUtils.disposeQuietly(it);
839
            DisposeUtils.disposeQuietly(set);
840
            taskStatus.remove();
841
        }
842
    }
843

    
844
    public File getAuxFile(String extension) {
845
        File data_file = SimpleReaderStoreParameters.getFile(this.getSimpleReaderParameters());
846
        if (data_file == null){
847
            return null;
848
        }
849
        File index_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + "." + extension);
850
        return index_file;
851
    }
852

    
853
    public SpatialIndex getSpatialIndex() {
854
        return spatialIndex;
855
    }
856

    
857
    protected void bboxFileSave(Envelope envelope) {
858
        File bboxfile = this.getAuxFile("bbox");
859
        bboxFileSave(bboxfile, envelope);
860
    }
861
    
862
    protected void bboxFileSave(File bboxfile, Envelope envelope) {
863
        if( envelope == null ) {
864
            bboxfile.delete();
865
            return;
866
        }
867
        try {
868
            FileUtils.write(
869
                    bboxfile, 
870
                    envelope.getBox2D().convertToWKTQuietly(), 
871
                    StandardCharsets.UTF_8
872
            );
873
        } catch(Exception ex) {
874
            LOGGER.warn("Can't write bbox file '"+Objects.toString(bboxfile)+"'.",ex);
875
        }
876
    }
877
    
878
    protected Envelope bboxFileLoad() {
879
        File bboxfile = this.getAuxFile("bbox");
880
        return bboxFileLoad(bboxfile);
881
    }
882
    
883
    protected Envelope bboxFileLoad(File bboxfile) {
884
        try {
885
            if( bboxfile.exists() ) {
886
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
887
                String wkt = FileUtils.readFileToString(bboxfile, StandardCharsets.UTF_8);                    
888
                Geometry geom = geomManager.createFrom(wkt);
889
                if( geom!=null ) {
890
                    return geom.getEnvelope();
891
                }
892
            }
893
        } catch(Exception ex) {
894
            LOGGER.warn("Can't load bbox file",ex);
895
        }
896
        return null;
897
    }
898
    
899
    protected abstract SimpleReader getSimpleReader(SimpleReaderStoreParameters parameters, Reader in) throws IOException ;//{
900
//        return CSVUtils.getSimpleReader(getSimpleReaderParameters(), in);
901
//    }
902
    
903
}