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.lib / src / main / java / org / gvsig / fmap / dal / store / simplereader / SimpleReaderStoreProvider.java @ 47656

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

    
107
@SuppressWarnings("UseSpecificCatch")
108
public abstract class SimpleReaderStoreProvider extends AbstractMemoryStoreProvider implements
109
        ResourceConsumer {
110

    
111
    protected static final Logger LOGGER = LoggerFactory.getLogger(SimpleReaderStoreProvider.class);
112

    
113
//    public static final String NAME = DataStore.CSV_PROVIDER_NAME;
114
//    public static final String DESCRIPTION = "CSV file";
115
//
116
//    public static final String METADATA_DEFINITION_NAME = NAME;
117

    
118
    protected final ResourceProvider resource;
119

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

    
144
        this.envelopes = new HashMap<>();
145
        counterNewsOIDs = 0;
146

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

    
153
        resource.addConsumer(this);
154

    
155
        initializeFeatureTypes();
156
    }
157

    
158
    private SimpleReaderStoreParameters getSimpleReaderParameters() {
159
        return (SimpleReaderStoreParameters) this.getParameters();
160
    }
161

    
162
    @Override
163
    public abstract String getProviderName();
164

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

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

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

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

    
213
    }
214

    
215
    @Override
216
    @SuppressWarnings("Convert2Lambda")
217
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
218
        throw new UnsupportedOperationException();
219
    }
220

    
221
    @Override
222
    public boolean closeResourceRequested(ResourceProvider resource) {
223
        return true;
224
    }
225

    
226
    @Override
227
    public int getOIDType() {
228
        return DataTypes.LONG;
229
    }
230

    
231
    @Override
232
    public boolean supportsAppendMode() {
233
        return false;
234
    }
235

    
236
    @Override
237
    @SuppressWarnings("Convert2Lambda")
238
    public void append(final FeatureProvider featureProvider) {
239
        throw new UnsupportedOperationException();
240
    }
241

    
242
    @Override
243
    @SuppressWarnings("Convert2Lambda")
244
    public void beginAppend() throws DataException {
245
        throw new UnsupportedOperationException();
246
    }
247

    
248
    @Override
249
    @SuppressWarnings("Convert2Lambda")
250
    public void endAppend() {
251
        throw new UnsupportedOperationException();
252
    }
253

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

    
258
    public void loadFromState(PersistentState state) throws PersistenceException {
259
        throw new NotYetImplemented();
260
    }
261

    
262
    @Override
263
    public Object createNewOID() {
264
        return counterNewsOIDs++;
265
    }
266

    
267
    protected void initializeFeatureTypes() throws InitializeException {
268
        try {
269
            this.open();
270
        } catch (OpenException e) {
271
            throw new InitializeException(this.getProviderName(), e);
272
        }
273
    }
274

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

    
332
        this.need_calculate_envelope = false;
333
        return env;
334
    }
335

    
336
    @Override
337
    public Object getDynValue(String name) throws DynFieldNotFoundException {
338
        if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
339
            try {
340
                return this.getEnvelope();
341
            } catch (DataException e) {
342
                return null;
343
            }
344
        } else {
345
            if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
346
                IProjection pro = SimpleReaderStoreParameters.getCRS(this.getSimpleReaderParameters());
347
                if (pro != null) {
348
                    return pro;
349
                }
350
            }
351
        }
352
        return super.getDynValue(name);
353
    }
354

    
355
    @Override
356
    public void resourceChanged(ResourceProvider resource) {
357
        this.getStoreServices().notifyChange(
358
                DataStoreNotification.RESOURCE_CHANGED,
359
                resource);
360
    }
361

    
362
    @Override
363
    public Object getSourceId() {
364
        return this.getSimpleReaderParameters().getFile();
365
    }
366

    
367
    @Override
368
    public String getName() {
369
        String name = this.getSimpleReaderParameters().getFile().getName();
370
        return FilenameUtils.getBaseName(name);
371
    }
