/* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2010-2015, Open Source Geospatial Foundation (OSGeo)
*
* This file is hereby placed into the Public Domain. This means anyone is
* free to do whatever they wish with this file. Use it well and enjoy!
*/
package org.geotools.data.csv;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.NoSuchElementException;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Query;
import org.geotools.data.csv.parse.CSVIterator;
import org.geotools.data.csv.parse.CSVStrategy;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import com.csvreader.CsvWriter;
/**
* Iterator supporting writing of feature content.
*
* @author Jody Garnett (Boundless)
* @author Lee Breisacher
*/
public class CSVFeatureWriter implements FeatureWriter<SimpleFeatureType, SimpleFeature> {
private SimpleFeatureType featureType;
private CSVStrategy csvStrategy;
private CSVFileState csvFileState;
/** Temporary file used to stage output */
private File temp;
/** iterator handling reading of original file */
private CSVIterator iterator;
/** CsvWriter used for temp file output */
private CsvWriter csvWriter;
/** Flag indicating we have reached the end of the file */
private boolean appending = false;
/** Row count used to generate FeatureId when appending */
int nextRow = 0;
/** Current feature available for modification. May be null if feature removed */
private SimpleFeature currentFeature;
public CSVFeatureWriter(CSVFileState csvFileState, CSVStrategy csvStrategy)
throws IOException {
this(csvFileState, csvStrategy, Query.ALL);
}
public CSVFeatureWriter(CSVFileState csvFileState, CSVStrategy csvStrategy, Query query)
throws IOException {
this.csvFileState = csvFileState;
File file = csvFileState.getFile();
File directory = file.getParentFile();
String typeName = query.getTypeName();
this.temp = File.createTempFile(typeName + System.currentTimeMillis(), "csv", directory);
this.featureType = csvStrategy.getFeatureType();
this.iterator = csvStrategy.iterator();
this.csvStrategy = csvStrategy;
this.csvWriter = new CsvWriter(new FileWriter(this.temp), ',');
this.csvWriter.writeRecord(this.csvFileState.getCSVHeaders());
}
// featureType start
@Override
public SimpleFeatureType getFeatureType() {
return this.featureType;
}
// featureType end
// hasNext start
@Override
public boolean hasNext() throws IOException {
if( csvWriter == null ){
return false;
}
if (this.appending) {
return false; // reader has no more contents
}
return iterator.hasNext();
}
// hasNext end
// next start
@Override
public SimpleFeature next() throws IOException, IllegalArgumentException,
NoSuchElementException {
if(csvWriter == null){
throw new IOException("Writer has been closed");
}
if (this.currentFeature != null) {
this.write(); // the previous one was not written, so do it now.
}
try {
if(!appending){
if(iterator.hasNext()){
this.currentFeature = iterator.next();
return this.currentFeature;
}
else {
this.appending = true;
}
}
String fid = featureType.getTypeName()+"-fid"+nextRow;
Object values[] = DataUtilities.defaultValues( featureType );
this.currentFeature = SimpleFeatureBuilder.build( featureType, values, fid );
return this.currentFeature;
}
catch (IllegalArgumentException invalid ){
throw new IOException("Unable to create feature:"+invalid.getMessage(),invalid);
}
}
// next end
// remove start
/**
* Mark our {@link #currentFeature} feature as null, it will be skipped when written effectively removing it.
*/
public void remove() throws IOException {
this.currentFeature = null; // just mark it done which means it will not get written out.
}
// remove end
// write start
public void write() throws IOException {
if (this.currentFeature == null) {
return; // current feature has been deleted
}
this.csvWriter.writeRecord(this.csvStrategy.encode(this.currentFeature));
nextRow++;
this.currentFeature = null; // indicate that it has been written
}
// write end
// close start
@Override
public void close() throws IOException {
if( csvWriter == null ){
throw new IOException("Writer alread closed");
}
if (this.currentFeature != null) {
this.write(); // the previous one was not written, so do it now.
}
// Step 1: Write out remaining contents (if applicable)
while (hasNext()) {
next();
write();
}
csvWriter.close();
csvWriter = null;
if( this.iterator != null ){
this.iterator.close();
this.iterator = null;
}
// Step 2: Replace file contents
File file = this.csvFileState.getFile();
Files.copy(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING );
}
}