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

History | View | Annotate | Download (39.1 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.logger.FilteredLogger;
97
import org.gvsig.tools.persistence.PersistentState;
98
import org.gvsig.tools.persistence.exception.PersistenceException;
99
import org.gvsig.tools.task.SimpleTaskStatus;
100
import org.gvsig.tools.task.TaskStatusManager;
101
import org.gvsig.tools.task.UserCancelTaskException;
102
import org.gvsig.tools.util.GetItemWithSize64;
103
import org.gvsig.tools.visitor.VisitCanceledException;
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
    private static FilteredLogger timedLogger;
114

    
115
    protected final ResourceProvider resource;
116

    
117
    protected long counterNewsOIDs = 0;
118
    protected Map<String,Envelope> envelopes;
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 Map<String,SpatialIndex> spatialIndexes;
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(this.getProviderName());
139
        this.taskStatus.setAutoremove(true);
140

    
141
        this.envelopes = new HashMap<>();
142
        counterNewsOIDs = 0;
143

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

    
150
        resource.addConsumer(this);
151

    
152
        initializeFeatureTypes();
153
    }
154

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

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

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

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

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

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

    
210
    }
211

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

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

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

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

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

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

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

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

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

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

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

    
272
    @Override
273
    public Envelope getEnvelope(String geomName) throws DataException {
274
        this.open();
275
        if(geomName == null){
276
            FeatureAttributeDescriptor geomdesc = this.featureType.getDefaultGeometryAttribute();
277
            geomName = geomdesc.getName();
278
        }
279
        Envelope env = this.envelopes.get(geomName);
280
        if (env != null) {
281
            return env;
282
        }
283

    
284
        File data_file = SimpleReaderStoreParameters.getFile(this.getSimpleReaderParameters());
285
        File bboxfile = this.getAuxFile("_"+geomName, "bbox");
286
        if (bboxfile.exists() && SimpleReaderUtils.isFileNewer(bboxfile, data_file)) {
287
            env = bboxFileLoad(bboxfile);
288
            if (env != null) {
289
                this.envelopes.put(geomName, env);
290
                return env;
291
            }
292
        }
293
        if (!this.need_calculate_envelope) {
294
            return null;
295
        }
296
        final Envelope theEnv;
297
        try {
298
            I18nManager i18n = ToolsLocator.getI18nManager();
299
            this.taskStatus.add();
300
            this.taskStatus.message(i18n.getTranslation("_Calculating_envelope"));
301
            FeatureStore fs = this.getFeatureStore();
302
            FeatureType ft = fs.getDefaultFeatureType();
303
            FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
304
            this.taskStatus.setRangeOfValues(0, fs.getFeatureCount());
305
            theEnv = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
306
            final String theGeomName = geomName;
307
            fs.accept((Object obj) -> {
308
                taskStatus.incrementCurrentValue();
309
                if(taskStatus.isCancellationRequested()){
310
                    taskStatus.cancel();
311
                    throw new VisitCanceledException();
312
                }
313
                Feature f = (Feature) obj;
314
                Geometry geom = f.getGeometry(theGeomName);
315
                if (geom != null) {
316
                    try {
317
                        Envelope envelope = geom.getEnvelope();
318
                        theEnv.add(envelope);
319
                    } catch(Exception ex) {
320
                        LOGGER.warn("Can't calculate envelop of geometry in feature '"+Objects.toString(f.getReference())+"'.",ex);
321
                    }
322
                }
323
            });
324
            bboxFileSave("_"+theGeomName,theEnv);
325
            taskStatus.terminate();
326
        } catch (VisitCanceledException e) {
327
            return null;
328
        } catch (BaseException e) {
329
            taskStatus.abort();
330
            LOGGER.warn("Can't calculate the envelope of file '" + this.getFullName() + "'.", e);
331
            return null;
332
        }
333

    
334
        this.need_calculate_envelope = false;
335
        return theEnv;
336
        
337
    }
338
    
339
    @Override
340
    public Envelope getEnvelope() throws DataException {
341
        return getEnvelope(null);
342
    }