372

    
373
    @Override
374
    public String getFullName() {
375
        return this.getSimpleReaderParameters().getFile().getAbsolutePath();
376
    }
377

    
378
    @Override
379
    public ResourceProvider getResource() {
380
        return resource;
381
    }
382

    
383
    private boolean isEmpty(String s) {
384
        if (s == null) {
385
            return true;
386
        }
387
        return s.trim().length() == 0;
388
    }
389

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

    
394
    static class ToPointEvaluaror extends AbstractEvaluator {
395

    
396
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
397

    
398
        private GeometryManager geommgr = null;
399
        private String xname = null;
400
        private String yname = null;
401
        private String zname = null;
402
        private final Coercion toDouble;
403
        private int errorcount = 0;
404

    
405
        ToPointEvaluaror(String[] pointDimensionNames) {
406
            this.xname = pointDimensionNames[0];
407
            this.yname = pointDimensionNames[1];
408
            if (pointDimensionNames.length > 2) {
409
                this.zname = pointDimensionNames[2];
410
            }
411
            this.geommgr = GeometryLocator.getGeometryManager();
412
            this.toDouble = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.DOUBLE);
413
        }
414

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

    
435
        @Override
436
        public String getName() {
437
            return "ToPointEvaluaror";
438
        }
439

    
440
    }
