Author: remy.maucherat(a)jboss.com
Date: 2008-03-27 13:49:20 -0400 (Thu, 27 Mar 2008)
New Revision: 559
Added:
trunk/java/org/apache/tomcat/util/http/Parameters2.java
Modified:
trunk/java/org/apache/tomcat/util/collections/MultiMap.java
trunk/java/org/apache/tomcat/util/http/Parameters.java
trunk/webapps/docs/changelog.xml
Log:
- Move to MultiMap backend (I never reviewed this class and did not realize it was still
based on a Hashtable).
- The old class stays there just in case for now as Parameters2.
Modified: trunk/java/org/apache/tomcat/util/collections/MultiMap.java
===================================================================
--- trunk/java/org/apache/tomcat/util/collections/MultiMap.java 2008-03-27 14:22:16 UTC
(rev 558)
+++ trunk/java/org/apache/tomcat/util/collections/MultiMap.java 2008-03-27 17:49:20 UTC
(rev 559)
@@ -17,6 +17,8 @@
package org.apache.tomcat.util.collections;
+import java.util.Enumeration;
+
import org.apache.tomcat.util.buf.MessageBytes;
// Originally MimeHeaders
@@ -74,6 +76,13 @@
}
/**
+ * Return names enumeration.
+ */
+ public Enumeration names() {
+ return new MultiMapNamesEnumeration(this, true, true);
+ }
+
+ /**
* Returns the Nth header name
* This may be used to iterate through all header fields.
*
Modified: trunk/java/org/apache/tomcat/util/http/Parameters.java
===================================================================
--- trunk/java/org/apache/tomcat/util/http/Parameters.java 2008-03-27 14:22:16 UTC (rev
558)
+++ trunk/java/org/apache/tomcat/util/http/Parameters.java 2008-03-27 17:49:20 UTC (rev
559)
@@ -18,8 +18,8 @@
package org.apache.tomcat.util.http;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.Hashtable;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.CharChunk;
@@ -30,215 +30,106 @@
/**
*
* @author Costin Manolache
+ * @author Remy Maucherat
*/
public final class Parameters extends MultiMap {
-
- private static org.jboss.logging.Logger log=
- org.jboss.logging.Logger.getLogger(Parameters.class );
-
- // Transition: we'll use the same Hashtable( String->String[] )
- // for the beginning. When we are sure all accesses happen through
- // this class - we can switch to MultiMap
- private Hashtable paramHashStringArray=new Hashtable();
- private boolean didQueryParameters=false;
- private boolean didMerge=false;
-
- MessageBytes queryMB;
- MimeHeaders headers;
+ protected static org.jboss.logging.Logger log =
+ org.jboss.logging.Logger.getLogger(Parameters.class);
- UDecoder urlDec;
- MessageBytes decodedQuery=MessageBytes.newInstance();
+ public static final int INITIAL_SIZE = 8;
+ protected static final String[] ARRAY_TYPE = new String[0];
+
+ protected boolean didQueryParameters = false;
+ protected boolean didMerge = false;
- public static final int INITIAL_SIZE=4;
+ protected MessageBytes queryMB;
- // Garbage-less parameter merging.
- // In a sub-request with parameters, the new parameters
- // will be stored in child. When a getParameter happens,
- // the 2 are merged togheter. The child will be altered
- // to contain the merged values - the parent is allways the
- // original request.
- private Parameters child=null;
- private Parameters parent=null;
- private Parameters currentChild=null;
+ protected UDecoder urlDec;
+ protected MessageBytes decodedQuery = MessageBytes.newInstance();
- String encoding=null;
- String queryStringEncoding=null;
+ protected String encoding = null;
+ protected String queryStringEncoding = null;
/**
*
*/
public Parameters() {
- super( INITIAL_SIZE );
+ super(INITIAL_SIZE);
}
- public void setQuery( MessageBytes queryMB ) {
- this.queryMB=queryMB;
+ public void setQuery(MessageBytes queryMB) {
+ this.queryMB = queryMB;
}
- public void setHeaders( MimeHeaders headers ) {
- this.headers=headers;
+ public void setHeaders(MimeHeaders headers) {
+ // Not used anymore at the moment
}
- public void setEncoding( String s ) {
- encoding=s;
- if(debug>0) log( "Set encoding to " + s );
+ public void setEncoding(String s) {
+ encoding = s;
}
- public void setQueryStringEncoding( String s ) {
- queryStringEncoding=s;
- if(debug>0) log( "Set query string encoding to " + s );
+ public void setURLDecoder(UDecoder u) {
+ urlDec = u;
}
+ public void setQueryStringEncoding(String s) {
+ queryStringEncoding = s;
+ }
+
public void recycle() {
super.recycle();
- paramHashStringArray.clear();
- didQueryParameters=false;
- currentChild=null;
- didMerge=false;
- encoding=null;
+ didQueryParameters = false;
+ didMerge = false;
+ encoding = null;
decodedQuery.recycle();
}
- // -------------------- Sub-request support --------------------
-
- public Parameters getCurrentSet() {
- if( currentChild==null )
- return this;
- return currentChild;
- }
-
- /** Create ( or reuse ) a child that will be used during a sub-request.
- All future changes ( setting query string, adding parameters )
- will affect the child ( the parent request is never changed ).
- Both setters and getters will return the data from the deepest
- child, merged with data from parents.
- */
- public void push() {
- // We maintain a linked list, that will grow to the size of the
- // longest include chain.
- // The list has 2 points of interest:
- // - request.parameters() is the original request and head,
- // - request.parameters().currentChild() is the current set.
- // The ->child and parent<- links are preserved ( currentChild is not
- // the last in the list )
-
- // create a new element in the linked list
- // note that we reuse the child, if any - pop will not
- // set child to null !
- if( currentChild==null ) {
- currentChild=new Parameters();
- currentChild.setURLDecoder( urlDec );
- currentChild.parent=this;
- return;
- }
- if( currentChild.child==null ) {
- currentChild.child=new Parameters();
- currentChild.setURLDecoder( urlDec );
- currentChild.child.parent=currentChild;
- } // it is not null if this object already had a child
- // i.e. a deeper include() ( we keep it )
-
- // the head will be the new element.
- currentChild=currentChild.child;
- currentChild.setEncoding( encoding );
- }
-
- /** Discard the last child. This happens when we return from a
- sub-request and the parameters are locally modified.
- */
- public void pop() {
- if( currentChild==null ) {
- throw new RuntimeException( "Attempt to pop without a push" );
- }
- currentChild.recycle();
- currentChild=currentChild.parent;
- // don't remove the top.
- }
-
// -------------------- Data access --------------------
// Access to the current name/values, no side effect ( processing ).
// You must explicitely call handleQueryParameters and the post methods.
// This is the original data representation ( hash of String->String[])
- public void addParameterValues( String key, String[] newValues) {
- if ( key==null ) return;
- String values[];
- if (paramHashStringArray.containsKey(key)) {
- String oldValues[] = (String[])paramHashStringArray.get(key);
- values = new String[oldValues.length + newValues.length];
- for (int i = 0; i < oldValues.length; i++) {
- values[i] = oldValues[i];
- }
- for (int i = 0; i < newValues.length; i++) {
- values[i+ oldValues.length] = newValues[i];
- }
- } else {
- values = newValues;
+ public void addParameterValues(String name, String[] values) {
+ if (name == null || values == null) {
+ return;
}
-
- paramHashStringArray.put(key, values);
+ for (int i = 0; i < values.length; i++) {
+ String value = values[i];
+ int pos = addField();
+ getName(pos).setString(name);
+ getValue(pos).setString(value);
+ }
}
public String[] getParameterValues(String name) {
handleQueryParameters();
- // sub-request
- if( currentChild!=null ) {
- currentChild.merge();
- return (String[])currentChild.paramHashStringArray.get(name);
+ int pos = findFirst(name);
+ if (pos > 0) {
+ ArrayList<String> result = new ArrayList<String>();
+ while (pos > 0) {
+ result.add(getValue(pos).toString());
+ pos = findNext(pos);
+ }
+ return result.toArray(ARRAY_TYPE);
+ } else {
+ return null;
}
-
- // no "facade"
- String values[]=(String[])paramHashStringArray.get(name);
- return values;
}
public Enumeration getParameterNames() {
handleQueryParameters();
- // Slow - the original code
- if( currentChild!=null ) {
- currentChild.merge();
- return currentChild.paramHashStringArray.keys();
- }
-
- // merge in child
- return paramHashStringArray.keys();
+ return names();
}
- /** Combine the parameters from parent with our local ones
- */
- private void merge() {
- // recursive
- if( debug > 0 ) {
- log("Before merging " + this + " " + parent + "
" + didMerge );
- log( paramsAsString());
- }
- // Local parameters first - they take precedence as in spec.
- handleQueryParameters();
-
- // we already merged with the parent
- if( didMerge ) return;
-
- // we are the top level
- if( parent==null ) return;
-
- // Add the parent props to the child ( lower precedence )
- parent.merge();
- Hashtable parentProps=parent.paramHashStringArray;
- merge2( paramHashStringArray , parentProps);
- didMerge=true;
- if(debug > 0 )
- log("After " + paramsAsString());
- }
-
-
// Shortcut.
- public String getParameter(String name ) {
- String[] values = getParameterValues(name);
- if (values != null) {
- if( values.length==0 ) return "";
- return values[0];
+ public String getParameter(String name) {
+ handleQueryParameters();
+ int pos = findFirst(name);
+ if (pos > 0) {
+ return getValue(pos).toString();
} else {
return null;
}
@@ -266,80 +157,24 @@
processParameters( decodedQuery, queryStringEncoding );
}
- // --------------------
-
- /** Combine 2 hashtables into a new one.
- * ( two will be added to one ).
- * Used to combine child parameters ( RequestDispatcher's query )
- * with parent parameters ( original query or parent dispatcher )
- */
- private static void merge2(Hashtable one, Hashtable two ) {
- Enumeration e = two.keys();
-
- while (e.hasMoreElements()) {
- String name = (String) e.nextElement();
- String[] oneValue = (String[]) one.get(name);
- String[] twoValue = (String[]) two.get(name);
- String[] combinedValue;
-
- if (twoValue == null) {
- continue;
- } else {
- if( oneValue==null ) {
- combinedValue = new String[twoValue.length];
- System.arraycopy(twoValue, 0, combinedValue,
- 0, twoValue.length);
- } else {
- combinedValue = new String[oneValue.length +
- twoValue.length];
- System.arraycopy(oneValue, 0, combinedValue, 0,
- oneValue.length);
- System.arraycopy(twoValue, 0, combinedValue,
- oneValue.length, twoValue.length);
- }
- one.put(name, combinedValue);
- }
+ protected void addParam( String name, String value ) {
+ if (name == null) {
+ return;
}
+ int pos = addField();
+ getName(pos).setString(name);
+ getValue(pos).setString(value);
}
- // incredibly inefficient data representation for parameters,
- // until we test the new one
- private void addParam( String key, String value ) {
- if( key==null ) return;
- String values[];
- if (paramHashStringArray.containsKey(key)) {
- String oldValues[] = (String[])paramHashStringArray.
- get(key);
- values = new String[oldValues.length + 1];
- for (int i = 0; i < oldValues.length; i++) {
- values[i] = oldValues[i];
- }
- values[oldValues.length] = value;
- } else {
- values = new String[1];
- values[0] = value;
- }
-
-
- paramHashStringArray.put(key, values);
- }
-
- public void setURLDecoder( UDecoder u ) {
- urlDec=u;
- }
-
// -------------------- Parameter parsing --------------------
- // This code is not used right now - it's the optimized version
- // of the above.
-
// we are called from a single thread - we can do it the hard way
// if needed
- ByteChunk tmpName=new ByteChunk();
- ByteChunk tmpValue=new ByteChunk();
- CharChunk tmpNameC=new CharChunk(1024);
- CharChunk tmpValueC=new CharChunk(1024);
-
+ protected ByteChunk tmpName = new ByteChunk();
+ protected ByteChunk tmpValue = new ByteChunk();
+ protected CharChunk tmpNameC = new CharChunk(32);
+ protected CharChunk tmpValueC = new CharChunk(128);
+
public void processParameters( byte bytes[], int start, int len ) {
processParameters(bytes, start, len, encoding);
}
@@ -515,11 +350,11 @@
*/
public String paramsAsString() {
StringBuffer sb=new StringBuffer();
- Enumeration en= paramHashStringArray.keys();
+ Enumeration en= names();
while( en.hasMoreElements() ) {
String k=(String)en.nextElement();
sb.append( k ).append("=");
- String v[]=(String[])paramHashStringArray.get( k );
+ String v[]=(String[])getParameterValues(k);
for( int i=0; i<v.length; i++ )
sb.append( v[i] ).append(",");
sb.append("\n");
Added: trunk/java/org/apache/tomcat/util/http/Parameters2.java
===================================================================
--- trunk/java/org/apache/tomcat/util/http/Parameters2.java (rev
0)
+++ trunk/java/org/apache/tomcat/util/http/Parameters2.java 2008-03-27 17:49:20 UTC (rev
559)
@@ -0,0 +1,611 @@
+/*
+ * 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.tomcat.util.http;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.UDecoder;
+import org.apache.tomcat.util.collections.MultiMap;
+
+/**
+ *
+ * @author Costin Manolache
+ * @deprecated
+ */
+public final class Parameters2 extends MultiMap {
+
+
+ private static org.jboss.logging.Logger log=
+ org.jboss.logging.Logger.getLogger(Parameters2.class );
+
+ // Transition: we'll use the same Hashtable( String->String[] )
+ // for the beginning. When we are sure all accesses happen through
+ // this class - we can switch to MultiMap
+ private Hashtable paramHashStringArray=new Hashtable();
+ private boolean didQueryParameters=false;
+ private boolean didMerge=false;
+
+ MessageBytes queryMB;
+ MimeHeaders headers;
+
+ UDecoder urlDec;
+ MessageBytes decodedQuery=MessageBytes.newInstance();
+
+ public static final int INITIAL_SIZE=4;
+
+ // Garbage-less parameter merging.
+ // In a sub-request with parameters, the new parameters
+ // will be stored in child. When a getParameter happens,
+ // the 2 are merged togheter. The child will be altered
+ // to contain the merged values - the parent is allways the
+ // original request.
+ private Parameters2 child=null;
+ private Parameters2 parent=null;
+ private Parameters2 currentChild=null;
+
+ String encoding=null;
+ String queryStringEncoding=null;
+
+ /**
+ *
+ */
+ public Parameters2() {
+ super( INITIAL_SIZE );
+ }
+
+ public void setQuery( MessageBytes queryMB ) {
+ this.queryMB=queryMB;
+ }
+
+ public void setHeaders( MimeHeaders headers ) {
+ this.headers=headers;
+ }
+
+ public void setEncoding( String s ) {
+ encoding=s;
+ if(debug>0) log( "Set encoding to " + s );
+ }
+
+ public void setQueryStringEncoding( String s ) {
+ queryStringEncoding=s;
+ if(debug>0) log( "Set query string encoding to " + s );
+ }
+
+ public void recycle() {
+ super.recycle();
+ paramHashStringArray.clear();
+ didQueryParameters=false;
+ currentChild=null;
+ didMerge=false;
+ encoding=null;
+ decodedQuery.recycle();
+ }
+
+ // -------------------- Sub-request support --------------------
+
+ public Parameters2 getCurrentSet() {
+ if( currentChild==null )
+ return this;
+ return currentChild;
+ }
+
+ /** Create ( or reuse ) a child that will be used during a sub-request.
+ All future changes ( setting query string, adding parameters )
+ will affect the child ( the parent request is never changed ).
+ Both setters and getters will return the data from the deepest
+ child, merged with data from parents.
+ */
+ public void push() {
+ // We maintain a linked list, that will grow to the size of the
+ // longest include chain.
+ // The list has 2 points of interest:
+ // - request.parameters() is the original request and head,
+ // - request.parameters().currentChild() is the current set.
+ // The ->child and parent<- links are preserved ( currentChild is not
+ // the last in the list )
+
+ // create a new element in the linked list
+ // note that we reuse the child, if any - pop will not
+ // set child to null !
+ if( currentChild==null ) {
+ currentChild=new Parameters2();
+ currentChild.setURLDecoder( urlDec );
+ currentChild.parent=this;
+ return;
+ }
+ if( currentChild.child==null ) {
+ currentChild.child=new Parameters2();
+ currentChild.setURLDecoder( urlDec );
+ currentChild.child.parent=currentChild;
+ } // it is not null if this object already had a child
+ // i.e. a deeper include() ( we keep it )
+
+ // the head will be the new element.
+ currentChild=currentChild.child;
+ currentChild.setEncoding( encoding );
+ }
+
+ /** Discard the last child. This happens when we return from a
+ sub-request and the parameters are locally modified.
+ */
+ public void pop() {
+ if( currentChild==null ) {
+ throw new RuntimeException( "Attempt to pop without a push" );
+ }
+ currentChild.recycle();
+ currentChild=currentChild.parent;
+ // don't remove the top.
+ }
+
+ // -------------------- Data access --------------------
+ // Access to the current name/values, no side effect ( processing ).
+ // You must explicitely call handleQueryParameters and the post methods.
+
+ // This is the original data representation ( hash of String->String[])
+
+ public void addParameterValues( String key, String[] newValues) {
+ if ( key==null ) return;
+ String values[];
+ if (paramHashStringArray.containsKey(key)) {
+ String oldValues[] = (String[])paramHashStringArray.get(key);
+ values = new String[oldValues.length + newValues.length];
+ for (int i = 0; i < oldValues.length; i++) {
+ values[i] = oldValues[i];
+ }
+ for (int i = 0; i < newValues.length; i++) {
+ values[i+ oldValues.length] = newValues[i];
+ }
+ } else {
+ values = newValues;
+ }
+
+ paramHashStringArray.put(key, values);
+ }
+
+ public String[] getParameterValues(String name) {
+ handleQueryParameters();
+ // sub-request
+ if( currentChild!=null ) {
+ currentChild.merge();
+ return (String[])currentChild.paramHashStringArray.get(name);
+ }
+
+ // no "facade"
+ String values[]=(String[])paramHashStringArray.get(name);
+ return values;
+ }
+
+ public Enumeration getParameterNames() {
+ handleQueryParameters();
+ // Slow - the original code
+ if( currentChild!=null ) {
+ currentChild.merge();
+ return currentChild.paramHashStringArray.keys();
+ }
+
+ // merge in child
+ return paramHashStringArray.keys();
+ }
+
+ /** Combine the parameters from parent with our local ones
+ */
+ private void merge() {
+ // recursive
+ if( debug > 0 ) {
+ log("Before merging " + this + " " + parent + "
" + didMerge );
+ log( paramsAsString());
+ }
+ // Local parameters first - they take precedence as in spec.
+ handleQueryParameters();
+
+ // we already merged with the parent
+ if( didMerge ) return;
+
+ // we are the top level
+ if( parent==null ) return;
+
+ // Add the parent props to the child ( lower precedence )
+ parent.merge();
+ Hashtable parentProps=parent.paramHashStringArray;
+ merge2( paramHashStringArray , parentProps);
+ didMerge=true;
+ if(debug > 0 )
+ log("After " + paramsAsString());
+ }
+
+
+ // Shortcut.
+ public String getParameter(String name ) {
+ String[] values = getParameterValues(name);
+ if (values != null) {
+ if( values.length==0 ) return "";
+ return values[0];
+ } else {
+ return null;
+ }
+ }
+ // -------------------- Processing --------------------
+ /** Process the query string into parameters
+ */
+ public void handleQueryParameters() {
+ if( didQueryParameters ) return;
+
+ didQueryParameters=true;
+
+ if( queryMB==null || queryMB.isNull() )
+ return;
+
+ if( debug > 0 )
+ log( "Decoding query " + decodedQuery + " " +
queryStringEncoding);
+
+ try {
+ decodedQuery.duplicate( queryMB );
+ } catch (IOException e) {
+ // Can't happen, as decodedQuery can't overflow
+ e.printStackTrace();
+ }
+ processParameters( decodedQuery, queryStringEncoding );
+ }
+
+ // --------------------
+
+ /** Combine 2 hashtables into a new one.
+ * ( two will be added to one ).
+ * Used to combine child parameters ( RequestDispatcher's query )
+ * with parent parameters ( original query or parent dispatcher )
+ */
+ private static void merge2(Hashtable one, Hashtable two ) {
+ Enumeration e = two.keys();
+
+ while (e.hasMoreElements()) {
+ String name = (String) e.nextElement();
+ String[] oneValue = (String[]) one.get(name);
+ String[] twoValue = (String[]) two.get(name);
+ String[] combinedValue;
+
+ if (twoValue == null) {
+ continue;
+ } else {
+ if( oneValue==null ) {
+ combinedValue = new String[twoValue.length];
+ System.arraycopy(twoValue, 0, combinedValue,
+ 0, twoValue.length);
+ } else {
+ combinedValue = new String[oneValue.length +
+ twoValue.length];
+ System.arraycopy(oneValue, 0, combinedValue, 0,
+ oneValue.length);
+ System.arraycopy(twoValue, 0, combinedValue,
+ oneValue.length, twoValue.length);
+ }
+ one.put(name, combinedValue);
+ }
+ }
+ }
+
+ // incredibly inefficient data representation for parameters,
+ // until we test the new one
+ private void addParam( String key, String value ) {
+ if( key==null ) return;
+ String values[];
+ if (paramHashStringArray.containsKey(key)) {
+ String oldValues[] = (String[])paramHashStringArray.
+ get(key);
+ values = new String[oldValues.length + 1];
+ for (int i = 0; i < oldValues.length; i++) {
+ values[i] = oldValues[i];
+ }
+ values[oldValues.length] = value;
+ } else {
+ values = new String[1];
+ values[0] = value;
+ }
+
+
+ paramHashStringArray.put(key, values);
+ }
+
+ public void setURLDecoder( UDecoder u ) {
+ urlDec=u;
+ }
+
+ // -------------------- Parameter parsing --------------------
+
+ // This code is not used right now - it's the optimized version
+ // of the above.
+
+ // we are called from a single thread - we can do it the hard way
+ // if needed
+ ByteChunk tmpName=new ByteChunk();
+ ByteChunk tmpValue=new ByteChunk();
+ CharChunk tmpNameC=new CharChunk(1024);
+ CharChunk tmpValueC=new CharChunk(1024);
+
+ public void processParameters( byte bytes[], int start, int len ) {
+ processParameters(bytes, start, len, encoding);
+ }
+
+ public void processParameters( byte bytes[], int start, int len,
+ String enc ) {
+ int end=start+len;
+ int pos=start;
+
+ if( debug>0 )
+ log( "Bytes: " + new String( bytes, start, len ));
+
+ do {
+ boolean noEq=false;
+ int valStart=-1;
+ int valEnd=-1;
+
+ int nameStart=pos;
+ int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
+ // Workaround for a&b&c encoding
+ int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
+ if( (nameEnd2!=-1 ) &&
+ ( nameEnd==-1 || nameEnd > nameEnd2) ) {
+ nameEnd=nameEnd2;
+ noEq=true;
+ valStart=nameEnd;
+ valEnd=nameEnd;
+ if( debug>0) log("no equal " + nameStart + " " +
nameEnd + " " + new String(bytes, nameStart, nameEnd-nameStart) );
+ }
+ if( nameEnd== -1 )
+ nameEnd=end;
+
+ if( ! noEq ) {
+ valStart= (nameEnd < end) ? nameEnd+1 : end;
+ valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
+ if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
+ }
+
+ pos=valEnd+1;
+
+ if( nameEnd<=nameStart ) {
+ log.warn("Parameters: Invalid chunk ignored.");
+ continue;
+ // invalid chunk - it's better to ignore
+ }
+ tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
+ tmpValue.setBytes( bytes, valStart, valEnd-valStart );
+
+ try {
+ addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
+ } catch (IOException e) {
+ // Exception during character decoding: skip parameter
+ log.warn("Parameters: Character decoding failed. " +
+ "Parameter skipped.", e);
+ }
+
+ tmpName.recycle();
+ tmpValue.recycle();
+
+ } while( pos<end );
+ }
+
+ private String urlDecode(ByteChunk bc, String enc)
+ throws IOException {
+ if( urlDec==null ) {
+ urlDec=new UDecoder();
+ }
+ urlDec.convert(bc);
+ String result = null;
+ if (enc != null) {
+ bc.setEncoding(enc);
+ result = bc.toString();
+ } else {
+ CharChunk cc = tmpNameC;
+ int length = bc.getLength();
+ cc.allocate(length, -1);
+ // Default encoding: fast conversion
+ byte[] bbuf = bc.getBuffer();
+ char[] cbuf = cc.getBuffer();
+ int start = bc.getStart();
+ for (int i = 0; i < length; i++) {
+ cbuf[i] = (char) (bbuf[i + start] & 0xff);
+ }
+ cc.setChars(cbuf, 0, length);
+ result = cc.toString();
+ cc.recycle();
+ }
+ return result;
+ }
+
+ public void processParameters( char chars[], int start, int len ) {
+ int end=start+len;
+ int pos=start;
+
+ if( debug>0 )
+ log( "Chars: " + new String( chars, start, len ));
+ do {
+ boolean noEq=false;
+ int nameStart=pos;
+ int valStart=-1;
+ int valEnd=-1;
+
+ int nameEnd=CharChunk.indexOf(chars, nameStart, end, '=' );
+ int nameEnd2=CharChunk.indexOf(chars, nameStart, end, '&' );
+ if( (nameEnd2!=-1 ) &&
+ ( nameEnd==-1 || nameEnd > nameEnd2) ) {
+ nameEnd=nameEnd2;
+ noEq=true;
+ valStart=nameEnd;
+ valEnd=nameEnd;
+ if( debug>0) log("no equal " + nameStart + " " +
nameEnd + " " + new String(chars, nameStart, nameEnd-nameStart) );
+ }
+ if( nameEnd== -1 ) nameEnd=end;
+
+ if( ! noEq ) {
+ valStart= (nameEnd < end) ? nameEnd+1 : end;
+ valEnd=CharChunk.indexOf(chars, valStart, end, '&');
+ if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
+ }
+
+ pos=valEnd+1;
+
+ if( nameEnd<=nameStart ) {
+ continue;
+ // invalid chunk - no name, it's better to ignore
+ // XXX log it ?
+ }
+
+ try {
+ tmpNameC.append( chars, nameStart, nameEnd-nameStart );
+ tmpValueC.append( chars, valStart, valEnd-valStart );
+
+ if( debug > 0 )
+ log( tmpNameC + "= " + tmpValueC);
+
+ if( urlDec==null ) {
+ urlDec=new UDecoder();
+ }
+
+ urlDec.convert( tmpNameC );
+ urlDec.convert( tmpValueC );
+
+ if( debug > 0 )
+ log( tmpNameC + "= " + tmpValueC);
+
+ addParam( tmpNameC.toString(), tmpValueC.toString() );
+ } catch( IOException ex ) {
+ ex.printStackTrace();
+ }
+
+ tmpNameC.recycle();
+ tmpValueC.recycle();
+
+ } while( pos<end );
+ }
+
+ public void processParameters( MessageBytes data ) {
+ processParameters(data, encoding);
+ }
+
+ public void processParameters( MessageBytes data, String encoding ) {
+ if( data==null || data.isNull() || data.getLength() <= 0 ) return;
+
+ if (data.getType() != MessageBytes.T_BYTES) {
+ data.toBytes();
+ }
+ ByteChunk bc=data.getByteChunk();
+ processParameters( bc.getBytes(), bc.getOffset(),
+ bc.getLength(), encoding);
+ }
+
+ /** Debug purpose
+ */
+ public String paramsAsString() {
+ StringBuffer sb=new StringBuffer();
+ Enumeration en= paramHashStringArray.keys();
+ while( en.hasMoreElements() ) {
+ String k=(String)en.nextElement();
+ sb.append( k ).append("=");
+ String v[]=(String[])paramHashStringArray.get( k );
+ for( int i=0; i<v.length; i++ )
+ sb.append( v[i] ).append(",");
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ private static int debug=0;
+ private void log(String s ) {
+ if (log.isDebugEnabled())
+ log.debug("Parameters: " + s );
+ }
+
+ // -------------------- Old code, needs rewrite --------------------
+
+ /** Used by RequestDispatcher
+ */
+ public void processParameters( String str ) {
+ int end=str.length();
+ int pos=0;
+ if( debug > 0)
+ log("String: " + str );
+
+ do {
+ boolean noEq=false;
+ int valStart=-1;
+ int valEnd=-1;
+
+ int nameStart=pos;
+ int nameEnd=str.indexOf('=', nameStart );
+ int nameEnd2=str.indexOf('&', nameStart );
+ if( nameEnd2== -1 ) nameEnd2=end;
+ if( (nameEnd2!=-1 ) &&
+ ( nameEnd==-1 || nameEnd > nameEnd2) ) {
+ nameEnd=nameEnd2;
+ noEq=true;
+ valStart=nameEnd;
+ valEnd=nameEnd;
+ if( debug>0) log("no equal " + nameStart + " " +
nameEnd + " " + str.substring(nameStart, nameEnd) );
+ }
+
+ if( nameEnd== -1 ) nameEnd=end;
+
+ if( ! noEq ) {
+ valStart=nameEnd+1;
+ valEnd=str.indexOf('&', valStart);
+ if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
+ }
+
+ pos=valEnd+1;
+
+ if( nameEnd<=nameStart ) {
+ continue;
+ }
+ if( debug>0)
+ log( "XXX " + nameStart + " " + nameEnd + "
"
+ + valStart + " " + valEnd );
+
+ try {
+ tmpNameC.append(str, nameStart, nameEnd-nameStart );
+ tmpValueC.append(str, valStart, valEnd-valStart );
+
+ if( debug > 0 )
+ log( tmpNameC + "= " + tmpValueC);
+
+ if( urlDec==null ) {
+ urlDec=new UDecoder();
+ }
+
+ urlDec.convert( tmpNameC );
+ urlDec.convert( tmpValueC );
+
+ if( debug > 0 )
+ log( tmpNameC + "= " + tmpValueC);
+
+ addParam( tmpNameC.toString(), tmpValueC.toString() );
+ } catch( IOException ex ) {
+ ex.printStackTrace();
+ }
+
+ tmpNameC.recycle();
+ tmpValueC.recycle();
+
+ } while( pos<end );
+ }
+
+
+}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2008-03-27 14:22:16 UTC (rev 558)
+++ trunk/webapps/docs/changelog.xml 2008-03-27 17:49:20 UTC (rev 559)
@@ -64,6 +64,10 @@
<update>
For consistency, refactor character output using the NIO character decoders.
(remm)
</update>
+ <update>
+ Move the parameters backend to the probably more efficient MultiMap from
Hashtable,
+ and remove the nesting capabilities inherited from Tomcat 3. (remm)
+ </update>
</changelog>
</subsection>
</section>