Author: evan
Date: Tue Jul  1 17:48:26 2008
New Revision: 673243

URL: http://svn.apache.org/viewvc?rev=673243&view=rev
Log:
Working on supporting end-to-end testing of social data APIs... added 
FakeHttpServletRequest utility that can be passed into calls to HttpServlet.

Added:
    
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/testing/FakeHttpServletRequest.java
Modified:
    incubator/shindig/trunk/java/common/pom.xml

Modified: incubator/shindig/trunk/java/common/pom.xml
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/pom.xml?rev=673243&r1=673242&r2=673243&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/pom.xml (original)
+++ incubator/shindig/trunk/java/common/pom.xml Tue Jul  1 17:48:26 2008
@@ -46,6 +46,10 @@
       <artifactId>guice</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.code.google-collections</groupId>
+      <artifactId>google-collect</artifactId>
+    </dependency>
+    <dependency>
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
     </dependency>

Added: 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/testing/FakeHttpServletRequest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/testing/FakeHttpServletRequest.java?rev=673243&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/testing/FakeHttpServletRequest.java
 (added)
+++ 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/testing/FakeHttpServletRequest.java
 Tue Jul  1 17:48:26 2008
@@ -0,0 +1,881 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.common.testing;
+
+import com.google.common.collect.Maps;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * This class fakes a HttpServletRequest for unit test purposes. Currently, it
+ * supports servlet API 2.4.
+ * 
+ * <p>
+ * To use this class, you specify the request info (URL, parameters) in the
+ * constructors.
+ * 
+ * <p>
+ * Lots of stuff are still not implemented here. Feel free to implement them.
+ */
+public class FakeHttpServletRequest implements HttpServletRequest {
+  protected static final String DEFAULT_HOST = "localhost";
+  protected static final int DEFAULT_PORT = 80;
+  private static final String COOKIE_HEADER = "Cookie";
+  private static final String HOST_HEADER = "Host";
+  private static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+  protected String scheme_ = "http";
+  protected String host_;
+  protected int port_;
+  protected boolean secure_ = false;
+  protected String method_ = "GET";
+  protected String protocol_ = "HTTP/1.0";
+  protected String contextPath_;
+  protected String servletPath_;
+  protected String pathInfo_ = null;
+  protected String queryString_;
+  protected String ip_ = "127.0.0.1";
+  protected String contentType_;
+
+  protected Hashtable<String, String> headers_ =
+      new Hashtable<String, String>();
+
+  // Use a LinkedHashMap so we can generate a query string that is in the same
+  // order that we set the parameters
+  protected Map<String, String[]> parameters_ =
+      new LinkedHashMap<String, String[]>();
+
+  protected Set<String> postParameters_ = new HashSet<String>();
+
+  protected Map<String, Cookie> cookies_ = new Hashtable<String, Cookie>();
+
+
+  // Use a Map rather than a table since the specified behavior of
+  // setAttribute() allows null values.
+  protected Map<String, Object> attributes_ = Maps.newHashMap();
+
+  protected Locale locale_ = Locale.US;
+  protected List<Locale> locales_ = null;
+
+  // used by POST methods
+  protected byte[] postData;
+  protected String characterEncoding;
+
+  // the following two booleans ensure that either getReader() or
+  // getInputStream is called, but not both, to conform to specs for the
+  // HttpServletRequest class.
+  protected boolean getReaderCalled = false;
+  protected boolean getInputStreamCalled = false;
+
+  private HttpSession session;
+
+  static final String METHOD_POST = "POST";
+
+  /**
+   * Example: http://www.example.com:1234/foo/bar?abc=xyz "www.example.com" is
+   * the host 1234 is the port "/foo" is the contextPath "/bar" is the
+   * servletPath "abc=xyz" is the queryString
+   */
+  public FakeHttpServletRequest(String host, int port, String contextPath,
+      String servletPath, String queryString) {
+    constructor(host, port, contextPath, servletPath, queryString);
+  }
+
+  public FakeHttpServletRequest(String host, String port, String contextPath,
+      String servletPath, String queryString) {
+    this(host, Integer.parseInt(port), contextPath, servletPath, queryString);
+  }
+
+  public FakeHttpServletRequest(String contextPath, String servletPath,
+      String queryString) {
+    this(DEFAULT_HOST, -1, contextPath, servletPath, queryString);
+  }
+
+  public FakeHttpServletRequest() {
+    this(DEFAULT_HOST, DEFAULT_PORT, "", null, null);
+  }
+
+  public FakeHttpServletRequest(String urlStr) throws MalformedURLException {
+    URL url = new URL(urlStr);
+    String contextPath;
+    String servletPath;
+    String path = url.getPath();
+    if (path.length() <= 1) {
+      // path must be either empty string or "/"
+      contextPath = path;
+      servletPath = null;
+    } else {
+      // Look for the second slash which separates the servlet path from the
+      // context path. e.g. "/foo/bar"
+      int secondSlash = path.indexOf("/", 1);
+      if (secondSlash < 0) {
+        // No second slash
+        contextPath = path;
+        servletPath = null;
+      } else {
+        contextPath = path.substring(0, secondSlash);
+        servletPath = path.substring(secondSlash);
+      }
+    }
+
+    // Set the scheme
+    scheme_ = url.getProtocol();
+    if (scheme_.equalsIgnoreCase("https")) {
+      secure_ = true;
+    }
+
+    int port = url.getPort();
+
+    // Call constructor() instead of this() because the later is only allowed
+    // at the begining of a constructor
+    constructor(url.getHost(), port, contextPath, servletPath, url.getQuery());
+  }
+
+  public FakeHttpServletRequest setLocale(Locale locale) {
+    locale_ = locale;
+    return this;
+  }
+
+  public FakeHttpServletRequest setLocales(List<Locale> locales) {
+    locales_ = locales;
+    return this;
+  }
+
+  public FakeHttpServletRequest setProtocol(String prot) {
+    protocol_ = prot;
+    return this;
+  }
+
+  public FakeHttpServletRequest setSecure(boolean secure) {
+    secure_ = secure;
+    return this;
+  }
+
+  /*
+   * Set a header on this request. Note that if the header implies other
+   * attributes of the request I will set them accordingly. Specifically:
+   * 
+   * If the header is "Cookie:" then I will automatically call setCookie on all
+   * of the name-value pairs found therein.
+   * 
+   * This makes the object easier to use because you can just feed it headers
+   * and the object will remain consistent with the behavior you'd expect from 
a
+   * request.
+   */
+  public FakeHttpServletRequest setHeader(String name, String value) {
+    if (name.equals(COOKIE_HEADER)) {
+      String[] pairs = splitAndTrim(value, ";");
+      for (int i = 0; i < pairs.length; i++) {
+        int equalsPos = pairs[i].indexOf('=');
+        if (equalsPos != -1) {
+          String cookieName = pairs[i].substring(0, equalsPos);
+          String cookieValue = pairs[i].substring(equalsPos + 1);
+          addToCookieMap(new Cookie(cookieName, cookieValue));
+        }
+      }
+      setCookieHeader();
+      return this;
+    }
+
+    addToHeaderMap(name, value);
+
+    if (name.equals(HOST_HEADER)) {
+      host_ = value;
+    }
+    return this;
+  }
+
+  private void addToHeaderMap(String name, String value) {
+    headers_.put(name.toLowerCase(), value);
+  }
+
+  /**
+   * Associates a set of cookies with this fake request.
+   * 
+   * @param cookies the cookies associated with this request.
+   */
+  public FakeHttpServletRequest setCookies(Cookie... cookies) {
+    for (Cookie cookie : cookies) {
+      addToCookieMap(cookie);
+    }
+    setCookieHeader();
+    return this;
+  }
+
+  /**
+   * Sets a single cookie associated with this fake request. Cookies are
+   * cumulative, but ones with the same name will overwrite one another.
+   * 
+   * @param c the cookie to associate with this request.
+   */
+  public FakeHttpServletRequest setCookie(Cookie c) {
+    addToCookieMap(c);
+    setCookieHeader();
+    return this;
+  }
+
+  private void addToCookieMap(Cookie c) {
+    cookies_.put(c.getName(), c);
+  }
+
+  /**
+   * Sets the "Cookie" HTTP header based on the current cookies.
+   */
+  private void setCookieHeader() {
+    StringBuilder sb = new StringBuilder();
+    boolean isFirst = true;
+    for (Cookie c : cookies_.values()) {
+      if (!isFirst) {
+        sb.append("; ");
+      }
+      sb.append(c.getName());
+      sb.append("=");
+      sb.append(c.getValue());
+      isFirst = false;
+    }
+
+    // We cannot use setHeader() here, because setHeader() calls this method
+    addToHeaderMap(COOKIE_HEADER, sb.toString());
+  }
+
+  /**
+   * Sets the a parameter in this fake request.
+   * 
+   * @param name the string key
+   * @param values the string array value
+   * @param isPost if the paramenter comes in the post body.
+   */
+  public FakeHttpServletRequest setParameter(String name, boolean isPost, 
String... values) {
+    if (isPost) {
+      postParameters_.add(name);
+    }
+    parameters_.put(name, values);
+    // Old query string no longer matches up, so set it to null so it can be
+    // regenerated on the next call of getQueryString()
+    queryString_ = null;
+    return this;
+  }
+
+  /**
+   * Sets the a parameter in this fake request.
+   * 
+   * @param name the string key
+   * @param values the string array value
+   */
+  public FakeHttpServletRequest setParameter(String name, String... values) {
+    setParameter(name, false, values);
+    return this;
+  }
+
+
+  /** Set the path info field. */
+  public FakeHttpServletRequest setPathInfo(String path) {
+    pathInfo_ = path;
+    return this;
+  }
+
+  /**
+   * Specify the mock POST data.
+   * 
+   * @param postString the mock post data
+   * @param encoding format with which to encode mock post data
+   */
+  public FakeHttpServletRequest setPostData(String postString, String encoding)
+      throws UnsupportedEncodingException {
+    setPostData(postString.getBytes(encoding));
+    characterEncoding = encoding;
+    return this;
+  }
+
+  /**
+   * Specify the mock POST data in raw binary format.
+   * 
+   * This implicitly sets character encoding to not specified.
+   * 
+   * @param data the mock post data; this is owned by the caller, so
+   *        modifications made after this call will show up when the post data
+   *        is read
+   */
+  public FakeHttpServletRequest setPostData(byte[] data) {
+    postData = data;
+    characterEncoding = null;
+    method_ = METHOD_POST;
+    return this;
+  }
+
+  /**
+   * Set a new value for the query string. The query string will be parsed and
+   * all parameters reset.
+   * 
+   * @param queryString representing the new value. i.e.: "bug=1&id=23"
+   */
+  public FakeHttpServletRequest setQueryString(String queryString) {
+    queryString_ = queryString;
+    parameters_.clear();
+    decodeQueryString(queryString, parameters_);
+    return this;
+  }
+
+  /**
+   * Sets the session for this request.
+   * 
+   * @param session the new session
+   */
+  public FakeHttpServletRequest setSession(HttpSession session) {
+    this.session = session;
+    return this;
+  }
+
+  /**
+   * Sets the content type.
+   * 
+   * @param contentType of the request.
+   */
+  public FakeHttpServletRequest setContentType(String contentType) {
+    this.contentType_ = contentType;
+    return this;
+  }
+
+  // 
///////////////////////////////////////////////////////////////////////////
+  // Implements methods from HttpServletRequest
+  // 
///////////////////////////////////////////////////////////////////////////
+
+  public String getAuthType() {
+    throw new UnsupportedOperationException();
+  }
+
+  public java.lang.String getContextPath() {
+    return contextPath_;
+  }
+
+  public Cookie[] getCookies() {
+    if (cookies_.isEmpty()) {
+      // API promises null return if no cookies
+      return null;
+    }
+    return cookies_.values().toArray(new Cookie[0]);
+  }
+
+  public long getDateHeader(String name) {
+    String value = getHeader(name);
+    if (value == null) return -1;
+
+    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT, Locale.US);
+    format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    try {
+      return format.parse(value).getTime();
+    } catch (ParseException e) {
+      throw new IllegalArgumentException("Cannot parse number from header "
+          + name + ":" + value, e);
+    }
+  }
+
+  public FakeHttpServletRequest setDateHeader(String name, long value) {
+    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT, Locale.US);
+    format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    setHeader(name, format.format(new Date(value)));
+    return this;
+  }
+
+  public String getHeader(String name) {
+    return headers_.get(name.toLowerCase());
+  }
+
+  public Enumeration<String> getHeaderNames() {
+    return headers_.keys();
+  }
+
+  public Enumeration<?> getHeaders(String name) {
+    List<String> values = new ArrayList<String>();
+    for (Map.Entry<String, String> entry : headers_.entrySet()) {
+      if (name.equalsIgnoreCase(entry.getKey())) {
+        values.add(entry.getValue());
+      }
+    }
+    return Collections.enumeration(values);
+  }
+
+  public int getIntHeader(String name) {
+    return Integer.parseInt(getHeader(name));
+  }
+
+  public String getMethod() {
+    return method_;
+  }
+
+  public FakeHttpServletRequest setMethod(String method) {
+    method_ = method;
+    return this;
+  }
+
+  public String getPathInfo() {
+    return pathInfo_;
+  }
+
+  public String getPathTranslated() {
+    throw new UnsupportedOperationException();
+  }
+
+  public String getQueryString() {
+    try {
+      if (queryString_ == null && !parameters_.isEmpty()) {
+        boolean hasPrevious = false;
+        StringBuilder queryString = new StringBuilder();
+        for (Iterator<String> it = parameters_.keySet().iterator(); 
it.hasNext();) {
+          String key = it.next();
+  
+          // We're not interested in blank keys
+          if (key == null || key.equals("") || postParameters_.contains(key)) {
+            continue;
+          }
+          if (hasPrevious) {
+            queryString.append("&");
+          }
+  
+          String[] values = parameters_.get(key);
+          // Append the parameters to the query string
+          if (values.length == 0) {
+            queryString.append(URLEncoder.encode(key, "UTF-8"));
+          } else {
+            for (int i = 0; i < values.length; i++) {
+              queryString.append(URLEncoder.encode(key, 
"UTF-8")).append("=").append(
+                  URLEncoder.encode(values[i], "UTF-8"));
+              if (i < values.length - 1) {
+                queryString.append("&");
+              }
+            }
+          }
+          hasPrevious = true;
+  
+        }
+        queryString_ = queryString.toString();
+      }
+      return queryString_;
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException("Should always support UTF-8", e);
+    }
+  }
+
+  public String getRemoteUser() {
+    throw new UnsupportedOperationException();
+  }
+
+  public String getRequestedSessionId() {
+    throw new UnsupportedOperationException();
+  }
+
+  public String getRequestURI() {
+    StringBuffer buf = new StringBuffer();
+    if (!contextPath_.equals("")) {
+      buf.append(contextPath_);
+    }
+
+    if (servletPath_ != null && !"".equals(servletPath_)) {
+      buf.append(servletPath_);
+    }
+
+    if (buf.length() == 0) {
+      buf.append('/');
+    }
+
+    return buf.toString();
+  }
+
+  public StringBuffer getRequestURL() {
+    StringBuffer buf =
+        secure_ ? new StringBuffer("https://";) : new StringBuffer("http://";);
+    buf.append(host_);
+    if (port_ >= 0) {
+      buf.append(':');
+      buf.append(port_);
+    }
+    buf.append(getRequestURI()); // always begins with '/'
+    return buf;
+  }
+
+  public String getServletPath() {
+    return servletPath_;
+  }
+
+  public FakeHttpServletRequest setServletPath(String servletPath) {
+    this.servletPath_ = servletPath;
+    return this;
+  }
+
+  public HttpSession getSession() {
+    return getSession(true);
+  }
+
+  public HttpSession getSession(boolean create) {
+    // TODO return fake session if create && session == null
+    return session;
+  }
+
+  public java.security.Principal getUserPrincipal() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isRequestedSessionIdFromCookie() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Deprecated
+  public boolean isRequestedSessionIdFromUrl() {
+    throw new UnsupportedOperationException("This method is deprecated");
+  }
+
+  public boolean isRequestedSessionIdFromURL() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isRequestedSessionIdValid() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isUserInRole(String role) {
+    throw new UnsupportedOperationException();
+  }
+
+  // Implements methods from ServletRequest ///////////////////////////////////
+
+  public Object getAttribute(String name) {
+    return attributes_.get(name);
+  }
+
+  public Enumeration<?> getAttributeNames() {
+    return Collections.enumeration(attributes_.keySet());
+  }
+
+  public String getCharacterEncoding() {
+    return characterEncoding;
+  }
+
+  public int getContentLength() {
+    return (postData == null) ? 0 : postData.length;
+  }
+
+  public String getContentType() {
+    return contentType_;
+  }
+
+  /**
+   * Get the body of the request (i.e. the POST data) as a binary stream. As 
per
+   * Java docs, this OR getReader() may be called, but not both (attempting 
that
+   * will result in an IllegalStateException)
+   * 
+   */
+  public ServletInputStream getInputStream() {
+    if (getReaderCalled) {
+      throw new IllegalStateException(
+          "getInputStream() called after getReader()");
+    }
+    getInputStreamCalled = true; // so that getReader() can no longer be called
+
+    final InputStream in = new ByteArrayInputStream(postData);
+    return new ServletInputStream() {
+      @Override public int read() throws IOException {
+        return in.read();
+      }
+    };
+  }
+
+  public Locale getLocale() {
+    return locale_;
+  }
+
+  public Enumeration<?> getLocales() {
+    return Collections.enumeration(locales_);
+  }
+
+  public String getParameter(String name) {
+    String[] parameters = getParameterValues(name);
+    if (parameters == null || parameters.length < 1) {
+      return null;
+    } else {
+      return parameters[0];
+    }
+  }
+
+  public Map<String, String[]> getParameterMap() {
+    return parameters_;
+  }
+
+  public Enumeration<String> getParameterNames() {
+    return Collections.enumeration(parameters_.keySet());
+  }
+
+  public String[] getParameterValues(String name) {
+    return parameters_.get(name);
+  }
+
+  public String getProtocol() {
+    return protocol_;
+  }
+
+  public BufferedReader getReader() throws IOException {
+    if (getInputStreamCalled) {
+      throw new IllegalStateException(
+          "getReader() called after getInputStream()");
+    }
+
+    getReaderCalled = true;
+    BufferedReader br = null;
+    ByteArrayInputStream bais = new ByteArrayInputStream(postData);
+    InputStreamReader isr;
+    if (characterEncoding != null) {
+      isr = new InputStreamReader(bais, characterEncoding);
+    } else {
+      isr = new InputStreamReader(bais);
+    }
+    br = new BufferedReader(isr);
+    return br;
+  }
+
+  @Deprecated
+  public String getRealPath(String path) {
+    throw new UnsupportedOperationException("This method is deprecated");
+  }
+
+  public String getRemoteAddr() {
+    return ip_;
+  }
+
+  /**
+   * Sets the remote IP address for this [EMAIL PROTECTED] 
FakeHttpServletRequest}.
+   * 
+   * @param ip the IP to set
+   * @return this [EMAIL PROTECTED] FakeHttpServletRequest} object
+   */
+  public FakeHttpServletRequest setRemoteAddr(String ip) {
+    ip_ = ip;
+    return this;
+  }
+
+  public String getRemoteHost() {
+    return "localhost";
+  }
+
+
+  /*
+   * (non-Javadoc)
+   * 
+   * New Servlet 2.4 method
+   * 
+   * @see javax.servlet.ServletRequest#getLocalPort()
+   */
+  public int getLocalPort() {
+    return 8080;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * New Servlet 2.4 method
+   * 
+   * @see javax.servlet.ServletRequest#getLocalAddr()
+   */
+  public String getLocalAddr() {
+    return "127.0.0.1";
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * New Servlet 2.4 method
+   * 
+   * @see javax.servlet.ServletRequest#getLocalName()
+   */
+  public String getLocalName() {
+    return "localhost";
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * New Servlet 2.4 method
+   * 
+   * @see javax.servlet.ServletRequest#getRemotePort()
+   */
+  public int getRemotePort() {
+    throw new UnsupportedOperationException();
+  }
+
+
+  public RequestDispatcher getRequestDispatcher(String path) {
+    throw new UnsupportedOperationException();
+  }
+
+  public String getScheme() {
+    return scheme_;
+  }
+
+  public String getServerName() {
+    return host_;
+  }
+
+  public int getServerPort() {
+    return (port_ < 0) ? DEFAULT_PORT : port_;
+  }
+
+  public boolean isSecure() {
+    return secure_;
+  }
+
+  public void removeAttribute(String name) {
+    attributes_.remove(name);
+  }
+
+  public void setAttribute(String name, Object value) {
+    attributes_.put(name, value);
+  }
+
+  /**
+   * @inheritDoc
+   * 
+   * For POST requests, this affects interpretation of POST body.
+   * 
+   * For non-POST requests (original author's comment): Do nothing - all 
request
+   * components were created as unicode Strings, so this can't affect how
+   * they're interpreted anyway.
+   */
+  public void setCharacterEncoding(String env) {
+    if (method_.equals(METHOD_POST)) {
+      characterEncoding = env;
+    }
+  }
+
+  // Helper methods ///////////////////////////////////////////////////////////
+
+  /**
+   * This method serves as the central constructor of this class. The reason it
+   * is not an actual constructor is that Java doesn't allow calling another
+   * constructor at the end of a constructor. e.g.
+   * 
+   * <pre>
+   * public FakeHttpServletRequest(String foo) {
+   *   // Do something here
+   *   this(foo, bar); // calling another constructor here is not allowed
+   * }
+   * </pre>
+   */
+  protected void constructor(String host, int port, String contextPath,
+      String servletPath, String queryString) {
+    setHeader(HOST_HEADER, host);
+    port_ = port;
+    contextPath_ = contextPath;
+    servletPath_ = servletPath;
+    queryString_ = queryString;
+    if (queryString != null) {
+      decodeQueryString(queryString, parameters_);
+    }
+  }
+
+  protected void decodeQueryString(String queryString,
+      Map<String, String[]> parameters) {
+    for (String param : queryString.split("&")) {
+      // The first '=' separates the name and value
+      int sepPos = param.indexOf('=');
+      String name, value;
+      if (sepPos < 0) {
+        // if no equal is present, assume a blank value
+        name = param;
+        value = "";
+      } else {
+        name = param.substring(0, sepPos);
+        value = param.substring(sepPos + 1);
+      }
+
+      addParameter(parameters, decodeParameterPart(name),
+          decodeParameterPart(value));
+    }
+  }
+
+  private String decodeParameterPart(String str) {
+    // borrowed from FormUrlDecoder
+    try {
+      // we could infer proper encoding from headers, but setCharacterEncoding
+      // is a noop.
+      return URLDecoder.decode(str, "UTF-8");
+    } catch (IllegalArgumentException iae) {
+      // According to the javadoc of URLDecoder, when the input string is
+      // illegal, it could either leave the illegal characters alone or throw
+      // an IllegalArgumentException! To deal with both consistently, we
+      // ignore IllegalArgumentException and just return the original string.
+      return str;
+    } catch (UnsupportedEncodingException e) {
+      return str;
+    }
+  }
+
+  protected void addParameter(Map<String, String[]> parameters, String name,
+      String value) {
+    if (parameters.containsKey(name)) {
+      String[] existingParamValues = parameters.get(name);
+      String[] newParamValues = new String[existingParamValues.length + 1];
+      System.arraycopy(existingParamValues, 0, newParamValues, 0,
+          existingParamValues.length);
+      newParamValues[newParamValues.length - 1] = value;
+      parameters.put(name, newParamValues);
+    } else {
+      String[] paramValues = {value,};
+      parameters.put(name, paramValues);
+    }
+  }
+  
+  private static String[] splitAndTrim(String str, String delims) {
+    StringTokenizer tokenizer = new StringTokenizer(str, delims);
+    int n = tokenizer.countTokens();
+    String[] list = new String[n];
+    for (int i = 0; i < n; i++) {
+      list[i] = tokenizer.nextToken().trim();
+    }
+    return list;
+  }
+}


Reply via email to