1 /*
2 * Copyright 2012 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package org.jboss.netty.handler.codec.http;
17
18 import java.io.UnsupportedEncodingException;
19 import java.net.URI;
20 import java.net.URISyntaxException;
21 import java.net.URLEncoder;
22 import java.nio.charset.Charset;
23 import java.nio.charset.UnsupportedCharsetException;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 /**
28 * Creates an URL-encoded URI from a path string and key-value parameter pairs.
29 * This encoder is for one time use only. Create a new instance for each URI.
30 *
31 * <pre>
32 * {@link QueryStringEncoder} encoder = new {@link QueryStringEncoder}("/hello");
33 * encoder.addParam("recipient", "world");
34 * assert encoder.toString().equals("/hello?recipient=world");
35 * </pre>
36 * @see QueryStringDecoder
37 *
38 * @apiviz.stereotype utility
39 * @apiviz.has org.jboss.netty.handler.codec.http.HttpRequest oneway - - encodes
40 */
41 public class QueryStringEncoder {
42
43 private final Charset charset;
44 private final String uri;
45 private final List<Param> params = new ArrayList<Param>();
46
47 /**
48 * Creates a new encoder that encodes a URI that starts with the specified
49 * path string. The encoder will encode the URI in UTF-8.
50 */
51 public QueryStringEncoder(String uri) {
52 this(uri, HttpConstants.DEFAULT_CHARSET);
53 }
54
55 /**
56 * Creates a new encoder that encodes a URI that starts with the specified
57 * path string in the specified charset.
58 */
59 public QueryStringEncoder(String uri, Charset charset) {
60 if (uri == null) {
61 throw new NullPointerException("uri");
62 }
63 if (charset == null) {
64 throw new NullPointerException("charset");
65 }
66
67 this.uri = uri;
68 this.charset = charset;
69 }
70
71 /**
72 * @deprecated Use {@link #QueryStringEncoder(String, Charset)} instead.
73 */
74 @Deprecated
75 public QueryStringEncoder(String uri, String charset) {
76 this(uri, Charset.forName(charset));
77 }
78
79 /**
80 * Adds a parameter with the specified name and value to this encoder.
81 */
82 public void addParam(String name, String value) {
83 if (name == null) {
84 throw new NullPointerException("name");
85 }
86 if (value == null) {
87 throw new NullPointerException("value");
88 }
89 params.add(new Param(name, value));
90 }
91
92 /**
93 * Returns the URL-encoded URI object which was created from the path string
94 * specified in the constructor and the parameters added by
95 * {@link #addParam(String, String)} method.
96 */
97 public URI toUri() throws URISyntaxException {
98 return new URI(toString());
99 }
100
101 /**
102 * Returns the URL-encoded URI which was created from the path string
103 * specified in the constructor and the parameters added by
104 * {@link #addParam(String, String)} method.
105 */
106 @Override
107 public String toString() {
108 if (params.isEmpty()) {
109 return uri;
110 } else {
111 StringBuilder sb = new StringBuilder(uri).append('?');
112 for (int i = 0; i < params.size(); i++) {
113 Param param = params.get(i);
114 sb.append(encodeComponent(param.name, charset));
115 sb.append('=');
116 sb.append(encodeComponent(param.value, charset));
117 if (i != params.size() - 1) {
118 sb.append('&');
119 }
120 }
121 return sb.toString();
122 }
123 }
124
125 private static String encodeComponent(String s, Charset charset) {
126 try {
127 return URLEncoder.encode(s, charset.name()).replaceAll("\\+", "%20");
128 } catch (UnsupportedEncodingException e) {
129 throw new UnsupportedCharsetException(charset.name());
130 }
131 }
132
133 private static final class Param {
134
135 final String name;
136 final String value;
137
138 Param(String name, String value) {
139 this.value = value;
140 this.name = name;
141 }
142 }
143 }