KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tomcat > util > http > Parameters


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.tomcat.util.http;
19
20 import java.io.IOException JavaDoc;
21 import java.util.Enumeration JavaDoc;
22 import java.util.Hashtable JavaDoc;
23
24 import org.apache.tomcat.util.buf.ByteChunk;
25 import org.apache.tomcat.util.buf.CharChunk;
26 import org.apache.tomcat.util.buf.MessageBytes;
27 import org.apache.tomcat.util.buf.UDecoder;
28 import org.apache.tomcat.util.collections.MultiMap;
29
30 /**
31  *
32  * @author Costin Manolache
33  */

34 public final class Parameters extends MultiMap {
35
36     
37     private static org.apache.commons.logging.Log log=
38         org.apache.commons.logging.LogFactory.getLog(Parameters.class );
39     
40     // Transition: we'll use the same Hashtable( String->String[] )
41
// for the beginning. When we are sure all accesses happen through
42
// this class - we can switch to MultiMap
43
private Hashtable JavaDoc paramHashStringArray=new Hashtable JavaDoc();
44     private boolean didQueryParameters=false;
45     private boolean didMerge=false;
46     
47     MessageBytes queryMB;
48     MimeHeaders headers;
49
50     UDecoder urlDec;
51     MessageBytes decodedQuery=MessageBytes.newInstance();
52     
53     public static final int INITIAL_SIZE=4;
54
55     // Garbage-less parameter merging.
56
// In a sub-request with parameters, the new parameters
57
// will be stored in child. When a getParameter happens,
58
// the 2 are merged togheter. The child will be altered
59
// to contain the merged values - the parent is allways the
60
// original request.
61
private Parameters child=null;
62     private Parameters parent=null;
63     private Parameters currentChild=null;
64
65     String JavaDoc encoding=null;
66     String JavaDoc queryStringEncoding=null;
67     
68     /**
69      *
70      */

71     public Parameters() {
72     super( INITIAL_SIZE );
73     }
74
75     public void setQuery( MessageBytes queryMB ) {
76     this.queryMB=queryMB;
77     }
78
79     public void setHeaders( MimeHeaders headers ) {
80     this.headers=headers;
81     }
82
83     public void setEncoding( String JavaDoc s ) {
84     encoding=s;
85     if(debug>0) log( "Set encoding to " + s );
86     }
87
88     public void setQueryStringEncoding( String JavaDoc s ) {
89     queryStringEncoding=s;
90     if(debug>0) log( "Set query string encoding to " + s );
91     }
92
93     public void recycle() {
94     super.recycle();
95     paramHashStringArray.clear();
96     didQueryParameters=false;
97     currentChild=null;
98     didMerge=false;
99     encoding=null;
100     decodedQuery.recycle();
101     }
102     
103     // -------------------- Sub-request support --------------------
104

105     public Parameters getCurrentSet() {
106     if( currentChild==null )
107         return this;
108     return currentChild;
109     }
110     
111     /** Create ( or reuse ) a child that will be used during a sub-request.
112     All future changes ( setting query string, adding parameters )
113     will affect the child ( the parent request is never changed ).
114     Both setters and getters will return the data from the deepest
115     child, merged with data from parents.
116     */

117     public void push() {
118     // We maintain a linked list, that will grow to the size of the
119
// longest include chain.
120
// The list has 2 points of interest:
121
// - request.parameters() is the original request and head,
122
// - request.parameters().currentChild() is the current set.
123
// The ->child and parent<- links are preserved ( currentChild is not
124
// the last in the list )
125

126     // create a new element in the linked list
127
// note that we reuse the child, if any - pop will not
128
// set child to null !
129
if( currentChild==null ) {
130         currentChild=new Parameters();
131         currentChild.setURLDecoder( urlDec );
132         currentChild.parent=this;
133         return;
134     }
135     if( currentChild.child==null ) {
136         currentChild.child=new Parameters();
137         currentChild.setURLDecoder( urlDec );
138         currentChild.child.parent=currentChild;
139     } // it is not null if this object already had a child
140
// i.e. a deeper include() ( we keep it )
141

142     // the head will be the new element.
143
currentChild=currentChild.child;
144     currentChild.setEncoding( encoding );
145     }
146
147     /** Discard the last child. This happens when we return from a
148     sub-request and the parameters are locally modified.
149      */

150     public void pop() {
151     if( currentChild==null ) {
152         throw new RuntimeException JavaDoc( "Attempt to pop without a push" );
153     }
154     currentChild.recycle();
155     currentChild=currentChild.parent;
156     // don't remove the top.
157
}
158     
159     // -------------------- Data access --------------------
160
// Access to the current name/values, no side effect ( processing ).
161
// You must explicitely call handleQueryParameters and the post methods.
162

163     // This is the original data representation ( hash of String->String[])
164

165     public void addParameterValues( String JavaDoc key, String JavaDoc[] newValues) {
166         if ( key==null ) return;
167         String JavaDoc values[];
168         if (paramHashStringArray.containsKey(key)) {
169             String JavaDoc oldValues[] = (String JavaDoc[])paramHashStringArray.get(key);
170             values = new String JavaDoc[oldValues.length + newValues.length];
171             for (int i = 0; i < oldValues.length; i++) {
172                 values[i] = oldValues[i];
173             }
174             for (int i = 0; i < newValues.length; i++) {
175                 values[i+ oldValues.length] = newValues[i];
176             }
177         } else {
178             values = newValues;
179         }
180
181         paramHashStringArray.put(key, values);
182     }
183
184     public String JavaDoc[] getParameterValues(String JavaDoc name) {
185     handleQueryParameters();
186     // sub-request
187
if( currentChild!=null ) {
188         currentChild.merge();
189         return (String JavaDoc[])currentChild.paramHashStringArray.get(name);
190     }
191
192     // no "facade"
193
String JavaDoc values[]=(String JavaDoc[])paramHashStringArray.get(name);
194     return values;
195     }
196  
197     public Enumeration JavaDoc getParameterNames() {
198     handleQueryParameters();
199     // Slow - the original code
200
if( currentChild!=null ) {
201         currentChild.merge();
202         return currentChild.paramHashStringArray.keys();
203     }
204
205     // merge in child
206
return paramHashStringArray.keys();
207     }
208
209     /** Combine the parameters from parent with our local ones
210      */

211     private void merge() {
212     // recursive
213
if( debug > 0 ) {
214         log("Before merging " + this + " " + parent + " " + didMerge );
215         log( paramsAsString());
216     }
217     // Local parameters first - they take precedence as in spec.
218
handleQueryParameters();
219
220     // we already merged with the parent
221
if( didMerge ) return;
222
223     // we are the top level
224
if( parent==null ) return;
225
226     // Add the parent props to the child ( lower precedence )
227
parent.merge();
228     Hashtable JavaDoc parentProps=parent.paramHashStringArray;
229     merge2( paramHashStringArray , parentProps);
230     didMerge=true;
231     if(debug > 0 )
232         log("After " + paramsAsString());
233     }
234
235
236     // Shortcut.
237
public String JavaDoc getParameter(String JavaDoc name ) {
238     String JavaDoc[] values = getParameterValues(name);
239         if (values != null) {
240         if( values.length==0 ) return "";
241             return values[0];
242         } else {
243         return null;
244         }
245     }
246     // -------------------- Processing --------------------
247
/** Process the query string into parameters
248      */

249     public void handleQueryParameters() {
250     if( didQueryParameters ) return;
251
252     didQueryParameters=true;
253
254     if( queryMB==null || queryMB.isNull() )
255         return;
256     
257     if( debug > 0 )
258         log( "Decoding query " + decodedQuery + " " + queryStringEncoding);
259
260         try {
261             decodedQuery.duplicate( queryMB );
262         } catch (IOException JavaDoc e) {
263             // Can't happen, as decodedQuery can't overflow
264
e.printStackTrace();
265         }
266         processParameters( decodedQuery, queryStringEncoding );
267     }
268
269     // --------------------
270

271     /** Combine 2 hashtables into a new one.
272      * ( two will be added to one ).
273      * Used to combine child parameters ( RequestDispatcher's query )
274      * with parent parameters ( original query or parent dispatcher )
275      */

276     private static void merge2(Hashtable JavaDoc one, Hashtable JavaDoc two ) {
277         Enumeration JavaDoc e = two.keys();
278
279     while (e.hasMoreElements()) {
280         String JavaDoc name = (String JavaDoc) e.nextElement();
281         String JavaDoc[] oneValue = (String JavaDoc[]) one.get(name);
282         String JavaDoc[] twoValue = (String JavaDoc[]) two.get(name);
283         String JavaDoc[] combinedValue;
284
285         if (twoValue == null) {
286         continue;
287         } else {
288         if( oneValue==null ) {
289             combinedValue = new String JavaDoc[twoValue.length];
290             System.arraycopy(twoValue, 0, combinedValue,
291                      0, twoValue.length);
292         } else {
293             combinedValue = new String JavaDoc[oneValue.length +
294                            twoValue.length];
295             System.arraycopy(oneValue, 0, combinedValue, 0,
296                      oneValue.length);
297             System.arraycopy(twoValue, 0, combinedValue,
298                      oneValue.length, twoValue.length);
299         }
300         one.put(name, combinedValue);
301         }
302     }
303     }
304
305     // incredibly inefficient data representation for parameters,
306
// until we test the new one
307
private void addParam( String JavaDoc key, String JavaDoc value ) {
308     if( key==null ) return;
309     String JavaDoc values[];
310     if (paramHashStringArray.containsKey(key)) {
311         String JavaDoc oldValues[] = (String JavaDoc[])paramHashStringArray.
312         get(key);
313         values = new String JavaDoc[oldValues.length + 1];
314         for (int i = 0; i < oldValues.length; i++) {
315         values[i] = oldValues[i];
316         }
317         values[oldValues.length] = value;
318     } else {
319         values = new String JavaDoc[1];
320         values[0] = value;
321     }
322     
323     
324     paramHashStringArray.put(key, values);
325     }
326
327     public void setURLDecoder( UDecoder u ) {
328     urlDec=u;
329     }
330
331     // -------------------- Parameter parsing --------------------
332

333     // This code is not used right now - it's the optimized version
334
// of the above.
335

336     // we are called from a single thread - we can do it the hard way
337
// if needed
338
ByteChunk tmpName=new ByteChunk();
339     ByteChunk tmpValue=new ByteChunk();
340     CharChunk tmpNameC=new CharChunk(1024);
341     CharChunk tmpValueC=new CharChunk(1024);
342     
343     public void processParameters( byte bytes[], int start, int len ) {
344         processParameters(bytes, start, len, encoding);
345     }
346
347     public void processParameters( byte bytes[], int start, int len,
348                                    String JavaDoc enc ) {
349     int end=start+len;
350     int pos=start;
351     
352     if( debug>0 )
353         log( "Bytes: " + new String JavaDoc( bytes, start, len ));
354
355         do {
356         boolean noEq=false;
357         int valStart=-1;
358         int valEnd=-1;
359         
360         int nameStart=pos;
361         int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
362         // Workaround for a&b&c encoding
363
int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
364         if( (nameEnd2!=-1 ) &&
365         ( nameEnd==-1 || nameEnd > nameEnd2) ) {
366         nameEnd=nameEnd2;
367         noEq=true;
368         valStart=nameEnd;
369         valEnd=nameEnd;
370         if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String JavaDoc(bytes, nameStart, nameEnd-nameStart) );
371         }
372         if( nameEnd== -1 )
373         nameEnd=end;
374
375         if( ! noEq ) {
376         valStart= (nameEnd < end) ? nameEnd+1 : end;
377         valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
378         if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
379         }
380         
381         pos=valEnd+1;
382         
383         if( nameEnd<=nameStart ) {
384         continue;
385         // invalid chunk - it's better to ignore
386
// XXX log it ?
387
}
388         tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
389         tmpValue.setBytes( bytes, valStart, valEnd-valStart );
390
391             try {
392                 addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
393             } catch (IOException JavaDoc e) {
394                 // Exception during character decoding: skip parameter
395
}
396
397         tmpName.recycle();
398         tmpValue.recycle();
399
400     } while( pos<end );
401     }
402
403     private String JavaDoc urlDecode(ByteChunk bc, String JavaDoc enc)
404         throws IOException JavaDoc {
405         if( urlDec==null ) {
406             urlDec=new UDecoder();
407         }
408         urlDec.convert(bc);
409         String JavaDoc result = null;
410         if (enc != null) {
411             bc.setEncoding(enc);
412             result = bc.toString();
413         } else {
414             CharChunk cc = tmpNameC;
415             cc.allocate(bc.getLength(), -1);
416             // Default encoding: fast conversion
417
byte[] bbuf = bc.getBuffer();
418             char[] cbuf = cc.getBuffer();
419             int start = bc.getStart();
420             for (int i = 0; i < bc.getLength(); i++) {
421                 cbuf[i] = (char) (bbuf[i + start] & 0xff);
422             }
423             cc.setChars(cbuf, 0, bc.getLength());
424             result = cc.toString();
425             cc.recycle();
426         }
427         return result;
428     }
429
430     public void processParameters( char chars[], int start, int len ) {
431     int end=start+len;
432     int pos=start;
433     
434     if( debug>0 )
435         log( "Chars: " + new String JavaDoc( chars, start, len ));
436         do {
437         boolean noEq=false;
438         int nameStart=pos;
439         int valStart=-1;
440         int valEnd=-1;
441         
442         int nameEnd=CharChunk.indexOf(chars, nameStart, end, '=' );
443         int nameEnd2=CharChunk.indexOf(chars, nameStart, end, '&' );
444         if( (nameEnd2!=-1 ) &&
445         ( nameEnd==-1 || nameEnd > nameEnd2) ) {
446         nameEnd=nameEnd2;
447         noEq=true;
448         valStart=nameEnd;
449         valEnd=nameEnd;
450         if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String JavaDoc(chars, nameStart, nameEnd-nameStart) );
451         }
452         if( nameEnd== -1 ) nameEnd=end;
453         
454         if( ! noEq ) {
455         valStart= (nameEnd < end) ? nameEnd+1 : end;
456         valEnd=CharChunk.indexOf(chars, valStart, end, '&');
457         if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
458         }
459         
460         pos=valEnd+1;
461         
462         if( nameEnd<=nameStart ) {
463         continue;
464         // invalid chunk - no name, it's better to ignore
465
// XXX log it ?
466
}
467         
468         try {
469         tmpNameC.append( chars, nameStart, nameEnd-nameStart );
470         tmpValueC.append( chars, valStart, valEnd-valStart );
471
472         if( debug > 0 )
473             log( tmpNameC + "= " + tmpValueC);
474
475         if( urlDec==null ) {
476             urlDec=new UDecoder();
477         }
478
479         urlDec.convert( tmpNameC );
480         urlDec.convert( tmpValueC );
481
482         if( debug > 0 )
483             log( tmpNameC + "= " + tmpValueC);
484         
485         addParam( tmpNameC.toString(), tmpValueC.toString() );
486         } catch( IOException JavaDoc ex ) {
487         ex.printStackTrace();
488         }
489
490         tmpNameC.recycle();
491         tmpValueC.recycle();
492
493     } while( pos<end );
494     }
495     
496     public void processParameters( MessageBytes data ) {
497         processParameters(data, encoding);
498     }
499
500     public void processParameters( MessageBytes data, String JavaDoc encoding ) {
501     if( data==null || data.isNull() || data.getLength() <= 0 ) return;
502
503     if( data.getType() == MessageBytes.T_BYTES ) {
504         ByteChunk bc=data.getByteChunk();
505         processParameters( bc.getBytes(), bc.getOffset(),
506                    bc.getLength(), encoding);
507     } else {
508         if (data.getType()!= MessageBytes.T_CHARS )
509         data.toChars();
510         CharChunk cc=data.getCharChunk();
511         processParameters( cc.getChars(), cc.getOffset(),
512                    cc.getLength());
513     }
514     }
515
516     /** Debug purpose
517      */

