KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > httpclient > HeaderElement


1 /*
2  * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HeaderElement.java,v 1.18.2.1 2004/02/22 18:21:13 olegk Exp $
3  * $Revision: 1.18.2.1 $
4  * $Date: 2004/02/22 18:21:13 $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2004 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  * ====================================================================
22  *
23  * This software consists of voluntary contributions made by many
24  * individuals on behalf of the Apache Software Foundation. For more
25  * information on the Apache Software Foundation, please see
26  * <http://www.apache.org/>.
27  *
28  * [Additional notices, if required by prior licensing conditions]
29  *
30  */

31
32 package org.apache.commons.httpclient;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 import java.util.BitSet JavaDoc;
38 import java.util.NoSuchElementException JavaDoc;
39 import java.util.StringTokenizer JavaDoc;
40 import java.util.Vector JavaDoc;
41
42
43 /**
44  * <p>One element of an HTTP header's value.</p>
45  * <p>
46  * Some HTTP headers (such as the set-cookie header) have values that
47  * can be decomposed into multiple elements. Such headers must be in the
48  * following form:
49  * </p>
50  * <pre>
51  * header = [ element ] *( "," [ element ] )
52  * element = name [ "=" [ value ] ] *( ";" [ param ] )
53  * param = name [ "=" [ value ] ]
54  *
55  * name = token
56  * value = ( token | quoted-string )
57  *
58  * token = 1*&lt;any char except "=", ",", ";", &lt;"&gt; and
59  * white space&gt;
60  * quoted-string = &lt;"&gt; *( text | quoted-char ) &lt;"&gt;
61  * text = any char except &lt;"&gt;
62  * quoted-char = "\" char
63  * </pre>
64  * <p>
65  * Any amount of white space is allowed between any part of the
66  * header, element or param and is ignored. A missing value in any
67  * element or param will be stored as the empty {@link String};
68  * if the "=" is also missing <var>null</var> will be stored instead.
69  * </p>
70  * <p>
71  * This class represents an individual header element, containing
72  * both a name/value pair (value may be <tt>null</tt>) and optionally
73  * a set of additional parameters.
74  * </p>
75  * <p>
76  * This class also exposes a {@link #parse} method for parsing a
77  * {@link Header} value into an array of elements.
78  * </p>
79  *
80  * @see Header
81  *
82  * @author <a HREF="mailto:bcholmes@interlog.com">B.C. Holmes</a>
83  * @author <a HREF="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
84  * @author <a HREF="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
85  *
86  * @since 1.0
87  * @version $Revision: 1.18.2.1 $ $Date: 2004/02/22 18:21:13 $
88  */

