View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.util;
23  
24  import java.io.UnsupportedEncodingException;
25  import java.net.URLEncoder;
26  
27  import javax.faces.context.FacesContext;
28  import javax.servlet.http.HttpServletRequest;
29  
30  import org.primefaces.extensions.converter.JsonConverter;
31  import org.primefaces.extensions.converter.JsonExposeAwareConverter;
32  
33  /**
34   * Builder for request parameters.
35   *
36   * @author Oleg Varaksin
37   * @since 1.1.0
38   */
39  public class RequestParameterBuilder {
40  
41      private StringBuilder buffer;
42      private final String originalUrl;
43      private final JsonConverter jsonConverter;
44      private String encoding;
45      private boolean added;
46  
47      /**
48       * Creates a builder instance. This constructor is useful when we only use encode() and encodeJson() methods.
49       */
50      public RequestParameterBuilder() {
51          this(null);
52      }
53  
54      /**
55       * Creates a builder instance wihout URL or with the current request URL.
56       *
57       * @param useCurrentRequest boolean flag if the current request URL should be used or not
58       */
59      public RequestParameterBuilder(final boolean useCurrentRequest) {
60          this(useCurrentRequest
61                      ? ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext()
62                                  .getRequest()).getRequestURL()
63                                  .toString()
64                      : null);
65      }
66  
67      /**
68       * Creates a builder instance by the given URL.
69       *
70       * @param url URL
71       */
72      public RequestParameterBuilder(final String url) {
73          this(url, null);
74      }
75  
76      /**
77       * Creates a builder instance by the given URL.
78       *
79       * @param url URL
80       * @param jsonConverter specific JsonConverter. It should extends {@link JsonConverter} from PrimeFaces Extensions. If null, the default implementation
81       *            {@link JsonExposeAwareConverter} is used.
82       */
83      public RequestParameterBuilder(final String url, final JsonConverter jsonConverter) {
84          buffer = new StringBuilder(url != null ? url : org.primefaces.util.Constants.EMPTY_STRING);
85          originalUrl = url != null ? url : org.primefaces.util.Constants.EMPTY_STRING;
86  
87          this.jsonConverter = jsonConverter == null ? new JsonExposeAwareConverter(false) : jsonConverter;
88  
89          encoding = FacesContext.getCurrentInstance().getExternalContext().getRequestCharacterEncoding();
90          if (encoding == null) {
91              encoding = "UTF-8";
92          }
93      }
94  
95      /**
96       * Adds a request parameter to the URL without specifying a data type of the given parameter value. Parameter's value is converted to JSON notation when
97       * adding. Furthermore, it will be encoded according to the acquired encoding.
98       *
99       * @param name name of the request parameter
100      * @param value value of the request parameter
101      * @return RequestParameterBuilder updated this instance which can be reused
102      * @throws UnsupportedEncodingException DOCUMENT_ME
103      */
104     public RequestParameterBuilder paramJson(final String name, final Object value)
105                 throws UnsupportedEncodingException {
106         return paramJson(name, value, null);
107     }
108 
109     /**
110      * Adds a request parameter to the URL with specifying a data type of the given parameter value. Data type is sometimes required, especially for Java
111      * generic types, because type information is erased at runtime and the conversion to JSON will not work properly. Parameter's value is converted to JSON
112      * notation when adding. Furthermore, it will be encoded according to the acquired encoding.
113      *
114      * @param name name of the request parameter
115      * @param value value of the request parameter
116      * @param type data type of the value object. Any primitive type, array, non generic or generic type is supported. Data type is sometimes required to
117      *            convert a value to a JSON representation. All data types should be fully qualified. Examples: "boolean" "int" "long[]" "java.lang.String"
118      *            "java.util.Date" "java.util.Collection<java.lang.Integer>" "java.util.Map<java.lang.String, com.durr.FooPair<java.lang.Integer,
119      *            java.util.Date>>" "com.durr.FooNonGenericClass" "com.durr.FooGenericClass<java.lang.String, java.lang.Integer>"
120      *            "com.durr.FooGenericClass<int[], com.durr.FooGenericClass<com.durr.FooNonGenericClass, java.lang.Boolean>>".
121      * @return RequestParameterBuilder updated this instance which can be reused
122      * @throws UnsupportedEncodingException DOCUMENT_ME
123      */
124     public RequestParameterBuilder paramJson(final String name, final Object value, final String type)
125                 throws UnsupportedEncodingException {
126         final String encodedJsonValue = encodeJson(value, type);
127 
128         if (added || originalUrl.contains("?")) {
129             buffer.append("&");
130         }
131         else {
132             buffer.append("?");
133         }
134 
135         buffer.append(name);
136         buffer.append("=");
137         buffer.append(encodedJsonValue);
138 
139         // set a flag that at least one request parameter was added
140         added = true;
141 
142         return this;
143     }
144 
145     /**
146      * Adds a request parameter to the URL. This is a convenient method for primitive, plain data types. Parameter's value will not be converted to JSON
147      * notation when adding. It will be only encoded according to the acquired encoding. Note: null values will not be added.
148      *
149      * @param name name of the request parameter
150      * @param value value of the request parameter
151      * @return RequestParameterBuilder updated this instance which can be reused
152      * @throws UnsupportedEncodingException DOCUMENT_ME
153      */
154     public RequestParameterBuilder param(final String name, final Object value) throws UnsupportedEncodingException {
155         final String encodedValue = encode(value);
156 
157         if (encodedValue == null) {
158             return this;
159         }
160 
161         if (added || originalUrl.contains("?")) {
162             buffer.append("&");
163         }
164         else {
165             buffer.append("?");
166         }
167 
168         buffer.append(name);
169         buffer.append("=");
170         buffer.append(encodedValue);
171 
172         // set a flag that at least one request parameter was added
173         added = true;
174 
175         return this;
176     }
177 
178     /**
179      * Encodes given value with a proper encoding. This is a convenient method for primitive, plain data types. Value will not be converted to JSON. Note: Value
180      * can be null.
181      *
182      * @param value value to be encoded
183      * @return String encoded value
184      * @throws UnsupportedEncodingException DOCUMENT_ME
185      */
186     public String encode(final Object value) throws UnsupportedEncodingException {
187         if (value == null) {
188             return null;
189         }
190 
191         return URLEncoder.encode(value.toString(), encoding);
192     }
193 
194     /**
195      * Convertes give value to JSON and encodes the converted value with a proper encoding. Data type is sometimes required, especially for Java generic types,
196      * because type information is erased at runtime and the conversion to JSON will not work properly.
197      *
198      * @param value value to be converted and encoded
199      * @param type data type of the value object. Any primitive type, array, non generic or generic type is supported. Data type is sometimes required to
200      *            convert a value to a JSON representation. All data types should be fully qualified. Examples: "boolean" "int" "long[]" "java.lang.String"
201      *            "java.util.Date" "java.util.Collection<java.lang.Integer>" "java.util.Map<java.lang.String, com.durr.FooPair<java.lang.Integer,
202      *            java.util.Date>>" "com.durr.FooNonGenericClass" "com.durr.FooGenericClass<java.lang.String, java.lang.Integer>"
203      *            "com.durr.FooGenericClass<int[], com.durr.FooGenericClass<com.durr.FooNonGenericClass, java.lang.Boolean>>".
204      * @return String converted and encoded value
205      * @throws UnsupportedEncodingException DOCUMENT_ME
206      */
207     public String encodeJson(final Object value, final String type) throws UnsupportedEncodingException {
208         jsonConverter.setType(type);
209 
210         final String jsonValue;
211         if (value == null) {
212             jsonValue = "null";
213         }
214         else {
215             jsonValue = jsonConverter.getAsString(null, null, value);
216         }
217 
218         return URLEncoder.encode(jsonValue, encoding);
219     }
220 
221     /**
222      * Convertes give value to JSON without to know the data type and encodes the converted value with a proper encoding.
223      *
224      * @param value value to be converted and encoded
225      * @return String converted and encoded value
226      * @throws UnsupportedEncodingException DOCUMENT_ME
227      */
228     public String encodeJson(final Object value) throws UnsupportedEncodingException {
229         return encodeJson(value, null);
230     }
231 
232     /**
233      * Builds the end result.
234      *
235      * @return String end result
236      */
237     public String build() {
238         return buffer.toString();
239     }
240 
241     /**
242      * Resets the internal state in order to be reused.
243      *
244      * @return RequestParameterBuilder reseted builder
245      */
246     public RequestParameterBuilder reset() {
247         buffer = new StringBuilder(originalUrl);
248         jsonConverter.setType(null);
249         added = false;
250 
251         return this;
252     }
253 }