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 / CSVUtils.java @ 45929

History | View | Annotate | Download (17.7 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

    
25
package org.gvsig.fmap.dal.store.csv;
26

    
27
import java.io.File;
28
import java.io.FileInputStream;
29
import java.io.FileNotFoundException;
30
import java.io.FileReader;
31
import java.io.IOException;
32
import java.io.InputStreamReader;
33
import java.nio.charset.Charset;
34
import java.util.Locale;
35
import java.util.Map;
36
import org.apache.commons.io.FilenameUtils;
37
import org.apache.commons.io.IOUtils;
38
import org.apache.commons.lang3.ArrayUtils;
39
import org.apache.commons.lang3.StringUtils;
40
import org.gvsig.fmap.dal.DataTypes;
41
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
42
import org.gvsig.fmap.dal.feature.EditableFeatureType;
43
import static org.gvsig.fmap.dal.store.csv.CSVStoreProvider.NAME;
44
import org.gvsig.fmap.dal.store.csv.simplereaders.CSVReaderSuperCSV;
45
import org.gvsig.fmap.dal.store.csv.simplereaders.FixedLenReader;
46
import org.gvsig.fmap.dal.store.csv.simplereaders.JSonReader;
47
import org.gvsig.fmap.dal.store.csv.simplereaders.SimpleReader;
48
import org.gvsig.fmap.geom.Geometry;
49
import org.gvsig.fmap.geom.GeometryLocator;
50
import org.gvsig.fmap.geom.GeometryManager;
51
import org.gvsig.fmap.geom.type.GeometryType;
52
import org.gvsig.tools.dynobject.Tags;
53
import org.gvsig.tools.task.SimpleTaskStatus;
54
import org.slf4j.Logger;
55
import org.slf4j.LoggerFactory;
56

    
57
/**
58
 *
59
 * @author gvSIG  Team
60
 */
61
@SuppressWarnings("UseSpecificCatch")
62
public class CSVUtils {
63
    
64
    private static final Logger LOGGER = LoggerFactory.getLogger(CSVUtils.class);
65

    
66
    private CSVUtils() {
67
        
68
    }
69
    public static InputStreamReader openFile(File f, String charsetName) throws FileNotFoundException {
70
        String fullFileName = f==null? "NULL":f.getAbsolutePath();
71
        Charset charset = Charset.defaultCharset();
72
        FileInputStream fis = new FileInputStream(f);
73
        if (StringUtils.isNotBlank(charsetName)) {
74
            if (Charset.isSupported(charsetName)) {
75
                try {
76
                    charset = Charset.forName(charsetName);
77
                } catch (Throwable th) {
78
                    LOGGER.warn("Can't use charset '" + charsetName + "' for read csv '" + fullFileName + "'.", th);
79
                }
80
            } else {
81
                LOGGER.warn("charset '" + charsetName + "' not supported for read csv '" + fullFileName + "'.");
82
            }
83
        }
84
        InputStreamReader isr = new InputStreamReader(fis, charset);
85
        return isr;
86
    }
87
    
88
    public static boolean loadFeatureType(CSVStoreParameters parameters, EditableFeatureType featureType, boolean  detectTypes, SimpleTaskStatus status) throws IOException {
89
        InputStreamReader in = null;
90
        SimpleReader reader = null;
91
        try {
92
            String headers[];
93

    
94
            in = openFile(
95
                    parameters.getFile(),
96
                    CSVStoreParameters.getCharset(parameters)
97
            );
98

    
99
            reader = getSimpleReader(parameters, in);
100

    
101
            headers = CSVStoreParameters.getHeaders(parameters);
102
            if (headers == null) {
103
                if (CSVStoreParameters.isFirstLineHeader(parameters)) {
104
                    headers = reader.getHeader();
105
                    if (headers == null) {
106
                        if (CSVStoreParameters.getIgnoreErrors(parameters)) {
107
                            headers = getFixedHeaders(reader.getColumnsCount());
108
                        } else {
109
                            String msg = "Can't retrieve header from csv file '"
110
                                    + parameters.getFile()
111
                                            .getAbsolutePath()
112
                                    + "' and not specified in the parameters.";
113
                            LOGGER.warn(msg);
114
                            throw new RuntimeException(msg);
115
                        }
116
                    }
117
                } else {
118
                    headers = getFixedHeaders(reader.getColumnsCount());
119
                }
120
            } else {
121
                if (CSVStoreParameters.isFirstLineHeader(parameters)) {
122
                    reader.getHeader(); // Skip and ignore the header of file
123
                }
124
            }
125

    
126
            AutomaticDetectionOfTypes.DetectedValue[] detectedTypes = null;
127
            if( detectTypes ) {
128
                detectedTypes = automaticDetectionOfTypes(parameters, headers, status);
129
            }
130
            if( StringUtils.isBlank(headers[headers.length-1]) &&
131
                (detectedTypes==null || detectedTypes[headers.length-1].isBlank()) ) {
132
                headers = ArrayUtils.remove(headers, headers.length-1);
133
            }
134
            if (detectedTypes != null && detectedTypes.length > headers.length) {
135
                // Se han detectado mas columnas que las que hay en la cabezera,
136
                // a?adimos mas columnas a la cabezera.
137
                String[] headers2 = new String[detectedTypes.length];
138
                for (int i = 0; i < headers2.length; i++) {
139
                    if (i < headers.length) {
140
                        headers2[i] = headers[i];
141
                    } else {
142
                        headers2[i] = getFixedHeader(i);
143
                    }
144
                }
145
                headers = headers2;
146
            }
147
            for (int i = 0; i < headers.length; i++) {
148
                if (StringUtils.isBlank(headers[i])) {
149
                    headers[i] = getFixedHeader(i);
150
                }
151
            }
152
            // Initialize the feature types
153
            return fillFeatureType(parameters, featureType,  headers, detectedTypes);
154
        } finally {
155
            IOUtils.closeQuietly(in);
156
            IOUtils.closeQuietly(reader);
157
        }
158
    }
159
    public static SimpleReader getSimpleReader(CSVStoreParameters parameters, InputStreamReader in) throws IOException {
160
        SimpleReader reader;
161
        String filename = CSVStoreParameters.getFileName(parameters);
162
        if (FilenameUtils.isExtension(filename, "json")){
163
            reader= new JSonReader(in,parameters);
164
        } else if (CSVStoreParameters.getRawFieldsDefinition(parameters) != null) {
165
            reader = new FixedLenReader(in, parameters);
166
        } else {
167
            reader = new CSVReaderSuperCSV(in, parameters);
168
        }
169
        return reader;
170
    }
171

    
172
    private static String getFixedHeader(int column) {
173
        char[] header = new char[3];
174

    
175
        String s = String.format("%03d", column);
176
        header[0] = (char) (s.charAt(0) + 17);
177
        header[1] = (char) (s.charAt(1) + 17);
178
        header[2] = (char) (s.charAt(2) + 17);
179
        return String.valueOf(header);
180
    }
181

    
182
    private static String[] getFixedHeaders(int count) {
183
        String[] headers = new String[count];
184
        for (int i = 0; i < headers.length; i++) {
185
            headers[i] = getFixedHeader(i);
186
        }
187
        return headers;
188
    }
189

    
190
    private static AutomaticDetectionOfTypes.DetectedValue[] automaticDetectionOfTypes(CSVStoreParameters parameters, String[] headers, SimpleTaskStatus status) throws IOException {
191
        String fullFileName = parameters.getFile()==null? "NULL":parameters.getFile().getAbsolutePath();
192
        boolean automatic_types_detection = CSVStoreParameters.getAutomaticTypesDetection(parameters);
193
        if (!automatic_types_detection) {
194
            return null;
195
        }
196
        AutomaticDetectionOfTypes.DetectedValue[] types = null;
197

    
198
        FileReader in = null;
199
        SimpleReader reader = null;
200

    
201
        try {
202
            in = new FileReader(parameters.getFile());
203
            reader = getSimpleReader(parameters, in);
204
            AutomaticDetectionOfTypes x = new AutomaticDetectionOfTypes(
205
                    fullFileName
206
            );
207
            types = x.detect(
208
                    headers.length,
209
                    reader,
210
                    CSVStoreParameters.isFirstLineHeader(parameters),
211
                    CSVStoreParameters.getLocale(parameters),
212
                    status
213
            );
214
        } catch (Exception ex) {
215
            int lineno = 0;
216
            if (reader != null) {
217
                lineno = reader.getLine();
218
            }
219
            throw new RuntimeException("Problems reading file '" + fullFileName + "' near line " + lineno + ".", ex);
220

    
221
        } finally {
222
            IOUtils.closeQuietly(reader);
223
            IOUtils.closeQuietly(in);
224
        }
225
        return types;
226
    }
227
    
228
    private static boolean fillFeatureType(CSVStoreParameters parameters, EditableFeatureType fType, String headers[], AutomaticDetectionOfTypes.DetectedValue automaticTypes[]) {
229
        String fullFileName = parameters.getFile()==null? "":parameters.getFile().getAbsolutePath();
230
        String providerName = NAME;
231
        
232
        fType.setHasOID(true);
233

    
234

    
235
        FieldTypeParser[] fieldTypes = new FieldTypeParser[headers.length];
236
        //
237
        // Calculamos cuales pueden ser los tipos de datos
238
        //
239
        for (int i = 0; i < fieldTypes.length; i++) {
240
            fieldTypes[i] = new FieldTypeParser(providerName, fullFileName);
241
        }
242

    
243
        // Asuminos los tipos pasados por parametro, que se supone
244
        // son los detectados automaticamente.
245
        if (automaticTypes != null) {
246
            for (int i = 0; i < fieldTypes.length && i < automaticTypes.length; i++) {
247
                fieldTypes[i].detectedValue = automaticTypes[i];
248
                fieldTypes[i].type = automaticTypes[i].getType();
249
            }
250
        }
251
        // Luego probamos con lo que diga las cabezeras del CVS, sobreescribiendo
252
        // los tipos anteriores en caso de definirse en la cabezara.
253
        boolean all_fields_declare_type = true;
254
        for (int i = 0; i < fieldTypes.length; i++) {
255
            if (!fieldTypes[i].parse(headers[i])) {
256
                LOGGER.warn("Can't parse header of field "+i+ "( "+headers[i]+") in '"+providerName+"' file '" + fullFileName + "'.");
257
            }
258
            if( fieldTypes[i].type == DataTypes.UNKNOWN ) {
259
                all_fields_declare_type = false;
260
                fieldTypes[i].type = DataTypes.STRING;
261
            }
262
        }
263

    
264
        // Y por ultimo hacemos caso a lo que se haya especificado en los parametros
265
        // de apertura del CSV, teniendo esto prioridad sobre todo.
266
        String param_types_def = CSVStoreParameters.getRawFieldTypes(parameters);
267
        if (StringUtils.isNotBlank(param_types_def)) {
268
            String sep = CSVStoreParameters.getDelimiter(param_types_def);
269
            if (StringUtils.isNotBlank(sep)) {
270
                String[] param_types = param_types_def.split(sep);
271
                FieldTypeParser parser = new FieldTypeParser(providerName, fullFileName);
272
                for (String param_type : param_types) {
273
                    parser.clear();
274
                    parser.parse(param_type);
275
                    for (FieldTypeParser fieldType : fieldTypes) {
276
                        if (StringUtils.equalsIgnoreCase(fieldType.name, parser.name)) {
277
                            fieldType.copyFrom(parser);
278
                            break;
279
                        }
280
                    }
281
                }
282
            }
283
        }
284
        //
285
        // Una vez ya sabemos los tipos de datos rellenamos el feature-type
286
        //
287
        Tags ftypeTags = fType.getTags();
288
        for (FieldTypeParser fieldType : fieldTypes) {
289
            EditableFeatureAttributeDescriptor fad = fType.add(fieldType.name, fieldType.type);
290
            if( fieldType.detectedValue!=null ) {
291
                fad.setDisplaySize(Math.max(fieldType.detectedValue.getDisplaySize(), fieldType.size));
292
                fad.setSize(Math.max(fieldType.detectedValue.getDisplaySize(), fieldType.size));
293
                if( fad.getPrecision()<fieldType.detectedValue.getPrecision() ) {
294
                    fad.setPrecision(fieldType.detectedValue.getPrecision());
295
                }
296
                if( fad.getScale()<fieldType.detectedValue.getScale()) {
297
                    fad.setScale(fieldType.detectedValue.getScale());
298
                }
299
            } else {
300
                fad.setDisplaySize(fieldType.size);
301
            }
302
            if (fieldType.type == DataTypes.GEOMETRY ) {
303
                fad.setGeometryType(fieldType.geomType, fieldType.geomSubtype);
304
                if( fType.getDefaultGeometryAttributeName() == null ) {
305
                    fType.setDefaultGeometryAttributeName(fieldType.name);
306
                }
307
            }
308
            Locale locale = CSVStoreParameters.getLocale(parameters);
309
            fad.setLocale(locale);
310
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
311
                try {
312
                    switch(entry.getKey().toLowerCase()) {
313
                        case "expression":
314
                            // Los campos calculados los procesamos en una segunda
315
                            // pasada, cuando ya estan definidos el resto de los campos
316
                            // ya que pueden requerir campos que aun no se han definido.
317
                            break;
318
                        default:
319
                                fad.set(entry.getKey(), entry.getValue());
320
                            }
321
                } catch (Exception ex) {
322
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' of '"+fad.getName()+"'.", ex);
323
                }
324
            }            
325
            Tags tags = fad.getTags();
326
            for (Map.Entry<String, String> entry : fieldType.tags.entrySet()) {
327
                tags.set(entry.getKey(), entry.getValue());
328
            }
329
            for (Map.Entry<String, String> entry : fieldType.typetags.entrySet()) {
330
                ftypeTags.set(entry.getKey(), entry.getValue());
331
            }
332
            for (Map.Entry<String, String> entry : fieldType.typeAssignments.entrySet()) {
333
                try {
334
                    fType.set(entry.getKey(), entry.getValue());
335
                } catch(Exception ex) {
336
                    LOGGER.warn("Can't set attribute '"+entry.getKey()+"' in the feature type.", ex);
337
                }
338
            }
339
        }
340
        // Processamos ahora los campos calculados
341
        for (FieldTypeParser fieldType : fieldTypes) {
342
            EditableFeatureAttributeDescriptor fad = fType.getEditableAttributeDescriptor(fieldType.name);
343
            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
344
                try {
345
                    switch(entry.getKey().toLowerCase()) {
346
                        case "expression":
347
                            fad.set(entry.getKey(), entry.getValue());
348
                            break;
349
                    }
350
                } catch (Exception ex) {
351
                    LOGGER.warn("Can't set property '"+entry.getKey()+"' in '"+fad.getName()+"' of '"+fullFileName+"'.", ex);
352
                }
353
            }
354
        }
355
        String[] pointDimensionNames = CSVStoreParameters.getPointDimensionNames(parameters);
356
        if ( pointDimensionNames != null ) {
357
            CSVPointAttributeEmulator emulator = new CSVPointAttributeEmulator(pointDimensionNames);
358
            String columnName = CSVStoreParameters.getPointColumnName(parameters);
359
            if( StringUtils.isBlank(columnName) ) {
360
                columnName = "geom";
361
            }
362
            EditableFeatureAttributeDescriptor attr = fType.add(columnName, DataTypes.GEOMETRY, emulator);
363
            GeometryManager geommgr = GeometryLocator.getGeometryManager();
364
            GeometryType gt;
365
            try {
366
                if ( emulator.getFieldNames() != null && emulator.getFieldNames().length <= 2 ) {
367
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
368
                } else {
369
                        gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
370
                }
371
                attr.setGeometryType(gt);
372
            } catch (Exception e) {
373
                LOGGER.warn("Can't set geometry type for the calculated field in '"+providerName+"' file '" + fullFileName + "'.", e);
374
            }
375
        }        
376
        
377
        String geometry_column = CSVStoreParameters.getGeometryColumn(parameters);
378
        if (!StringUtils.isEmpty(geometry_column)) {
379
            EditableFeatureAttributeDescriptor attr = (EditableFeatureAttributeDescriptor) fType.get(geometry_column);
380
            if (attr != null ) {
381
                if( attr.getType() != DataTypes.GEOMETRY ) {
382
                    attr.setDataType(DataTypes.GEOMETRY);
383
                }
384
                GeometryManager geommgr = GeometryLocator.getGeometryManager();
385
                GeometryType gt;
386
                try {
387
                    gt = geommgr.getGeometryType(
388
                            CSVStoreParameters.getGeometryType(parameters),
389
                            CSVStoreParameters.getGeometrySubType(parameters)
390
                    );
391
                    attr.setGeometryType(gt);
392
                } catch (Exception e) {
393
                    LOGGER.warn("Can't set geometry type for the calculated field in CSV file '" + fullFileName + "'.", e);
394
                }
395
                fType.setDefaultGeometryAttributeName(geometry_column);
396
            }
397
        }
398
        return all_fields_declare_type;
399
    }
400
    
401
}