518     public String JavaDoc paramsAsString() {
519     StringBuffer JavaDoc sb=new StringBuffer JavaDoc();
520     Enumeration JavaDoc en= paramHashStringArray.keys();
521     while( en.hasMoreElements() ) {
522         String JavaDoc k=(String JavaDoc)en.nextElement();
523         sb.append( k ).append("=");
524         String JavaDoc v[]=(String JavaDoc[])paramHashStringArray.get( k );
525         for( int i=0; i<v.length; i++ )
526         sb.append( v[i] ).append(",");
527         sb.append("\n");
528     }
529     return sb.toString();
530     }
531
532     private static int debug=0;
533     private void log(String JavaDoc s ) {
534         if (log.isDebugEnabled())
535             log.debug("Parameters: " + s );
536     }
537    
538     // -------------------- Old code, needs rewrite --------------------
539

540     /** Used by RequestDispatcher
541      */

542     public void processParameters( String JavaDoc str ) {
543     int end=str.length();
544     int pos=0;
545     if( debug > 0)
546         log("String: " + str );
547     
548         do {
549         boolean noEq=false;
550         int valStart=-1;
551         int valEnd=-1;
552         
553         int nameStart=pos;
554         int nameEnd=str.indexOf('=', nameStart );
555         int nameEnd2=str.indexOf('&', nameStart );
556         if( nameEnd2== -1 ) nameEnd2=end;
557         if( (nameEnd2!=-1 ) &&
558         ( nameEnd==-1 || nameEnd > nameEnd2) ) {
559         nameEnd=nameEnd2;
560         noEq=true;
561         valStart=nameEnd;
562         valEnd=nameEnd;
563         if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + str.substring(nameStart, nameEnd) );
564         }
565
566         if( nameEnd== -1 ) nameEnd=end;
567
568         if( ! noEq ) {
569         valStart=nameEnd+1;
570         valEnd=str.indexOf('&', valStart);
571         if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
572         }
573         
574         pos=valEnd+1;
575         
576         if( nameEnd<=nameStart ) {
577         continue;
578         }
579         if( debug>0)
580         log( "XXX " + nameStart + " " + nameEnd + " "
581              + valStart + " " + valEnd );
582         
583         try {
584         tmpNameC.append(str, nameStart, nameEnd-nameStart );
585         tmpValueC.append(str, valStart, valEnd-valStart );
586         
587         if( debug > 0 )
588             log( tmpNameC + "= " + tmpValueC);
589
590         if( urlDec==null ) {
591             urlDec=new UDecoder();
592         }
593
594         urlDec.convert( tmpNameC );
595         urlDec.convert( tmpValueC );
596
597         if( debug > 0 )
598             log( tmpNameC + "= " + tmpValueC);
599         
600         addParam( tmpNameC.toString(), tmpValueC.toString() );
601         } catch( IOException JavaDoc ex ) {
602         ex.printStackTrace();
603         }
604
605         tmpNameC.recycle();
606         tmpValueC.recycle();
607
608     } while( pos<end );
609     }
610
611
612 }
613
Popular Tags