/**
* Search Engine support
*/
package org.springmodules.web.servlet.mvc;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.mvc.LastModified;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author janm
*/
public class SearchEngineFriendlyHandlerAdapter implements HandlerAdapter {
/**
* List of <code>String</code>s
*/
private List suffixes;
private String queryStringSeparator = "~";
private String parameterSeparator = "_";
private String nameValueSeparator = "-";
/**
* Request parameter entry
* @author janm
*/
static class RequestParameterEntry {
private String name;
private String value;
/**
* Creates new instance of RequestParameterEntry
* @param name The parameter name
* @param value The parameter value
*/
public RequestParameterEntry(String name, String value) {
this.name = name;
this.value = value;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj instanceof RequestParameterEntry) {
RequestParameterEntry rhs = (RequestParameterEntry)obj;
return rhs.name.equals(this.name) && rhs.value.equals(this.value);
} else {
return false;
}
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
int result = 17;
result += name.hashCode() * 37;
result += value.hashCode() * 37;
return super.hashCode();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
StringBuffer result = new StringBuffer();
result.append(getName());
result.append(" [ name=");
result.append(name);
result.append(", value=");
result.append(value);
result.append("]");
return result.toString();
}
/**
* Gets the value of name
* @return Value of name.
*/
public final String getName() {
return name;
}
/**
* Gets the value of value
* @return Value of value.
*/
public final String getValue() {
return value;
}
}
/**
* The SE wrapper class
*/
static class SearchEngineRequestWrapper extends HttpServletRequestWrapper {
private String queryStringSeparator;
private String parameterSeparator;
private Pattern queryStringElementPattern;
private HttpServletRequest request;
private String servletPath;
private StringBuffer url;
private String queryString;
private Map queryParameterMap; // map of String => String[]
private List suffixes; // list of String
/**
* .ctor
* @param request The request
*/
public SearchEngineRequestWrapper(HttpServletRequest request, List suffixes, String queryStringSeparator, String parameterSeparator, String nameValueSeparator) {
super(request);
this.parameterSeparator = parameterSeparator;
this.queryStringSeparator = queryStringSeparator;
this.request = request;
this.suffixes = suffixes;
this.queryStringElementPattern = Pattern.compile("^(.+)" + nameValueSeparator + "(.*)$");
url = new StringBuffer(request.getRequestURL().length());
queryParameterMap = new HashMap();
for (Enumeration names = request.getParameterNames(); names.hasMoreElements();) {
String name = (String) names.nextElement();
String[] values = request.getParameterValues(name);
queryParameterMap.put(name, values);
}
parseRequest(request);
}
/**
* Parses the query string and builds the queryParameters
*/
private void parseQueryString() {
// don't bother with empty query strings
if (queryString.length() == 0) return;
// we have the query string; parse it
if (queryString.indexOf(parameterSeparator) == -1) {
parseQueryStringElement(queryString);
} else {
String sx[] = queryString.split(parameterSeparator);
for (int i = 0; i < sx.length; i++) {
parseQueryStringElement(sx[i]);
}
}
}
/**
* Parses the query string element
*
* @param element The element in form of {name}<code>queryStringValueSeparator</code>{value}
*/
private void parseQueryStringElement(String element) {
Matcher matcher = queryStringElementPattern.matcher(element);
if (matcher.find()) {
String name = matcher.group(1);
String value = matcher.group(2);
String[] values;
String[] existing = (String[])queryParameterMap.get(name);
if (existing != null) {
values = new String[existing.length + 1];
System.arraycopy(existing, 0, values, 0, existing.length);
values[values.length - 1] = value;
} else {
values = new String[] { value };
}
queryParameterMap.put(name, values);
}
}
/**
* Parses the request and separates the uri and the query string
*
* @param request The request
*/
private void parseRequest(HttpServletRequest request) {
String requestUri = request.getRequestURI();
String suffix = null;
for (Iterator i = suffixes.iterator(); i.hasNext();) {
String s = (String) i.next();
if (requestUri.endsWith(s)) {
suffix = s;
requestUri = requestUri.substring(0, requestUri.length() - s.length());
break;
}
}
if (suffix != null) {
// we have not identified the suffix; keep the request as is.
if (requestUri.indexOf(queryStringSeparator) == -1) {
// the request uri does not contain the uriSeparator; i.e. it
// represents just the uri
queryString = "";
url.append(requestUri);
} else {
String requestParts[] = requestUri.split(queryStringSeparator);
url.append(requestParts[0]);
queryString = requestParts[1];
}
url.append(suffix);
parseQueryString();
} else {
url.append(requestUri);
}
servletPath = request.getServletPath().replaceAll(queryStringSeparator + queryString, "");
}
public String getQueryString() {
return queryString;
}
public String getRequestURI() {
return url.toString();
}
public StringBuffer getRequestURL() {
return url;
}
public String getParameter(String name) {
String[] values = (String[])queryParameterMap.get(name);
return (values != null ? values[0] : null);
// return (value != null ? value.toString() : null);
}
public Map getParameterMap() {
return queryParameterMap;
}
public Enumeration getParameterNames() {
return Collections.enumeration(queryParameterMap.keySet());
}
public Enumeration getAttributeNames() {
return request.getAttributeNames();
}
public Object getAttribute(String name) {
return request.getAttribute(name);
}
public String[] getParameterValues(String name) {
return (String[])queryParameterMap.get(name);
}
public String getServletPath() {
return servletPath;
}
}
/* (non-Javadoc)
* @see org.springframework.web.servlet.HandlerAdapter#supports(java.lang.Object)
*/
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
/* (non-Javadoc)
* @see org.springframework.web.servlet.HandlerAdapter#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpServletRequest wrapper;
if (request.getQueryString() != null || request.getRequestURI().indexOf(queryStringSeparator) == -1) {
// we have the traditional one
wrapper = request;
} else {
// there is no traditional query separator
wrapper = new SearchEngineRequestWrapper(request, suffixes, queryStringSeparator, parameterSeparator, nameValueSeparator);
}
return ((Controller) handler).handleRequest(wrapper, response);
}
/* (non-Javadoc)
* @see org.springframework.web.servlet.HandlerAdapter#getLastModified(javax.servlet.http.HttpServletRequest, java.lang.Object)
*/
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
/**
* @param suffixes The suffixes to set.
*/
public final void setSuffixes(List suffixes) {
this.suffixes = suffixes;
}
/**
* Sets new value for field uriSeparator
* @param uriSeparator The uriSeparator to set.
*/
public final void setQueryStringSeparator(String uriSeparator) {
this.queryStringSeparator = uriSeparator;
}
/**
* Sets new value for field nameValueSeparator
* @param nameValueSeparator The nameValueSeparator to set.
*/
public final void setNameValueSeparator(String nameValueSeparator) {
this.nameValueSeparator = nameValueSeparator;
}
/**
* Sets new value for field parameterSeparator
* @param parameterSeparator The parameterSeparator to set.
*/
public final void setParameterSeparator(String parameterSeparator) {
this.parameterSeparator = parameterSeparator;
}
}