343

    
344
    @Override
345
    public Object getDynValue(String name) throws DynFieldNotFoundException {
346
        if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
347
            try {
348
                return this.getEnvelope();
349
            } catch (DataException e) {
350
                return null;
351
            }
352
        } else {
353
            if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
354
                IProjection pro = SimpleReaderStoreParameters.getCRS(this.getSimpleReaderParameters());
355
                if (pro != null) {
356
                    return pro;
357
                }
358
            }
359
        }
360
        return super.getDynValue(name);
361
    }
362

    
363
    @Override
364
    public void resourceChanged(ResourceProvider resource) {
365
        this.getStoreServices().notifyChange(
366
                DataStoreNotification.RESOURCE_CHANGED,
367
                resource);
368
    }
369

    
370
    @Override
371
    public Object getSourceId() {
372
        return this.getSimpleReaderParameters().getFile();
373
    }
374

    
375
    @Override
376
    public String getName() {
377
        String name = this.getSimpleReaderParameters().getFile().getName();
378
        return FilenameUtils.getBaseName(name);
379
    }
380

    
381
    @Override
382
    public String getFullName() {
383
        return this.getSimpleReaderParameters().getFile().getAbsolutePath();
384
    }
385

    
386
    @Override
387
    public ResourceProvider getResource() {
388
        return resource;
389
    }
390

    
391
    private boolean isEmpty(String s) {
392
        if (s == null) {
393
            return true;
394
        }
395
        return s.trim().length() == 0;
396
    }
397

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

    
402
    static class ToPointEvaluaror extends AbstractEvaluator {
403

    
404
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
405

    
406
        private GeometryManager geommgr = null;
407
        private String xname = null;
408
        private String yname = null;
409
        private String zname = null;
410
        private final Coercion toDouble;
411
        private int errorcount = 0;
412

    
413
        ToPointEvaluaror(String[] pointDimensionNames) {
414
            this.xname = pointDimensionNames[0];
415
            this.yname = pointDimensionNames[1];
416
            if (pointDimensionNames.length > 2) {
417
                this.zname = pointDimensionNames[2];
418
            }
419
            this.geommgr = GeometryLocator.getGeometryManager();
420
            this.toDouble = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.DOUBLE);
421
        }
422

    
423
        @Override
424
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
425
            try {
426
                double x = ((Double) toDouble.coerce(data.getDataValue(xname)));
427
                double y = ((Double) toDouble.coerce(data.getDataValue(yname)));
428
                Point point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
429
                if (zname != null) {
430
                    double z = ((Double) toDouble.coerce(data.getDataValue(zname)));
431
                    point.setCoordinateAt(2, z);
432
                }
433
                return point;
434
            } catch (Exception ex) {
435
                if (++errorcount < 5) {
436
                    logger.warn("[" + errorcount + "] Can't create point. XNAME='"
437
                            + xname + "', YNAME='" + yname + "', ZNAME='" + zname + "', data=" + data.toString());
438
                }
439
                return null;
440
            }
441
        }
442

    
443
        @Override
444
        public String getName() {
445
            return "ToPointEvaluaror";
446
        }
447

    
448
    }
