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 / csv / CSVStoreProvider.java @ 41348

History | View | Annotate | Download (39.8 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
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.csv;
25

    
26
import java.io.File;
27
import java.io.FileReader;
28
import java.io.FileWriter;
29
import java.io.IOException;
30
import java.net.URL;
31
import java.text.SimpleDateFormat;
32
import java.util.ArrayList;
33
import java.util.HashMap;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.Locale;
37

    
38
import org.apache.commons.io.FilenameUtils;
39
import org.cresques.cts.IProjection;
40
import org.gvsig.fmap.dal.DALLocator;
41
import org.gvsig.fmap.dal.DataManager;
42
import org.gvsig.fmap.dal.DataServerExplorer;
43
import org.gvsig.fmap.dal.DataStore;
44
import org.gvsig.fmap.dal.DataStoreNotification;
45
import org.gvsig.fmap.dal.DataTypes;
46
import org.gvsig.fmap.dal.FileHelper;
47
import org.gvsig.fmap.dal.exception.DataException;
48
import org.gvsig.fmap.dal.exception.InitializeException;
49
import org.gvsig.fmap.dal.exception.OpenException;
50
import org.gvsig.fmap.dal.exception.ReadException;
51
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
52
import org.gvsig.fmap.dal.feature.EditableFeature;
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.FeatureAttributeEmulator;
58
import org.gvsig.fmap.dal.feature.FeatureSet;
59
import org.gvsig.fmap.dal.feature.FeatureStore;
60
import org.gvsig.fmap.dal.feature.FeatureType;
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.FeatureStoreProviderServices;
64
import org.gvsig.fmap.dal.feature.spi.memory.AbstractMemoryStoreProvider;
65
import org.gvsig.fmap.dal.resource.ResourceAction;
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.geom.Geometry;
73
import org.gvsig.fmap.geom.GeometryLocator;
74
import org.gvsig.fmap.geom.GeometryManager;
75
import org.gvsig.fmap.geom.aggregate.MultiPoint;
76
import org.gvsig.fmap.geom.primitive.Envelope;
77
import org.gvsig.fmap.geom.primitive.Point;
78
import org.gvsig.fmap.geom.type.GeometryType;
79
import org.gvsig.tools.ToolsLocator;
80
import org.gvsig.tools.dataTypes.CoercionException;
81
import org.gvsig.tools.dataTypes.DataType;
82
import org.gvsig.tools.dataTypes.DataTypesManager;
83
import org.gvsig.tools.dataTypes.DataTypesManager.Coercion;
84
import org.gvsig.tools.dataTypes.DataTypesManager.CoercionWithLocale;
85
import org.gvsig.tools.dispose.DisposableIterator;
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.persistence.PersistentState;
94
import org.gvsig.tools.persistence.exception.PersistenceException;
95
import org.gvsig.tools.task.SimpleTaskStatus;
96
import org.gvsig.tools.task.TaskStatusManager;
97
import org.gvsig.tools.visitor.VisitCanceledException;
98
import org.gvsig.tools.visitor.Visitor;
99
import org.slf4j.Logger;
100
import org.slf4j.LoggerFactory;
101
import org.supercsv.comment.CommentStartsWith;
102
import org.supercsv.io.CsvListReader;
103
import org.supercsv.io.CsvListWriter;
104
import org.supercsv.prefs.CsvPreference;
105
import org.supercsv.quote.QuoteMode;
106

    
107
public class CSVStoreProvider extends AbstractMemoryStoreProvider implements
108
ResourceConsumer {
109
    private static final Logger logger = LoggerFactory.getLogger(CSVStoreProvider.class);
110

    
111
    public static final String NAME = "CSV";
112
    public static final String DESCRIPTION = "CSV file";
113

    
114
    public static final String METADATA_DEFINITION_NAME = NAME;
115

    
116
    private ResourceProvider resource;
117

    
118
    private long counterNewsOIDs = 0;
119
    private Envelope envelope;
120
    private boolean need_calculate_envelope = false;
121
    private SimpleTaskStatus taskStatus;
122

    
123

    
124
    public CSVStoreProvider(CSVStoreParameters parameters,
125
        DataStoreProviderServices storeServices) throws InitializeException {
126
        super(
127
            parameters, 
128
            storeServices,
129
            FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME)
130
        );
131

    
132
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
133
        this.taskStatus = manager.createDefaultSimpleTaskStatus("CSV");
134

    
135
        counterNewsOIDs = 0;
136

    
137
        File file = getCSVParameters().getFile();
138
        resource = this.createResource(
139
            FileResource.NAME,
140
            new Object[] { file.getAbsolutePath() }
141
        );
142

    
143
        resource.addConsumer(this);
144
        initializeFeatureTypes();
145
    }
146

    
147
    private CSVStoreParameters getCSVParameters() {
148
        return (CSVStoreParameters) this.getParameters();
149
    }
150

    
151
    public String getProviderName() {
152
        return NAME;
153
    }
154

    
155
    public boolean allowWrite() {
156
        return false;
157
    }
158

    
159
    private String getFullFileName() {
160
            // Usar solo para mostrar mensajes en el logger.
161
                String s = "(unknow)";
162
                try { 
163
                        s = getCSVParameters().getFile().getAbsolutePath();
164
                } catch(Exception e2) {
165
                        s = "(unknow)";
166
                }
167
                return s;
168
    }
169
    
170
    public void open() throws OpenException {
171
        if (this.data != null) {
172
            return;
173
        }
174
        this.data = new ArrayList<FeatureProvider>();
175
        resource.setData(new HashMap());
176
        counterNewsOIDs = 0;
177
                try {
178
                        loadFeatures();
179
                } catch (RuntimeException e) {
180
                        logger.warn("Can't load features from CSV '"+getFullFileName()+"'.", e);
181
                        throw e;
182
                } catch (Exception e) {
183
                        logger.warn("Can't load features from CSV '"+getFullFileName()+"'.", e);
184
                        throw new RuntimeException(e);
185
                }
186
    }
187

    
188
    public DataServerExplorer getExplorer() throws ReadException {
189
        DataManager manager = DALLocator.getDataManager();
190
        FilesystemServerExplorerParameters params;
191
        try {
192
            params = (FilesystemServerExplorerParameters) manager
193
            .createServerExplorerParameters(FilesystemServerExplorer.NAME);
194
            params.setRoot(this.getCSVParameters().getFile().getParent());
195
            return manager.openServerExplorer(FilesystemServerExplorer.NAME,params);
196
        } catch (DataException e) {
197
            throw new ReadException(this.getProviderName(), e);
198
        } catch (ValidateDataParametersException e) {
199
            throw new ReadException(this.getProviderName(), e);
200
        }
201

    
202
    }
203

    
204
    class Writer {
205
            private Envelope envelope = null;
206
                private boolean calculate_envelope = false;
207
                private CsvListWriter listWriter = null;
208
                private CsvPreference csvpreferences = null;
209
                private FileWriter fwriter = null;
210
                private FeatureType ftype;
211
                private File file;
212
                private String[] values;
213
                private FeatureAttributeDescriptor[] descriptors;
214
                private Coercion convert = null;
215
                private int errorcounts =0;
216
                private Throwable lasterror = null;
217
                private Locale locale = null;
218
            
219
            public void initialize(File file, FeatureType ftype, CsvPreference csvpreferences) {
220
                    this.file = file;
221
                    this.ftype = ftype;
222
                        this.csvpreferences = csvpreferences;
223
                        this.locale = CSVStoreParameters.getLocale(getCSVParameters());
224
                    if( csvpreferences == null ) {
225
                            this.csvpreferences = CsvPreference.STANDARD_PREFERENCE;
226
                    }
227
                    if( ftype.getDefaultGeometryAttributeName() != null ) {
228
                            this.calculate_envelope = true;
229
                    }
230
                    this.descriptors = this.ftype.getAttributeDescriptors();
231
                    this.convert = ToolsLocator.getDataTypesManager().getCoercion(org.gvsig.tools.dataTypes.DataTypes.STRING);
232
                    this.errorcounts = 0;
233
            }
234
            
235
            public void begin() {
236
                    try {
237
                                this.fwriter = new FileWriter(file);
238
                        } catch (IOException e) {
239
                                logger.warn("Can't open file for write ("+file.getAbsolutePath()+").",e);
240
                                throw new RuntimeException(e);
241
                        }
242
                    this.listWriter = new CsvListWriter(this.fwriter,this.csvpreferences);
243
                    int n = 0;
244
                    for(int i=0; i<descriptors.length; i++ ) {
245
                            FeatureAttributeDescriptor descriptor = descriptors[i];
246
                            if( descriptor.getEvaluator()== null ) {
247
                                    n++;
248
                            }
249
                    }
250
                            
251
                    String[] header = new String[n];
252
                    this.values = new String[n];
253
                    n = 0;
254
                    for(int i=0; i<descriptors.length; i++ ) {
255
                            FeatureAttributeDescriptor descriptor = descriptors[i];
256
                            if( descriptor.getEvaluator()== null ) {
257
                                    String name = descriptor.getName();
258
                                    String typeName = descriptor.getDataTypeName();
259
                                    if( descriptor.getDataType().getType() == DataTypes.STRING ) {
260
                                            header[n++] = name + "__" + typeName + "__" + descriptor.getSize();
261
                                    } else {
262
                                            header[n++] = name + "__" + typeName;
263
                                    }
264
                            }
265
                    }
266
            try {
267
                                listWriter.writeHeader(header);
268
                        } catch (Exception e) {
269
                                logger.warn("Can't write header '"+header.toString()+"' file for write ("+file.getAbsolutePath()+").",e);
270
                                throw new RuntimeException(e);
271
                        }
272
            }
273
            
274
            public void add(FeatureProvider feature) {
275
                        if (this.calculate_envelope) {
276
                                Geometry geom = feature.getDefaultGeometry();
277
                                if (geom != null) {
278
                                        if (envelope == null) {
279
                                                try {
280
                                                        envelope = (Envelope) geom.getEnvelope().clone();
281
                                                } catch (CloneNotSupportedException e) {
282
                                                        logger.warn("Este error no deberia pasar, siempre se puede hacer un clone de un envelope.",e);
283
                                                }
284
                                        } else {
285
                                                envelope.add(geom.getEnvelope());
286
                                        }
287
                                }
288
                        }
289
                    int n = 0;
290
                    for(int i=0; i<descriptors.length; i++ ) {
291
                            FeatureAttributeDescriptor descriptor = descriptors[i];
292
                            if( descriptor.getEvaluator()== null ) {
293
                                        Object value = feature.get(i);
294
                                    try {
295
                                            n++;
296
                                            if( this.convert!=null && this.convert instanceof CoercionWithLocale ) {
297
                                                    values[n] = (String) ((CoercionWithLocale)this.convert).coerce(value,this.locale);
298
                                            } else {
299
                                                    values[n] = (String) this.convert.coerce(value);
300
                                            }
301
                                        } catch (CoercionException e) {
302
                                                try {
303
                                                        values[n] = value.toString();
304
                                                } catch(Exception ex) {
305
                                                        values[n] = "";
306
                                                }
307
                                                if( errorcounts++ <= 10 ) {
308
                                                        this.lasterror = e;
309
                                                        logger.warn("Can't convert value of field "+i+" to string in CVS file '"+getFullFileName()+"'.",e);
310
                                                        if( errorcounts == 10 ) {
311
                                                                logger.warn("Too many error writing CVS file '"+getFullFileName()+"', don't output more.");
312
                                                        }
313
                                                } 
314
                                        } 
315
                            }
316
                    }
317
                    try {
318
                                this.listWriter.writeHeader(values);
319
                        } catch (IOException e) {
320
                                if( errorcounts++ <= 10 ) {
321
                                        this.lasterror = e;
322
                                        logger.warn("Can't write values to CVS file '"+getFullFileName()+"'.",e);
323
                                        if( errorcounts == 10 ) {
324
                                                logger.warn("Too many error writing CVS file '"+getFullFileName()+"', don't output more.");
325
                                        }
326
                                } 
327
                        }
328
                        
329
            }
330
            
331
            public void end() throws PerformEditingException {
332
                    if( this.errorcounts>0 ) {
333
                            throw new PerformEditingException(this.file.getAbsolutePath(), lasterror);
334
                    }
335
                        if( listWriter!=null ) {
336
                            try {
337
                                    listWriter.close();
338
                            } catch(Exception ex) {
339
                                    // Ignore error
340
                            }
341
                                listWriter=null ;
342
                        }
343
                        if( fwriter!=null ) {
344
                            try {
345
                                    fwriter.close();
346
                            } catch(Exception ex) {
347
                                    // Ignore error
348
                            }
349
                                fwriter=null ;
350
                        }
351
            }
352
            
353
            public Envelope getEnvelope() {
354
                    return this.envelope;
355
            }
356
    }
357

    
358
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds, Iterator originalFeatureTypesUpdated) throws PerformEditingException {
359

    
360
        try {
361
            this.taskStatus.add();
362
            taskStatus.message("_preparing");
363
            getResource().execute(new ResourceAction() {
364
                public Object run() throws Exception {
365
                    FeatureSet features = null;
366
                    DisposableIterator it = null;
367
                    try {
368
                        File file = (File) resource.get();
369
                        
370
                        Writer writer = new Writer();
371
                        writer.initialize(file,getStoreServices().getDefaultFeatureType(),getCSVPreferences());
372
                        features =
373
                            getStoreServices().getFeatureStore()
374
                            .getFeatureSet();
375
                        List<FeatureProvider> newdata = new ArrayList<FeatureProvider>();
376
                        writer.begin();
377
                        it = features.fastIterator();
378
                        taskStatus.setRangeOfValues(0,0);
379
                        long counter=0;
380
                        while (it.hasNext()) {
381
                            taskStatus.setCurValue(counter++);
382
                            FeatureProvider feature = getStoreServices().getFeatureProviderFromFeature(
383
                                (org.gvsig.fmap.dal.feature.Feature) it.next());
384
                            writer.add(feature);
385
                            if (feature.getOID() == null){
386
                                logger.warn("feature without OID");
387
                                feature.setOID(createNewOID());
388
                            }
389
                            newdata.add(feature);
390
                        }
391
                        data = newdata;
392
                        if (writer.getEnvelope() != null){
393
                            envelope = writer.getEnvelope().getGeometry().getEnvelope();
394
                        }
395
                        resource.notifyChanges();
396
                        writer.end();
397
                    } finally {
398
                        if (it != null) {
399
                            it.dispose();
400
                        }
401
                        if (features != null) {
402
                            features.dispose();
403
                        }
404
                    }
405
                    return null;
406
                }
407
            });
408
            this.taskStatus.terminate();
409
        } catch (Exception e) {
410
            this.taskStatus.abort();
411
            throw new PerformEditingException(getResource().toString(), e);
412
        } finally {
413
            this.taskStatus.remove();
414
        }
415
    }