89 public class HeaderElement extends NameValuePair {
90
91     // ----------------------------------------------------------- Constructors
92

93     /**
94      * Default constructor.
95      */

96     public HeaderElement() {
97         this(null, null, null);
98     }
99
100     /**
101       * Constructor.
102       * @param name my name
103       * @param value my (possibly <tt>null</tt>) value
104       */

105     public HeaderElement(String JavaDoc name, String JavaDoc value) {
106         this(name, value, null);
107     }
108
109     /**
110      * Constructor with name, value and parameters.
111      *
112      * @param name my name
113      * @param value my (possibly <tt>null</tt>) value
114      * @param parameters my (possibly <tt>null</tt>) parameters
115      */

116     public HeaderElement(String JavaDoc name, String JavaDoc value,
117             NameValuePair[] parameters) {
118         super(name, value);
119         setParameters(parameters);
120     }
121
122     // -------------------------------------------------------- Constants
123

124     /** Log object for this class. */
125     private static final Log LOG = LogFactory.getLog(HeaderElement.class);
126
127     /**
128      * Map of numeric values to whether or not the
129      * corresponding character is a "separator
130      * character" (tspecial).
131      */

132     private static final BitSet JavaDoc SEPARATORS = new BitSet JavaDoc(128);
133
134     /**
135      * Map of numeric values to whether or not the
136      * corresponding character is a "token
137      * character".
138      */

139     private static final BitSet JavaDoc TOKEN_CHAR = new BitSet JavaDoc(128);
140
141     /**
142      * Map of numeric values to whether or not the
143      * corresponding character is an "unsafe
144      * character".
145      */

146     private static final BitSet JavaDoc UNSAFE_CHAR = new BitSet JavaDoc(128);
147
148     /**
149      * Static initializer for {@link #SEPARATORS},
150      * {@link #TOKEN_CHAR}, and {@link #UNSAFE_CHAR}.
151      */

152     static {
153         // rfc-2068 tspecial
154
SEPARATORS.set('(');
155         SEPARATORS.set(')');
156         SEPARATORS.set('<');
157         SEPARATORS.set('>');
158         SEPARATORS.set('@');
159         SEPARATORS.set(',');
160         SEPARATORS.set(';');
161         SEPARATORS.set(':');
162         SEPARATORS.set('\\');
163         SEPARATORS.set('"');
164         SEPARATORS.set('/');
165         SEPARATORS.set('[');
166         SEPARATORS.set(']');
167         SEPARATORS.set('?');
168         SEPARATORS.set('=');
169         SEPARATORS.set('{');
170         SEPARATORS.set('}');
171         SEPARATORS.set(' ');
172         SEPARATORS.set('\t');
173
174         // rfc-2068 token
175
for (int ch = 32; ch < 127; ch++) {
176             TOKEN_CHAR.set(ch);
177         }
178         TOKEN_CHAR.xor(SEPARATORS);
179
180         // rfc-1738 unsafe characters, including CTL and SP, and excluding
181
// "#" and "%"
182
for (int ch = 0; ch < 32; ch++) {
183             UNSAFE_CHAR.set(ch);
184         }
185         UNSAFE_CHAR.set(' ');
186         UNSAFE_CHAR.set('<');
187         UNSAFE_CHAR.set('>');
188         UNSAFE_CHAR.set('"');
189         UNSAFE_CHAR.set('{');
190         UNSAFE_CHAR.set('}');
191         UNSAFE_CHAR.set('|');
192         UNSAFE_CHAR.set('\\');
193         UNSAFE_CHAR.set('^');
194         UNSAFE_CHAR.set('~');
195         UNSAFE_CHAR.set('[');
196         UNSAFE_CHAR.set(']');
197         UNSAFE_CHAR.set('`');
198         UNSAFE_CHAR.set(127);
199     }
200
201     // ----------------------------------------------------- Instance Variables
202

203     /** My parameters, if any. */
204     private NameValuePair[] parameters = null;
205
206     // ------------------------------------------------------------- Properties
207

208     /**
209      * Get parameters, if any.
210      *
211      * @since 2.0
212      * @return parameters as an array of {@link NameValuePair}s
213      */

214     public NameValuePair[] getParameters() {
215         return this.parameters;
216     }
217
218     /**
219      *
220      * @param pairs The new parameters. May be null.
221      */

222     protected void setParameters(final NameValuePair[] pairs) {
223         parameters = pairs;
224     }
225     // --------------------------------------------------------- Public Methods
226

227     /**
228      * This parses the value part of a header. The result is an array of
229      * HeaderElement objects.
230      *
231      * @param headerValue the string representation of the header value
232      * (as received from the web server).
233      * @return the header elements containing <code>Header</code> elements.
234      * @throws HttpException if the above syntax rules are violated.
235      */

236     public static final HeaderElement[] parse(String JavaDoc headerValue)
237         throws HttpException {
238             
239         LOG.trace("enter HeaderElement.parse(String)");
240
241         if (headerValue == null) {
242             return null;
243         }
244         
245         Vector JavaDoc elements = new Vector JavaDoc();
246         StringTokenizer JavaDoc tokenizer =
247             new StringTokenizer JavaDoc(headerValue.trim(), ",");
248
249         while (tokenizer.countTokens() > 0) {
250             String JavaDoc nextToken = tokenizer.nextToken();
251
252             // FIXME: refactor into private method named ?
253
// careful... there may have been a comma in a quoted string
254
try {
255                 while (HeaderElement.hasOddNumberOfQuotationMarks(nextToken)) {
256                     nextToken += "," + tokenizer.nextToken();
257                 }
258             } catch (NoSuchElementException JavaDoc exception) {
259                 throw new HttpException(
260                     "Bad header format: wrong number of quotation marks");
261             }
262
263             // FIXME: Refactor out into a private method named ?
264
try {
265                 /*
266                  * Following to RFC 2109 and 2965, in order not to conflict
267                  * with the next header element, make it sure to parse tokens.
268                  * the expires date format is "Wdy, DD-Mon-YY HH:MM:SS GMT".
269                  * Notice that there is always comma(',') sign.
270                  * For the general cases, rfc1123-date, rfc850-date.
271                  */

272                 if (tokenizer.hasMoreTokens()) {
273                     String JavaDoc s = nextToken.toLowerCase();
274                     if (s.endsWith("mon")
275                         || s.endsWith("tue")
276                         || s.endsWith("wed")
277                         || s.endsWith("thu")
278                         || s.endsWith("fri")
279                         || s.endsWith("sat")
280                         || s.endsWith("sun")
281                         || s.endsWith("monday")
282                         || s.endsWith("tuesday")
283                         || s.endsWith("wednesday")
284                         || s.endsWith("thursday")
285                         || s.endsWith("friday")
286                         || s.endsWith("saturday")
287                         || s.endsWith("sunday")) {
288
289                         nextToken += "," + tokenizer.nextToken();
290                     }
291                 }
292             } catch (NoSuchElementException JavaDoc exception) {
293                 throw new HttpException
294                     ("Bad header format: parsing with wrong header elements");
295             }
296
297             String JavaDoc tmp = nextToken.trim();
298             if (!tmp.endsWith(";")) {
299                 tmp += ";";
300             }
301             char[] header = tmp.toCharArray();
302
303             // FIXME: refactor into a private method named? parseElement?
304
boolean inAString = false;
305             int startPos = 0;
306             HeaderElement element = new HeaderElement();
307             Vector JavaDoc paramlist = new Vector JavaDoc();
308             for (int i = 0 ; i < header.length ; i++) {
309                 if (header[i] == ';' && !inAString) {
310                     NameValuePair pair = parsePair(header, startPos, i);
311                     if (pair == null) {
312                         throw new HttpException(
313                             "Bad header format: empty name/value pair in"
314                             + nextToken);
315
316                     // the first name/value pair are handled differently
317
} else if (startPos == 0) {
318                         element.setName(pair.getName());
319                         element.setValue(pair.getValue());
320                     } else {
321                         paramlist.addElement(pair);
322                     }
323                     startPos = i + 1;
324                 } else if (header[i] == '"'
325                     && !(inAString && i > 0 && header[i - 1] == '\\')) {
326                     inAString = !inAString;
327                 }
328             }
329
330             // now let's add all the parameters into the header element
331
if (paramlist.size() > 0) {
332                 NameValuePair[] tmp2 = new NameValuePair[paramlist.size()];
333                 paramlist.copyInto((NameValuePair[]) tmp2);
334                 element.setParameters (tmp2);
335                 paramlist.removeAllElements();
336             }
337
338             // and save the header element into the list of header elements
339
elements.addElement(element);
340         }
341
342         HeaderElement[] headerElements = new HeaderElement[elements.size()];
343         elements.copyInto((HeaderElement[]) headerElements);
344         return headerElements;
345     }
346
347     /**
348      * Return <tt>true</tt> if <i>string</i> has
349      * an odd number of <tt>"</tt> characters.
350      *
351      * @param string the string to test
352      * @return true if there are an odd number of quotation marks, false
353      * otherwise
354      */

355     private static final boolean hasOddNumberOfQuotationMarks(String JavaDoc string) {
356         boolean odd = false;
357         int start = -1;
358         while ((start = string.indexOf('"', start + 1)) != -1) {
359             odd = !odd;
360         }
361         return odd;
362     }
363
364     /**
365      * Parse a header character array into a {@link NameValuePair}
366      *
367      * @param header the character array to parse
368      * @param start the starting position of the text within the array
369      * @param end the end position of the text within the array
370      * @return a {@link NameValuePair} representing the header
371      */

372     private static final NameValuePair parsePair(char[] header,
373         int start, int end) {
374             
375         LOG.trace("enter HeaderElement.parsePair(char[], int, int)");
376
377         NameValuePair pair = null;
378         String JavaDoc name = new String JavaDoc(header, start, end - start).trim();
379         String JavaDoc value = null;
380
381         //TODO: This would certainly benefit from a StringBuffer
382
int index = name.indexOf("=");
383         if (index >= 0) {
384             if ((index + 1) < name.length()) {
385                 value = name.substring(index + 1).trim();
386                 // strip quotation marks
387
if (value.startsWith("\"") && value.endsWith("\"")) {
388                     value = value.substring(1, value.length() - 1);
389                 }
390             }
391             name = name.substring(0, index).trim();
392         }
393
394         pair = new NameValuePair(name, value);
395
396         return pair;
397     }
398
399
400     /**
401      * Returns parameter with the given name, if found. Otherwise null
402      * is returned
403      *
404      * @param name The name to search by.
405      * @return NameValuePair parameter with the given name
406      */

407
408     public NameValuePair getParameterByName(String JavaDoc name) {
409         if (name == null) {
410             throw new NullPointerException JavaDoc("Name is null");
411         }
412         NameValuePair found = null;
413         NameValuePair parameters[] = getParameters();
414         if (parameters != null) {
415             for (int i = 0; i < parameters.length; i++) {
416                 NameValuePair current = parameters[ i ];
417                 if (current.getName().equalsIgnoreCase(name)) {
418                     found = current;
419                     break;
420                 }
421             }
422         }
423         return found;
424     }
425
426
427 }
428
429
Popular Tags