449

    
450
    public static class RowToFeatureTranslator {
451

    
452
        private Coercion coercion[];
453
        private CoercionContext coercionContext[];
454
        private int sizes[];
455
        private String[] names;
456
        private final boolean ignore_errors;
457
        private long count_errors;
458
        private FeatureType csvFeatureType;
459

    
460
        public RowToFeatureTranslator(boolean ignore_errors) {
461
            this.ignore_errors = ignore_errors;
462
            this.count_errors = 0;
463
        }
464

    
465
        public int getColumnSize(int column) {
466
            return this.sizes[column];
467
        }
468

    
469
        public void initialize(FeatureType ftype) {
470
            this.csvFeatureType = ftype;
471
            int columns = this.csvFeatureType.size();
472
            this.names = new String[columns];
473
            this.coercion = new Coercion[columns];
474
            this.coercionContext = new CoercionContext[columns];
475
            this.sizes = new int[columns];
476
            int index = 0;
477
            for (int i = 0; i < this.csvFeatureType.size(); i++) {
478
                FeatureAttributeDescriptor ad = this.csvFeatureType.getAttributeDescriptor(i);
479
                names[i] = null;
480
                if( ad.isComputed() ) {
481
                    continue;
482
                }
483
                names[index] = ad.getName();
484
                coercion[index] = ad.getCoercion();
485
                coercionContext[index] = ad.getCoercionContext();
486
                sizes[index] = ad.getSize();
487
                index++;
488
            }
489
        }
490

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

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

    
534
    protected abstract SimpleReaderFeatureTypeLoader getFeatureTypeLoader();
535
    
536
    protected void loadFeatures() {
537
        InputStreamReader in = null;
538
        SimpleReader reader = null;
539
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
540

    
541
        try {
542
            taskStatus.setTitle(this.getProviderName()+" "+this.getName());
543
            taskStatus.add();
544

    
545
            // Initialize the feature types
546
            EditableFeatureType edftype = getStoreServices().createFeatureType(this.getName());
547
            SimpleReaderFeatureTypeLoader featureTypeLoader = getFeatureTypeLoader();
548
            featureTypeLoader.loadFeatureType(edftype, taskStatus);
549
            FeatureType ftype = edftype.getNotEditableCopy();
550
            this.setFeatureType(ftype);
551

    
552
            in = SimpleReaderUtils.openFile(
553
                    this.getSimpleReaderParameters().getFile(),
554
                    SimpleReaderStoreParameters.getCharset(this.getSimpleReaderParameters())
555
            );
556
            reader = getSimpleReader(getSimpleReaderParameters(), in);
557
            if (featureTypeLoader.isFirstLineHeader()) {
558
                reader.getHeader(); // Skip and ignore the header of file
559
            }
560
            this.rowToFeatureTranslator = new RowToFeatureTranslator(
561
                    SimpleReaderStoreParameters.getIgnoreErrors(getSimpleReaderParameters())
562
            );
563
            this.rowToFeatureTranslator.initialize(ftype);
564
            if (ftype.getDefaultGeometryAttributeName() != null) {
565
                this.need_calculate_envelope = true;
566
            }
567
            I18nManager i18n = ToolsLocator.getI18nManager();
568
            taskStatus.message(i18n.getTranslation("_Loading"));
569

    
570
            if(this.virtualrows != null && this.virtualrows instanceof Closeable){
571
                IOUtils.closeQuietly((Closeable) this.virtualrows);
572
                this.virtualrows = null;
573
            }
574
            
575
            this.virtualrows = ((AbstractSimpleReader) reader).getVirtualRows(this.taskStatus);
576
            if(this.taskStatus.isCancellationRequested()){
577
                throw new UserCancelTaskException();
578
            }
579
            if (this.virtualrows == null) {
580
                this.spatialIndexes = new HashMap<>();
581

    
582
                taskStatus.message(i18n.getTranslation("_Loading"));
583
                taskStatus.setIndeterminate();
584

    
585
                List<String> row = reader.read();
586

    
587
                int skipLines = SimpleReaderStoreParameters.getSkipLines(getSimpleReaderParameters());
588
                if (skipLines > 0) {
589
                    row = reader.skip(skipLines);
590
                }
591
                int limit = SimpleReaderStoreParameters.getLimit(getSimpleReaderParameters());
592
                while (row != null) {
593
                    taskStatus.incrementCurrentValue();
594
                    if( taskStatus.isCancellationRequested() ) {
595
                        taskStatus.cancel();
596
                        break;
597
                    }
598
                    FeatureProvider feature = this.createFeatureProvider(ftype);
599
                    this.rowToFeatureTranslator.translate(reader.getLine(), row, feature);
600
                    
601
                    if (feature != null) {
602
                        for (FeatureAttributeDescriptor geomdesc : this.featureType) {
603
                            if (geomdesc.getType() != DataTypes.GEOMETRY) {
604
                                continue;
605
                            }
606
                            String geomName = geomdesc.getName();
607
                            Envelope theEnvelope = this.envelopes.get(geomName);
608
                            if (theEnvelope == null) {
609
                                theEnvelope = geomManager.createEnvelope(geomdesc.getGeomType().getSubType());
610
                                this.envelopes.put(geomName, theEnvelope);
611
                            }
612
                            SpatialIndex theSpatialIndex = this.spatialIndexes.get(geomName);
613
                            if (theSpatialIndex == null) {
614
                                String indexTypeName = GeometryManager.SPATIALINDEX_DEFAULT_QUADTREE;
615
                                String extname = "qtree";
616
                                File indexfile = this.getAuxFile("_"+geomName, extname);
617

    
618
                                SpatialIndexFactory indexfactory = geomManager.getSpatialIndexFactory(indexTypeName);
619
                                DynObject params = indexfactory.createParameters();
620
                                params.setDynValue("file", indexfile);
621
                                theSpatialIndex = geomManager.createSpatialIndex(indexTypeName, params);
622
                                this.spatialIndexes.put(geomName, theSpatialIndex);
623
                            }
624
                            Object oid = null;
625
                            try {
626
                                oid = feature.getOID();
627
                                Geometry geom = (Geometry) feature.get(geomdesc.getName());
628
                                if (geom != null) {
629
                                    theEnvelope.add(geom);
630
                                    theSpatialIndex.insert(geom, oid);
631
                                }
632
                            } catch (Throwable ex) {
633
                                LOGGER.debug("Can't insert feature '" + Objects.toString(oid) + "' in spatial index.", ex);
634
                            }
635
                        }
636
                    }
637

    
638
                    this.addFeatureProvider(feature);
639
                    if (limit > 0) {
640
                        if (limit < this.data.size()) {
641
                            break;
642
                        }
643
                    }
644
                    row = reader.read();
645
                }
646
                for (SpatialIndex index : this.spatialIndexes.values()) {
647
                    index.flush();
648
                }
649
                bboxFileSave(envelopes);
650
                for (int i = 0; i < ftype.size(); i++) {
651
                    if (this.rowToFeatureTranslator.getColumnSize(i) > 0) {
652
                        EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor) edftype.getAttributeDescriptor(i));
653
                        efad.setSize(this.rowToFeatureTranslator.getColumnSize(i));
654
                    }
655
                }
656
                // Volvemos a asignar al store el featuretype, ya que puede
657
                // haber cambiado.
658
                ftype = edftype.getNotEditableCopy();
659
                this.setFeatureType(ftype);
660
            } else {
661
                this.loadOrCreateSpatialIndex();
662
            }
663
            if( taskStatus.isRunning() ) {
664
                taskStatus.terminate();
665
            }
666
        } catch (UserCancelTaskException ex) {
667
            this.taskStatus.cancel();
668
            throw ex;
669
        } catch (Throwable ex) {
670
            taskStatus.abort();
671
            int lineno = 0;
672
            if (reader != null) {
673
                lineno = reader.getLine();
674
            }
675
            throw new RuntimeException("Problems reading file '" + getFullFileName() + "' near record " + lineno + ".", ex);
676

    
677
        } finally {
678
            if (reader != null) {
679
                try {
680
                    reader.close();
681
                } catch (Exception ex) {
682
                    // Do nothing
683
                }
684
//                reader = null;
685
            }
686
            if (in != null) {
687
                try {
688
                    in.close();
689
                } catch (Exception ex) {
690
                    // Do nothing
691
                }
692
//                in = null;
693
            }
694
            taskStatus.remove();
695
        }
