1 // This file is part of OpenTSDB.
2 // Copyright (C) 2012  The OpenTSDB Authors.
3 //
4 // This program is free software: you can redistribute it and/or modify it
5 // under the terms of the GNU Lesser General Public License as published by
6 // the Free Software Foundation, either version 2.1 of the License, or (at your
7 // option) any later version.  This program is distributed in the hope that it
8 // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
10 // General Public License for more details.  You should have received a copy
11 // of the GNU Lesser General Public License along with this program.  If not,
12 // see <http://www.gnu.org/licenses/>.
13 package tsd.client;
14 
15 // I (tsuna) originally wrote this code for Netty.  Surprisingly, GWT has
16 // nothing to manually parse query string parameters...
17 
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 
21 /**
22  * Splits an HTTP query string into a path string and key-value parameter pairs.
23  */
24 public final class QueryString extends HashMap<String, ArrayList<String>> {
25 
26   /**
27    * Returns the decoded key-value parameter pairs of the URI.
28    */
decode(final String s)29   public static QueryString decode(final String s) {
30     final QueryString params = new QueryString();
31     String name = null;
32     int pos = 0; // Beginning of the unprocessed region
33     int i;       // End of the unprocessed region
34     for (i = 0; i < s.length(); i++) {
35       final char c = s.charAt(i);
36       if (c == '=' && name == null) {
37         if (pos != i) {
38           name = s.substring(pos, i);
39         }
40         pos = i + 1;
41       } else if (c == '&') {
42         if (name == null && pos != i) {
43           // We haven't seen an `=' so far but moved forward.
44           // Must be a param of the form '&a&' so add it with
45           // an empty value.
46           params.add(s.substring(pos, i), "");
47         } else if (name != null) {
48           params.add(name, s.substring(pos, i));
49           name = null;
50         }
51         pos = i + 1;
52       }
53     }
54 
55     if (pos != i) {  // Are there characters we haven't dealt with?
56       if (name == null) {     // Yes and we haven't seen any `='.
57         params.add(s.substring(pos, i), "");
58       } else {                // Yes and this must be the last value.
59         params.add(name, s.substring(pos, i));
60       }
61     } else if (name != null) {  // Have we seen a name without value?
62       params.add(name, "");
63     }
64 
65     return params;
66   }
67 
68   /**
69    * Adds a query string element.
70    * @param name The name of the element.
71    * @param value The value of the element.
72    */
add(final String name, final String value)73   public void add(final String name, final String value) {
74     ArrayList<String> values = super.get(name);
75     if (values == null) {
76       values = new ArrayList<String>(1);  // Often there's only 1 value.
77       super.put(name, values);
78     }
79     values.add(value);
80   }
81 
82   /**
83    * Returns the first value for the given key, or {@code null}.
84    */
getFirst(final String key)85   public String getFirst(final String key) {
86     final ArrayList<String> values = super.get(key);
87     return values == null ? null : values.get(0);
88   }
89 
90 }
91