441

    
442
    public static class RowToFeatureTranslator {
443

    
444
        private Coercion coercion[];
445
        private CoercionContext coercionContext[];
446
        private int sizes[];
447
        private String[] names;
448
        private final boolean ignore_errors;
449
        private long count_errors;
450
        private FeatureType csvFeatureType;
451

    
452
        public RowToFeatureTranslator(boolean ignore_errors) {
453
            this.ignore_errors = ignore_errors;
454
            this.count_errors = 0;
455
        }
456

    
457
        public int getColumnSize(int column) {
458
            return this.sizes[column];
459
        }
460

    
461
        public void initialize(FeatureType ftype) {
462
            this.csvFeatureType = ftype;
463
            int columns = this.csvFeatureType.size();
464
            this.names = new String[columns];
465
            this.coercion = new Coercion[columns];
466
            this.coercionContext = new CoercionContext[columns];
467
            this.sizes = new int[columns];
468
            int index = 0;
469
            for (int i = 0; i < this.csvFeatureType.size(); i++) {
470
                FeatureAttributeDescriptor ad = this.csvFeatureType.getAttributeDescriptor(i);
471
                names[i] = null;
472
                if( ad.isComputed() ) {
473
                    continue;
474
                }
475
                names[index] = ad.getName();
476
                coercion[index] = ad.getCoercion();
477
                coercionContext[index] = ad.getCoercionContext();
478
                sizes[index] = ad.getSize();
479
                index++;
480
            }
481
        }
482

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

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

    
526
    protected abstract SimpleReaderFeatureTypeLoader getFeatureTypeLoader();
527
    
528
    protected void loadFeatures() {
529
        InputStreamReader in = null;
530
        SimpleReader reader = null;
531
        try {
532
            taskStatus.setTitle("CSV "+this.getName());
533
            taskStatus.add();
534
//            boolean ignore_errors = CSVStoreParameters.getIgnoreErrors(getCSVParameters());
535

    
536
            // Initialize the feature types
537
            EditableFeatureType edftype = getStoreServices().createFeatureType(this.getName());
538
            SimpleReaderFeatureTypeLoader featureTypeLoader = getFeatureTypeLoader();
539
            featureTypeLoader.loadFeatureType(edftype, taskStatus);
540
            FeatureType ftype = edftype.getNotEditableCopy();
541
            this.setFeatureType(ftype);
542

    
543
            in = SimpleReaderUtils.openFile(
544
                    this.getSimpleReaderParameters().getFile(),
545
                    SimpleReaderStoreParameters.getCharset(this.getSimpleReaderParameters())
546
            );
547
            reader = getSimpleReader(getSimpleReaderParameters(), in);
548
            if (featureTypeLoader.isFirstLineHeader()) {
549
                reader.getHeader(); // Skip and ignore the header of file
550
            }
551
            this.rowToFeatureTranslator = new RowToFeatureTranslator(
552
                    SimpleReaderStoreParameters.getIgnoreErrors(getSimpleReaderParameters())
553
            );
554
            this.rowToFeatureTranslator.initialize(ftype);
555
            if (ftype.getDefaultGeometryAttributeName() != null) {
556
                this.need_calculate_envelope = true;
557
            }
558
            I18nManager i18n = ToolsLocator.getI18nManager();
559
            taskStatus.message(i18n.getTranslation("_Loading"));
560

    
561
            if(this.virtualrows != null && this.virtualrows instanceof Closeable){
562
                IOUtils.closeQuietly((Closeable) this.virtualrows);
563
                this.virtualrows = null;
564
            }
565
            
566
            this.virtualrows = ((AbstractSimpleReader) reader).getVirtualRows(this.taskStatus);
567
            if(this.taskStatus.isCancellationRequested()){
568
                throw new UserCancelTaskException();
569
            }
570
            if (this.virtualrows == null) {
571

    
572
                taskStatus.message(i18n.getTranslation("_Loading"));
573
                taskStatus.setIndeterminate();
574

    
575
                List<String> row = reader.read();
576

    
577
                int skipLines = SimpleReaderStoreParameters.getSkipLines(getSimpleReaderParameters());
578
                if (skipLines > 0) {
579
                    row = reader.skip(skipLines);
580
                }
581
                int limit = SimpleReaderStoreParameters.getLimit(getSimpleReaderParameters());
582
                while (row != null) {
583
                    taskStatus.incrementCurrentValue();
584
                    if( taskStatus.isCancellationRequested() ) {
585
                        taskStatus.cancel();
586
                        break;
587
                    }
588
                    FeatureProvider feature = this.createFeatureProvider(ftype);
589
                    this.rowToFeatureTranslator.translate(reader.getLine(), row, feature);
590

    
591
                    this.addFeatureProvider(feature);
592
                    if (limit > 0) {
593
                        if (limit < this.data.size()) {
594
                            break;
595
                        }
596
                    }
597
                    row = reader.read();
598
                }
599
                for (int i = 0; i < ftype.size(); i++) {
600
                    if (this.rowToFeatureTranslator.getColumnSize(i) > 0) {
601
//                    if (sizes[i] > 0) {
602
                        EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
603
//                        efad.setSize(sizes[i]);
604
                        efad.setSize(this.rowToFeatureTranslator.getColumnSize(i));
605
                    }
606
                }
607
                // Volvemos a asignar al store el featuretype, ya que puede
608
                // haber cambiado.
609
                ftype = edftype.getNotEditableCopy();
610
                this.setFeatureType(ftype);
611
            } else {
612
                this.loadOrCreateSpatialIndex();
613
            }
614
            if( taskStatus.isRunning() ) {
615
                taskStatus.terminate();
616
            }
617
        } catch (UserCancelTaskException ex) {
618
            this.taskStatus.cancel();
619
            throw ex;
620
        } catch (Throwable ex) {
621
            taskStatus.abort();
622
            int lineno = 0;
623
            if (reader != null) {
624
                lineno = reader.getLine();
625
            }
626
            throw new RuntimeException("Problems reading file '" + getFullFileName() + "' near record " + lineno + ".", ex);
627

    
628
        } finally {
629
            if (reader != null) {
630
                try {
631
                    reader.close();
632
                } catch (Exception ex) {
633
                    // Do nothing
634
                }
635
//                reader = null;
636
            }
637
            if (in != null) {
638
                try {
639
                    in.close();
640
                } catch (Exception ex) {
641
                    // Do nothing
642
                }
643
//                in = null;
644
            }
645
            taskStatus.remove();
646
        }
647
    }
648

    
649
    @Override
650
    public void fixFeatureTypeFromParameters() {
651
        if(mustFixFeatureType() && featureType != null){
652
            this.setFeatureType(featureType);
653
        }
654
    }
655
    
656
    protected boolean mustFixFeatureType() {
657
        return false;
658
    }
659

    
660
    private void setFeatureType(FeatureType ftype) {
661
        try {
662
            List<FeatureType> ftypes = new ArrayList<>();
663
            ftypes.add(ftype);
664
            this.featureType = ftype;
665
            if(this.getStoreServices().getDefaultFeatureType() == null){
666
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
667
                return;
668
            }
669
            if(mustFixFeatureType()){
670
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
671
            }
672
        } catch (DataException ex) {
673
            LOGGER.warn("Cant set feature type", ex);
674
        }
675
    }
676

    
677
    @Override
678
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType providerFeatureType, FeatureType featureType)
679
            throws DataException {
680
        this.open();
681
        if (this.virtualrows == null) {
682
            return super.createSet(query, providerFeatureType, featureType);
683
        }
684
        return new SimpleReaderSetProvider(this, query, providerFeatureType, featureType);
685
    }