696
    }
697

    
698
    @Override
699
    public void fixFeatureTypeFromParameters() {
700
        if(mustFixFeatureType() && featureType != null){
701
            this.setFeatureType(featureType);
702
        }
703
    }
704
    
705
    protected boolean mustFixFeatureType() {
706
        FeatureStore theStore = this.getStoreServices().getFeatureStore();
707
        FeatureType ft = theStore.getDefaultFeatureTypeQuietly();
708
        String geomName = ft.getDefaultGeometryAttributeName();
709
        if(StringUtils.isNotBlank(geomName)){
710
            if(this.featureType.getAttributeDescriptor(geomName)==null){
711
                return true;
712
            }
713
        }
714
        return false;
715
    }
716

    
717
    private void setFeatureType(FeatureType ftype) {
718
        try {
719
            List<FeatureType> ftypes = new ArrayList<>();
720
            ftypes.add(ftype);
721
            this.featureType = ftype;
722
            if(this.getStoreServices().getDefaultFeatureType() == null){
723
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
724
                return;
725
            }
726
            if(mustFixFeatureType()){
727
                this.getStoreServices().setFeatureTypes(ftypes, ftype);
728
            }
729
        } catch (DataException ex) {
730
            LOGGER.warn("Cant set feature type", ex);
731
        }
732
    }