416

    
417
    public boolean closeResourceRequested(ResourceProvider resource) {
418
        return true;
419
    }
420

    
421
    public int getOIDType() {
422
        return DataTypes.LONG;
423
    }
424

    
425
    public boolean supportsAppendMode() {
426
        return false;
427
    }
428

    
429
    public void append(FeatureProvider featureProvider) {
430
            throw new UnsupportedOperationException();
431
    }
432

    
433
    public void beginAppend() {
434
            throw new UnsupportedOperationException();
435
    }
436

    
437
    public void endAppend() {
438
            throw new UnsupportedOperationException();
439
    }
440

    
441
    public void saveToState(PersistentState state) throws PersistenceException {
442
        throw new NotYetImplemented();
443
    }
444

    
445
    public void loadFromState(PersistentState state) throws PersistenceException {
446
        throw new NotYetImplemented();
447
    }
448

    
449
    public Object createNewOID() {
450
        return new Long(counterNewsOIDs++);
451
    }
452

    
453
    protected void initializeFeatureTypes() throws InitializeException {
454
        try {
455
            this.open();
456
        } catch (OpenException e) {
457
            throw new InitializeException(this.getProviderName(), e);
458
        }
459
    }
460

    
461
    public Envelope getEnvelope() throws DataException {
462
        this.open();
463
        if( this.envelope!= null )  {
464
                return this.envelope;
465
        }
466
        if( !this.need_calculate_envelope ) {
467
                return null;
468
        }
469
        FeatureStore fs = this.getFeatureStore();
470
        FeatureType ft = fs.getDefaultFeatureType();
471
        FeatureAttributeDescriptor fad = ft.getAttributeDescriptor(ft.getDefaultGeometryAttributeIndex());
472

    
473
        try {
474
            this.envelope = GeometryLocator.getGeometryManager().createEnvelope(fad.getGeomType().getSubType());
475
                        fs.accept(new Visitor() {
476
                                public void visit(Object obj) throws VisitCanceledException, BaseException {
477
                                        Feature f = (Feature) obj;
478
                                        Geometry geom = f.getDefaultGeometry();
479
                                        if( geom!=null ) {
480
                                            envelope.add(geom.getEnvelope());
481
                                        }
482
                                }
483
                        });
484
                } catch (BaseException e) {
485
                        logger.warn("Can't calculate the envelope of CSV file '"+this.getFullName()+"'.",e);
486
                        this.envelope = null;
487
                }
488
        
489
        this.need_calculate_envelope = false;
490
        return this.envelope;
491
    }
