/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2011, Open Source Geospatial Foundation (OSGeo)
*
* 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.efeature;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.efeature.impl.ESimpleFeatureImpl;
import org.geotools.data.simple.SimpleFeatureWriter;
import org.opengis.feature.IllegalAttributeException;
import org.opengis.feature.simple.SimpleFeatureType;
/**
* @author kengu - 23. juni 2011
*
*/
public class EFeatureWriter implements SimpleFeatureWriter {
/**
* Cached {@link EFeatureReader} instance
*/
private final EFeatureReader eReader;
/**
* Next feature in line for a add, update or remove
*/
private ESimpleFeature eNext;
/**
* Copy of feature returned by {@link #eReader}.
* <p>
* It is this instance which is returned by
* {@link #next()}, allowing the writer to detect
* any changes made to it on the next call to
* {@link #next()} {@link #hasNext()} or {@link #write()}
* by comparing to the {@link #eNext} instance.
* </p>
*/
private ESimpleFeature eLive;
/**
* Writer mode flags ({@link #UPDATE} | {@link #APPEND})
*/
private final int flags;
/**
* Writer append mode flag.
* <p>
* If set, the writer returns a new
* {@link EFeature} of given type when the writer has
* no more updateable features. If this flag can be
* bit-wise OR'ed with the {@link #UPDATE} flag.
* </p>
*/
public final static int APPEND = 0x01<<0;
/**
* Writer update mode flag.
* <p>
* If set, the writer returns updateable
* features as long as there exists {@link EFeature}s
* matching given query. This flag can be
* bit-wise OR'ed with the {@link #UPDATE} flag.
* </p>
*/
public final static int UPDATE = 0x01<<1;
// -----------------------------------------------------
// Constructors
// -----------------------------------------------------
/**
* The {@link EFeatureWriter} constructor.
* <p>
* This constructor create a writer that supports both
* {@link #UPDATE updates} and {@link #UPDATE appending} of new
* features.
* </p>
* @param eStore - {@link EFeatureDataStore} instance containing
* {@link EFeature} resource information
* @param query - {@link Query} instance. Note that {@link Query#getTypeName()}
* is expected to be a name of a {@link SimpleFeatureType} in given data store.
* <p>
* {@link SimpleFeatureType} names have the following format:
*
* <pre>
* eName=<eFolder>.<eReference>
*
* where
*
* eFolder = {@link EFeature} folder name
* eReference = {@link EFeature} reference name
* </pre>
* @throws IOException
*/
public EFeatureWriter(EFeatureDataStore eStore, Query query) throws IOException {
this(eStore, query, Transaction.AUTO_COMMIT, 0);
}
/**
* The {@link EFeatureWriter} constructor.
*
* @param eStore - {@link EFeatureDataStore} instance containing
* {@link EFeature} resource information
* @param query - {@link Query} instance. Note that {@link Query#getTypeName()}
* is expected to be a name of a {@link SimpleFeatureType} in given data store.
* <p>
* {@link SimpleFeatureType} names have the following format:
*
* <pre>
* eName=<eFolder>.<eReference>
*
* where
*
* eFolder = {@link EFeature} folder name
* eReference = {@link EFeature} reference name
* </pre>
* @param eTx {@link Transaction} instance
* @param flags - writer move flags ({@link #UPDATE} | {@link #APPEND})
* @throws IOException
*/
public EFeatureWriter(EFeatureDataStore eStore, Query query, Transaction eTx, int flags) throws IOException {
this.eReader = new EFeatureReader(eStore, query, eTx);
this.flags = flags;
}
// -----------------------------------------------------
// SimpleFeatureWriter implementation
// -----------------------------------------------------
/**
* Check if this writer supports updating.
* <p>
* If set, the writer returns updateable
* features as long as there exists {@link EFeature}s
* matching given query.
* </p>
*/
public boolean isUpdating() {
return (flags & APPEND) == APPEND;
}
/**
* Check if this writer supports appending.
* <p>
* If <code>true</code>, the writer returns a new
* {@link EFeature} of given type when the writer has
* no more updateable features. </p>
*/
public boolean isAppending() {
return (flags & APPEND) == APPEND;
}
@Override
public SimpleFeatureType getFeatureType() {
return eReader.getFeatureType();
}
@Override
public boolean hasNext() throws IOException {
if (eReader == null) {
throw new IOException("Writer has been closed");
}
if (isModified()) {
write();
}
return eReader.hasNext();
}
@Override
public ESimpleFeature next() throws IOException {
if (eReader == null) {
throw new IOException("Writer has been closed");
}
String fid = null;
try {
//
// Get flag
//
boolean bHasNext = hasNext();
//
// Has more features?
//
if (isUpdating() && bHasNext) {
//
// Grab next feature matching given query
//
eNext = eReader.next();
//
// Create live copy
//
eLive = new ESimpleFeatureImpl(eNext, eReader.eTx);
//
// Finished
//
return eLive;
} else if(isAppending()) {
//
// No more EObject found, create new from structure
//
EFeature eFeature = eNewInstance();
//
// Get default values
//
Object eValues[] = DataUtilities.defaultValues(getFeatureType());
//
// Set default values
//
eSetValues(eFeature, Arrays.asList(eValues));
//
// Finished
//
return (ESimpleFeature)eFeature.getData(eReader.eTx);
}
//
// Illegal writer mode
//
if(bHasNext) {
throw new IOException("EFeatureWriter does allow updates");
} else {
throw new IOException("EFeatureWriter does allow appending");
}
} catch (IllegalAttributeException e) {
String message = "Problem creating feature "
+ (fid != null ? fid : "");
throw new DataSourceException(message, e);
}
}
@Override
public void remove() throws IOException {
//
// Do sanity check
//
if (eNext == null) {
throw new IOException("No current feature to remove");
}
//
// Remove from resource?
//
EObject eObject = eNext.eObject();
if(eObject.eResource()!=null) {
EcoreUtil.delete(eObject);
}
//
// Release strong references
//
eNext = null;
eLive = null;
}
@Override
public void write() throws IOException {
//
// Do sanity checks
//
if (eLive == null) {
throw new IOException("No current feature to write");
}
if (!isModified()) {
throw new IOException("Feature is not modified");
}
//
// Write feature values to EObject
//
eLive.write(eReader.eTx);
//
// Add to resource backing given data store?
//
if(eNext==null) {
EObject eObject = eLive.eObject();
if(eObject.eResource()==null) {
eReader.eDataStore().eResource().getContents().add(eObject);
}
}
//eReader.eDataStore.listenerManager;
//
// Release strong references
//
eNext = null;
eLive = null;
}
/**
* Reset current iterator.
* <p>
* Any changes made to the feature
* returned by last call to {@link #next()}
* is discarded.
* @throws IOException
*/
public void reset() throws IOException {
//
// Forward
//
eReader.reset();
//
// Release strong references
//
eNext = null;
eLive = null;
}
@Override
public void close() throws IOException {
//
// Forward
//
eReader.close();
//
// Release strong references
//
eNext = null;
eLive = null;
}
// -----------------------------------------------------
// Helper methods
// -----------------------------------------------------
protected EFeature eNewInstance() {
EFeatureInfo eStructure = eReader.eStructure;
EObject eObject = eStructure.eNewInstance();
return EFeatureReader.eAdapt(eStructure, eObject,eReader.eHints);
}
protected void eSetValues(EObject eObject, List<Object> eValues) {
EFeatureUtils.eSetFeatureValues(eReader.eStructure,eObject,eValues,eReader.eTx);
}
protected boolean isModified(){
return eLive!=null && (eNext == null || !eLive.equals(eNext));
}
}