/* * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package com.arsdigita.web; import com.arsdigita.util.Assert; import com.arsdigita.util.OrderedMap; import com.arsdigita.util.UncheckedWrapperException; import org.apache.commons.codec.net.URLCodec; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.EncoderException; import org.apache.log4j.Logger; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * @author Justin Ross <jross@redhat.com> * @version $Id: ParameterMap.java 287 2005-02-22 00:29:02Z sskracic $ */ public class ParameterMap implements Cloneable { private static final Logger s_log = Logger.getLogger(ParameterMap.class); private static ArrayList s_listeners = new ArrayList(); private OrderedMap m_params; public ParameterMap() { m_params = new OrderedMap(); } // Expects an *encoded* query string, just as // request.getQueryString() returns. private ParameterMap(final String query) { this(); if (query != null) { parseQueryString(query); } } public ParameterMap(final HttpServletRequest sreq) { this(); final Enumeration keys = sreq.getParameterNames(); while (keys.hasMoreElements()) { final String name = (String) keys.nextElement(); final String[] values = (String[]) sreq.getParameterValues(name); setParameterValues(name, values); } } public ParameterMap(final Map params) { this(); final Set keySet = params == null ? null : params.keySet(); if (keySet != null) { final Iterator keys = keySet.iterator(); while (keys.hasNext()) { final String name = (String)keys.next(); final String[] values = (String[])params.get(name); setParameterValues(name, values); } } } public static final ParameterMap fromString(final String query) { Assert.exists(query, "String query"); if (query.startsWith("?")) { return new ParameterMap(query.substring(1)); } else { return new ParameterMap(query); } } public static final void registerListener (final ParameterListener listener) { if (s_log.isDebugEnabled()) { s_log.debug("Registering parameter listener " + listener); } s_listeners.add(listener); } public Object clone() throws CloneNotSupportedException { final ParameterMap result = (ParameterMap) super.clone(); result.m_params = (OrderedMap) m_params.clone(); return result; } private void parseQueryString(final String query) { final int len = query.length(); int start = 0; while (true) { int end = -1; for (int i = start; i < len - 1; i++) { if (query.charAt(i) == '&' || query.charAt(i) == ';') { end = i; break; } } if (end == -1) { if (len > start) { try { parseParameter(query, start, len); } catch (DecoderException e) { throw new UncheckedWrapperException(e); } } break; } else { try { parseParameter(query, start, end); } catch (DecoderException e) { throw new UncheckedWrapperException(e); } start = end + 1; } } } private void parseParameter(final String query, final int start, final int end) throws DecoderException { final int sep = query.indexOf('=', start); if (Assert.isEnabled()) { Assert.isTrue(start > -1); Assert.isTrue(end > -1); } if (sep > -1) { URLCodec codec = new URLCodec(); final String name = codec.decode(query.substring(start, sep)); final String value = codec.decode (query.substring(sep + 1, end)); if (s_log.isDebugEnabled()) { s_log.debug("Parameter " + name + " = " + value); } final String[] values = getParameterValues(name); if (values == null) { setParameter(name, value); } else { final String[] newValues = new String[values.length + 1]; for (int i = 0; i < values.length; i++) { newValues[i] = values[i]; } newValues[values.length] = value; setParameterValues(name, newValues); } } } private void validateName(final String name) { Assert.exists(name, "String name"); Assert.isTrue(!name.equals(""), "The name must not be the empty string"); Assert.isTrue(name.indexOf(" ") == -1, "The name must not contain any spaces: '" + name + "'"); } public final void clear() { m_params.clear(); } public final String getParameter(final String name) { final String[] values = (String[]) m_params.get(name); if (values == null) { return null; } else { return values[0]; } } /** * Sets the parameter name to value. If * value is null, this method sets the value to the * empty string. * * Use of this method assumes that the parameter has only one * value; if you wish to give a parameter multiple values, use * {@link #setParameterValues(String, String[])}. * * @param name The String name of the parameter * @param value The String value of the parameter * @see javax.servlet.ServletRequest#getParameter(String) * @pre name != null && !name.trim().equals("") */ public final void setParameter(final String name, final String value) { if (Assert.isEnabled()) { validateName(name); } if (value == null) { m_params.put(name, new String[] {""}); } else { m_params.put(name, new String[] {value}); } } /** * A convenience method that calls {@link #setParameter(String, * String)} using value.toString(). If * value is null, it is converted to the empty * string. * * @param name The String name of the parameter * @param value The Object value of the parameter * @pre name != null && !name.trim().equals("") */ public final void setParameter(final String name, final Object value) { if (value == null) { setParameter(name, ""); } else { setParameter(name, value.toString()); } } public final String[] getParameterValues(final String name) { return (String[]) m_params.get(name); } public final void setParameterValues(final String name, final String[] values) { if (Assert.isEnabled()) { validateName(name); Assert.exists(values, "String[] values"); Assert.isTrue(values.length > 0, "The values array must have at least one value"); } m_params.put(name, values); } public final void clearParameter(final String name) { if (Assert.isEnabled()) { validateName(name); } m_params.remove(name); } public final Map getParameterMap() { if (m_params.isEmpty()) { return null; } else { return Collections.unmodifiableMap(m_params); } } public final String toString() { if (m_params.isEmpty()) { return ""; } else { return "?" + makeQueryString(); } } public final String getQueryString() { return makeQueryString(); } public final void runListeners(final HttpServletRequest sreq) { final Iterator iter = s_listeners.iterator(); while (iter.hasNext()) { final ParameterListener listener = (ParameterListener) iter.next(); listener.run(sreq, this); } } final String makeQueryString() { final StringBuffer buffer = new StringBuffer(); final Iterator iter = m_params.entrySet().iterator(); URLCodec codec = new URLCodec(); while (iter.hasNext()) { final Map.Entry entry = (Map.Entry) iter.next(); final String key = (String) entry.getKey(); final String[] values = (String[]) entry.getValue(); if (Assert.isEnabled()) { Assert.isTrue(key.indexOf('%') == -1, "The key '" + key + "' has already been " + "encoded"); } if (values != null) { if (Assert.isEnabled()) { Assert.isTrue(values.toString().indexOf('%') == -1, "One of the values " + Arrays.asList(values) + " has " + "already been encoded"); } for (int i = 0; i < values.length; i++) { try { buffer.append(codec.encode(key)); } catch (EncoderException e) { throw new UncheckedWrapperException(e); } buffer.append('='); final String value = values[i]; if (value != null) { try { buffer.append(codec.encode(value)); } catch (EncoderException e) { throw new UncheckedWrapperException(e); } } buffer.append('&'); } } } int last = buffer.length() - 1; if (last > -1 && buffer.charAt(last) == '&') { buffer.deleteCharAt(last); } return buffer.toString(); } }