You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SharedUtil.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.shared.util;
  17. import java.io.Serializable;
  18. import java.util.Arrays;
  19. import java.util.HashSet;
  20. import java.util.LinkedHashSet;
  21. import java.util.Locale;
  22. /**
  23. * Misc internal utility methods used by both the server and the client package.
  24. *
  25. * @author Vaadin Ltd
  26. * @since 7.1
  27. *
  28. */
  29. public class SharedUtil implements Serializable {
  30. /**
  31. * Checks if a and b are equals using {@link #equals(Object)}. Handles null
  32. * values as well. Does not ensure that objects are of the same type.
  33. * Assumes that the first object's equals method handle equals properly.
  34. *
  35. * @param o1
  36. * The first value to compare
  37. * @param o2
  38. * The second value to compare
  39. * @return true if the objects are equal, false otherwise
  40. */
  41. public static boolean equals(Object o1, Object o2) {
  42. if (o1 == null) {
  43. return o2 == null;
  44. }
  45. return o1.equals(o2);
  46. }
  47. /**
  48. * Trims trailing slashes (if any) from a string.
  49. *
  50. * @param value
  51. * The string value to be trimmed. Cannot be null.
  52. * @return String value without trailing slashes.
  53. */
  54. public static String trimTrailingSlashes(String value) {
  55. return value.replaceAll("/*$", "");
  56. }
  57. /**
  58. * RegEx pattern to extract the width/height values.
  59. */
  60. public static final String SIZE_PATTERN = "^(-?\\d*(?:\\.\\d+)?)(%|px|em|rem|ex|in|cm|mm|pt|pc)?$";
  61. /**
  62. * Splits a camelCaseString into an array of words with the casing
  63. * preserved.
  64. *
  65. * @since 7.4
  66. * @param camelCaseString
  67. * The input string in camelCase format
  68. * @return An array with one entry per word in the input string
  69. */
  70. public static String[] splitCamelCase(String camelCaseString) {
  71. StringBuilder sb = new StringBuilder();
  72. for (int i = 0; i < camelCaseString.length(); i++) {
  73. char c = camelCaseString.charAt(i);
  74. if (Character.isUpperCase(c)
  75. && isWordComplete(camelCaseString, i)) {
  76. sb.append(' ');
  77. }
  78. sb.append(c);
  79. }
  80. return sb.toString().split(" ");
  81. }
  82. private static boolean isWordComplete(String camelCaseString, int i) {
  83. if (i == 0) {
  84. // Word can't end at the beginning
  85. return false;
  86. } else if (!Character.isUpperCase(camelCaseString.charAt(i - 1))) {
  87. // Word ends if previous char wasn't upper case
  88. return true;
  89. }
  90. // Word ends if next char isn't upper case
  91. return i + 1 < camelCaseString.length()
  92. && !Character.isUpperCase(camelCaseString.charAt(i + 1));
  93. }
  94. /**
  95. * Converts a camelCaseString to a human friendly format (Camel case
  96. * string).
  97. * <p>
  98. * In general splits words when the casing changes but also handles special
  99. * cases such as consecutive upper case characters. Examples:
  100. * <p>
  101. * {@literal MyBeanContainer} becomes {@literal My Bean Container}
  102. * {@literal AwesomeURLFactory} becomes {@literal Awesome URL Factory}
  103. * {@literal SomeUriAction} becomes {@literal Some Uri Action}
  104. *
  105. * @since 7.4
  106. * @param camelCaseString
  107. * The input string in camelCase format
  108. * @return A human friendly version of the input
  109. */
  110. public static String camelCaseToHumanFriendly(String camelCaseString) {
  111. String[] parts = splitCamelCase(camelCaseString);
  112. for (int i = 0; i < parts.length; i++) {
  113. parts[i] = capitalize(parts[i]);
  114. }
  115. return join(parts, " ");
  116. }
  117. /**
  118. * Converts an UPPER_CASE_STRING to a human friendly format (Upper Case
  119. * String).
  120. * <p>
  121. * Splits words on {@code _}. Examples:
  122. * <p>
  123. * {@literal MY_BEAN_CONTAINER} becomes {@literal My Bean Container}
  124. * {@literal AWESOME_URL_FACTORY} becomes {@literal Awesome Url Factory}
  125. * {@literal SOMETHING} becomes {@literal Something}
  126. *
  127. * @since 7.7.4
  128. * @param upperCaseUnderscoreString
  129. * The input string in UPPER_CASE_UNDERSCORE format
  130. * @return A human friendly version of the input
  131. */
  132. public static String upperCaseUnderscoreToHumanFriendly(
  133. String upperCaseUnderscoreString) {
  134. String[] parts = upperCaseUnderscoreString.replaceFirst("^_*", "")
  135. .split("_");
  136. for (int i = 0; i < parts.length; i++) {
  137. parts[i] = capitalize(parts[i].toLowerCase(Locale.ENGLISH));
  138. }
  139. return join(parts, " ");
  140. }
  141. /**
  142. * Joins the words in the input array together into a single string by
  143. * inserting the separator string between each word.
  144. *
  145. * @since 7.4
  146. * @param parts
  147. * The array of words
  148. * @param separator
  149. * The separator string to use between words
  150. * @return The constructed string of words and separators
  151. */
  152. public static String join(String[] parts, String separator) {
  153. if (parts.length == 0) {
  154. return "";
  155. }
  156. StringBuilder sb = new StringBuilder();
  157. for (int i = 0; i < parts.length; i++) {
  158. sb.append(parts[i]);
  159. sb.append(separator);
  160. }
  161. return sb.substring(0, sb.length() - separator.length());
  162. }
  163. /**
  164. * Capitalizes the first character in the given string in a way suitable for
  165. * use in code (methods, properties etc)
  166. *
  167. * @since 7.4
  168. * @param string
  169. * The string to capitalize
  170. * @return The capitalized string
  171. */
  172. public static String capitalize(String string) {
  173. if (string == null) {
  174. return null;
  175. }
  176. if (string.length() <= 1) {
  177. return string.toUpperCase(Locale.ENGLISH);
  178. }
  179. return string.substring(0, 1).toUpperCase(Locale.ENGLISH)
  180. + string.substring(1);
  181. }
  182. /**
  183. * Converts a property id to a human friendly format. Handles nested
  184. * properties by only considering the last part, e.g. "address.streetName"
  185. * is equal to "streetName" for this method.
  186. *
  187. * @since 7.4
  188. * @param propertyId
  189. * The propertyId to format
  190. * @return A human friendly version of the property id
  191. */
  192. public static String propertyIdToHumanFriendly(Object propertyId) {
  193. String string = propertyId.toString();
  194. if (string.isEmpty()) {
  195. return "";
  196. }
  197. // For nested properties, only use the last part
  198. int dotLocation = string.lastIndexOf('.');
  199. if (dotLocation > 0 && dotLocation < string.length() - 1) {
  200. string = string.substring(dotLocation + 1);
  201. }
  202. if (string.matches("^[0-9A-Z_]+$")) {
  203. // Deal with UPPER_CASE_PROPERTY_IDS
  204. return upperCaseUnderscoreToHumanFriendly(string);
  205. }
  206. return camelCaseToHumanFriendly(string);
  207. }
  208. /**
  209. * Adds the get parameters to the uri and returns the new uri that contains
  210. * the parameters.
  211. *
  212. * @param uri
  213. * The uri to which the parameters should be added.
  214. * @param extraParams
  215. * One or more parameters in the format "a=b" or "c=d&amp;e=f". An
  216. * empty string is allowed but will not modify the url.
  217. * @return The modified URI with the get parameters in extraParams added.
  218. */
  219. public static String addGetParameters(String uri, String extraParams) {
  220. if (extraParams == null || extraParams.isEmpty()) {
  221. return uri;
  222. }
  223. // RFC 3986: The query component is indicated by the first question
  224. // mark ("?") character and terminated by a number sign ("#") character
  225. // or by the end of the URI.
  226. String fragment = null;
  227. int hashPosition = uri.indexOf('#');
  228. if (hashPosition != -1) {
  229. // Fragment including "#"
  230. fragment = uri.substring(hashPosition);
  231. // The full uri before the fragment
  232. uri = uri.substring(0, hashPosition);
  233. }
  234. if (uri.contains("?")) {
  235. uri += "&";
  236. } else {
  237. uri += "?";
  238. }
  239. uri += extraParams;
  240. if (fragment != null) {
  241. uri += fragment;
  242. }
  243. return uri;
  244. }
  245. /**
  246. * Converts a dash ("-") separated string into camelCase.
  247. * <p>
  248. * Examples:
  249. * <p>
  250. * {@literal foo} becomes {@literal foo} {@literal foo-bar} becomes
  251. * {@literal fooBar} {@literal foo--bar} becomes {@literal fooBar}
  252. *
  253. * @since 7.5
  254. * @param dashSeparated
  255. * The dash separated string to convert
  256. * @return a camelCase version of the input string
  257. */
  258. public static String dashSeparatedToCamelCase(String dashSeparated) {
  259. if (dashSeparated == null) {
  260. return null;
  261. }
  262. String[] parts = dashSeparated.split("-");
  263. for (int i = 1; i < parts.length; i++) {
  264. parts[i] = capitalize(parts[i]);
  265. }
  266. return join(parts, "");
  267. }
  268. /**
  269. * Checks if the given array contains duplicates (according to
  270. * {@link Object#equals(Object)}.
  271. *
  272. * @param values
  273. * the array to check for duplicates
  274. * @return <code>true</code> if the array contains duplicates,
  275. * <code>false</code> otherwise
  276. */
  277. public static boolean containsDuplicates(Object[] values) {
  278. int uniqueCount = new HashSet<Object>(Arrays.asList(values)).size();
  279. return uniqueCount != values.length;
  280. }
  281. /**
  282. * Return duplicate values in the given array in the format
  283. * "duplicateValue1, duplicateValue2".
  284. *
  285. * @param values
  286. * the values to check for duplicates
  287. * @return a comma separated string of duplicates or an empty string if no
  288. * duplicates were found
  289. */
  290. public static String getDuplicates(Object[] values) {
  291. HashSet<Object> set = new HashSet<Object>();
  292. LinkedHashSet<String> duplicates = new LinkedHashSet<String>();
  293. for (Object o : values) {
  294. if (!set.add(o)) {
  295. duplicates.add(String.valueOf(o));
  296. }
  297. }
  298. return join(duplicates.toArray(new String[duplicates.size()]), ", ");
  299. }
  300. }