492

    
493
    public Object getDynValue(String name) throws DynFieldNotFoundException {
494
        if( DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name) ) {
495
            try {
496
                return this.getEnvelope();
497
            } catch (DataException e) {
498
                return null;
499
            }
500
        } else {
501
            if( DataStore.METADATA_CRS.equalsIgnoreCase(name) ) {
502
                IProjection pro = CSVStoreParameters.getCRS(this.getCSVParameters());
503
                if (pro != null){
504
                    return pro;
505
                }
506
            }
507
        }
508
        return super.getDynValue(name);
509
    }
510

    
511

    
512
    public void resourceChanged(ResourceProvider resource) {
513
        this.getStoreServices().notifyChange(
514
            DataStoreNotification.RESOURCE_CHANGED,
515
            resource);
516
    }
517

    
518

    
519
    public Object getSourceId() {
520
        return this.getCSVParameters().getFile();
521
    }
522

    
523
    public String getName() {
524
        String name = this.getCSVParameters().getFile().getName();
525
        return FilenameUtils.getBaseName(name);
526
    }
527

    
528
    public String getFullName() {
529
        return this.getCSVParameters().getFile().getAbsolutePath();
530
    }
531

    
532
    public ResourceProvider getResource() {
533
        return resource;
534
    }
535

    
536
    private boolean isEmpty(String s) {
537
            if( s==null ) {
538
                    return true;
539
            }
540
            return s.trim().length()==0;
541
    }
