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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. } else if (i + 1 < camelCaseString.length()
  90. && !Character.isUpperCase(camelCaseString.charAt(i + 1))) {
  91. // Word ends if next char isn't upper case
  92. return true;
  93. } else {
  94. return false;
  95. }
  96. }
  97. /**
  98. * Converts a camelCaseString to a human friendly format (Camel case
  99. * string).
  100. * <p>
  101. * In general splits words when the casing changes but also handles special
  102. * cases such as consecutive upper case characters. Examples:
  103. * <p>
  104. * {@literal MyBeanContainer} becomes {@literal My Bean Container}
  105. * {@literal AwesomeURLFactory} becomes {@literal Awesome URL Factory}
  106. * {@literal SomeUriAction} becomes {@literal Some Uri Action}
  107. *
  108. * @since 7.4
  109. * @param camelCaseString
  110. * The input string in camelCase format
  111. * @return A human friendly version of the input
  112. */
  113. public static String camelCaseToHumanFriendly(String camelCaseString) {
  114. String[] parts = splitCamelCase(camelCaseString);
  115. for (int i = 0; i < parts.length; i++) {
  116. parts[i] = capitalize(parts[i]);
  117. }
  118. return join(parts, " ");
  119. }
  120. /**
  121. * Converts an UPPER_CASE_STRING to a human friendly format (Upper Case
  122. * String).
  123. * <p>
  124. * Splits words on {@code _}. Examples:
  125. * <p>
  126. * {@literal MY_BEAN_CONTAINER} becomes {@literal My Bean Container}
  127. * {@literal AWESOME_URL_FACTORY} becomes {@literal Awesome Url Factory}
  128. * {@literal SOMETHING} becomes {@literal Something}
  129. *
  130. * @since 7.7.4
  131. * @param upperCaseUnderscoreString
  132. * The input string in UPPER_CASE_UNDERSCORE format
  133. * @return A human friendly version of the input
  134. */
  135. public static String upperCaseUnderscoreToHumanFriendly(
  136. String upperCaseUnderscoreString) {
  137. String[] parts = upperCaseUnderscoreString.replaceFirst("^_*", "")
  138. .split("_");
  139. for (int i = 0; i < parts.length; i++) {
  140. parts[i] = capitalize(parts[i].toLowerCase(Locale.ENGLISH));
  141. }
  142. return join(parts, " ");
  143. }
  144. private static boolean isAllUpperCase(String string) {
  145. for (int i = 0; i < string.length(); i++) {
  146. char c = string.charAt(i);
  147. if (!Character.isUpperCase(c) && !Character.isDigit(c)) {
  148. return false;
  149. }
  150. }
  151. return true;
  152. }
  153. /**
  154. * Joins the words in the input array together into a single string by
  155. * inserting the separator string between each word.
  156. *
  157. * @since 7.4
  158. * @param parts
  159. * The array of words
  160. * @param separator
  161. * The separator string to use between words
  162. * @return The constructed string of words and separators
  163. */
  164. public static String join(String[] parts, String separator) {
  165. if (parts.length == 0) {
  166. return "";
  167. }
  168. StringBuilder sb = new StringBuilder();
  169. for (int i = 0; i < parts.length; i++) {
  170. sb.append(parts[i]);
  171. sb.append(separator);
  172. }
  173. return sb.substring(0, sb.length() - separator.length());
  174. }
  175. /**
  176. * Capitalizes the first character in the given string in a way suitable for
  177. * use in code (methods, properties etc)
  178. *
  179. * @since 7.4
  180. * @param string
  181. * The string to capitalize
  182. * @return The capitalized string
  183. */
  184. public static String capitalize(String string) {
  185. if (string == null) {
  186. return null;
  187. }
  188. if (string.length() <= 1) {
  189. return string.toUpperCase();
  190. }
  191. return string.substring(0, 1).toUpperCase(Locale.ENGLISH)
  192. + string.substring(1);
  193. }
  194. /**
  195. * Converts a property id to a human friendly format. Handles nested
  196. * properties by only considering the last part, e.g. "address.streetName"
  197. * is equal to "streetName" for this method.
  198. *
  199. * @since 7.4
  200. * @param propertyId
  201. * The propertyId to format
  202. * @return A human friendly version of the property id
  203. */
  204. public static String propertyIdToHumanFriendly(Object propertyId) {
  205. String string = propertyId.toString();
  206. if (string.isEmpty()) {
  207. return "";
  208. }
  209. // For nested properties, only use the last part
  210. int dotLocation = string.lastIndexOf('.');
  211. if (dotLocation > 0 && dotLocation < string.length() - 1) {
  212. string = string.substring(dotLocation + 1);
  213. }
  214. if (string.matches("^[0-9A-Z_]+$")) {
  215. // Deal with UPPER_CASE_PROPERTY_IDS
  216. return upperCaseUnderscoreToHumanFriendly(string);
  217. }
  218. return camelCaseToHumanFriendly(string);
  219. }
  220. /**
  221. * Adds the get parameters to the uri and returns the new uri that contains
  222. * the parameters.
  223. *
  224. * @param uri
  225. * The uri to which the parameters should be added.
  226. * @param extraParams
  227. * One or more parameters in the format "a=b" or "c=d&amp;e=f". An
  228. * empty string is allowed but will not modify the url.
  229. * @return The modified URI with the get parameters in extraParams added.
  230. */
  231. public static String addGetParameters(String uri, String extraParams) {
  232. if (extraParams == null || extraParams.length() == 0) {
  233. return uri;
  234. }
  235. // RFC 3986: The query component is indicated by the first question
  236. // mark ("?") character and terminated by a number sign ("#") character
  237. // or by the end of the URI.
  238. String fragment = null;
  239. int hashPosition = uri.indexOf('#');
  240. if (hashPosition != -1) {
  241. // Fragment including "#"
  242. fragment = uri.substring(hashPosition);
  243. // The full uri before the fragment
  244. uri = uri.substring(0, hashPosition);
  245. }
  246. if (uri.contains("?")) {
  247. uri += "&";
  248. } else {
  249. uri += "?";
  250. }
  251. uri += extraParams;
  252. if (fragment != null) {
  253. uri += fragment;
  254. }
  255. return uri;
  256. }
  257. /**
  258. * Converts a dash ("-") separated string into camelCase.
  259. * <p>
  260. * Examples:
  261. * <p>
  262. * {@literal foo} becomes {@literal foo} {@literal foo-bar} becomes
  263. * {@literal fooBar} {@literal foo--bar} becomes {@literal fooBar}
  264. *
  265. * @since 7.5
  266. * @param dashSeparated
  267. * The dash separated string to convert
  268. * @return a camelCase version of the input string
  269. */
  270. public static String dashSeparatedToCamelCase(String dashSeparated) {
  271. if (dashSeparated == null) {
  272. return null;
  273. }
  274. String[] parts = dashSeparated.split("-");
  275. for (int i = 1; i < parts.length; i++) {
  276. parts[i] = capitalize(parts[i]);
  277. }
  278. return join(parts, "");
  279. }
  280. /**
  281. * Checks if the given array contains duplicates (according to
  282. * {@link Object#equals(Object)}.
  283. *
  284. * @param values
  285. * the array to check for duplicates
  286. * @return <code>true</code> if the array contains duplicates,
  287. * <code>false</code> otherwise
  288. */
  289. public static boolean containsDuplicates(Object[] values) {
  290. int uniqueCount = new HashSet<Object>(Arrays.asList(values)).size();
  291. return uniqueCount != values.length;
  292. }
  293. /**
  294. * Return duplicate values in the given array in the format
  295. * "duplicateValue1, duplicateValue2".
  296. *
  297. * @param values
  298. * the values to check for duplicates
  299. * @return a comma separated string of duplicates or an empty string if no
  300. * duplicates were found
  301. */
  302. public static String getDuplicates(Object[] values) {
  303. HashSet<Object> set = new HashSet<Object>();
  304. LinkedHashSet<String> duplicates = new LinkedHashSet<String>();
  305. for (Object o : values) {
  306. if (!set.add(o)) {
  307. duplicates.add(String.valueOf(o));
  308. }
  309. }
  310. return join(duplicates.toArray(new String[duplicates.size()]), ", ");
  311. }
  312. }