int count = 0; //should probably be long
List results = new ArrayList();
try {
for (int i = 0; (i < request.getQuery().size()) && (count < maxFeatures); i++) {
QueryType query = (QueryType) request.getQuery().get(i);
FeatureTypeInfo meta = null;
if (query.getTypeName().size() == 1) {
meta = featureTypeInfo((QName) query.getTypeName().get(0));
} else {
//TODO: a join is taking place
}
FeatureSource<? extends FeatureType, ? extends Feature> source = meta.getFeatureSource(null,null);
List<AttributeTypeInfo> atts = meta.attributes();
List attNames = new ArrayList( atts.size() );
for ( AttributeTypeInfo att : atts ) {
attNames.add( att.getName() );
}
//make sure property names are cool
List propNames = query.getPropertyName();
for (Iterator iter = propNames.iterator(); iter.hasNext();) {
String propName = (String) iter.next();
//HACK: strip off namespace
if (propName.indexOf(':') != -1) {
propName = propName.substring(propName.indexOf(':') + 1);
}
if (!attNames.contains(propName)) {
String mesg = "Requested property: " + propName + " is " + "not available "
+ "for " + query.getTypeName() + ". " + "The possible propertyName "
+ "values are: " + attNames;
throw new WFSException(mesg);
}
}
//we must also include any properties that are mandatory ( even if not requested ),
// ie. those with minOccurs > 0
List extraGeometries = new ArrayList();
List properties = new ArrayList();
if (propNames.size() != 0) {
Iterator ii = atts.iterator();
while (ii.hasNext()) {
AttributeTypeInfo ati = (AttributeTypeInfo) ii.next();
LOGGER.finer("checking to see if " + propNames + " contains" + ati);
if (((ati.getMinOccurs() > 0) && (ati.getMaxOccurs() != 0))) {
//mandatory, add it
properties.add(ati.getName());
continue;
}
//check if it was requested
for (Iterator p = propNames.iterator(); p.hasNext();) {
String propName = (String) p.next();
if (propName.matches("(\\w+:)?" + ati.getName())) {
properties.add(ati.getName());
break;
}
}
// if we need to force feature bounds computation, we have to load
// all of the geometries, but we'll have to remove them in the
// returned feature type
if(wfs.isFeatureBounding() && meta.getFeatureType().getDescriptor(ati.getName()) instanceof GeometryDescriptor
&& !properties.contains(ati.getName())) {
properties.add(ati.getName());
extraGeometries.add(ati.getName());
}
}
//replace property names
query.getPropertyName().clear();
query.getPropertyName().addAll(properties);
}
//make sure filters are sane
//
// Validation of filters on non-simple feature types is not yet supported.
// FIXME: Support validation of filters on non-simple feature types:
// need to consider xpath properties and how to configure namespace prefixes in
// GeoTools app-schema FeaturePropertyAccessorFactory.
if (query.getFilter() != null && source.getSchema() instanceof SimpleFeatureType) {
//1. ensure any property name refers to a property that
// actually exists
final FeatureType featureType = source.getSchema();
ExpressionVisitor visitor = new AbstractExpressionVisitor() {
public Object visit(PropertyName name, Object data) {
// case of multiple geometries being returned
if (name.evaluate(featureType) == null) {
throw new WFSException("Illegal property name: "
+ name.getPropertyName(), "InvalidParameterValue");
}
return name;
}
;
};
query.getFilter().accept(new AbstractFilterVisitor(visitor), null);
//2. ensure any spatial predicate is made against a property
// that is actually special
AbstractFilterVisitor fvisitor = new AbstractFilterVisitor() {
protected Object visit( BinarySpatialOperator filter, Object data ) {
PropertyName name = null;
if ( filter.getExpression1() instanceof PropertyName ) {
name = (PropertyName) filter.getExpression1();
}
else if ( filter.getExpression2() instanceof PropertyName ) {
name = (PropertyName) filter.getExpression2();
}
if ( name != null ) {
//check against fetaure type to make sure its
// a geometric type
AttributeDescriptor att = (AttributeDescriptor) name.evaluate(featureType);
if ( !( att instanceof GeometryDescriptor ) ) {
throw new WFSException("Property " + name + " is not geometric", "InvalidParameterValue");
}
}
return filter;
}
};
query.getFilter().accept(fvisitor, null);
//3. ensure that any bounds specified as part of the query
// are valid with respect to the srs defined on the query
if ( wfs.isCiteCompliant() ) {
if ( query.getSrsName() != null ) {
final QueryType fquery = query;
fvisitor = new AbstractFilterVisitor() {
public Object visit(BBOX filter, Object data) {
if ( filter.getSRS() != null &&
!fquery.getSrsName().toString().equals( filter.getSRS() ) ) {
//back project bounding box into geographic coordinates
CoordinateReferenceSystem geo = DefaultGeographicCRS.WGS84;
GeneralEnvelope e = new GeneralEnvelope(
new double[] { filter.getMinX(), filter.getMinY()},
new double[] { filter.getMaxX(), filter.getMaxY()}
);
CoordinateReferenceSystem crs = null;
try {
crs = CRS.decode( filter.getSRS() );
e = CRS.transform(CRS.findMathTransform(crs, geo, true), e);
}
catch( Exception ex ) {
throw new WFSException( ex );
}
//ensure within bounds defined by srs specified on
// query
try {
crs = CRS.decode( fquery.getSrsName().toString() );
}
catch( Exception ex ) {
throw new WFSException( ex );
}
GeographicBoundingBox valid =
(GeographicBoundingBox) crs.getDomainOfValidity()
.getGeographicElements().iterator().next();
if ( e.getMinimum(0) < valid.getWestBoundLongitude() ||
e.getMinimum(0) > valid.getEastBoundLongitude() ||
e.getMaximum(0) < valid.getWestBoundLongitude() ||
e.getMaximum(0) > valid.getEastBoundLongitude() ||
e.getMinimum(1) < valid.getSouthBoundLatitude() ||
e.getMinimum(1) > valid.getNorthBoundLatitude() ||
e.getMaximum(1) < valid.getSouthBoundLatitude() ||
e.getMaximum(1) > valid.getNorthBoundLatitude() ) {
throw new WFSException( "bounding box out of valid range of crs", "InvalidParameterValue");
}
}
return data;
}
};
query.getFilter().accept(fvisitor, null);
}
}
}
// handle local maximum
int queryMaxFeatures = maxFeatures - count;
if(meta.getMaxFeatures() > 0 && meta.getMaxFeatures() < queryMaxFeatures)
queryMaxFeatures = meta.getMaxFeatures();
Map<String, String> viewParam = viewParams != null ? viewParams.get(i) : null;
org.geotools.data.Query gtQuery = toDataQuery(query, queryMaxFeatures, source, request, viewParam);
LOGGER.fine("Query is " + query + "\n To gt2: " + gtQuery);
FeatureCollection<? extends FeatureType, ? extends Feature> features = getFeatures(request, source, gtQuery);
// optimization: WFS 1.0 does not require count unless we have multiple query elements
// and we are asked to perform a global limit on the results returned
if(("1.0".equals(request.getVersion()) || "1.0.0".equals(request.getVersion())) &&
(request.getQuery().size() == 1 || maxFeatures == Integer.MAX_VALUE)) {
// skip the count update, in this case we don't need it
} else {
count += features.size();
}
// we may need to shave off geometries we did load only to make bounds
// computation happy
// TODO: support non-SimpleFeature geometry shaving
if(features.getSchema() instanceof SimpleFeatureType && extraGeometries.size() > 0) {
List residualProperties = new ArrayList(properties);
residualProperties.removeAll(extraGeometries);
String[] residualNames = (String[]) residualProperties.toArray(new String[residualProperties.size()]);
SimpleFeatureType targetType = DataUtilities.createSubType((SimpleFeatureType) features.getSchema(), residualNames);
features = new FeatureBoundsFeatureCollection((SimpleFeatureCollection) features, targetType);
}
//JD: TODO reoptimize
// if ( i == request.getQuery().size() - 1 ) {
// //DJB: dont calculate feature count if you dont have to. The MaxFeatureReader will take care of the last iteration
// maxFeatures -= features.getCount();
// }
//GR: I don't know if the featuresults should be added here for later
//encoding if it was a lock request. may be after ensuring the lock
//succeed?
results.add(features);
}
} catch (IOException e) {
throw new WFSException("Error occurred getting features", e, request.getHandle());
} catch (SchemaException e) {
throw new WFSException("Error occurred getting features", e, request.getHandle());
}
//locking
String lockId = null;
if (request instanceof GetFeatureWithLockType) {
GetFeatureWithLockType withLockRequest = (GetFeatureWithLockType) request;
LockFeatureType lockRequest = WfsFactory.eINSTANCE.createLockFeatureType();
lockRequest.setExpiry(withLockRequest.getExpiry());
lockRequest.setHandle(withLockRequest.getHandle());
lockRequest.setLockAction(AllSomeType.ALL_LITERAL);
for (int i = 0; i < request.getQuery().size(); i++) {
QueryType query = (QueryType) request.getQuery().get(i);
LockType lock = WfsFactory.eINSTANCE.createLockType();
lock.setFilter(query.getFilter());
lock.setHandle(query.getHandle());
//TODO: joins?
lock.setTypeName((QName) query.getTypeName().get(0));
lockRequest.getLock().add(lock);
}
LockFeature lockFeature = new LockFeature(wfs, catalog);
lockFeature.setFilterFactory(filterFactory);