View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
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,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.util;
17  
18  import org.apache.commons.lang.StringEscapeUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.util.type.TypeUtils;
21  
22  import java.beans.PropertyDescriptor;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  /**
29   * Utility class for generating JavaScript
30   *
31   * @author Kuali Rice Team (rice.collab@kuali.org)
32   */
33  public class ScriptUtils {
34  
35      /**
36       * Translates an Object to a String for representing the given Object as
37       * a JavaScript value
38       *
39       * <p>
40       * Handles null, List, Map, and Set collections, along with non quoting for numeric and
41       * boolean types. Complex types are treated as a String value using toString
42       * </p>
43       *
44       * @param value - Object instance to translate
45       * @return String JS value
46       */
47      public static String translateValue(Object value) {
48          String jsValue = "";
49  
50          if (value == null) {
51              jsValue = "null";
52              return jsValue;
53          }
54  
55          if (value instanceof List) {
56              jsValue = "[";
57  
58              List<Object> list = (List<Object>) value;
59              for (Object listItem : list) {
60                  jsValue += translateValue(listItem);
61                  jsValue += ",";
62              }
63              jsValue = StringUtils.removeEnd(jsValue, ",");
64  
65              jsValue += "]";
66          } else if (value instanceof Set) {
67              jsValue = "[";
68  
69              Set<Object> set = (Set<Object>) value;
70              for (Object setItem : set) {
71                  jsValue += translateValue(setItem);
72                  jsValue += ",";
73              }
74              jsValue = StringUtils.removeEnd(jsValue, ",");
75  
76              jsValue += "]";
77          } else if (value instanceof Map) {
78              jsValue = "{";
79  
80              Map<Object, Object> map = (Map<Object, Object>) value;
81              for (Map.Entry<Object, Object> mapEntry : map.entrySet()) {
82                  jsValue += mapEntry.getKey().toString() + ":";
83                  jsValue += translateValue(mapEntry.getValue());
84                  jsValue += ",";
85              }
86              jsValue = StringUtils.removeEnd(jsValue, ",");
87  
88              jsValue += "}";
89          } else {
90              Class<?> valueClass = value.getClass();
91              if (TypeUtils.isSimpleType(valueClass) || TypeUtils.isClassClass(valueClass)) {
92                  boolean quoteValue = true;
93  
94                  if (TypeUtils.isBooleanClass(valueClass) ||
95                          TypeUtils.isDecimalClass(valueClass) ||
96                          TypeUtils.isIntegralClass(valueClass)) {
97                      quoteValue = false;
98                  }
99  
100                 if (quoteValue) {
101                     jsValue = "\"";
102                 }
103 
104                 // TODO: should this go through property editors?
105                 jsValue += value.toString();
106 
107                 if (quoteValue) {
108                     jsValue += "\"";
109                 }
110             } else {
111                 // treat as data object
112                 jsValue = "{";
113 
114                 PropertyDescriptor[] propertyDescriptors = ObjectPropertyUtils.getPropertyDescriptors(value);
115                 for (int i = 0; i < propertyDescriptors.length; i++) {
116                     PropertyDescriptor descriptor = propertyDescriptors[i];
117                     if ((descriptor.getReadMethod() != null) && !"class".equals(descriptor.getName())) {
118                         Object propertyValue = ObjectPropertyUtils.getPropertyValue(value, descriptor.getName());
119 
120                         jsValue += descriptor.getName() + ":";
121                         jsValue += translateValue(propertyValue);
122                         jsValue += ",";
123                     }
124                 }
125                 jsValue = StringUtils.removeEnd(jsValue, ",");
126 
127                 jsValue += "}";
128             }
129         }
130 
131         return jsValue;
132     }
133 
134     /**
135      * Builds a JSON string form the given map
136      *
137      * @param map - map to translate
138      * @return String in JSON format
139      */
140     public static String toJSON(Map<String, String> map) {
141         StringBuffer sb = new StringBuffer("{");
142 
143         for (String key : map.keySet()) {
144             String optionValue = map.get(key);
145             if (sb.length() > 1) {
146                 sb.append(",");
147             }
148             sb.append("\"" + key + "\"");
149 
150             sb.append(":");
151             sb.append("\"" + escapeJSONString(optionValue) + "\"");
152         }
153         sb.append("}");
154 
155         return sb.toString();
156     }
157 
158     /**
159      * Escapes double quotes present in the given string
160      *
161      * @param jsonString - string to escape
162      * @return String escaped string
163      */
164     public static String escapeJSONString(String jsonString) {
165         if (jsonString != null) {
166             jsonString = jsonString.replace("\"", "&quot;");
167         }
168 
169         return jsonString;
170     }
171 
172     /**
173      * Convert a string to a javascript value - especially for use for options used to initialize widgets such as the
174      * tree and rich table
175      *
176      * @param value - the string to be converted
177      * @return - the converted value
178      */
179     public static String convertToJsValue(String value) {
180         boolean isNumber = false;
181 
182         // save input value to preserve any whitespace formatting
183         String originalValue = value;
184 
185         // remove whitespace for correct string matching
186         value = StringUtils.strip(value);
187         if (StringUtils.isNotBlank(value) && (StringUtils.isNumeric(value.substring(0, 1)) || value.substring(0, 1)
188                 .equals("-"))) {
189             try {
190                 Double.parseDouble(value);
191                 isNumber = true;
192             } catch (NumberFormatException e) {
193                 isNumber = false;
194             }
195         }
196 
197         // If an option value starts with { or [, it would be a nested value
198         // and it should not use quotes around it
199         if (StringUtils.startsWith(value, "{") || StringUtils.startsWith(value, "[")) {
200             return originalValue;
201         }
202         // need to be the base boolean value "false" is true in js - a non
203         // empty string
204         else if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("true")) {
205             return originalValue;
206         }
207         // if it is a call back function, do not add the quotes
208         else if (StringUtils.startsWith(value, "function") && StringUtils.endsWith(value, "}")) {
209             return originalValue;
210         }
211         // for numerics
212         else if (isNumber) {
213             return originalValue;
214         } else {
215             // use single quotes since hidden scripts are placed in the value attribute which surrounds the script with double quotes
216             return "'" + originalValue + "'";
217         }
218     }
219 
220     /**
221      * Escapes the ' character present in collection names so it can be properly used in js without causing
222      * javascript errors due to an early completion of a ' string.
223      *
224      * @param name
225      * @return
226      */
227     public static String escapeName(String name) {
228         name = name.replace("'", "\\'");
229         return name;
230     }
231 
232     /**
233      * Converts a list of string to a valid js string array
234      *
235      * @param list - list of Strings to be converted
236      * @return String representing the js array
237      */
238     public static String convertStringListToJsArray(List<String> list) {
239         String array = "[";
240 
241         if (list != null) {
242             for (String s : list) {
243                 array = array + "'" + s + "',";
244             }
245             array = StringUtils.removeEnd(array, ",");
246         }
247         array = array + "]";
248 
249         return array;
250     }
251 
252     /**
253      * escapes a string using {@link org.apache.commons.lang.StringEscapeUtils#escapeHtml(String)}
254      *
255      * <p>The apostrophe character is included as <code>StringEscapeUtils#escapeHtml(String)</code>
256      * does not consider it a legal entity. </p>
257      *
258      * @param string - the string to be escaped
259      * @return - the escaped string - useful for embedding in server side generated JS scripts
260      */
261     public static String escapeHtml(String string) {
262         if (string == null) {
263             return null;
264         } else {
265             return StringEscapeUtils.escapeHtml(string).replace("'", "&apos;");
266         }
267     }
268 
269     /**
270      * escape an array of strings
271      *
272      * @param strings - an array of strings to escape
273      * @return - the array, with the strings escaped
274      */
275     public static List<String> escapeHtml(List<String> strings) {
276         if (strings == null) {
277             return null;
278         } else if (strings.isEmpty()) {
279             return strings;
280         } else {
281             List<String> result = new ArrayList<String>(strings.size());
282             for (String string : strings) {
283                 result.add(escapeHtml(string));
284             }
285             return result;
286         }
287     }
288 
289     /**
290      * Will append the second script parameter to the first if the first is not empty, also checks to see if the
291      * first script needs an end semi-colon added
292      *
293      * @param script - script that will be added to (null is allowed and converted to empty string)
294      * @param appendScript - script to append
295      * @return String result of appending the two script parameters
296      */
297     public static String appendScript(String script, String appendScript) {
298         String appendedScript = script;
299         if (appendedScript == null) {
300             appendedScript = "";
301         } else if (StringUtils.isNotBlank(appendedScript) && !appendedScript.trim().endsWith(";")) {
302             appendedScript += "; ";
303         }
304 
305         appendedScript += appendScript;
306 
307         return appendedScript;
308     }
309 
310     /**
311      * Helper method to build a JS string that will invoke the given function with the given arguments
312      * 
313      * @param functionName name of the JS function to invoke
314      * @param arguments zero or more arguments to pass, each will be converted to the corresponding JS type
315      * @return JS String for invoking the function
316      */
317     public static String buildFunctionCall(String functionName, Object... arguments) {
318         StringBuffer sb = new StringBuffer(functionName).append("(");
319 
320         if (arguments != null) {
321             List<String> jsArguments = new ArrayList<String>();
322             for (Object argument : arguments) {
323                 jsArguments.add(translateValue(argument));
324             }
325 
326             sb.append(StringUtils.join(jsArguments, ","));
327         }
328 
329         sb.append(");");
330 
331         return sb.toString();
332     }
333 }