001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.geoserver.wfs;
006:
007: import net.opengis.wfs.AllSomeType;
008: import net.opengis.wfs.PropertyType;
009: import net.opengis.wfs.TransactionResponseType;
010: import net.opengis.wfs.TransactionType;
011: import net.opengis.wfs.UpdateElementType;
012: import org.eclipse.emf.ecore.EObject;
013: import org.geoserver.feature.ReprojectingFeatureCollection;
014: import org.geotools.data.FeatureLocking;
015: import org.geotools.data.FeatureStore;
016: import org.geotools.factory.CommonFactoryFinder;
017: import org.geotools.factory.GeoTools;
018: import org.geotools.feature.AttributeType;
019: import org.geotools.feature.Feature;
020: import org.geotools.feature.FeatureCollection;
021: import org.geotools.feature.FeatureType;
022: import org.geotools.feature.type.GeometricAttributeType;
023: import org.geotools.geometry.jts.GeometryCoordinateSequenceTransformer;
024: import org.geotools.geometry.jts.JTS;
025: import org.geotools.referencing.CRS;
026: import org.geotools.referencing.operation.projection.PointOutsideEnvelopeException;
027: import org.opengis.filter.Filter;
028: import org.opengis.filter.FilterFactory;
029: import org.opengis.filter.FilterFactory2;
030: import org.opengis.filter.Id;
031: import org.opengis.filter.expression.PropertyName;
032: import org.opengis.referencing.crs.CoordinateReferenceSystem;
033: import org.opengis.referencing.operation.MathTransform;
034: import org.vfny.geoserver.global.FeatureTypeInfo;
035:
036: import com.vividsolutions.jts.geom.Geometry;
037:
038: import java.io.IOException;
039: import java.math.BigInteger;
040: import java.util.HashSet;
041: import java.util.Iterator;
042: import java.util.Map;
043: import java.util.NoSuchElementException;
044: import java.util.Set;
045: import java.util.logging.Logger;
046: import javax.xml.namespace.QName;
047:
048: /**
049: * Processes standard update elements
050: *
051: * @author Andrea Aime - TOPP
052: *
053: */
054: public class UpdateElementHandler implements TransactionElementHandler {
055: /**
056: * logger
057: */
058: static Logger LOGGER = org.geotools.util.logging.Logging
059: .getLogger("org.geoserver.wfs");
060: private WFS wfs;
061:
062: public UpdateElementHandler(WFS wfs) {
063: this .wfs = wfs;
064: }
065:
066: public void checkValidity(EObject element, Map typeInfos)
067: throws WFSTransactionException {
068: // check inserts are enabled
069: if ((wfs.getServiceLevel() & WFS.SERVICE_UPDATE) == 0) {
070: throw new WFSException(
071: "Transaction Update support is not enabled");
072: }
073:
074: FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
075:
076: // check that all required properties have a specified value
077: UpdateElementType update = (UpdateElementType) element;
078:
079: try {
080: FeatureTypeInfo meta = (FeatureTypeInfo) typeInfos.values()
081: .iterator().next();
082: FeatureType featureType = meta.getFeatureType();
083:
084: for (Iterator prop = update.getProperty().iterator(); prop
085: .hasNext();) {
086: PropertyType property = (PropertyType) prop.next();
087:
088: //check that valus that are non-nillable exist
089: if (property.getValue() == null) {
090: String propertyName = property.getName()
091: .getLocalPart();
092: AttributeType attributeType = featureType
093: .getAttributeType(propertyName);
094:
095: if ((attributeType != null)
096: && (attributeType.getMinOccurs() > 0)) {
097: String msg = "Property '"
098: + attributeType.getLocalName()
099: + "' is mandatory but no value specified.";
100: throw new WFSException(msg,
101: "MissingParameterValue");
102: }
103: }
104:
105: //check that property names are actually valid
106: QName name = property.getName();
107: PropertyName propertyName = null;
108:
109: if (name.getPrefix() != null
110: && !"".equals(name.getPrefix())) {
111: propertyName = ff.property(name.getPrefix() + ":"
112: + name.getLocalPart());
113: } else {
114: propertyName = ff.property(name.getLocalPart());
115: }
116:
117: if (propertyName.evaluate(featureType) == null) {
118: String msg = "No such property: "
119: + property.getName();
120: throw new WFSException(msg);
121: }
122: }
123: } catch (IOException e) {
124: throw new WFSTransactionException(
125: "Could not locate feature type information for "
126: + update.getTypeName(), e, update
127: .getHandle());
128: }
129: }
130:
131: public void execute(EObject element, TransactionType request,
132: Map featureStores, TransactionResponseType response,
133: TransactionListener listener)
134: throws WFSTransactionException {
135: UpdateElementType update = (UpdateElementType) element;
136: final QName elementName = update.getTypeName();
137: String handle = update.getHandle();
138: long updated = response.getTransactionSummary()
139: .getTotalUpdated().longValue();
140:
141: FeatureStore store = (FeatureStore) featureStores
142: .get(elementName);
143:
144: if (store == null) {
145: throw new WFSException(
146: "Could not locate FeatureStore for '" + elementName
147: + "'");
148: }
149:
150: LOGGER.finer("Transaction Update:" + element);
151:
152: try {
153: Filter filter = (Filter) update.getFilter();
154:
155: // make sure all geometric elements in the filter have a crs, and that the filter
156: // is reprojected to store's native crs as well
157: CoordinateReferenceSystem declaredCRS = WFSReprojectionUtil
158: .getDeclaredCrs(store.getSchema(), request
159: .getVersion());
160: filter = WFSReprojectionUtil.normalizeFilterCRS(filter,
161: store.getSchema(), declaredCRS);
162:
163: AttributeType[] types = new AttributeType[update
164: .getProperty().size()];
165: Object[] values = new Object[update.getProperty().size()];
166:
167: for (int j = 0; j < update.getProperty().size(); j++) {
168: PropertyType property = (PropertyType) update
169: .getProperty().get(j);
170: types[j] = store.getSchema().getAttributeType(
171: property.getName().getLocalPart());
172: values[j] = property.getValue();
173:
174: // if geometry, it may be necessary to reproject it to the native CRS before
175: // update
176: if (values[j] instanceof Geometry) {
177: Geometry geometry = (Geometry) values[j];
178:
179: // get the source crs, check the geometry itself first. If not set, assume
180: // the default one
181: CoordinateReferenceSystem source = null;
182: if (geometry.getUserData() instanceof CoordinateReferenceSystem) {
183: source = (CoordinateReferenceSystem) geometry
184: .getUserData();
185: } else {
186: geometry.setUserData(declaredCRS);
187: source = declaredCRS;
188: }
189:
190: // see if the geometry has a CRS other than the default one
191: CoordinateReferenceSystem target = null;
192: if (types[j] instanceof GeometricAttributeType) {
193: target = ((GeometricAttributeType) types[j])
194: .getCoordinateSystem();
195: }
196:
197: if (wfs.getCiteConformanceHacks())
198: JTS.checkCoordinatesRange(geometry,
199: source != null ? source : target);
200:
201: //if we have a source and target and they are not equal, do
202: // the reprojection, otherwise just update the value as is
203: if (source != null
204: && target != null
205: && !CRS
206: .equalsIgnoreMetadata(source,
207: target)) {
208: try {
209: //TODO: this code should be shared with the code
210: // from ReprojectingFeatureCollection --JD
211: MathTransform tx = CRS.findMathTransform(
212: source, target, true);
213: GeometryCoordinateSequenceTransformer gtx = new GeometryCoordinateSequenceTransformer();
214: gtx.setMathTransform(tx);
215:
216: values[j] = gtx.transform(geometry);
217: } catch (Exception e) {
218: String msg = "Failed to reproject geometry:"
219: + e.getLocalizedMessage();
220: throw new WFSTransactionException(msg, e);
221: }
222: }
223:
224: }
225: }
226:
227: // Pass through data to collect fids and damaged
228: // region
229: // for validation
230: //
231: Set fids = new HashSet();
232: LOGGER
233: .finer("Preprocess to remember modification as a set of fids");
234:
235: FeatureCollection features = store.getFeatures(filter);
236: listener.dataStoreChange(new TransactionEvent(
237: TransactionEventType.PRE_UPDATE, elementName,
238: features));
239:
240: Iterator preprocess = features.iterator();
241:
242: try {
243: while (preprocess.hasNext()) {
244: Feature feature = (Feature) preprocess.next();
245: fids.add(feature.getID());
246: }
247: } catch (NoSuchElementException e) {
248: throw new WFSException("Could not aquire FeatureIDs", e);
249: } finally {
250: features.close(preprocess);
251: }
252:
253: try {
254: if (types.length == 1) {
255: store.modifyFeatures(types[0], values[0], filter);
256: } else {
257: store.modifyFeatures(types, values, filter);
258: }
259: } finally {
260: // make sure we unlock
261: if ((request.getLockId() != null)
262: && store instanceof FeatureLocking
263: && (request.getReleaseAction() == AllSomeType.SOME_LITERAL)) {
264: FeatureLocking locking = (FeatureLocking) store;
265: locking.unLockFeatures(filter);
266: }
267: }
268:
269: // Post process - gather the same features after the update, and
270: if (!fids.isEmpty()) {
271: LOGGER
272: .finer("Post process update for boundary update and featureValidation");
273:
274: Set featureIds = new HashSet();
275:
276: FilterFactory2 ff = CommonFactoryFinder
277: .getFilterFactory2(GeoTools.getDefaultHints());
278: for (Iterator f = fids.iterator(); f.hasNext();) {
279: featureIds.add(ff.featureId((String) f.next()));
280: }
281:
282: Id modified = ff.id(featureIds);
283:
284: FeatureCollection changed = store.getFeatures(modified);
285: listener.dataStoreChange(new TransactionEvent(
286: TransactionEventType.POST_UPDATE, elementName,
287: changed));
288: }
289:
290: // update the update counter
291: updated += fids.size();
292: } catch (IOException ioException) {
293: // JD: changing from throwing service exception to
294: // adding action that failed
295: throw new WFSTransactionException(ioException, null, handle);
296: } catch (PointOutsideEnvelopeException poe) {
297: throw new WFSTransactionException(poe, null, handle);
298: }
299:
300: // update transaction summary
301: response.getTransactionSummary().setTotalUpdated(
302: BigInteger.valueOf(updated));
303: }
304:
305: public Class getElementClass() {
306: return UpdateElementType.class;
307: }
308:
309: public QName[] getTypeNames(EObject element)
310: throws WFSTransactionException {
311: return new QName[] { ((UpdateElementType) element)
312: .getTypeName() };
313: }
314: }
|