FileDocCategorySizeDatePackage
Parameters.javaAPI DocApache Tomcat 6.0.1421269Fri Jul 20 04:20:36 BST 2007org.apache.tomcat.util.http

Parameters

public final class Parameters extends org.apache.tomcat.util.collections.MultiMap
author
Costin Manolache

Fields Summary
private static org.apache.juli.logging.Log
log
private Hashtable
paramHashStringArray
private boolean
didQueryParameters
private boolean
didMerge
org.apache.tomcat.util.buf.MessageBytes
queryMB
MimeHeaders
headers
org.apache.tomcat.util.buf.UDecoder
urlDec
org.apache.tomcat.util.buf.MessageBytes
decodedQuery
public static final int
INITIAL_SIZE
private Parameters
child
private Parameters
parent
private Parameters
currentChild
String
encoding
String
queryStringEncoding
org.apache.tomcat.util.buf.ByteChunk
tmpName
org.apache.tomcat.util.buf.ByteChunk
tmpValue
org.apache.tomcat.util.buf.CharChunk
tmpNameC
org.apache.tomcat.util.buf.CharChunk
tmpValueC
private static int
debug
Constructors Summary
public Parameters()

    
          
      
        super( INITIAL_SIZE );
    
Methods Summary
private voidaddParam(java.lang.String key, java.lang.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 voidaddParameterValues(java.lang.String key, java.lang.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 org.apache.tomcat.util.http.ParametersgetCurrentSet()

        if( currentChild==null )
            return this;
        return currentChild;
    
public java.lang.StringgetParameter(java.lang.String name)

        String[] values = getParameterValues(name);
        if (values != null) {
            if( values.length==0 ) return "";
            return values[0];
        } else {
            return null;
        }
    
public java.util.EnumerationgetParameterNames()

        handleQueryParameters();
        // Slow - the original code
        if( currentChild!=null ) {
            currentChild.merge();
            return currentChild.paramHashStringArray.keys();
        }

        // merge in child
        return paramHashStringArray.keys();
    
public java.lang.String[]getParameterValues(java.lang.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 voidhandleQueryParameters()
Process the query string into parameters

        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 );
    
private voidlog(java.lang.String s)

         
        if (log.isDebugEnabled())
            log.debug("Parameters: " + s );
    
private voidmerge()
Combine the parameters from parent with our local ones

        // 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());
    
private static voidmerge2(java.util.Hashtable one, java.util.Hashtable two)
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 )

        Enumeration e = two.keys();

        while (e.hasMoreElements()) {
            String name = (String) 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);
            }
        }
    
public java.lang.StringparamsAsString()
Debug purpose

        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();
    
public voidpop()
Discard the last child. This happens when we return from a sub-request and the parameters are locally modified.

        if( currentChild==null ) {
            throw new RuntimeException( "Attempt to pop without a push" );
        }
        currentChild.recycle();
        currentChild=currentChild.parent;
        // don't remove the top.
    
public voidprocessParameters(byte[] bytes, int start, int len)

    
              
        processParameters(bytes, start, len, encoding);
    
public voidprocessParameters(byte[] bytes, int start, int len, java.lang.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 );
    
public voidprocessParameters(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 voidprocessParameters(org.apache.tomcat.util.buf.MessageBytes data)

        processParameters(data, encoding);
    
public voidprocessParameters(org.apache.tomcat.util.buf.MessageBytes data, java.lang.String encoding)

        if( data==null || data.isNull() || data.getLength() <= 0 ) return;

        if( data.getType() == MessageBytes.T_BYTES ) {
            ByteChunk bc=data.getByteChunk();
            processParameters( bc.getBytes(), bc.getOffset(),
                               bc.getLength(), encoding);
        } else {
            if (data.getType()!= MessageBytes.T_CHARS ) 
                data.toChars();
            CharChunk cc=data.getCharChunk();
            processParameters( cc.getChars(), cc.getOffset(),
                               cc.getLength());
        }
    
public voidprocessParameters(java.lang.String str)
Used by RequestDispatcher

        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 );
    
public voidpush()
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.

        // 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 );
    
public voidrecycle()

        super.recycle();
        paramHashStringArray.clear();
        didQueryParameters=false;
        currentChild=null;
        didMerge=false;
        encoding=null;
        decodedQuery.recycle();
    
public voidsetEncoding(java.lang.String s)

        encoding=s;
        if(debug>0) log( "Set encoding to " + s );
    
public voidsetHeaders(MimeHeaders headers)

        this.headers=headers;
    
public voidsetQuery(org.apache.tomcat.util.buf.MessageBytes queryMB)

        this.queryMB=queryMB;
    
public voidsetQueryStringEncoding(java.lang.String s)

        queryStringEncoding=s;
        if(debug>0) log( "Set query string encoding to " + s );
    
public voidsetURLDecoder(org.apache.tomcat.util.buf.UDecoder u)

        urlDec=u;
    
private java.lang.StringurlDecode(org.apache.tomcat.util.buf.ByteChunk bc, java.lang.String enc)

        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;