/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2010-2015, Geomatys * * 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.geotoolkit.data.csv; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.WKTWriter; import java.io.IOException; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.sis.feature.FeatureExt; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.ObjectConverters; import org.geotoolkit.data.FeatureStoreRuntimeException; import org.geotoolkit.data.FeatureWriter; import org.geotoolkit.temporal.object.TemporalUtilities; import org.opengis.feature.Feature; import org.opengis.feature.FeatureType; import org.opengis.feature.PropertyType; import org.opengis.filter.identity.Identifier; import org.apache.sis.internal.feature.AttributeConvention; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.WRITE; import static org.geotoolkit.data.csv.CSVFeatureStore.UTF8_ENCODING; /** * * @author Johann Sorel (Geomatys) */ class CSVFeatureWriter extends CSVFeatureReader implements FeatureWriter { private final WKTWriter wktWriter = new WKTWriter(2); private final Writer writer; private final Path writeFile; private Feature edited = null; private Feature lastWritten = null; private boolean appendMode = false; protected final Set<Identifier> deletedIds = new HashSet<>(); protected final Set<Identifier> updatedIds = new HashSet<>(); protected final Set<Identifier> addedIds = new HashSet<>(); private final ReadWriteLock tempLock = new ReentrantReadWriteLock(); private boolean closed = false; CSVFeatureWriter(CSVFeatureStore store, FeatureType featureType, ReadWriteLock lock) throws DataStoreException { super(store, featureType, false, lock); tempLock.writeLock().lock(); try { writeFile = store.createWriteFile(); writer = Files.newBufferedWriter(writeFile, UTF8_ENCODING, CREATE, WRITE); final String firstLine = store.createHeader(featureType); writer.write(firstLine); writer.write('\n'); writer.flush(); } catch (IOException ex) { throw new DataStoreException(ex); } finally { tempLock.writeLock().unlock(); } } @Override public Feature next() throws FeatureStoreRuntimeException { try { write(); edited = super.next(); appendMode = false; } catch (FeatureStoreRuntimeException ex) { //we reach append mode appendMode = true; edited = CSVUtils.defaultFeature(featureType, Integer.toString(inc++)); } return edited; } @Override public void remove() { if (edited == null) { throw new FeatureStoreRuntimeException("No feature selected."); } deletedIds.add(FeatureExt.getId(edited)); // mark the current feature as null, this will result in it not // being rewritten to the stream edited = null; } @Override public void write() throws FeatureStoreRuntimeException { if (edited == null || lastWritten == edited) { return; } lastWritten = edited; tempLock.writeLock().lock(); try { int i = 0; int n = atts.length; for (PropertyType att : atts) { final Object value = edited.getPropertyValue(att.getName().toString()); String str; if (value == null) { str = ""; } else if (AttributeConvention.isGeometryAttribute(att)) { str = wktWriter.write((Geometry) value); } else { if (value instanceof Date) { str = TemporalUtilities.toISO8601((Date) value); } else if (value instanceof Boolean) { str = value.toString(); } else if (value instanceof CharSequence) { str = value.toString(); //escape strings } else { str = ObjectConverters.convert(value, String.class); } if (str != null) { final boolean escape = str.indexOf('"') >= 0 || str.indexOf(';') >= 0 || str.indexOf('\n') >= 0; if (escape) { str = "\"" + str.replace("\"", "\"\"") + "\""; } } } writer.append(str); if (i != n - 1) { writer.append(store.getSeparator()); } i++; } writer.append('\n'); writer.flush(); if (appendMode) { addedIds.add(FeatureExt.getId(edited)); } else { updatedIds.add(FeatureExt.getId(edited)); } } catch (IOException ex) { throw new FeatureStoreRuntimeException(ex); } finally { tempLock.writeLock().unlock(); } } @Override public void close() { if (closed) return; try { writer.flush(); writer.close(); } catch (IOException ex) { throw new FeatureStoreRuntimeException(ex); } //close read iterator super.close(); //flip files fileLock.writeLock().lock(); tempLock.writeLock().lock(); try { Files.move(writeFile, store.getFile(), StandardCopyOption.REPLACE_EXISTING); } catch (IOException ex) { throw new FeatureStoreRuntimeException(ex); } finally { fileLock.writeLock().unlock(); tempLock.writeLock().unlock(); } // Fire content change events only if we succeed replacing original file. store.fireDataChangeEvents(addedIds, updatedIds, deletedIds); closed = true; } }