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 }