/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wfs.kvp;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.KvpParser;
import org.geoserver.ows.util.KvpUtils;
import org.geoserver.platform.ServiceException;
import org.geoserver.util.EntityResolverProvider;
import org.geotools.filter.FilterFilter;
import org.geotools.gml.GMLFilterDocument;
import org.geotools.gml.GMLFilterGeometry;
import org.geotools.xml.Configuration;
import org.geotools.xml.Parser;
import org.opengis.filter.Filter;
import org.vfny.geoserver.util.requests.FilterHandlerImpl;
import org.vfny.geoserver.util.requests.readers.XmlRequestReader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.ParserAdapter;
/**
* A base {@code FILTER} parameter parser that expects a subclass to provide the
* actual {@link Parser} configuration for the expected OGC Filter Encoding spec
* version.
*
* @author Justin Deoliveira
* @author Gabriel Roldan
*/
public abstract class FilterKvpParser extends KvpParser {
private EntityResolverProvider entityResolverProvider;
public FilterKvpParser(GeoServer geoServer) {
super("filter", List.class);
this.entityResolverProvider = new EntityResolverProvider(geoServer);
}
/**
* Subclasses shall implement to provide the parse() method the appropriate
* parser Configuration for the filter spec version they specialize on.
*
* @return The Configuration for the appropriate Filter spec version.
*/
protected abstract Configuration getParserConfiguration();
public Object parse(String value) throws Exception {
// create the parser
final Configuration configuration = getParserConfiguration();
final Parser parser = new Parser(configuration);
parser.setEntityResolver(entityResolverProvider.getEntityResolver());
// seperate the individual filter strings
List unparsed = KvpUtils.readFlat(value, KvpUtils.OUTER_DELIMETER);
List filters = new ArrayList();
Iterator i = unparsed.listIterator();
while (i.hasNext()) {
String string = (String) i.next();
if ("".equals(string.trim())) {
filters.add(Filter.INCLUDE);
} else {
try {
Filter filter = (Filter) parser.parse(new StringReader(string));
if (filter == null) {
throw new NullPointerException();
}
filters.add(filter);
} catch (Exception e) {
// parsing failed, fall back to old parser
String msg = "Unable to parse filter: " + string;
LOGGER.log(Level.WARNING, msg, e);
Filter filter = parseXMLFilterWithOldParser(new StringReader(string));
if (filter != null) {
filters.add(filter);
}
}
}
}
return filters;
}
/**
* Reads the Filter XML request into a geotools Feature object.
* <p>
* This uses the "old" filter parser and is around to maintain some
* backwards compatability with cases in which the new parser chokes on a
* filter that hte old one could handle.
* </p>
*
* @param rawRequest
* The plain POST text from the client.
*
* @return The geotools filter constructed from rawRequest.
*
* @throws WfsException
* For any problems reading the request.
*/
protected Filter parseXMLFilterWithOldParser(Reader rawRequest) throws ServiceException {
// translate string into a proper SAX input source
InputSource requestSource = new InputSource(rawRequest);
// instantiante parsers and content handlers
FilterHandlerImpl contentHandler = new FilterHandlerImpl();
contentHandler.setEntityResolver(entityResolverProvider.getEntityResolver());
FilterFilter filterParser = new FilterFilter(contentHandler, null);
GMLFilterGeometry geometryFilter = new GMLFilterGeometry(filterParser);
GMLFilterDocument documentFilter = new GMLFilterDocument(geometryFilter);
// read in XML file and parse to content handler
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
ParserAdapter adapter = new ParserAdapter(parser.getParser());
adapter.setEntityResolver(entityResolverProvider.getEntityResolver());
adapter.setContentHandler(documentFilter);
adapter.parse(requestSource);
LOGGER.fine("just parsed: " + requestSource);
} catch (SAXException e) {
throw new ServiceException(e, "XML getFeature request SAX parsing error",
XmlRequestReader.class.getName());
} catch (IOException e) {
throw new ServiceException(e, "XML get feature request input error",
XmlRequestReader.class.getName());
} catch (ParserConfigurationException e) {
throw new ServiceException(e, "Some sort of issue creating parser",
XmlRequestReader.class.getName());
}
LOGGER.fine("passing filter: " + contentHandler.getFilter());
return contentHandler.getFilter();
}
}