686

    
687
    @Override
688
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType featureType)
689
            throws DataException {
690
        this.open();
691
        if (this.virtualrows == null) {
692
            return super.createSet(query, featureType);
693
        }
694
        return new SimpleReaderSetProvider(this, query, featureType, featureType);
695
    }
696

    
697
    public List<String> getRowByIndex(long index) {
698
        try {
699
            this.open();
700
        } catch(Exception ex) {
701
            throw new RuntimeException("Can't get row by index", ex);
702
        }
703
        if (this.virtualrows == null) {
704
            return null;
705
        }
706
        List<String> line = this.virtualrows.get64(index);
707
        return line;
708
    }
709

    
710
    public RowToFeatureTranslator getRowToFeatureTranslator() {
711
        if (this.rowToFeatureTranslator == null) {
712
            boolean ignore_errors = SimpleReaderStoreParameters.getIgnoreErrors(getSimpleReaderParameters());
713
            this.rowToFeatureTranslator = new RowToFeatureTranslator(ignore_errors);
714
            this.rowToFeatureTranslator.initialize(featureType);
715
        }
716
        return this.rowToFeatureTranslator;
717
    }
718

    
719
    @Override
720
    public long getFeatureCount() throws DataException {
721
        this.open();
722
        if (this.virtualrows == null) {
723
            return super.getFeatureCount();
724
        }
725
        return this.virtualrows.size64();
726
    }
727

    
728
    @Override
729
    public long getDataSize() throws DataException {
730
        this.open();
731
        if (this.virtualrows == null) {
732
            return super.getDataSize();
733
        }
734
        return this.virtualrows.size64();
735
    }
736

    
737
    @Override
738
    protected FeatureProvider internalGetFeatureProviderByReference(
739
            FeatureReferenceProviderServices reference) throws DataException {
740
        this.open();
741
        if (this.virtualrows == null) {
742
            return super.internalGetFeatureProviderByReference(reference);
743
        }
744
        int oid = ((Long) reference.getOID()).intValue();
745
        RowToFeatureTranslator translator = getRowToFeatureTranslator();
746
        FeatureProvider feature = this.createFeatureProvider(this.featureType);
747
        try {
748
            translator.translate(oid, this.virtualrows.get64(oid), feature);
749
        } catch (Exception ex) {
750
            throw new CreateFeatureException(ex, this.getName());
751
        }
752
        return feature;
753
    }
754

    
755
    @Override