733

    
734
    @Override
735
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType providerFeatureType, FeatureType featureType)
736
            throws DataException {
737
        this.open();
738
        if (this.virtualrows == null) {
739
            return super.createSet(query, providerFeatureType, featureType);
740
        }
741
        return new SimpleReaderSetProvider(this, query, providerFeatureType, featureType);
742
    }
743

    
744
    @Override
745
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType featureType)
746
            throws DataException {
747
        this.open();
748
        if (this.virtualrows == null) {
749
            return super.createSet(query, featureType);
750
        }
751
        return new SimpleReaderSetProvider(this, query, featureType, featureType);
752
    }
753

    
754
    public List<String> getRowByIndex(long index) {
755
        try {
756
            this.open();
757
        } catch(Exception ex) {
758
            throw new RuntimeException("Can't get row by index", ex);
759
        }
760
        if (this.virtualrows == null) {
761
            return null;
762
        }
763
        List<String> line = this.virtualrows.get64(index);
764
        return line;
765
    }
766

    
767
    public RowToFeatureTranslator getRowToFeatureTranslator() {
768
        if (this.rowToFeatureTranslator == null) {
769
            boolean ignore_errors = SimpleReaderStoreParameters.getIgnoreErrors(getSimpleReaderParameters());
770
            this.rowToFeatureTranslator = new RowToFeatureTranslator(ignore_errors);
771
            this.rowToFeatureTranslator.initialize(featureType);
772
        }
773
        return this.rowToFeatureTranslator;
774
    }
775

    
776
    @Override
777
    public long getFeatureCount() throws DataException {
778
        this.open();
779
        if (this.virtualrows == null) {
780
            return super.getFeatureCount();
781
        }
782
        return this.virtualrows.size64();
783
    }
784

    
785
    @Override
786
    public long getDataSize() throws DataException {
787
        this.open();
788
        if (this.virtualrows == null) {
789
            return super.getDataSize();
790
        }
791
        return this.virtualrows.size64();
792
    }
793

    
794
    @Override
795
    protected FeatureProvider internalGetFeatureProviderByReference(
796
            FeatureReferenceProviderServices reference) throws DataException {
797
        this.open();
798
        if (this.virtualrows == null) {
799
            return super.internalGetFeatureProviderByReference(reference);
800
        }
801
        int oid = ((Long) reference.getOID()).intValue();
802
        RowToFeatureTranslator translator = getRowToFeatureTranslator();
803
        FeatureProvider feature = this.createFeatureProvider(this.featureType);
804
        try {
805
            translator.translate(oid, this.virtualrows.get64(oid), feature);
806
        } catch (Exception ex) {
807
            throw new CreateFeatureException(ex, this.getName());
808
        }
809
        return feature;
810
    }
811

    
812
    @Override
813
    protected void doDispose() throws BaseException {
814
        super.doDispose();
815
        if (this.virtualrows != null && this.virtualrows instanceof Closeable) {
816
            IOUtils.closeQuietly((Closeable) this.virtualrows);
817
            this.virtualrows = null;
818
        }
819
    }
820
    
821
    @Override
822
     public void refresh() throws OpenException {
823
        try {
824
            this.close();
825
        } catch (CloseException e) {
826
            throw new OpenException(this.getProviderName(), e);
827
        }
828
        this.open();
829
    }