542
    
543
        private CsvPreference getCSVPreferences() {
544
                try {
545
                        String s = null;
546
                        char quoteChar;
547
                        int delimiterChar;
548
                        String endOfLineSymbols;
549

    
550
                        DynObject params = this.getParameters();
551

    
552
                        CsvPreference.Builder builder = null;
553

    
554
                        CsvPreference defaultPreference = CSVStoreParameters
555
                                        .getPredefinedCSVPreferences(params);
556
                        if (defaultPreference == null) {
557
                                defaultPreference = CsvPreference.STANDARD_PREFERENCE;
558
                        }
559

    
560
                        endOfLineSymbols = CSVStoreParameters.getRecordSeparator(params);
561
                        if (isEmpty(endOfLineSymbols)) {
562
                                endOfLineSymbols = defaultPreference.getEndOfLineSymbols();
563
                        }
564
                        s = CSVStoreParameters.getQuoteCharacter(params);
565
                        if (isEmpty(s)) {
566
                                quoteChar = (char) defaultPreference.getQuoteChar();
567
                        } else {
568
                                quoteChar = s.charAt(0);
569
                        }
570
                        s = CSVStoreParameters.getDelimiter(params);
571
                        if (isEmpty(s)) {
572
                                delimiterChar = defaultPreference.getDelimiterChar();
573
                        } else {
574
                                delimiterChar = s.charAt(0);
575
                        }
576

    
577
                        builder = new CsvPreference.Builder(quoteChar, delimiterChar,
578
                                        endOfLineSymbols);
579

    
580
                        s = CSVStoreParameters.getCommentStartMarker(params);
581
                        if (!isEmpty(s)) {
582
                                CommentStartsWith cs = new CommentStartsWith(s);
583
                                builder.skipComments(cs);
584
                        }
585

    
586
                        builder.surroundingSpacesNeedQuotes(CSVStoreParameters
587
                                        .getSurroundingSpacesNeedQuotes(params));
588
                        QuoteMode quoteMode = CSVStoreParameters.getQuoteMode(params);
589
                        if( quoteMode != null ) {
590
                                builder.useQuoteMode(quoteMode);
591
                        }
592
                        return builder.build();
593
                } catch (Exception e) {
594
                        logger.warn("Can't make preferences for CSV '" + getFullFileName()
595
                                        + "'.", e);
596
                        return null;
597
                }
598
        }
599
    