756
    protected void doDispose() throws BaseException {
757
        super.doDispose();
758
        if (this.virtualrows != null && this.virtualrows instanceof Closeable) {
759
            IOUtils.closeQuietly((Closeable) this.virtualrows);
760
            this.virtualrows = null;
761
        }
762
    }
763
    
764
    @Override
765
     public void refresh() throws OpenException {
766
        try {
767
            this.close();
768
        } catch (CloseException e) {
769
            throw new OpenException(this.getProviderName(), e);
770
        }
771
        this.open();
772
    }
773

    
774
    @Override
775
    public void close() throws CloseException {
776
        super.close(); //To change body of generated methods, choose Tools | Templates.
777
        this.data = null;
778
        if(this.virtualrows != null && this.virtualrows instanceof Closeable){
779
            IOUtils.closeQuietly((Closeable) this.virtualrows);
780
            this.virtualrows = null;
781
            this.envelopes = null;
782
            this.spatialIndexes = null;
783
        }
784
        
785
    }
786
     
787
    
788
    private void loadOrCreateSpatialIndex() {
789
        FeatureSetProvider set = null;
790
        DisposableIterator<FeatureProvider> it = null;
791
        try {
792
            if (this.virtualrows == null) {
793
                return;
794
            }
795
            
796
            this.spatialIndexes = new HashMap<>();
797

    
798
            for (FeatureAttributeDescriptor geomdesc : this.featureType) {
799
                if (geomdesc.getType() != DataTypes.GEOMETRY) {
800
                    continue;
801
                }
802
                String indexTypeName = GeometryManager.SPATIALINDEX_DEFAULT_QUADTREE;
803
                String extname = "qtree";
804
                String geomName = geomdesc.getName();
805
                Envelope env = bboxFileLoad("_"+geomName);
806
                File indexfile = this.getAuxFile("_"+geomName, extname);
807
                boolean createIndex = !indexfile.exists();
808

    
809
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
810
                SpatialIndexFactory indexfactory = geomManager.getSpatialIndexFactory(indexTypeName);
811
                DynObject params = indexfactory.createParameters();
812
                params.setDynValue("file", indexfile);
813
                SpatialIndex index = geomManager.createSpatialIndex(indexTypeName, params);
814
                if (createIndex) {
815
                    I18nManager i18n = ToolsLocator.getI18nManager();
816
                    this.taskStatus.add();
817
                    taskStatus.message(i18n.getTranslation("_Creating_spatial_index"));
818
                    taskStatus.setRangeOfValues(0, this.virtualrows.size64());
819
                    taskStatus.setCurValue(0);
820
                    Envelope theEnvelope = geomManager.createEnvelope(Geometry.SUBTYPES.GEOM2D);
821
                    set = this.createSet(null, featureType);
822
                    it = set.fastIterator();
823
                    while (it.hasNext()) {
824
                        taskStatus.incrementCurrentValue();
825
                        if (taskStatus.isCancellationRequested()) {
826
                            taskStatus.cancel();
827
                            LOGGER.info("Spatial index creation cancelled (" + getFullFileName() + ")");
828
                            break;
829
                        }
830
                        FeatureProvider f = it.next();
831
                        if (f == null) {
832
                            continue;
833
                        }
834
                        Object oid = null;
835
                        try {
836
                            oid = f.getOID();
837
                            Geometry geom = (Geometry) f.get(geomdesc.getName());
838
                            if (geom != null) {
839
                                index.insert(geom, oid);
840
                                theEnvelope.add(geom);
841
                            }
842
                        } catch (Throwable ex) {
843
                            LOGGER.debug("Can't insert feature '" + Objects.toString(oid) + "' in spatial index.", ex);
844
                        }
845
                    }
846
                    taskStatus.message(i18n.getTranslation("_Saving_spatial_index"));
847
                    taskStatus.setIndeterminate();
848
                    index.flush();
849
                    if (!theEnvelope.isEmpty()) {
850
                        bboxFileSave("_"+geomName, theEnvelope);
851
                    }
852
                    this.envelopes.put(geomName,theEnvelope);
853
                } else {
854
                    this.envelopes.put(geomName,env);
855
                }
856
                this.spatialIndexes.put(geomdesc.getName(), index);
857
            }
858
            taskStatus.terminate();
859
        } catch (Exception ex) {
860
            taskStatus.abort();
861
            LOGGER.warn("Can't create spatial index.", ex);
862
        } finally {
863
            DisposeUtils.disposeQuietly(it);
864
            DisposeUtils.disposeQuietly(set);
865
            taskStatus.remove();
866
        }
867
    }
