/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved * (c) 2012 - 2014 OpenPlans * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data.csv.parse; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.geotools.data.csv.CSVFileState; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import com.csvreader.CsvReader; public abstract class CSVStrategy { protected final CSVFileState csvFileState; public CSVStrategy(CSVFileState csvFileState) { this.csvFileState = csvFileState; } public CSVIterator iterator() throws IOException { return new CSVIterator(csvFileState, this); } protected abstract SimpleFeatureType buildFeatureType(); public abstract void createSchema(SimpleFeatureType featureType) throws IOException; public abstract SimpleFeature decode(String recordId, String[] csvRecord); public abstract String[] encode(SimpleFeature feature); protected volatile SimpleFeatureType featureType = null; public SimpleFeatureType getFeatureType() { if (featureType == null) { synchronized (this) { if (featureType == null) { featureType = buildFeatureType(); } } } return featureType; } /** * Originally in a strategy support class - giving a chance to override them to * improve efficiency and utilize the different strategies */ public static SimpleFeatureTypeBuilder createBuilder(CSVFileState csvFileState) { CsvReader csvReader = null; Map<String, Class<?>> typesFromData = null; String[] headers = null; try { csvReader = csvFileState.openCSVReader(); headers = csvReader.getHeaders(); typesFromData = findMostSpecificTypesFromData(csvReader, headers); } catch (IOException e) { throw new RuntimeException("Failure reading csv file", e); } finally { if (csvReader != null) { csvReader.close(); } } return createBuilder(csvFileState, headers, typesFromData); } public static SimpleFeatureTypeBuilder createBuilder(CSVFileState csvFileState, String[] headers, Map<String, Class<?>> typesFromData) { SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.setName(csvFileState.getTypeName()); builder.setCRS(csvFileState.getCrs()); if (csvFileState.getNamespace() != null) { builder.setNamespaceURI(csvFileState.getNamespace()); } for (String col : headers) { Class<?> type = typesFromData.get(col); builder.add(col, type); } return builder; } /** * Performs a full file scan attempting to guess the type of each column * Specific strategy implementations will expand this functionality by * overriding the buildFeatureType() method. */ protected static Map<String, Class<?>> findMostSpecificTypesFromData(CsvReader csvReader, String[] headers) throws IOException { Map<String, Class<?>> result = new HashMap<String, Class<?>>(); // start off assuming Integers for everything for (String header : headers) { result.put(header, Integer.class); } // Read through the whole file in case the type changes in later rows while (csvReader.readRecord()) { String[] record = csvReader.getValues(); List<String> values = Arrays.asList(record); if (record.length >= headers.length) { values = values.subList(0, headers.length); } int i = 0; for (String value : values) { String header = headers[i]; Class<?> type = result.get(header); // For each value in the row, ensure we can still parse it as the // defined type for this column; if not, make it more general if (type == Integer.class) { try { Integer.parseInt(value); } catch (NumberFormatException e) { try { Double.parseDouble(value); type = Double.class; } catch (NumberFormatException ex) { type = String.class; } } } else if (type == Double.class) { try { Double.parseDouble(value); } catch (NumberFormatException e) { type = String.class; } } result.put(header, type); i++; } } return result; } }