Source code: org/apache/http/NameValuePair.java
1 /*
2 * $HeadURL: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpcore/tags/4.0-alpha2/src/java/org/apache/http/NameValuePair.java $
3 * $Revision: 376961 $
4 * $Date: 2006-02-11 11:32:50 +0100 (Sat, 11 Feb 2006) $
5 *
6 * ====================================================================
7 *
8 * Copyright 1999-2006 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 */
29
30 package org.apache.http;
31
32 import java.util.ArrayList;
33 import java.util.List;
34
35 import org.apache.http.io.CharArrayBuffer;
36 import org.apache.http.protocol.HTTP;
37 import org.apache.http.util.LangUtils;
38
39 /**
40 * A simple class encapsulating an attribute/value pair.
41 * <p>
42 * This class comforms to the generic grammar and formatting rules outlined in the
43 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2">Section 2.2</a>
44 * and
45 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>
46 * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
47 * </p>
48 * <h>2.2 Basic Rules</h>
49 * <p>
50 * The following rules are used throughout this specification to describe basic parsing constructs.
51 * The US-ASCII coded character set is defined by ANSI X3.4-1986.
52 * </p>
53 * <pre>
54 * OCTET = <any 8-bit sequence of data>
55 * CHAR = <any US-ASCII character (octets 0 - 127)>
56 * UPALPHA = <any US-ASCII uppercase letter "A".."Z">
57 * LOALPHA = <any US-ASCII lowercase letter "a".."z">
58 * ALPHA = UPALPHA | LOALPHA
59 * DIGIT = <any US-ASCII digit "0".."9">
60 * CTL = <any US-ASCII control character
61 * (octets 0 - 31) and DEL (127)>
62 * CR = <US-ASCII CR, carriage return (13)>
63 * LF = <US-ASCII LF, linefeed (10)>
64 * SP = <US-ASCII SP, space (32)>
65 * HT = <US-ASCII HT, horizontal-tab (9)>
66 * <"> = <US-ASCII double-quote mark (34)>
67 * </pre>
68 * <p>
69 * Many HTTP/1.1 header field values consist of words separated by LWS or special
70 * characters. These special characters MUST be in a quoted string to be used within
71 * a parameter value (as defined in section 3.6).
72 * <p>
73 * <pre>
74 * token = 1*<any CHAR except CTLs or separators>
75 * separators = "(" | ")" | "<" | ">" | "@"
76 * | "," | ";" | ":" | "\" | <">
77 * | "/" | "[" | "]" | "?" | "="
78 * | "{" | "}" | SP | HT
79 * </pre>
80 * <p>
81 * A string of text is parsed as a single word if it is quoted using double-quote marks.
82 * </p>
83 * <pre>
84 * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
85 * qdtext = <any TEXT except <">>
86 * </pre>
87 * <p>
88 * The backslash character ("\") MAY be used as a single-character quoting mechanism only
89 * within quoted-string and comment constructs.
90 * </p>
91 * <pre>
92 * quoted-pair = "\" CHAR
93 * </pre>
94 * <h>3.6 Transfer Codings</h>
95 * <p>
96 * Parameters are in the form of attribute/value pairs.
97 * </p>
98 * <pre>
99 * parameter = attribute "=" value
100 * attribute = token
101 * value = token | quoted-string
102 * </pre>
103 *
104 * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
105 *
106 */
107 public class NameValuePair {
108
109 private final String name;
110 private final String value;
111
112 /**
113 * Default Constructor taking a name and a value. The value may be null.
114 *
115 * @param name The name.
116 * @param value The value.
117 */
118 public NameValuePair(final String name, final String value) {
119 super();
120 if (name == null) {
121 throw new IllegalArgumentException("Name may not be null");
122 }
123 this.name = name;
124 this.value = value;
125 }
126
127 /**
128 * Returns the name.
129 *
130 * @return String name The name
131 */
132 public String getName() {
133 return this.name;
134 }
135
136 /**
137 * Returns the value.
138 *
139 * @return String value The current value.
140 */
141 public String getValue() {
142 return this.value;
143 }
144
145 public static final NameValuePair[] parseAll(
146 final CharArrayBuffer buffer, final int indexFrom, final int indexTo) {
147 if (buffer == null) {
148 throw new IllegalArgumentException("Char array buffer may not be null");
149 }
150 if (indexFrom < 0) {
151 throw new IndexOutOfBoundsException();
152 }
153 if (indexTo > buffer.length()) {
154 throw new IndexOutOfBoundsException();
155 }
156 if (indexFrom > indexTo) {
157 throw new IndexOutOfBoundsException();
158 }
159 List params = new ArrayList();
160 int cur = indexFrom;
161 int from = indexFrom;
162 boolean qouted = false;
163 boolean escaped = false;
164 while (cur < indexTo) {
165 char ch = buffer.charAt(cur);
166 if (ch == '"' && !escaped) {
167 qouted = !qouted;
168 }
169 NameValuePair param = null;
170 if (!qouted && ch == ';') {
171 param = parse(buffer, from, cur);
172 from = cur + 1;
173 } else if (cur == indexTo - 1) {
174 param = parse(buffer, from, indexTo);
175 }
176 if (param != null && !(param.getName().equals("") && param.getValue() == null)) {
177 params.add(param);
178 }
179 if (escaped) {
180 escaped = false;
181 } else {
182 escaped = qouted && ch == '\\';
183 }
184 cur++;
185 }
186 return (NameValuePair[]) params.toArray(new NameValuePair[params.size()]);
187 }
188
189 public static final NameValuePair[] parseAll(final String s) {
190 if (s == null) {
191 throw new IllegalArgumentException("String may not be null");
192 }
193 CharArrayBuffer buffer = new CharArrayBuffer(s.length());
194 buffer.append(s);
195 return parseAll(buffer, 0, buffer.length());
196 }
197
198 public static NameValuePair parse(
199 final CharArrayBuffer buffer, final int indexFrom, final int indexTo) {
200 if (buffer == null) {
201 throw new IllegalArgumentException("Char array buffer may not be null");
202 }
203 if (indexFrom < 0) {
204 throw new IndexOutOfBoundsException();
205 }
206 if (indexTo > buffer.length()) {
207 throw new IndexOutOfBoundsException();
208 }
209 if (indexFrom > indexTo) {
210 throw new IndexOutOfBoundsException();
211 }
212 int eq = buffer.indexOf('=', indexFrom, indexTo);
213 if (eq < 0) {
214 return new NameValuePair(buffer.substringTrimmed(indexFrom, indexTo), null);
215 }
216 String name = buffer.substringTrimmed(indexFrom, eq);
217 int i1 = eq + 1;
218 int i2 = indexTo;
219 // Trim leading white spaces
220 while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
221 i1++;
222 }
223 // Trim trailing white spaces
224 while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) {
225 i2--;
226 }
227 // Strip away quotes if necessary
228 if (((i2 - i1) >= 2)
229 && (buffer.charAt(i1) == '"')
230 && (buffer.charAt(i2 - 1) == '"')) {
231 i1++;
232 i2--;
233 }
234 String value = buffer.substring(i1, i2);
235 return new NameValuePair(name, value);
236 }
237
238 public static final NameValuePair parse(final String s) {
239 if (s == null) {
240 throw new IllegalArgumentException("String may not be null");
241 }
242 CharArrayBuffer buffer = new CharArrayBuffer(s.length());
243 buffer.append(s);
244 return parse(buffer, 0, buffer.length());
245 }
246
247 /**
248 * Special characters that can be used as separators in HTTP parameters.
249 * These special characters MUST be in a quoted string to be used within
250 * a parameter value
251 */
252 private static final char[] SEPARATORS = {
253 '(', ')', '<', '>', '@',
254 ',', ';', ':', '\\', '"',
255 '/', '[', ']', '?', '=',
256 '{', '}', ' ', '\t'
257 };
258
259 /**
260 * Unsafe special characters that must be escaped using the backslash
261 * character
262 */
263 private static final char[] UNSAFE_CHARS = {
264 '"', '\\'
265 };
266
267 private static boolean isOneOf(char[] chars, char ch) {
268 for (int i = 0; i < chars.length; i++) {
269 if (ch == chars[i]) {
270 return true;
271 }
272 }
273 return false;
274 }
275
276 private static boolean isUnsafeChar(char ch) {
277 return isOneOf(UNSAFE_CHARS, ch);
278 }
279
280 private static boolean isSeparator(char ch) {
281 return isOneOf(SEPARATORS, ch);
282 }
283
284 private static void format(
285 final CharArrayBuffer buffer,
286 final String value,
287 boolean alwaysUseQuotes) {
288 boolean unsafe = false;
289 if (alwaysUseQuotes) {
290 unsafe = true;
291 } else {
292 for (int i = 0; i < value.length(); i++) {
293 if (isSeparator(value.charAt(i))) {
294 unsafe = true;
295 break;
296 }
297 }
298 }
299 if (unsafe) buffer.append('"');
300 for (int i = 0; i < value.length(); i++) {
301 char ch = value.charAt(i);
302 if (isUnsafeChar(ch)) {
303 buffer.append('\\');
304 }
305 buffer.append(ch);
306 }
307 if (unsafe) buffer.append('"');
308 }
309
310 /**
311 * Produces textual representaion of the attribute/value pair using
312 * formatting rules defined in RFC 2616
313 *
314 * @param buffer output buffer
315 * @param param the parameter to be formatted
316 * @param alwaysUseQuotes <tt>true</tt> if the parameter values must
317 * always be enclosed in quotation marks, <tt>false</tt> otherwise
318 */
319 public static void format(
320 final CharArrayBuffer buffer,
321 final NameValuePair param,
322 boolean alwaysUseQuotes) {
323 if (buffer == null) {
324 throw new IllegalArgumentException("String buffer may not be null");
325 }
326 if (param == null) {
327 throw new IllegalArgumentException("Parameter may not be null");
328 }
329 buffer.append(param.getName());
330 String value = param.getValue();
331 if (value != null) {
332 buffer.append("=");
333 format(buffer, value, alwaysUseQuotes);
334 }
335 }
336
337 /**
338 * Produces textual representaion of the attribute/value pairs using
339 * formatting rules defined in RFC 2616
340 *
341 * @param buffer output buffer
342 * @param params the parameters to be formatted
343 * @param alwaysUseQuotes <tt>true</tt> if the parameter values must
344 * always be enclosed in quotation marks, <tt>false</tt> otherwise
345 */
346 public static void formatAll(
347 final CharArrayBuffer buffer,
348 final NameValuePair[] params,
349 boolean alwaysUseQuotes) {
350 if (buffer == null) {
351 throw new IllegalArgumentException("String buffer may not be null");
352 }
353 if (params == null) {
354 throw new IllegalArgumentException("Array of parameter may not be null");
355 }
356 for (int i = 0; i < params.length; i++) {
357 if (i > 0) {
358 buffer.append("; ");
359 }
360 format(buffer, params[i], alwaysUseQuotes);
361 }
362 }
363
364 /**
365 * Produces textual representaion of the attribute/value pair using
366 * formatting rules defined in RFC 2616
367 *
368 * @param param the parameter to be formatted
369 * @param alwaysUseQuotes <tt>true</tt> if the parameter values must
370 * always be enclosed in quotation marks, <tt>false</tt> otherwise
371 *
372 * @return RFC 2616 conformant textual representaion of the
373 * attribute/value pair
374 */
375 public static String format(final NameValuePair param, boolean alwaysUseQuotes) {
376 CharArrayBuffer buffer = new CharArrayBuffer(16);
377 format(buffer, param, alwaysUseQuotes);
378 return buffer.toString();
379 }
380
381 /**
382 * Produces textual representaion of the attribute/value pair using
383 * formatting rules defined in RFC 2616
384 *
385 * @param params the parameters to be formatted
386 * @param alwaysUseQuotes <tt>true</tt> if the parameter values must
387 * always be enclosed in quotation marks, <tt>false</tt> otherwise
388 *
389 * @return RFC 2616 conformant textual representaion of the
390 * attribute/value pair
391 */
392 public static String formatAll(final NameValuePair[] params, boolean alwaysUseQuotes) {
393 CharArrayBuffer buffer = new CharArrayBuffer(16);
394 formatAll(buffer, params, alwaysUseQuotes);
395 return buffer.toString();
396 }
397
398 /**
399 * Get a string representation of this pair.
400 *
401 * @return A string representation.
402 */
403 public String toString() {
404 CharArrayBuffer buffer = new CharArrayBuffer(16);
405 buffer.append(this.name);
406 if (this.value != null) {
407 buffer.append("=");
408 format(buffer, this.value, false);
409 }
410 return buffer.toString();
411 }
412
413 public boolean equals(final Object object) {
414 if (object == null) return false;
415 if (this == object) return true;
416 if (object instanceof NameValuePair) {
417 NameValuePair that = (NameValuePair) object;
418 return this.name.equals(that.name)
419 && LangUtils.equals(this.value, that.value);
420 } else {
421 return false;
422 }
423 }
424
425 public int hashCode() {
426 int hash = LangUtils.HASH_SEED;
427 hash = LangUtils.hashCode(hash, this.name);
428 hash = LangUtils.hashCode(hash, this.value);
429 return hash;
430 }
431
432 }