600
    private class FieldTypeParser {
601
        public String name = null;
602
        public int type = DataTypes.STRING;
603
        public int size = 0;
604
        public boolean allowNulls = true;
605

    
606
        private String typename = "string";
607
        
608
        FieldTypeParser() {
609
        }
610
        
611
        private int getType(String value) {
612
            DataTypesManager  dataTypesManager = ToolsLocator.getDataTypesManager();
613
            return dataTypesManager.getType(typename);
614
        }
615
        
616
        // El formato seria:
617
        //   name[:typename[:size[:notnull|null]]]
618
        //   name[__typename[__size[__notnull|null]]]
619
        //
620
        public boolean parse(String value) {
621
            String typename = null;
622
            String[] ss = null;
623
            if( value.contains(":") ) {
624
                ss = value.split(":");
625
            } else if( value.contains("__") ) {
626
                ss = value.split("__");
627
            }
628
            if( ss==null ) {
629
                this.name = value;
630
                return true;
631
            }
632
            switch(ss.length) {
633
            case 4:
634
                if(ss[3].length()>0) {
635
                    if( "notnull".equalsIgnoreCase(ss[3]) ) {
636
                        this.allowNulls = false;
637
                    } else {
638
                        this.allowNulls = true;
639
                    }
640
                }
641
            case 3:
642
                if(ss[2].length()>0) {
643
                    try {
644
                        this.size = Integer.parseInt(ss[2]);
645
                    } catch(Exception ex) {
646
                        logger.warn("Ignore incorrect field size for field "+value+" in CSV header of '"+getFullFileName()+"'.",ex);
647
                    }
648
                }
649
            case 2:
650
                if(ss[1].length()>0) {
651
                    this.typename = ss[1];
652
                    this.type = this.getType(this.typename); 
653
                    if( this.type==DataTypes.INVALID ) {
654
                        this.type = DataTypes.STRING;
655
                        logger.info("Type '"+typename+"' not valid for attribute '"+value+"' in CSV file '"+getFullFileName()+"'.");
656
                    }
657
                }
658
            case 1:
659
                this.name = ss[0];
660
                break;
661
            }
662
            
663
            if( this.type != DataTypes.STRING ) {
664
                this.size = 0;
665
            }
666
            return true;
667
        }
668
        
669
    }
670
    
671
    private EditableFeatureType getFeatureType(String headers[], int automaticTypes[]) {
672
        EditableFeatureType fType = getStoreServices().createFeatureType(this.getName());
673
        fType.setHasOID(true);
674
        DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
675

    
676
        FieldTypeParser[] fieldTypes = new FieldTypeParser[headers.length];
677
            //
678
        // Calculamos cuales pueden ser los tipos de datos
679
        //
680
        for ( int i = 0; i < fieldTypes.length; i++ ) {
681
            fieldTypes[i] = new FieldTypeParser();
682
        }
683

    
684
            // Asuminos los tipos pasados por parametro, que se supone
685
        // son los detectados automaticamente.
686
        if ( automaticTypes != null ) {
687
            for ( int i = 0; i < fieldTypes.length && i < automaticTypes.length; i++ ) {
688
                fieldTypes[i].type = automaticTypes[i];
689
            }
690
        }
691
        // Luego probamos con lo que diga las cabezeras del CVS, sobreescribiendo
692
        // los tipos anteriores en caso de definirse en la cabezara.
693
        for ( int i = 0; i < fieldTypes.length; i++ ) {
694
            if ( !fieldTypes[i].parse(headers[i]) ) {
695
                continue;
696
            }
697

    
698
        }
699

    
700
            // Y por ultimo hacemos caso a lo que se haya especificado en los parametros
701
        // de apertura del CSV, teniendo esto prioridad sobre todo.
702
        int[] param_types = CSVStoreParameters.getFieldTypes(this.getParameters());
703
        if ( param_types != null ) {
704
            for ( int i = 0; i < fieldTypes.length && i < param_types.length; i++ ) {
705
                fieldTypes[i].type = param_types[i];
706
            }
707
        }
708

    
709
        int[] param_sizes = CSVStoreParameters.getFieldSizes(this.getParameters());
710
        if ( param_sizes != null ) {
711
            for ( int i = 0; i < param_sizes.length; i++ ) {
712
                if ( param_sizes[i] > 0 ) {
713
                    fieldTypes[i].size = param_sizes[i];
714
                }
715
            }
716
        }
717
            //
718
        // Una vez ya sabemos los tipos de datos rellenamos el feature-type
719
        //
720
        for ( int i = 0; i < fieldTypes.length; i++ ) {
721
            EditableFeatureAttributeDescriptor fad = fType.add(
722
                    fieldTypes[i].name,
723
                    fieldTypes[i].type
724
            );
725
            fad.setSize(fieldTypes[i].size);
726
            fad.setAllowNull(fieldTypes[i].allowNulls);
727
            if ( fieldTypes[i].type == DataTypes.GEOMETRY
728
                    && fType.getDefaultGeometryAttributeName() == null ) {
729
                fType.setDefaultGeometryAttributeName(fieldTypes[i].name);
730
            }
731
        }
732
        String[] pointDimensionNames = CSVStoreParameters.getPointDimensionNames(this.getParameters());
733
        if ( pointDimensionNames != null ) {
734
//            ToPointEvaluaror evaluator = new ToPointEvaluaror(pointDimensionNames);
735
            PointAttributeEmulator emulator = new PointAttributeEmulator(pointDimensionNames);
736
            EditableFeatureAttributeDescriptor attr = fType.add("GEOM", DataTypes.GEOMETRY, emulator);
737
            GeometryManager geommgr = GeometryLocator.getGeometryManager();
738
            GeometryType gt;
739
            try {
740
                gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
741
                attr.setGeometryType(gt);
742
            } catch (Exception e) {
743
                logger.warn("Can't set geometry type for the calculated field in CSV file '" + getFullFileName() + "'.", e);
744
            }
745
        }
746
        return fType;
747
    }
748
    
