All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.vfny.geoserver.util.requests.readers.KvpRequestReader Maven / Gradle / Ivy

There is a newer version: 1.7.0
Show newest version
/* 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.vfny.geoserver.util.requests.readers;

import com.vividsolutions.jts.geom.Envelope;

import org.geoserver.ows.util.KvpUtils;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.FidFilter;
import org.geotools.filter.FilterFilter;
import org.geotools.filter.parser.ParseException;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.gml.GMLFilterDocument;
import org.geotools.gml.GMLFilterGeometry;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.Id;
import org.vfny.geoserver.Request;
import org.vfny.geoserver.ServiceException;
import org.vfny.geoserver.servlets.AbstractService;
import org.vfny.geoserver.util.requests.FilterHandlerImpl;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.ParserAdapter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;


/**
 * Base class for all KVP readers, with some generalized convenience methods.
 *
 * 

* If you pass this utility a KVP request (everything after the '?' in the GET * request URI), it will translate this into a list of key-word value * pairs.These pairs represent every element in the KVP GET request, legal or * otherwise. This class may then be subclassed and used by request-specific * classes. Because there is no error checking for the KVPs in this class, * subclasses must check for validity of their KVPs before passing the their * requests along, but - in return - this parent class is quite flexible. For * example, native KVPs may be easily parsed in its subclasses, since they are * simply read and stored (without analysis) in the constructer in this class. * Note that all keys are translated to upper case to avoid case conflicts. *

* * @author Rob Hranac, TOPP * @author Chris Holmes, TOPP * @author Gabriel Roldan * @version $Id: KvpRequestReader.java 8179 2008-01-16 16:56:48Z groldan $ */ abstract public class KvpRequestReader { /** Class logger */ private static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.requests.readers"); /** Delimeter for KVPs in the raw string */ private static final String KEYWORD_DELIMITER = "&"; /** Delimeter that seperates keywords from values */ private static final String VALUE_DELIMITER = "="; /** Delimeter for outer value lists in the KVPs */ protected static final String OUTER_DELIMETER = "()"; /** Delimeter for inner value lists in the KVPs */ protected static final String INNER_DELIMETER = ","; /** Holds mappings between HTTP and ASCII encodings */ protected final static FilterFactory factory = CommonFactoryFinder.getFilterFactory(null); /** KVP pair listing; stores all data from the KVP request */ protected Map kvpPairs = new HashMap(10); /** Reference to the service using the reader */ protected AbstractService service; /** * Creates a reader from paramters and a service. * * @param kvpPairs The key-value pairs. * @param service The service using the reader. */ public KvpRequestReader(Map kvpPairs, AbstractService service) { this.kvpPairs = kvpPairs; this.service = service; } /** * returns the value asociated with key on the set of * key/value pairs of this request reader * * @param key DOCUMENT ME! * * @return DOCUMENT ME! */ protected String getValue(String key) { return (String) kvpPairs.get(key); } /** * DOCUMENT ME! * * @param key DOCUMENT ME! * * @return DOCUMENT ME! */ protected boolean keyExists(String key) { return kvpPairs.containsKey(key); } /** * returns the propper Request subclass for the set of parameters it was * setted up and the kind of request it is specialized for * * @return DOCUMENT ME! */ public abstract Request getRequest(HttpServletRequest request) throws ServiceException; /** * Attempts to parse out the proper typeNames from the FeatureId filters. * It simply uses the value before the '.' character. * * @param rawFidList the strings after the FEATUREID url component. Should be found * using kvpPairs.get("FEATUREID") in this class or one of its * children * * @return A list of typenames, made from the featureId filters. * * @throws WfsException * If the structure can not be read. */ protected List getTypesFromFids(String rawFidList) { List typeList = new ArrayList(10); List unparsed = readNested(rawFidList); Iterator i = unparsed.listIterator(); while (i.hasNext()) { List ids = (List) i.next(); ListIterator innerIterator = ids.listIterator(); while (innerIterator.hasNext()) { String fid = innerIterator.next().toString(); if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("looking at featureId" + fid); } String typeName = fid.substring(0, fid.lastIndexOf(".")); if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("adding typename: " + typeName + " from fid"); } typeList.add(typeName); } } return typeList; } /** * Reads a tokenized string and turns it into a list. In this method, the * tokenizer is quite flexible. Note that if the list is unspecified (ie. is * null) or is unconstrained (ie. is ''), then the method returns an empty * list. * * @param rawList * The tokenized string. * @param delimiter * The delimeter for the string tokens. * * @return A list of the tokenized string. */ protected static List readFlat(String rawList, String delimiter) { return KvpUtils.readFlat(rawList,delimiter); } /** * Reads a nested tokenized string and turns it into a list. This method is * much more specific to the KVP get request syntax than the more general * readFlat method. In this case, the outer tokenizer '()' and inner * tokenizer ',' are both from the specification. Returns a list of lists. * * @param rawList * The tokenized string. * * @return A list of lists, containing outer and inner elements. * * @throws WfsException * When the string structure cannot be read. */ protected static List readNested(String rawList) { return KvpUtils.readNested(rawList); } /** * creates a Map of key/value pairs from a HTTP style query String * * @param qString * DOCUMENT ME! * * @return DOCUMENT ME! */ public static Map parseKvpSet(String qString) { // uses the request cleaner to remove HTTP junk String cleanRequest = clean(qString); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("clean request is " + cleanRequest); } Map kvps = null; kvps = new HashMap(10); // parses initial request sream into KVPs StringTokenizer requestKeywords = new StringTokenizer(cleanRequest.trim(), KEYWORD_DELIMITER); // parses KVPs into values and keywords and puts them in a HashTable while (requestKeywords.hasMoreTokens()) { String kvpPair = requestKeywords.nextToken(); String key; String value; // a bit of a horrible hack for filters, which handles problems of // delimeters, which may appear in XML (such as '=' for // attributes. unavoidable and illustrates the problems with // mixing nasty KVP Get syntax and pure XML syntax! // JD-adding SLD_BODY, when wms moves to new architecture this // can be fixed if (kvpPair.toUpperCase().startsWith("FILTER")) { String filterVal = kvpPair.substring(7); // int index = filterVal.lastIndexOf(""); // String filt2 = kvpPair.subString if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("putting filter value " + filterVal); } kvps.put("FILTER", filterVal); } else if (kvpPair.toUpperCase().startsWith("SLD_BODY")) { String sldBodyVal = kvpPair.substring(9); kvps.put("SLD_BODY", sldBodyVal); } else { // handles all other standard cases by looking for the correct // delimeter and then sticking the KVPs into the hash table StringTokenizer requestValues = new StringTokenizer(kvpPair, VALUE_DELIMITER); // make sure that there is a key token if (requestValues.hasMoreTokens()) { // assign key as uppercase to eliminate case conflict key = requestValues.nextToken().toUpperCase(); // make sure that there is a value token if (requestValues.hasMoreTokens()) { // assign value and store in hash with key value = requestValues.nextToken(); LOGGER.finest("putting kvp pair: " + key + ": " + value); kvps.put(key, value); } else { kvps.put(key, ""); } } } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("returning parsed " + kvps); } return kvps; } /** * Cleans an HTTP string and returns pure ASCII as a string. * * @param raw * The HTTP-encoded string. * * @return The string with the url escape characters replaced. */ private static String clean(String raw) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("raw request: " + raw); } String clean = null; if (raw != null) { try { clean = java.net.URLDecoder.decode(raw, "UTF-8"); } catch (java.io.UnsupportedEncodingException e) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("Bad encoding for decoder " + e); } } } else { return ""; } if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("cleaned request: " + raw); } return clean; } /** * Returns the service handling request. */ public AbstractService getServiceRef() { return service; } /** * sets the service handling request. */ public void setServiceRef(AbstractService service) { this.service = service; } /** * parses the BBOX parameter, wich must be a String of the form * minx,miny,maxx,maxy and returns a corresponding * Envelope object * @param bboxParam TODO * * @return the Envelope represented by the request BBOX * parameter * * @throws WmsException if the value of the BBOX request parameter can't be * parsed as four double's */ protected Envelope parseBbox(String bboxParam) throws ServiceException { Envelope bbox = null; Object[] bboxValues = readFlat(bboxParam, INNER_DELIMETER).toArray(); if (bboxValues.length != 4) { throw new ServiceException(bboxParam + " is not a valid pair of coordinates", getClass().getName()); } try { double minx = Double.parseDouble(bboxValues[0].toString()); double miny = Double.parseDouble(bboxValues[1].toString()); double maxx = Double.parseDouble(bboxValues[2].toString()); double maxy = Double.parseDouble(bboxValues[3].toString()); bbox = new Envelope(minx, maxx, miny, maxy); if (minx > maxx) { throw new ServiceException("illegal bbox, minX: " + minx + " is " + "greater than maxX: " + maxx); } if (miny > maxy) { throw new ServiceException("illegal bbox, minY: " + miny + " is " + "greater than maxY: " + maxy); } } catch (NumberFormatException ex) { throw new ServiceException(ex, "Illegal value for BBOX parameter: " + bboxParam, getClass().getName() + "::parseBbox()"); } return bbox; } /** * Parses an OGC filter * @param filter * @return * @throws ServiceException * * */ protected List readOGCFilter(String filter) throws ServiceException { List filters = new ArrayList(); List unparsed; ListIterator i; LOGGER.finest("reading filter: " + filter); unparsed = readFlat(filter, OUTER_DELIMETER); i = unparsed.listIterator(); while (i.hasNext()) { String next = (String) i.next(); if (next.trim().equals("")) { filters.add(Filter.INCLUDE); } else { filters.add(parseXMLFilter(new StringReader(next))); } } return filters; } /** * Parses a CQL filter * @param filter * @return * @throws ServiceException * * @deprecated use {@link CQLFilterKvpParser} */ protected List readCQLFilter(String filter) throws ServiceException { try { return CQL.toFilterList(filter); } catch (CQLException pe) { throw new ServiceException("Could not parse CQL filter list." + pe.getMessage(), pe); } } /** * Parses fid filters * @param fid * @return */ protected List readFidFilters(String fid) { List filters = new ArrayList(); List unparsed; ListIterator i; LOGGER.finest("reading fid filter: " + fid); unparsed = readNested(fid); i = unparsed.listIterator(); while (i.hasNext()) { List ids = (List) i.next(); ListIterator innerIterator = ids.listIterator(); while (innerIterator.hasNext()) { HashSet set = new HashSet(); set.add(factory.featureId((String) innerIterator.next())); Id fidFilter = factory.id(set); filters.add(fidFilter); LOGGER.finest("added fid filter: " + fidFilter); } } return filters; } /** * Reads the Filter XML request into a geotools Feature object. * * @param rawRequest The plain POST text from the client. * * @return The geotools filter constructed from rawRequest. * * @throws WfsException For any problems reading the request. * * @deprecated use {@link FilterKvpParser} */ protected Filter parseXMLFilter(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(); 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.setContentHandler(documentFilter); adapter.parse(requestSource); LOGGER.fine("just parsed: " + requestSource); } catch (SAXException e) { //SAXException does not sets initCause(). Instead, it holds its own "exception" field. if(e.getException() != null && e.getCause() == null){ e.initCause(e.getException()); } 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(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy