private Hashtable<String,String[]> paramHashStringArray =
new Hashtable<String,String[]>();
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 Parameters child=null;
- private Parameters parent=null;
- private Parameters currentChild=null;
-
String encoding=null;
String queryStringEncoding=null;
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 );
super.recycle();
paramHashStringArray.clear();
didQueryParameters=false;
- currentChild=null;
- 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.
+ // You must explicitly call handleQueryParameters and the post methods.
// This is the original data representation ( hash of String->String[])
public String[] getParameterValues(String name) {
handleQueryParameters();
- // sub-request
- if( currentChild!=null ) {
- currentChild.merge();
- return currentChild.paramHashStringArray.get(name);
- }
-
// no "facade"
String values[] = paramHashStringArray.get(name);
return values;
public Enumeration<String> 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<String,String[]> parentProps=parent.paramHashStringArray;
- merge2( paramHashStringArray , parentProps);
- didMerge=true;
- if(debug > 0 )
- log("After " + paramsAsString());
- }
-
-
// Shortcut.
public String getParameter(String name ) {
String[] values = getParameterValues(name);
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<String,String[]> one,
- Hashtable<String,String[]> two ) {
- Enumeration<String> e = two.keys();
-
- while (e.hasMoreElements()) {
- String name = e.nextElement();
- String[] oneValue = one.get(name);
- String[] twoValue = 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 ) {
}
// -------------------- 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 origName=new ByteChunk();
ByteChunk origValue=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);
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 (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 );
- }
-
}