749
    static class PointAttributeEmulator implements FeatureAttributeEmulator {
750

    
751
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
752

    
753
        private static final int XNAME = 0;
754
        private static final int YNAME = 1;
755
        private static final int ZNAME = 2;
756
        
757
        private final GeometryManager geommgr;
758
        private final String[] fieldNames;
759
        private final Coercion toDouble;
760
        private final DataType dataType;
761
        private int errorcount = 0;
762

    
763
        public PointAttributeEmulator(String[] pointDimensionNames) {
764
            if ( pointDimensionNames.length > 2 ) {
765
                this.fieldNames = new String[3];
766
                this.fieldNames[ZNAME] = pointDimensionNames[2];
767
            } else {
768
                this.fieldNames = new String[2];
769
            }
770
            this.fieldNames[XNAME] = pointDimensionNames[0];
771
            this.fieldNames[YNAME] = pointDimensionNames[1];
772
            this.geommgr = GeometryLocator.getGeometryManager();
773
            DataTypesManager datatypeManager = ToolsLocator.getDataTypesManager();
774

    
775
            this.toDouble = datatypeManager.getCoercion(DataTypes.DOUBLE);
776
            this.dataType = datatypeManager.get(DataTypes.GEOMETRY);
777
        }
778
        
779
        public Object get(Feature feature) {
780
            try {
781
                Object valueX = feature.get(this.fieldNames[XNAME]);
782
                valueX = toDouble.coerce(valueX);
783
                if( valueX == null ) {
784
                    return null;
785
                }
786
                Object valueY = feature.get(this.fieldNames[YNAME]);
787
                valueY = toDouble.coerce(valueY);
788
                if( valueY == null ) {
789
                    return null;
790
                }
791
                Object valueZ = null;
792
                if( this.fieldNames.length>2 ) {
793
                    valueZ = toDouble.coerce(feature.get(this.fieldNames[ZNAME]));
794
                    if( valueZ == null ) {
795
                        return null;
796
                    }
797
                }
798
                
799
                double x = ((Double)valueX).doubleValue();
800
                double y = ((Double)valueY).doubleValue();
801
                Point point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
802
                if( this.fieldNames.length>2 ) {
803
                    double z = ((Double)valueZ).doubleValue();
804
                    point.setCoordinateAt(2, z);
805
                }
806
                return point;
807
            } catch (Exception ex) {
808
                if ( ++errorcount < 5 ) {
809
                    logger.warn("[" + errorcount + "] Can't create point in CSV provider. XNAME='"
810
                            + this.fieldNames[XNAME] + "', YNAME='" + this.fieldNames[XNAME] + "' feature=" + feature.toString(),ex);
811
                }
812
                return null;
813
            }
814
        }
815

    
816
        public void set(EditableFeature feature, Object value) {
817
            if( value == null ) {
818
                return;
819
            }
820
            Point point = null;
821
            if( value instanceof MultiPoint ) {
822
                point = (Point) ((MultiPoint)value).getPrimitiveAt(0);
823
            } else {
824
                point = (Point) value;
825
            }
826
            feature.set(this.fieldNames[XNAME], point.getX());
827
            feature.set(this.fieldNames[YNAME], point.getY());
828
            if( this.fieldNames.length>2 ) {
829
                feature.set(this.fieldNames[ZNAME], point.getCoordinateAt(2));
830
            }
831
        }
832

    
833
        public boolean allowSetting() {
834
            return true;
835
        }
836

    
837
        public String[] getRequiredFieldNames() {
838
            return this.fieldNames;
839
        }
840
        
841
        
842
    }
843
    
844
    static class ToPointEvaluaror extends AbstractEvaluator {
845

    
846
        private static final Logger logger = LoggerFactory.getLogger(ToPointEvaluaror.class);
847

    
848
        private GeometryManager geommgr = null;
849
        private String xname = null;
850
        private String yname = null;
851
        private String zname = null;
852
        private Coercion toDouble;
853
        private int errorcount = 0;
854

    
855
        ToPointEvaluaror(String[] pointDimensionNames) {
856
            this.xname = pointDimensionNames[0];
857
            this.yname = pointDimensionNames[1];
858
            if ( pointDimensionNames.length > 2 ) {
859
                this.zname = pointDimensionNames[2];
860
            }
861
            this.geommgr = GeometryLocator.getGeometryManager();
862
            this.toDouble = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.DOUBLE);
863
        }
864

    
865
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
866
            try {
867
                double x = ((Double) toDouble.coerce(data.getDataValue(xname))).doubleValue();
868
                double y = ((Double) toDouble.coerce(data.getDataValue(yname))).doubleValue();
869
                Point point = geommgr.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
870
                if ( zname != null ) {
871
                    double z = ((Double) toDouble.coerce(data.getDataValue(zname))).doubleValue();
872
                    point.setCoordinateAt(2, z);
873
                }
874
                return point;
875
            } catch (Exception ex) {
876
                if ( ++errorcount < 5 ) {
877
                    logger.warn("[" + errorcount + "] Can't create point in CSV provider. XNAME='"
878
                            + xname + "', YNAME='" + yname + "', ZNAME='" + zname + "', data=" + data.toString());
879
                }
880
                return null;
881
            }
882
        }
883

    
884
        public String getName() {
885
            return "ToPointEvaluaror";
886
        }
887

    
888
    }
889

    
890
        private void loadFeatures() throws IOException, DataException,
