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

History | View | Annotate | Download (33.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 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, 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
                taskStatus.message(i18n.getTranslation("_Loading"));
562
                taskStatus.setIndeterminate();
563

    
564
                List<String> row = reader.read();
565

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
849
    public File getAuxFile(String extension) {
850
        File data_file = SimpleReaderStoreParameters.getFile(this.getSimpleReaderParameters());
851
        if (data_file == null){
852
            return null;
853
        }
854
        File index_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + "." + extension);
855
        return index_file;
856
    }
857

    
858
    public SpatialIndex getSpatialIndex() {
859
        return spatialIndex;
860
    }
861

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