830

    
831
    @Override
832
    public void close() throws CloseException {
833
        super.close(); //To change body of generated methods, choose Tools | Templates.
834
        this.data = null;
835
        if(this.virtualrows != null && this.virtualrows instanceof Closeable){
836
            IOUtils.closeQuietly((Closeable) this.virtualrows);
837
            this.virtualrows = null;
838
            this.envelopes = null;
839
            this.spatialIndexes = null;
840
        }
841
        
842
    }
843
     
844
    
845
    private void loadOrCreateSpatialIndex() {
846
        FeatureSetProvider set = null;
847
        DisposableIterator<FeatureProvider> it = null;
848
        try {
849
            if (this.virtualrows == null) {
850
                return;
851
            }
852
            
853
            this.spatialIndexes = new HashMap<>();
854

    
855
            for (FeatureAttributeDescriptor geomdesc : this.featureType) {
856
                if (geomdesc.getType() != DataTypes.GEOMETRY) {
857
                    continue;
858
                }
859
                String indexTypeName = GeometryManager.SPATIALINDEX_DEFAULT_QUADTREE;
860
                String extname = "qtree";
861
                String geomName = geomdesc.getName();
862
                Envelope env = bboxFileLoad("_"+geomName);
863
                File indexfile = this.getAuxFile("_"+geomName, extname);
864

    
865
                File data_file = SimpleReaderStoreParameters.getFile(this.getSimpleReaderParameters());
866
                boolean createIndex = !indexfile.exists() || SimpleReaderUtils.isFileNewer(data_file, indexfile);
867

    
868
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
869
                SpatialIndexFactory indexfactory = geomManager.getSpatialIndexFactory(indexTypeName);
870
                DynObject params = indexfactory.createParameters();
871
                params.setDynValue("file", indexfile);
872
                SpatialIndex index = geomManager.createSpatialIndex(indexTypeName, params);
873
                if (createIndex) {
874
                    I18nManager i18n = ToolsLocator.getI18nManager();
875
                    this.taskStatus.add();
876
                    taskStatus.message(i18n.getTranslation("_Creating_spatial_index")+" ("+geomName+")");
877
                    taskStatus.setRangeOfValues(0, this.virtualrows.size64());
878
                    taskStatus.setCurValue(0);
879
                    Envelope theEnvelope = geomManager.createEnvelope(Geometry.SUBTYPES.GEOM2D);
880
                    set = this.createSet(null, featureType);
881
                    it = set.fastIterator();
882
                    while (it.hasNext()) {
883
                        taskStatus.incrementCurrentValue();
884
                        if (taskStatus.isCancellationRequested()) {
885
                            taskStatus.cancel();
886
                            LOGGER.info("Spatial index creation cancelled (" + getFullFileName() + ")");
887
                            break;
888
                        }
889
                        FeatureProvider f = it.next();
890
                        if (f == null) {
891
                            continue;
892
                        }
893
                        Object oid = null;
894
                        try {
895
                            oid = f.getOID();
896
                            Geometry geom = (Geometry) f.get(geomdesc.getName());
897
                            if (geom != null) {
898
                                index.insert(geom, oid);
899
                                theEnvelope.add(geom);
900
                            }
901
                        } catch (Throwable ex) {
902
                            LOGGER.debug("Can't insert feature '" + Objects.toString(oid) + "' in spatial index.", ex);
903
                        }
904
                    }
905
                    taskStatus.message(i18n.getTranslation("_Saving_spatial_index"));
906
                    taskStatus.setIndeterminate();
907
                    index.flush();
908
                    if (!theEnvelope.isEmpty()) {
909
                        bboxFileSave("_"+geomName, theEnvelope);
910
                    }
911
                    this.envelopes.put(geomName,theEnvelope);
912
                } else {
913
                    this.envelopes.put(geomName,env);
914
                }
915
                this.spatialIndexes.put(geomdesc.getName(), index);
916
            }
917
            taskStatus.terminate();
918
        } catch (Exception ex) {
919
            taskStatus.abort();
920
            LOGGER.warn("Can't create spatial index.", ex);
921
        } finally {
922
            DisposeUtils.disposeQuietly(it);
923
            DisposeUtils.disposeQuietly(set);
924
            taskStatus.remove();
925
        }
926
    }
