View Javadoc
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