You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beehive.apache.org by ek...@apache.org on 2006/01/14 19:19:29 UTC

svn commit: r369072 - in /beehive/trunk/netui: src/core/org/apache/beehive/netui/core/factory/ src/core/org/apache/beehive/netui/core/urls/ test/src/junitTests/org/apache/beehive/netui/test/core/urls/

Author: ekoneil
Date: Sat Jan 14 10:19:24 2006
New Revision: 369072

URL: http://svn.apache.org/viewcvs?rev=369072&view=rev
Log:
Fix for BEEHIVE-1042.  This change keeps URI parameters in FIFO order rather than in an order determined by the previous Map<String, List> data structure.  The java.net.URI class uses parameter order when running equals() on two URIs.  Since MutableURI is just an editable wrapper atop URI, it should implement the same ordering principles.

This change necessitated updating two JUnit tests that assumed parameters could be in any order.  They now expect parameters to be in a fixed order.

This implementation added a QueryParameters and Parameter private inner classes to the MutableURI that are used for managing the query string.  QueryParameters uses a simple LinkedList to hold query parameter name / value Strings in order.  Probably worth looking at using an optimized data structure for making List-by-param-name and Map lookups faster, but this implementation is now isolated in QueryParametrs and can easily change.

An additional fix was made to return unmodifiable lists from MutableURI's "Map getParameters()" call -- previously, modifiable List elements were stored as values in an unmodifiable Map.  Now, both the Map and contained List(s) are unmodifiable.  Seemed like it should work this way in the first place; if this assumption is incorrect, let me know.

BB: self
Test: NetUI BVT && distribution test pass


Modified:
    beehive/trunk/netui/src/core/org/apache/beehive/netui/core/factory/Factory.java
    beehive/trunk/netui/src/core/org/apache/beehive/netui/core/urls/MutableURI.java
    beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/FreezableMutableURITest.java
    beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/MutableURITest.java

