/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.wfs;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import net.opengis.wfs.InsertElementType;
import net.opengis.wfs.InsertedFeatureType;
import net.opengis.wfs.TransactionResponseType;
import net.opengis.wfs.TransactionType;
import net.opengis.wfs.WfsFactory;
import org.eclipse.emf.ecore.EObject;
import org.geoserver.config.GeoServer;
import org.geoserver.feature.ReprojectingFeatureCollection;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.operation.projection.PointOutsideEnvelopeException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.identity.FeatureId;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Geometry;
/**
* Handler for the insert element
*
* @author Andrea Aime - TOPP
*
*/
public class InsertElementHandler extends AbstractTransactionElementHandler {
/**
* logger
*/
static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.wfs");
private FilterFactory filterFactory;
public InsertElementHandler(GeoServer gs, FilterFactory filterFactory) {
super(gs);
this.filterFactory = filterFactory;
}
public void checkValidity(EObject element, Map featureTypeInfos)
throws WFSTransactionException {
if (!getInfo().getServiceLevel().getOps().contains( WFSInfo.Operation.TRANSACTION_INSERT)) {
throw new WFSException("Transaction INSERT support is not enabled");
}
}
@SuppressWarnings("unchecked")
public void execute(EObject element, TransactionType request, Map featureStores,
TransactionResponseType response, TransactionListener listener)
throws WFSTransactionException {
LOGGER.finer("Transasction Insert:" + element);
InsertElementType insert = (InsertElementType) element;
long inserted = response.getTransactionSummary().getTotalInserted().longValue();
try {
// group features by their schema
HashMap /* <SimpleFeatureType,FeatureCollection> */ schema2features = new HashMap();
for (Iterator f = insert.getFeature().iterator(); f.hasNext();) {
SimpleFeature feature = (SimpleFeature) f.next();
SimpleFeatureType schema = feature.getFeatureType();
SimpleFeatureCollection collection;
collection = (SimpleFeatureCollection) schema2features.get(schema);
if (collection == null) {
collection = new DefaultFeatureCollection(null, schema);
schema2features.put(schema, collection);
}
collection.add(feature);
}
// JD: change from set fo list because if inserting
// features into different feature stores, they could very well
// get given the same id
// JD: change from list to map so that the map can later be
// processed and we can report the fids back in the same order
// as they were supplied
HashMap schema2fids = new HashMap();
for (Iterator c = schema2features.values().iterator(); c.hasNext();) {
SimpleFeatureCollection collection = (SimpleFeatureCollection) c.next();
SimpleFeatureType schema = collection.getSchema();
final QName elementName = new QName(schema.getName().getNamespaceURI(), schema.getTypeName());
SimpleFeatureStore store;
store = DataUtilities.simple((FeatureStore) featureStores.get(elementName));
if (store == null) {
throw new WFSException("Could not locate FeatureStore for '" + elementName
+ "'");
}
if (collection != null) {
// if we really need to, make sure we are inserting coordinates that do
// match the CRS area of validity
if(getInfo().isCiteCompliant()) {
checkFeatureCoordinatesRange(collection);
}
// reprojection
final GeometryDescriptor defaultGeometry = store.getSchema().getGeometryDescriptor();
if(defaultGeometry != null) {
CoordinateReferenceSystem target = defaultGeometry.getCoordinateReferenceSystem();
if (target != null) {
collection = new ReprojectingFeatureCollection(collection, target);
}
}
// Need to use the namespace here for the
// lookup, due to our weird
// prefixed internal typenames. see
// http://jira.codehaus.org/secure/ViewIssue.jspa?key=GEOS-143
// Once we get our datastores making features
// with the correct namespaces
// we can do something like this:
// FeatureTypeInfo typeInfo =
// catalog.getFeatureTypeInfo(schema.getTypeName(),
// schema.getNamespace());
// until then (when geos-144 is resolved) we're
// stuck with:
// QName qName = (QName) typeNames.get( i );
// FeatureTypeInfo typeInfo =
// catalog.featureType( qName.getPrefix(),
// qName.getLocalPart() );
// this is possible with the insert hack above.
LOGGER.finer("Use featureValidation to check contents of insert");
// featureValidation(
// typeInfo.getDataStore().getId(), schema,
// collection );
List fids = (List) schema2fids.get(schema.getTypeName());
if (fids == null) {
fids = new LinkedList();
schema2fids.put(schema.getTypeName(), fids);
}
//fire pre insert event
TransactionEvent event = new TransactionEvent(TransactionEventType.PRE_INSERT,
elementName, collection);
event.setSource( insert );
listener.dataStoreChange( event );
fids.addAll(store.addFeatures(collection));
//fire post insert event
//event = new TransactionEvent(TransactionEventType.POST_INSERT, elementName, collection, insert );
//listener.dataStoreChange( event );
}
}
// report back fids, we need to keep the same order the
// fids were reported in the original feature collection
InsertedFeatureType insertedFeature = null;
for (Iterator f = insert.getFeature().iterator(); f.hasNext();) {
SimpleFeature feature = (SimpleFeature) f.next();
SimpleFeatureType schema = feature.getFeatureType();
// get the next fid
LinkedList fids = (LinkedList) schema2fids.get(schema.getTypeName());
String fid = ((FeatureId) fids.removeFirst()).getID();
insertedFeature = WfsFactory.eINSTANCE.createInsertedFeatureType();
insertedFeature.setHandle(insert.getHandle());
insertedFeature.getFeatureId().add(filterFactory.featureId(fid));
response.getInsertResults().getFeature().add(insertedFeature);
}
// update the insert counter
inserted += insert.getFeature().size();
} catch (Exception e) {
String msg = "Error performing insert: " + e.getMessage();
throw new WFSTransactionException(msg, e, insert.getHandle());
}
// update transaction summary
response.getTransactionSummary().setTotalInserted(BigInteger.valueOf(inserted));
}
/**
* Checks that all features coordinates are within the expected coordinate range
* @param collection
* @throws PointOutsideEnvelopeException
*/
void checkFeatureCoordinatesRange(SimpleFeatureCollection collection)
throws PointOutsideEnvelopeException {
List types = collection.getSchema().getAttributeDescriptors();
SimpleFeatureIterator fi = collection.features();
try {
while(fi.hasNext()) {
SimpleFeature f = fi.next();
for (int i = 0; i < types.size(); i++) {
if(types.get(i) instanceof GeometryDescriptor) {
GeometryDescriptor gat = (GeometryDescriptor) types.get(i);
if(gat.getCoordinateReferenceSystem() != null) {
Geometry geom = (Geometry) f.getAttribute(i);
if(geom != null)
JTS.checkCoordinatesRange(geom, gat.getCoordinateReferenceSystem());
}
}
}
}
} finally {
fi.close();
}
}
public Class getElementClass() {
return InsertElementType.class;
}
public QName[] getTypeNames(EObject element) throws WFSTransactionException {
InsertElementType insert = (InsertElementType) element;
List typeNames = new ArrayList();
if (!insert.getFeature().isEmpty()) {
for (Iterator f = insert.getFeature().iterator(); f.hasNext();) {
SimpleFeature feature = (SimpleFeature) f.next();
String name = feature.getFeatureType().getTypeName();
String namespaceURI = feature.getFeatureType().getName().getNamespaceURI();
typeNames.add(new QName(namespaceURI, name));
}
} else {
LOGGER.finer("Insert was empty - does not need a FeatuerSoruce");
}
return (QName[]) typeNames.toArray(new QName[typeNames.size()]);
}
}