您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

SharedUtil.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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. private static boolean isAllUpperCase(String string) {
  142. for (int i = 0; i < string.length(); i++) {
  143. char c = string.charAt(i);
  144. if (!Character.isUpperCase(c) && !Character.isDigit(c)) {
  145. return false;
  146. }
  147. }
  148. return true;
  149. }
  150. /**
  151. * Joins the words in the input array together into a single string by
  152. * inserting the separator string between each word.
  153. *
  154. * @since 7.4
  155. * @param parts
  156. * The array of words
  157. * @param separator
  158. * The separator string to use between words
  159. * @return The constructed string of words and separators
  160. */
  161. public static String join(String[] parts, String separator) {
  162. if (parts.length == 0) {
  163. return "";
  164. }
  165. StringBuilder sb = new StringBuilder();
  166. for (int i = 0; i < parts.length; i++) {
  167. sb.append(parts[i]);
  168. sb.append(separator);
  169. }
  170. return sb.substring(0, sb.length() - separator.length());
  171. }
  172. /**
  173. * Capitalizes the first character in the given string in a way suitable for
  174. * use in code (methods, properties etc)
  175. *
  176. * @since 7.4
  177. * @param string
  178. * The string to capitalize
  179. * @return The capitalized string
  180. */
  181. public static String capitalize(String string) {
  182. if (string == null) {
  183. return null;
  184. }
  185. if (string.length() <= 1) {
  186. return string.toUpperCase(Locale.ENGLISH);
  187. }
  188. return string.substring(0, 1).toUpperCase(Locale.ENGLISH)
  189. + string.substring(1);
  190. }
  191. /**
  192. * Converts a property id to a human friendly format. Handles nested
  193. * properties by only considering the last part, e.g. "address.streetName"
  194. * is equal to "streetName" for this method.
  195. *
  196. * @since 7.4
  197. * @param propertyId
  198. * The propertyId to format
  199. * @return A human friendly version of the property id
  200. */
  201. public static String propertyIdToHumanFriendly(Object propertyId) {
  202. String string = propertyId.toString();
  203. if (string.isEmpty()) {
  204. return "";
  205. }
  206. // For nested properties, only use the last part
  207. int dotLocation = string.lastIndexOf('.');
  208. if (dotLocation > 0 && dotLocation < string.length() - 1) {
  209. string = string.substring(dotLocation + 1);
  210. }
  211. if (string.matches("^[0-9A-Z_]+$")) {
  212. // Deal with UPPER_CASE_PROPERTY_IDS
  213. return upperCaseUnderscoreToHumanFriendly(string);
  214. }
  215. return camelCaseToHumanFriendly(string);
  216. }
  217. /**
  218. * Adds the get parameters to the uri and returns the new uri that contains
  219. * the parameters.
  220. *
  221. * @param uri
  222. * The uri to which the parameters should be added.
  223. * @param extraParams
  224. * One or more parameters in the format "a=b" or "c=d&amp;e=f". An
  225. * empty string is allowed but will not modify the url.
  226. * @return The modified URI with the get parameters in extraParams added.
  227. */
  228. public static String addGetParameters(String uri, String extraParams) {
  229. if (extraParams == null || extraParams.length() == 0) {
  230. return uri;
  231. }
  232. // RFC 3986: The query component is indicated by the first question
  233. // mark ("?") character and terminated by a number sign ("#") character
  234. // or by the end of the URI.
  235. String fragment = null;
  236. int hashPosition = uri.indexOf('#');
  237. if (hashPosition != -1) {
  238. // Fragment including "#"
  239. fragment = uri.substring(hashPosition);
  240. // The full uri before the fragment
  241. uri = uri.substring(0, hashPosition);
  242. }
  243. if (uri.contains("?")) {
  244. uri += "&";
  245. } else {
  246. uri += "?";
  247. }
  248. uri += extraParams;
  249. if (fragment != null) {
  250. uri += fragment;
  251. }
  252. return uri;
  253. }
  254. /**
  255. * Converts a dash ("-") separated string into camelCase.
  256. * <p>
  257. * Examples:
  258. * <p>
  259. * {@literal foo} becomes {@literal foo} {@literal foo-bar} becomes
  260. * {@literal fooBar} {@literal foo--bar} becomes {@literal fooBar}
  261. *
  262. * @since 7.5
  263. * @param dashSeparated
  264. * The dash separated string to convert
  265. * @return a camelCase version of the input string
  266. */
  267. public static String dashSeparatedToCamelCase(String dashSeparated) {
  268. if (dashSeparated == null) {
  269. return null;
  270. }
  271. String[] parts = dashSeparated.split("-");
  272. for (int i = 1; i < parts.length; i++) {
  273. parts[i] = capitalize(parts[i]);
  274. }
  275. return join(parts, "");
  276. }
  277. /**
  278. * Checks if the given array contains duplicates (according to
  279. * {@link Object#equals(Object)}.
  280. *
  281. * @param values
  282. * the array to check for duplicates
  283. * @return <code>true</code> if the array contains duplicates,
  284. * <code>false</code> otherwise
  285. */
  286. public static boolean containsDuplicates(Object[] values) {
  287. int uniqueCount = new HashSet<Object>(Arrays.asList(values)).size();
  288. return uniqueCount != values.length;
  289. }
  290. /**
  291. * Return duplicate values in the given array in the format
  292. * "duplicateValue1, duplicateValue2".
  293. *
  294. * @param values
  295. * the values to check for duplicates
  296. * @return a comma separated string of duplicates or an empty string if no
  297. * duplicates were found
  298. */
  299. public static String getDuplicates(Object[] values) {
  300. HashSet<Object> set = new HashSet<Object>();
  301. LinkedHashSet<String> duplicates = new LinkedHashSet<String>();
  302. for (Object o : values) {
  303. if (!set.add(o)) {
  304. duplicates.add(String.valueOf(o));
  305. }
  306. }
  307. return join(duplicates.toArray(new String[duplicates.size()]), ", ");
  308. }
  309. }