Modified: beehive/trunk/netui/src/core/org/apache/beehive/netui/core/factory/Factory.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/netui/src/core/org/apache/beehive/netui/core/factory/Factory.java?rev=369072&r1=369071&r2=369072&view=diff
==============================================================================
--- beehive/trunk/netui/src/core/org/apache/beehive/netui/core/factory/Factory.java (original)
+++ beehive/trunk/netui/src/core/org/apache/beehive/netui/core/factory/Factory.java Sat Jan 14 10:19:24 2006
@@ -28,8 +28,6 @@
 public abstract class Factory
         implements Serializable
 {
-    private static final Logger _log = Logger.getInstance( Factory.class );
-    
     private transient ServletContext _servletContext;
     private FactoryConfig _config;
 

Modified: beehive/trunk/netui/src/core/org/apache/beehive/netui/core/urls/MutableURI.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/netui/src/core/org/apache/beehive/netui/core/urls/MutableURI.java?rev=369072&r1=369071&r2=369072&view=diff
==============================================================================
--- beehive/trunk/netui/src/core/org/apache/beehive/netui/core/urls/MutableURI.java (original)
+++ beehive/trunk/netui/src/core/org/apache/beehive/netui/core/urls/MutableURI.java Sat Jan 14 10:19:24 2006
@@ -17,21 +17,19 @@
  */
 package org.apache.beehive.netui.core.urls;
 
-import org.apache.beehive.netui.util.internal.InternalStringBuilder;
-
-import org.apache.beehive.netui.core.URLCodec;
-
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
-import java.util.HashMap;
+
+import org.apache.beehive.netui.core.URLCodec;
+import org.apache.beehive.netui.util.internal.InternalStringBuilder;
 
 /**
  * Mutable class for creating URIs.
@@ -95,7 +93,7 @@
     private String _path;
 
     /** Query parameters */
-    private LinkedHashMap/*< String, List< String > >*/ _params;
+    private QueryParameters _parameters;
 
     /** Fragment */
     private String _fragment;
@@ -104,9 +102,6 @@
     private static final String AMP_ENTITY = "&amp;";
     private static final String AMP_CHAR = "&";
 
-    private static final Map/*< String, List< String > >*/ EMPTY_MAP =
-            Collections.unmodifiableMap( new HashMap/*< String, List< String > >*/() );
-    
     /**
      * Constructs a <code>MutableURI</code>.
      */
@@ -483,14 +478,13 @@
 
     /**
      * Sets (and resets) the query string.
-     * This method assumes that the query is already encoded and
-     * escaped.
+     * This method assumes that the query is already encoded and escaped.
      *
      * @param query Query string
      */
     public void setQuery( String query )
     {
-        _params = null;
+        _parameters = null;
 
         if ( query == null || query.length() == 0 ) { return; }
 
@@ -528,7 +522,8 @@
      */
     public String getQuery( URIContext uriContext )
     {
-        if ( _params == null || _params.isEmpty() ) { return null; }
+        if(_parameters == null || !_parameters.hasParameters())
+            return null;
 
         String paramSeparator = AMP_ENTITY;
         if ( uriContext == null )
@@ -543,26 +538,20 @@
 
         InternalStringBuilder query = new InternalStringBuilder( 64 );
         boolean firstParam = true;
-        for ( Iterator i = _params.keySet().iterator(); i.hasNext(); )  
-        {
-            String name = ( String ) i.next();
-            
-            for ( Iterator j = ( ( List ) _params.get( name ) ).iterator(); j.hasNext(); )  
-            {
-                String value = ( String ) j.next();
-                
-                if ( firstParam )
-                {
-                    firstParam = false;
-                }
-                else
-                {
-                    query.append( paramSeparator );
-                }
-
-                query.append( name );
-
-                if ( value != null ) { query.append( '=' ).append( value ); }
+        for(Iterator iterator = _parameters.iterator(); iterator.hasNext(); ) {
+            QueryParameters.Parameter parameter = (QueryParameters.Parameter)iterator.next();
+            String name = parameter.name;
+            String value = parameter.value;
+
+            if (firstParam)
+                firstParam = false;
+            else query.append( paramSeparator );
+
+            query.append( name );
+
+            /* todo: does the '=' need to be here in order for the parsing to work correctly? */
+            if(value != null) {
+                query.append( '=' ).append( value );
             }
         }
 
@@ -598,51 +587,44 @@
             value = encode( value );
         }
 
-        if ( _params == null )
+        if ( _parameters == null )
         {
-            _params = new LinkedHashMap/*< String, List< String > >*/();
+            _parameters = new QueryParameters();
             _opaque = false;
             setSchemeSpecificPart( null );
         }
-        List/*< String >*/ values = ( List ) _params.get( name );
-        
-        if ( values == null )
-        {
-            values = new ArrayList/*< String >*/();
-            _params.put( name, values );
-        }
 
-        values.add( value );
+        _parameters.addParameter(name, value);
     }
 
     /**
-     * Add a parameter to the query string.
-     * <p> If the encoded flag is true then this method assumes that
+     * <p>
+     * Adds all the parameters in a {@link Map} to a URI's query string.
+     * </p>
+     * <p>
+     * If the encoded flag is true then this method assumes that
      * the name and value do not need encoding or are already encoded
      * correctly. Otherwise, it translates the name and value with the
      * character encoding of this URI and adds them to the set of
      * parameters for the query. If the encoding for this URI has
-     * not been set, then the default encoding used is "UTF-8". </p>
+     * not been set, then the default encoding used is "UTF-8".
+     * </p>
+     * <p>
+     * The query parameters are added in the order the keys of the Map.
+     * </p>
      *
      * @param newParams the map of new parameters to add to the URI
-     * @param encoded Flag indicating whether the names and values are
-     *                already encoded.
+     * @param encoded Flag indicating whether the names and values are already encoded.
      */
-    public void addParameters( Map newParams, boolean encoded )
-    {
+    public void addParameters( Map newParams, boolean encoded ) {
         if ( newParams == null )
-        {
-            throw new IllegalArgumentException( "Cannot add null map of parameters." );
-        }
-        
+            throw new IllegalArgumentException("Cannot add null map of parameters.");
+
         if ( newParams.size() == 0 )
-        {
             return;
-        }
 
-        if ( _params == null )
-        {
-            _params = new LinkedHashMap/*< String, List< String > >*/();
+        if(_parameters == null) {
+            _parameters = new QueryParameters();
             _opaque = false;
             setSchemeSpecificPart( null );
         }
@@ -655,28 +637,21 @@
 
             if ( !encoded ) { encodedName = encode( name ); }
 
-            List/*< String >*/ values = ( List ) _params.get( encodedName );
-            if ( values == null )
-            {
-                values = new ArrayList/*< String >*/();
-                _params.put( encodedName, values );
-            }
-
             Object newValue = newParams.get( name );
             if ( newValue == null )
             {
-                values.add( null );
+                addParameter(encodedName, null, false);
             }
             else if ( newValue instanceof String )
             {
-                addValue( values, ( String ) newValue, encoded );
+                addValue( encodedName, ( String ) newValue, encoded );
             }
             else if ( newValue instanceof String[] )
             {
                 String newValues[] = ( String[] ) newValue;
                 for ( int i = 0; i < newValues.length; i++ )
                 {
-                    addValue( values, newValues[i], encoded );
+                    addValue( encodedName, newValues[i], encoded );
                 }
             }
             else if ( newValue instanceof List )
@@ -684,47 +659,39 @@
                 Iterator newValues = ( ( List ) newValue ).iterator();
                 while ( newValues.hasNext() )
                 {
-                    addValue( values, newValues.next().toString(), encoded );
+                    addValue( encodedName, newValues.next().toString(), encoded );
                 }
             }
             else /* Convert other objects to a string */
             {
-                addValue( values, newValue.toString(), encoded );
+                addValue( encodedName, newValue.toString(), encoded );
             }
         }
     }
 
-    private void addValue( List/*< String >*/ list, String value, boolean encoded )
-    {
-        if ( !encoded )
-        {
-            value = encode( value );
-        }
+    private void addValue( String name, String value, boolean encoded ) {
+        if (!encoded)
+            value = encode(value);
 
-        list.add( value );
+        _parameters.addParameter(name, value);
     }
 
     /**
-     * Returns the value of the parameter. If the parameter has
-     * several values, returns the first value.
+     * Returns the value of the parameter. If several values are associated with the given
+     * parameter name, the first value is returned.
      *
-     * @param name name of the parameter
-     * @return value of the given parameter name (or just the first in the list
-     *         if there are multiple values for the given name)
+     * @param name a name of the parameter
+     * @return value associated with the given parameter name
      */
     public String getParameter( String name )
     {
-        if ( _params == null ) { return null; }
-
-        List/*< String >*/ values = ( List ) _params.get( name );
-        if ( values != null && values.size() > 0 )
-        {
-            return ( String ) values.get( 0 );
-        }
-        else
-        {
+        if (_parameters == null || !_parameters.hasParameters())
             return null;
-        }
+
+        List parameters = _parameters.getParameterValues(name);
+        if(parameters != null && parameters.size() > 0)
+            return (String)parameters.get(0);
+        else return null;
     }
 
     /**
@@ -735,22 +702,14 @@
      */
     public List/*< String >*/ getParameters( String name )
     {
-        if ( _params == null )
-        {
+        if ( _parameters == null || !_parameters.hasParameters())
             return Collections.EMPTY_LIST;
-        }
-        else
-        {
-            List/*< String >*/ values = ( List ) _params.get( name );
+        else {
 
-            if ( values == null )
-            {
+            List parameters = _parameters.getParameterValues(name);
+            if(parameters == null)
                 return Collections.EMPTY_LIST;
-            }
-            else
-            {
-                return Collections.unmodifiableList( values );
-            }
+            else return Collections.unmodifiableList(parameters);
         }
     }
 
@@ -761,13 +720,12 @@
      */
     public Map/*< String, List< String > >*/ getParameters()
     {
-        if ( _params == null )
-        {
-            return EMPTY_MAP;
+        if(_parameters == null || !_parameters.hasParameters()) {
+            /* return an unmodifiable map */
+            return Collections.EMPTY_MAP;
         }
-        else
-        {
-            return Collections.unmodifiableMap( _params );
+        else {
+            return _parameters.getParameters();
         }
     }
 
@@ -778,9 +736,10 @@
      */
     public void removeParameter( String name )
     {
-        if ( _params == null ) { return; }
+        if (_parameters == null || !_parameters.hasParameters())
+            return;
 
-        _params.remove( name );
+        _parameters.removeParameter(name);
     }
 
     /**
@@ -942,7 +901,7 @@
         }
 
         // Append the parameters (the query)
-        if ( _params != null && _params.size() > 0 )
+        if (_parameters != null && _parameters.hasParameters())
         {
             buf.append( '?' );
             buf.append( getQuery( uriContext ) );
@@ -1065,12 +1024,16 @@
                 ( _fragment == testURI.getFragment() || ( _fragment != null && _fragment.equals( testURI.getFragment() ) ) ) &&
                 ( _encoding == testURI.getEncoding() || ( _encoding != null && _encoding.equals( testURI.getEncoding() ) ) ) )
         {
-            Map/*< String, List< String > >*/ params = getParameters();
-            Map/*< String, List< String > >*/ testParams = testURI.getParameters();
+            if(_parameters == testURI._parameters || _parameters != null && _parameters.equals(testURI._parameters))
+                return true;
+/*
+            Map params = getParameters();
+            Map testParams = testURI.getParameters();
 
             if ( params == testParams || ( params != null && params.equals( testParams ) ) ) {
                 return true;
             }
+*/
         }
 
         return false;
@@ -1092,7 +1055,6 @@
      *
      * @return a hash code value for this object.
      */
-    
     public int hashCode()
     {
         return 0;
@@ -1266,4 +1228,196 @@
 
         return ( min == s.length() ) ? -1 : min;
     }
-}
+
+   /**
+     * <p/>
+     * Abstraction to deal with query parameters on a URI.  This class holds name / value pairs of parameters
+     * in a fixed order of addition and provides methods for manipulating and querying information
+     * from the query parameter set.
+     * </p>
+     * <p/>
+     * All modifications to a set of query parameters should be made through this API; for example, to
+     * add a query parameter, call {@link #addParameter(String, String)} rather than obtaining the
+     * value {@link List} for a query parameter key and adding a parameter directly to this {@link List}.
+     * </p>
+     * <p/>
+     * In general, this class returns unmodifiable {@link List} and {@link Map} data structures.
+     * </p>
+     */
+    private static class QueryParameters {
+
+        /* todo: could probably invent a more complex but faster data structure to store these */
+        /**
+         * The list of query parameters, maintained in FIFO order.
+         */
+        private LinkedList _orderedQueryParameters;
+
+       private QueryParameters() {
+       }
+
+        /**
+         * Checks whether the query string is empty or contains a set of parameters.
+         *
+         * @return <code>true</code> if the query string has parameters; <code>false</code> otherwise
+         */
+        private boolean hasParameters() {
+            return _orderedQueryParameters != null && _orderedQueryParameters.size() > 0;
+        }
+
+        /**
+         * Add a parameter to the list of query parameters.  The caller is responsible for encoding query parameters
+         * names and values as needed.
+         *
+         * @param name  the name of the parameter
+         * @param value the value of the parameter
+         */
+        private void addParameter(String name, String value) {
+            if (_orderedQueryParameters == null)
+                _orderedQueryParameters = new LinkedList();
+            _orderedQueryParameters.add(new Parameter(name, value));
+        }
+
+        /**
+         * Remove all of the query string parameters associated with the given name.
+         *
+         * @param name the name of the parameters to remove
+         */
+        private void removeParameter(String name) {
+            if (_orderedQueryParameters == null || _orderedQueryParameters.size() == 0)
+                return;
+
+            if (name == null)
+                return;
+
+            for (int i = 0; i < _orderedQueryParameters.size(); i++) {
+                Parameter param = (Parameter) _orderedQueryParameters.get(i);
+                if (param.name.equals(name))
+                    _orderedQueryParameters.remove(param);
+            }
+        }
+
+        /**
+         * Get the parameters associated with a particular query parameter name.  This method returns
+         * an unmodifiable {@link List}.
+         */
+       private List getParameterValues(String name) {
+            if (_orderedQueryParameters == null || _orderedQueryParameters.size() == 0)
+                return Collections.EMPTY_LIST;
+
+            LinkedList queryParamsForName = new LinkedList();
+            for (int i = 0; i < _orderedQueryParameters.size(); i++) {
+                Parameter parameter = (Parameter) _orderedQueryParameters.get(i);
+                if (parameter.name.equals(name))
+                    queryParamsForName.add(parameter.value);
+            }
+
+            return Collections.unmodifiableList(queryParamsForName);
+        }
+
+        /**
+         * Get an interator for parameters that iterates over the natural order of the query parameters
+         * in the URL.  This iterator contains elements of type {@link QueryParameters.Parameter}.
+         *
+         * @return an iterator
+         */
+        private Iterator iterator() {
+            if(_orderedQueryParameters == null)
+                return Collections.emptyList().iterator();
+            else return _orderedQueryParameters.iterator();
+        }
+
+        /**
+         * <p/>
+         * Get a {@link Map} of all query parameters.  This Map contains pairs of &lt;String, List&gt; where
+         * the String is the name of a parameter and the List is the set of values associated with a name.
+         * There is <b>no</b> guarantee of order of either the the parameters names or the values associated
+         * with a given name.
+         * </p>
+         *
+         * @return a Map containing &lt;String, List&gt; pairs
+         */
+        private Map getParameters() {
+            if(_orderedQueryParameters == null)
+                return Collections.EMPTY_MAP;
+
+            LinkedHashMap paramMap = new LinkedHashMap();
+            Iterator iterator = _orderedQueryParameters.iterator();
+            while (iterator.hasNext()) {
+                Parameter parameter = (Parameter) iterator.next();
+                String name = parameter.name;
+                String value = parameter.value;
+
+                if (paramMap.containsKey(name)) {
+                    LinkedList list = (LinkedList) paramMap.get(name);
+                    list.add(value);
+                }
+                else {
+                    LinkedList list = new LinkedList();
+                    list.add(value);
+                    paramMap.put(name, list);
+                }
+            }
+
+            /* mark all of the contained List elements as unmodifiable */
+            Iterator keyIterator = paramMap.keySet().iterator();
+            while (keyIterator.hasNext()) {
+                Object key = keyIterator.next();
+                List value = (List) paramMap.get(key);
+                paramMap.put(key, Collections.unmodifiableList(value));
+            }
+
+            /* todo: need to wrap these List(s) in unmodificable lists */
+            return Collections.unmodifiableMap(paramMap);
+        }
+
+       public boolean equals(Object object) {
+           if(this == object) return true;
+           if(!(object instanceof QueryParameters)) return false;
+
+           final QueryParameters queryParameters = (QueryParameters)object;
+           if((_orderedQueryParameters == null || _orderedQueryParameters.size() == 0) &&
+               (queryParameters._orderedQueryParameters == null || queryParameters._orderedQueryParameters.size() == 0))
+               return true;
+
+           return this._orderedQueryParameters.equals(queryParameters._orderedQueryParameters);
+       }
+
+       public int hashCode() {
+           return (_orderedQueryParameters != null ? _orderedQueryParameters.hashCode() : 0);
+       }
+
+       /**
+        * Struct representing a URI query parameter.
+        */
+        private static class Parameter {
+           private String name;
+           private String value;
+
+           private Parameter(String name, String value) {
+               assert name != null;
+
+               this.name = name;
+               this.value = value;
+           }
+
+           public boolean equals(Object object) {
+               if(this == object) return true;
+               if(!(object instanceof Parameter)) return false;
+
+               final Parameter parameter = (Parameter)object;
+
+               if(value != null ? !value.equals(parameter.value) : parameter.value != null) return false;
+
+               assert name != null : "Encountered a parameter with a null name!";
+               return name.equals(parameter.name);
+           }
+
+           public int hashCode() {
+               int result;
+               result = (name != null ? name.hashCode() : 0);
+               result = 29 * result + value.hashCode();
+               return result;
+           }
+       }
+   }
+}
\ No newline at end of file

Modified: beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/FreezableMutableURITest.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/FreezableMutableURITest.java?rev=369072&r1=369071&r2=369072&view=diff
==============================================================================
--- beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/FreezableMutableURITest.java (original)
+++ beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/FreezableMutableURITest.java Sat Jan 14 10:19:24 2006
@@ -114,8 +114,7 @@
 
             try
             {
-                FreezableMutableURI uri = new FreezableMutableURI( scheme, userInfo, host, port,
-                        path, query, fragment );
+                FreezableMutableURI uri = new FreezableMutableURI( scheme, userInfo, host, port, path, query, fragment );
                 assertEquals( uriString, uri.getURIString( uriContext ) );
                 FreezableMutableURI other = new FreezableMutableURI( uriString, false );
                 assertEquals( uri, other );
@@ -405,14 +404,8 @@
 
     public void testEquals()
     {
-        //
-        // Note that the current implementation MutableURI, with query
-        // parameters stored in a LinkedHashMap of Lists, has the
-        // potential to change order of params in the query... such
-        // as this example. So the resulting string has params in a
-        // different order. However equality should still hold true.
-        //
-        String uriString = "https://localhost:443/test?param1&param1=&param1=true&param2&param3=true";
+        String queryString = "param1&param1=&param1=true&param2&param3=true";
+        String uriString = "https://localhost:443/test?" + queryString;
 
         try
         {
@@ -421,7 +414,7 @@
             uriA.setHost( "localhost" );
             uriA.setPort( 443 );
             uriA.setPath( "/test" );
-            uriA.setQuery( "param1&param2&param1=&param3=true&param1=true" );
+            uriA.setQuery(queryString);
 
             FreezableMutableURI uriB = new FreezableMutableURI( uriString, true );
             URIContext uriContext = new URIContext();

Modified: beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/MutableURITest.java
URL: http://svn.apache.org/viewcvs/beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/MutableURITest.java?rev=369072&r1=369071&r2=369072&view=diff
==============================================================================
--- beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/MutableURITest.java (original)
+++ beehive/trunk/netui/test/src/junitTests/org/apache/beehive/netui/test/core/urls/MutableURITest.java Sat Jan 14 10:19:24 2006
@@ -100,29 +100,6 @@
           "http://localhost:8080/Web/d/newAction1.do;jsessionid=F0C07D10C0E8CD22618ED1178F0F62C8" }
     };
 
-    public MutableURITest( String name )
-    {
-        super( name );
-    }
-
-    public static void main( String[] args )
-    {
-        junit.textui.TestRunner.run( suite() );
-    }
-
-    public static Test suite()
-    {
-        return new TestSuite( MutableURITest.class );
-    }
-
-    protected void setUp()
-    {
-    }
-
-    protected void tearDown()
-    {
-    }
-
     public void testConstructors()
     {
         URIContext uriContext = MutableURI.getDefaultContext();
@@ -476,14 +453,10 @@
 
     public void testEquals()
     {
-        //
-        // Note that the current implementation MutableURI, with query
-        // parameters stored in a LinkedHashMap of Lists, has the
-        // potential to change order of params in the query... such
-        // as this example. So the resulting string has params in a
-        // different order. However equality should still hold true.
-        //
-        String uriString = "https://localhost:443/test?param1&param1=&param1=true&param2&param3=true";
+        /*
+        Note, query strings need to maintain the same order of parameters in order to be considered equal */
+        String queryString = "param1&param2&param1=&param3=true&param1=true";
+        String uriString = "https://localhost:443/test?" + queryString;
 
         try
         {
@@ -492,7 +465,7 @@
             uriA.setHost( "localhost" );
             uriA.setPort( 443 );
             uriA.setPath( "/test" );
-            uriA.setQuery( "param1&param2&param1=&param3=true&param1=true" );
+            uriA.setQuery(queryString);
 
             MutableURI uriB = new MutableURI( uriString, true );
             URIContext uriContext = new URIContext();
@@ -598,15 +571,17 @@
         }
     }
 
-    // basic diagnostic utility when writing tests
+    /**
+     * This method is used as a diagnostic utility when writing tests.  It should
+     * not be removed!
+     *
+     * @param uri the URI to print
+     */
     private void dumpURI( MutableURI uri )
     {
         if ( uri == null )
-        {
             System.out.println( "uri == null" );
-        }
-        else
-        {
+        else {
             System.out.println( "uri:        " + uri.getURIString( null) );
             System.out.println( "scheme:     " + uri.getScheme() );
             System.out.println( "user info:  " + uri.getUserInfo() );
@@ -617,5 +592,20 @@
             System.out.println( "fragment:   " + uri.getFragment() );
             System.out.println( "encoding:   " + uri.getEncoding() );
         }
+    }
+
+    public MutableURITest( String name )
+    {
+        super( name );
+    }
+
+    public static void main( String[] args )
+    {
+        junit.textui.TestRunner.run( suite() );
+    }
+
+    public static Test suite()
+    {
+        return new TestSuite( MutableURITest.class );
     }
 }