868

    
869
    public File getAuxFile(String extension) {
870
        return getAuxFile(null, extension);
871
    }
872
    
873
    public File getAuxFile(String suffix, String extension) {
874
        File data_file = SimpleReaderStoreParameters.getFile(this.getSimpleReaderParameters());
875
        if (data_file == null){
876
            return null;
877
        }
878
        File index_file;
879
        if(StringUtils.isBlank(suffix)){
880
            index_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + "." + extension);
881
        } else {
882
            index_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + suffix + "." + extension);
883
        }
884
        return index_file;
885
    }
886

    
887
    public SpatialIndex getSpatialIndex() {
888
        if(spatialIndexes == null){
889
            return null;
890
        }
891
        FeatureAttributeDescriptor geomdesc = this.featureType.getDefaultGeometryAttribute();
892
        if(geomdesc == null){
893
            return null;
894
        }
895
        return spatialIndexes.get(geomdesc.getName());
896
    }
897

    
898
    protected void bboxFileSave(Map<String,Envelope> envelopes) {
899
        for (Map.Entry<String, Envelope> entry : envelopes.entrySet()) {
900
            String key = entry.getKey();
901
            Envelope val = entry.getValue();
902
            bboxFileSave("_"+key, val);
903
        }
904
    }
905
    
906
    protected void bboxFileSave(Envelope envelope) {
907
        bboxFileSave((String)null, envelope);
908
    }
909
    
910
    protected void bboxFileSave(String suffix, Envelope envelope) {
911
        File bboxfile = this.getAuxFile(suffix,"bbox");
912
        bboxFileSave(bboxfile, envelope);
913
    }
914
    
915
    protected void bboxFileSave(File bboxfile, Envelope envelope) {
916
        if( envelope == null ) {
917
            bboxfile.delete();
918
            return;
919
        }
920
        try {
921
            FileUtils.write(
922
                    bboxfile, 
923
                    envelope.getBox2D().convertToWKTQuietly(), 
924
                    StandardCharsets.UTF_8
925
            );
926
        } catch(Exception ex) {
927
            LOGGER.warn("Can't write bbox file '"+Objects.toString(bboxfile)+"'.",ex);
928
        }
929
    }
930
    
931
    protected Envelope bboxFileLoad(String suffix) {
932
        File bboxfile = this.getAuxFile(suffix, "bbox");
933
        return bboxFileLoad(bboxfile);
934
    }
935
    
936
    protected Envelope bboxFileLoad(File bboxfile) {
937
        try {
938
            if( bboxfile.exists() ) {
939
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
940
                String wkt = FileUtils.readFileToString(bboxfile, StandardCharsets.UTF_8);                    
941
                Geometry geom = geomManager.createFrom(wkt);
942
                if( geom!=null ) {
943
                    return geom.getEnvelope();
944
                }
945
            }
946
        } catch(Exception ex) {
947
            LOGGER.warn("Can't load bbox file",ex);
948
        }
949
        return null;
950
    }
951
    
952
    protected abstract SimpleReader getSimpleReader(SimpleReaderStoreParameters parameters, Reader in) throws IOException ;//{
953
//        return CSVUtils.getSimpleReader(getSimpleReaderParameters(), in);
954
//    }
955
    
956
}