891
                        CoercionException, CloneNotSupportedException {
892
                //
893
                // http://supercsv.sourceforge.net/examples_reading.html
894
                // http://supercsv.sourceforge.net/apidocs/index.html
895
                //
896
                FileReader in = null;
897
                CsvListReader reader = null;
898
                try {
899
                        String headers[] = null;
900
                        FeatureStoreProviderServices store = this.getStoreServices();
901

    
902
                        boolean ignore_errors = CSVStoreParameters.getIgnoreErrors(getCSVParameters());
903
                        
904
                        // Initiaize the CSV parser
905
                        CsvPreference preferences = getCSVPreferences();
906
                        in = new FileReader(this.getCSVParameters().getFile());
907

    
908
                        reader = new CsvListReader(in, preferences);
909
                        headers = CSVStoreParameters.getHeaders(getCSVParameters());
910
                        if (headers == null) {
911
                                headers = reader.getHeader(true);
912
                                if (headers == null) {
913
                                        String msg = "Can't retrieve header from csv file '"
914
                                                        + this.getCSVParameters().getFile()
915
                                                                        .getAbsolutePath()
916
                                                        + "' and not specified in the parameters.";
917
                                        logger.warn(msg);
918
                                        throw new RuntimeException(msg);
919
                                }
920
                        }
921

    
922
                        // Initialize the feature types
923
                        EditableFeatureType edftype = this.getFeatureType(headers, automaticDetectionOfTypes());
924
                        FeatureType ftype = edftype.getNotEditableCopy();
925
                        List<FeatureType> ftypes = new ArrayList<FeatureType>();
926
                        ftypes.add(ftype);
927
                        store.setFeatureTypes(ftypes, ftype);
928

    
929
                        Coercion coercion[] = new Coercion[ftype.size()];
930
                        int sizes[] = new int[ftype.size()];
931
                        for (int i = 0; i < ftype.size(); i++) {
932
                                sizes[i] = -1;
933
                                FeatureAttributeDescriptor ad = ftype.getAttributeDescriptor(i);
934
                                coercion[i] = ad.getDataType().getCoercion();
935
                                if( ad.getDataType().getType() == DataTypes.STRING ) {
936
                                        if( ad.getSize() == 0 ) {
937
                                                // Es un string y no tiene un size asignado.
938
                                                // Lo ponemos a cero para calcularlo.
939
                                                sizes[i] = 0;
940
                                        }
941
                                }
942
                        }
943
                        if( ftype.getDefaultGeometryAttributeName() != null) {
944
                                this.need_calculate_envelope = true;
945
                        }
946
                        
947
                        Locale locale = CSVStoreParameters.getLocale(getCSVParameters());
948
                        taskStatus.message("_loading");
949
                        int count = 0;
950

    
951
                        int count_errors = 0;
952
                        List<String> row = reader.read();
953
                        
954
                        while (row != null) {
955
                                taskStatus.setCurValue(++count);
956
                                FeatureProvider feature = this.createFeatureProvider(ftype);
957
                                for (int i = 0; i < row.size(); i++) {
958
                                        Object rawvalue = row.get(i);
959
                                        try {
960
                                                Object value = null;
961
                                                if( locale != null && coercion[i] instanceof CoercionWithLocale ) {
962
                                                        value = ((CoercionWithLocale)(coercion[i])).coerce(rawvalue, locale);
963
                                                } else {
964
                                                        value = coercion[i].coerce(rawvalue);
965
                                                }
966
                                                feature.set(i, value);
967
                                                if (sizes[i] >= 0 && value != null) {
968
                                                        int x = ((String) value).length();
969
                                                        if (sizes[i] < x) {
970
                                                                sizes[i] = x;
971
                                                        }
972
                                                }
973
                                        } catch (RuntimeException ex) {
974
                                                if (!ignore_errors) {
975
                                                        throw ex;
976
                                                }
977
                                                if( count_errors++ <10 ) {
978
                                                        logger.warn("Can't load value of attribute "+i+" in row "+count+".",ex);
979
                                                }
980
                                                if( count_errors==10 ) {
981
                                                        logger.info("Too many errors, suppress messages.");
982
                                                }
983
                                        }
984
                                }
985
                                this.addFeatureProvider(feature);
986
                                row = reader.read();
987
                        }
988
                        for (int i = 0; i < ftype.size(); i++) {
989
                                if( sizes[i]>0 ) {
990
                                        EditableFeatureAttributeDescriptor efad = ((EditableFeatureAttributeDescriptor)edftype.getAttributeDescriptor(i));
991
                                        efad.setSize(sizes[i]);
992
                                }
993
                        }
994
                        // Volvemos a asignar al store el featuretype, ya que puede
995
                        // haber cambiado.
996
                        ftype = edftype.getNotEditableCopy();
997
                        ftypes = new ArrayList<FeatureType>();
998
                        ftypes.add(ftype);
999
                        store.setFeatureTypes(ftypes, ftype);
1000

    
1001
                        taskStatus.terminate();
1002
                } finally {
1003
                        if( reader != null ) {
1004
                                try { 
1005
                                        reader.close();
1006
                                } catch(Exception ex) {
1007
                                        // Do nothing
1008
                                }
1009
                                reader = null;
1010
                        }
1011
                        if( in != null ) {
1012
                                try { 
1013
                                        in.close();
1014
                                } catch(Exception ex) {
1015
                                        // Do nothing
1016
                                }
1017
                                in = null;
1018
                        }
1019
                }
1020
        }
1021
        