927

    
928
    public File getAuxFile(String extension) {
929
        return getAuxFile(null, extension);
930
    }
931
    
932
    public File getAuxFile(String suffix, String extension) {
933
        File data_file = SimpleReaderStoreParameters.getFile(this.getSimpleReaderParameters());
934
        if (data_file == null){
935
            return null;
936
        }
937
        File index_file;
938
        if(StringUtils.isBlank(suffix)){
939
            index_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + "." + extension);
940
        } else {
941
            index_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + suffix + "." + extension);
942
        }
943
        return index_file;
944
    }
945

    
946
    public SpatialIndex getSpatialIndex(String geomName) {
947
        if(spatialIndexes == null){
948
            return null;
949
        }
950
        return spatialIndexes.get(geomName);
951
    }
952

    
953
    public SpatialIndex getSpatialIndex() {
954
        if(spatialIndexes == null){
955
            return null;
956
        }
957
        FeatureAttributeDescriptor geomdesc = this.featureType.getDefaultGeometryAttribute();
958
        if(geomdesc == null){
959
            return null;
960
        }
961
        return spatialIndexes.get(geomdesc.getName());
962
    }
963

    
964
    protected void bboxFileSave(Map<String,Envelope> envelopes) {
965
        for (Map.Entry<String, Envelope> entry : envelopes.entrySet()) {
966
            String key = entry.getKey();
967
            Envelope val = entry.getValue();
968
            bboxFileSave("_"+key, val);
969
        }
970
    }
971
    
972
    protected void bboxFileSave(Envelope envelope) {
973
        bboxFileSave((String)null, envelope);
974
    }
975
    
976
    protected void bboxFileSave(String suffix, Envelope envelope) {
977
        File bboxfile = this.getAuxFile(suffix,"bbox");
978
        bboxFileSave(bboxfile, envelope);
979
    }
980
    
981
    protected void bboxFileSave(File bboxfile, Envelope envelope) {
982
        if( envelope == null || envelope.isEmpty() ) {
983
            bboxfile.delete();
984
            return;
985
        }
986
        try {
987
            FileUtils.write(
988
                    bboxfile, 
989
                    envelope.getBox2D().convertToWKTQuietly(), 
990
                    StandardCharsets.UTF_8
991
            );
992
        } catch(Exception ex) {
993
            LOGGER.warn("Can't write bbox file '"+Objects.toString(bboxfile)+"'.",ex);
994
        }
995
    }
996
    
997
    protected Envelope bboxFileLoad(String suffix) {
998
        File bboxfile = this.getAuxFile(suffix, "bbox");
999
        return bboxFileLoad(bboxfile);
1000
    }
1001
    
1002
    protected Envelope bboxFileLoad(File bboxfile) {
1003
        try {
1004
            if( bboxfile.exists() ) {
1005
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
1006
                String wkt = FileUtils.readFileToString(bboxfile, StandardCharsets.UTF_8);                    
1007
                Geometry geom = geomManager.createFrom(wkt);
1008
                if( geom!=null ) {
1009
                    return geom.getEnvelope();
1010
                }
1011
            }
1012
        } catch(Exception ex) {
1013
            LOGGER.warn("Can't load bbox file",ex);
1014
        }
1015
        return null;
1016
    }
1017
    
1018
    protected abstract SimpleReader getSimpleReader(SimpleReaderStoreParameters parameters, Reader in) throws IOException ;
1019

    
1020
    public FilteredLogger getTimedLogger() {
1021
        if(timedLogger == null){
1022
            timedLogger = new FilteredLogger(LOGGER, "AbstractFeatureProviderLoadedOnDemand", 5000L);
1023
        }
1024
        return timedLogger;
1025
    }
1026
    
1027
}