1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package com.foxinmy.weixin4j.http;
29
30 import com.foxinmy.weixin4j.util.CharArrayBuffer;
31 import com.foxinmy.weixin4j.util.NameValue;
32
33 /**
34 * Basic implementation for formatting header value elements.
35 * Instances of this class are stateless and thread-safe.
36 * Derived classes are expected to maintain these properties.
37 *
38 * @since 4.0
39 */
40 public class HeaderValueFormatter {
41
42 public final static HeaderValueFormatter INSTANCE = new HeaderValueFormatter();
43
44 /**
45 * Special characters that can be used as separators in HTTP parameters.
46 * These special characters MUST be in a quoted string to be used within
47 * a parameter value .
48 */
49 public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
50
51 /**
52 * Unsafe special characters that must be escaped using the backslash
53 * character
54 */
55 public final static String UNSAFE_CHARS = "\"\\";
56
57 public HeaderValueFormatter() {
58 super();
59 }
60
61 /**
62 * Formats a set of parameters.
63 *
64 * @param nvps the parameters to format
65 * @param quote <code>true</code> to always format with quoted values,
66 * <code>false</code> to use quotes only when necessary
67 * @param formatter the formatter to use, or <code>null</code>
68 * for the {@link #INSTANCE default}
69 *
70 * @return the formatted parameters
71 */
72 public static
73 String formatParameters(final NameValue[] nvps,
74 final boolean quote,
75 final HeaderValueFormatter formatter) {
76 return (formatter != null ? formatter : HeaderValueFormatter.INSTANCE)
77 .formatParameters(null, nvps, quote).toString();
78 }
79
80
81 // non-javadoc, see interface HeaderValueFormatter
82 public CharArrayBuffer formatParameters(final CharArrayBuffer charBuffer,
83 final NameValue[] nvps,
84 final boolean quote) {
85 final int len = estimateParametersLen(nvps);
86 CharArrayBuffer buffer = charBuffer;
87 if (buffer == null) {
88 buffer = new CharArrayBuffer(len);
89 } else {
90 buffer.ensureCapacity(len);
91 }
92
93 for (int i = 0; i < nvps.length; i++) {
94 if (i > 0) {
95 buffer.append("; ");
96 }
97 formatNameValuePair(buffer, nvps[i], quote);
98 }
99
100 return buffer;
101 }
102
103
104 /**
105 * Estimates the length of formatted parameters.
106 *
107 * @param nvps the parameters to format, or <code>null</code>
108 *
109 * @return a length estimate, in number of characters
110 */
111 protected int estimateParametersLen(final NameValue[] nvps) {
112 if ((nvps == null) || (nvps.length < 1)) {
113 return 0;
114 }
115
116 int result = (nvps.length-1) * 2; // "; " between the parameters
117 for (final NameValue nvp : nvps) {
118 result += estimateNameValuePairLen(nvp);
119 }
120
121 return result;
122 }
123
124
125 /**
126 * Formats a name-value pair.
127 *
128 * @param nvp the name-value pair to format
129 * @param quote <code>true</code> to always format with a quoted value,
130 * <code>false</code> to use quotes only when necessary
131 * @param formatter the formatter to use, or <code>null</code>
132 * for the {@link #INSTANCE default}
133 *
134 * @return the formatted name-value pair
135 */
136 public static
137 String formatNameValuePair(final NameValue nvp,
138 final boolean quote,
139 final HeaderValueFormatter formatter) {
140 return (formatter != null ? formatter : HeaderValueFormatter.INSTANCE)
141 .formatNameValuePair(null, nvp, quote).toString();
142 }
143
144
145 // non-javadoc, see interface HeaderValueFormatter
146 public CharArrayBuffer formatNameValuePair(final CharArrayBuffer charBuffer,
147 final NameValue nvp,
148 final boolean quote) {
149 final int len = estimateNameValuePairLen(nvp);
150 CharArrayBuffer buffer = charBuffer;
151 if (buffer == null) {
152 buffer = new CharArrayBuffer(len);
153 } else {
154 buffer.ensureCapacity(len);
155 }
156
157 buffer.append(nvp.getName());
158 final String value = nvp.getValue();
159 if (value != null) {
160 buffer.append('=');
161 doFormatValue(buffer, value, quote);
162 }
163
164 return buffer;
165 }
166
167
168 /**
169 * Estimates the length of a formatted name-value pair.
170 *
171 * @param nvp the name-value pair to format, or <code>null</code>
172 *
173 * @return a length estimate, in number of characters
174 */
175 protected int estimateNameValuePairLen(final NameValue nvp) {
176 if (nvp == null) {
177 return 0;
178 }
179
180 int result = nvp.getName().length(); // name
181 final String value = nvp.getValue();
182 if (value != null) {
183 // assume quotes, but no escaped characters
184 result += 3 + value.length(); // ="value"
185 }
186 return result;
187 }
188
189
190 /**
191 * Actually formats the value of a name-value pair.
192 * This does not include a leading = character.
193 * Called from {@link #formatNameValuePair formatNameValuePair}.
194 *
195 * @param buffer the buffer to append to, never <code>null</code>
196 * @param value the value to append, never <code>null</code>
197 * @param quote <code>true</code> to always format with quotes,
198 * <code>false</code> to use quotes only when necessary
199 */
200 protected void doFormatValue(final CharArrayBuffer buffer,
201 final String value,
202 final boolean quote) {
203
204 boolean quoteFlag = quote;
205 if (!quoteFlag) {
206 for (int i = 0; (i < value.length()) && !quoteFlag; i++) {
207 quoteFlag = isSeparator(value.charAt(i));
208 }
209 }
210
211 if (quoteFlag) {
212 buffer.append('"');
213 }
214 for (int i = 0; i < value.length(); i++) {
215 final char ch = value.charAt(i);
216 if (isUnsafe(ch)) {
217 buffer.append('\\');
218 }
219 buffer.append(ch);
220 }
221 if (quoteFlag) {
222 buffer.append('"');
223 }
224 }
225
226
227 /**
228 * Checks whether a character is a {@link #SEPARATORS separator}.
229 *
230 * @param ch the character to check
231 *
232 * @return <code>true</code> if the character is a separator,
233 * <code>false</code> otherwise
234 */
235 protected boolean isSeparator(final char ch) {
236 return SEPARATORS.indexOf(ch) >= 0;
237 }
238
239
240 /**
241 * Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
242 *
243 * @param ch the character to check
244 *
245 * @return <code>true</code> if the character is unsafe,
246 * <code>false</code> otherwise
247 */
248 protected boolean isUnsafe(final char ch) {
249 return UNSAFE_CHARS.indexOf(ch) >= 0;
250 }
251
252
253 } // class BasicHeaderValueFormatter