1022
        private int[] automaticDetectionOfTypes() throws IOException {
1023
                boolean automatic_types_detection = CSVStoreParameters.getAutomaticTypesDetection(getCSVParameters());
1024
                if( !automatic_types_detection ) {
1025
                        return  null;
1026
                }
1027
                
1028
                final int T_INT = 0;
1029
                final int T_FLOAT = 1;
1030
                final int T_DOUBLE = 2;
1031
                final int T_LONG = 3;
1032
                final int T_URL = 4;
1033
                final int T_DATE = 5;
1034
                boolean possibleDataTypes[][] = null;
1035
                Locale locale = null;
1036
                int[] types = null;
1037

    
1038
                FileReader in = null;
1039
                CsvListReader reader = null;
1040
                String headers[] = null;
1041
                SimpleDateFormat dateFormat = new SimpleDateFormat();
1042

    
1043
                try {
1044
                        CsvPreference preferences = getCSVPreferences();
1045
                        in = new FileReader(this.getCSVParameters().getFile());
1046

    
1047
                        reader = new CsvListReader(in, preferences);
1048
                        headers = reader.getHeader(true);
1049
                        if (headers == null) {
1050
                                headers = CSVStoreParameters.getHeaders(getCSVParameters());
1051
                        }
1052
                        types = new int[headers.length]; 
1053

    
1054
                        possibleDataTypes = new boolean[headers.length][6]; 
1055
                        for (int i = 0; i < possibleDataTypes.length; i++) {
1056
                                for( int j=0; j<4; j++ ) {
1057
                                        possibleDataTypes[i][j] = true;  
1058
                                }
1059
                        }
1060
                        locale = CSVStoreParameters.getLocale(getCSVParameters());
1061
                        if( locale == null ) {
1062
                            locale = Locale.getDefault();
1063
                        }
1064
                        
1065
                        DataTypesManager typeManager = ToolsLocator.getDataTypesManager();
1066
                        CoercionWithLocale toDouble = (CoercionWithLocale) typeManager.getCoercion(DataTypes.DOUBLE);
1067
                        CoercionWithLocale toFloat = (CoercionWithLocale) typeManager.getCoercion(DataTypes.FLOAT);
1068
                        
1069
                        List<String> row = reader.read();
1070

    
1071
                        while (row != null) {
1072
                                for (int i = 0; i < row.size(); i++) {
1073
                                        Object rawvalue = row.get(i);
1074
                                        Object value = null;
1075
                                        if( possibleDataTypes[i][T_DOUBLE] ) {
1076
                                                try {
1077
                                                    value = toDouble.coerce(rawvalue, locale);
1078
                                                    possibleDataTypes[i][T_DOUBLE] = true;
1079
                                                } catch (Exception ex) {
1080
                                                        possibleDataTypes[i][T_DOUBLE] = false;
1081
                                                }
1082
                                        }
1083
                                        if( possibleDataTypes[i][T_FLOAT] ) {
1084
                                                try {
1085
                                                    value = toFloat.coerce(rawvalue, locale);
1086
                                                    possibleDataTypes[i][T_FLOAT] = true;
1087
                                                } catch (Exception ex) {
1088
                                                        possibleDataTypes[i][T_FLOAT] = false;
1089
                                                }
1090
                                        }
1091
                                        if (possibleDataTypes[i][T_LONG]) {
1092
                                                try {
1093
                                                        value = Long.parseLong((String) rawvalue);
1094
                                                        possibleDataTypes[i][T_LONG] = true;
1095
                                                } catch (Exception ex) {
1096
                                                        possibleDataTypes[i][T_LONG] = false;
1097
                                                }
1098
                                        }
1099
                                        if (possibleDataTypes[i][T_INT]) {
1100
                                                try {
1101
                                                        value = Integer.parseInt((String) rawvalue);
1102
                                                        possibleDataTypes[i][T_INT] = true;
1103
                                                } catch (Exception ex) {
1104
                                                        possibleDataTypes[i][T_INT] = false;
1105
                                                }
1106
                                        }                                        
1107
                                        if (possibleDataTypes[i][T_DATE]) {
1108
                                                try {
1109
                                                        value = dateFormat.parse((String) rawvalue);
1110
                                                        possibleDataTypes[i][T_DATE] = true;
1111
                                                } catch (Exception ex) {
1112
                                                        possibleDataTypes[i][T_DATE] = false;
1113
                                                }
1114
                                        }                                        
1115
                                        if (possibleDataTypes[i][T_URL]) {
1116
                                                try {
1117
                                                        value = new URL((String) rawvalue);
1118
                                                        possibleDataTypes[i][T_URL] = true;
1119
                                                } catch (Exception ex) {
1120
                                                        possibleDataTypes[i][T_URL] = false;
1121
                                                }
1122
                                        }                                        
1123
                                }
1124
                                row = reader.read();
1125
                        }
1126
                } finally {
1127
                        if (reader != null) {
1128
                                try {
1129
                                        reader.close();
1130
                                } catch (Exception ex) {
1131
                                        // Do nothing
1132
                                }
1133
                                reader = null;
1134
                        }
1135
                        if (in != null) {
1136
                                try {
1137
                                        in.close();
1138
                                } catch (Exception ex) {
1139
                                        // Do nothing
1140
                                }
1141
                                in = null;
1142
                        }
1143

    
1144
                }
1145
                for (int i = 0; i < possibleDataTypes.length ; i++) {
1146
                        if( possibleDataTypes[i][T_INT] ) {
1147
                                types[i] = DataTypes.INT;
1148
                                continue;
1149
                        }
1150
                        if( possibleDataTypes[i][T_LONG] ) {
1151
                                types[i] = DataTypes.LONG;
1152
                                continue;
1153
                        }
1154
                        if( possibleDataTypes[i][T_FLOAT] ) {
1155
                                types[i] = DataTypes.FLOAT;
1156
                                continue;
1157
                        }
1158
                        if( possibleDataTypes[i][T_DOUBLE] ) {
1159
                                types[i] = DataTypes.DOUBLE;
1160
                                continue;
1161
                        }
1162
                        if( possibleDataTypes[i][T_URL] ) {
1163
                                types[i] = DataTypes.URL;
1164
                                continue;
1165
                        }
1166
                        if( possibleDataTypes[i][T_DATE] ) {
1167
                                types[i] = DataTypes.DATE;
1168
                                continue;
1169
                        }
1170
                        types[i] = DataTypes.STRING;
1171
                }
1172
                return types;
1173
        }
1174

    
1175
}