Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

StringUtils.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache 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.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,
  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 com.gitblit.utils;
  17. import java.io.UnsupportedEncodingException;
  18. import java.security.MessageDigest;
  19. import java.security.NoSuchAlgorithmException;
  20. import java.util.ArrayList;
  21. import java.util.Collection;
  22. import java.util.Collections;
  23. import java.util.Comparator;
  24. import java.util.List;
  25. import java.util.regex.PatternSyntaxException;
  26. /**
  27. * Utility class of string functions.
  28. *
  29. * @author James Moger
  30. *
  31. */
  32. public class StringUtils {
  33. public static final String MD5_TYPE = "MD5:";
  34. public static final String COMBINED_MD5_TYPE = "CMD5:";
  35. /**
  36. * Returns true if the string is null or empty.
  37. *
  38. * @param value
  39. * @return true if string is null or empty
  40. */
  41. public static boolean isEmpty(String value) {
  42. return value == null || value.trim().length() == 0;
  43. }
  44. /**
  45. * Replaces carriage returns and line feeds with html line breaks.
  46. *
  47. * @param string
  48. * @return plain text with html line breaks
  49. */
  50. public static String breakLinesForHtml(String string) {
  51. return string.replace("\r\n", "<br/>").replace("\r", "<br/>").replace("\n", "<br/>");
  52. }
  53. /**
  54. * Prepare text for html presentation. Replace sensitive characters with
  55. * html entities.
  56. *
  57. * @param inStr
  58. * @param changeSpace
  59. * @return plain text escaped for html
  60. */
  61. public static String escapeForHtml(String inStr, boolean changeSpace) {
  62. StringBuffer retStr = new StringBuffer();
  63. int i = 0;
  64. while (i < inStr.length()) {
  65. if (inStr.charAt(i) == '&') {
  66. retStr.append("&amp;");
  67. } else if (inStr.charAt(i) == '<') {
  68. retStr.append("&lt;");
  69. } else if (inStr.charAt(i) == '>') {
  70. retStr.append("&gt;");
  71. } else if (inStr.charAt(i) == '\"') {
  72. retStr.append("&quot;");
  73. } else if (changeSpace && inStr.charAt(i) == ' ') {
  74. retStr.append("&nbsp;");
  75. } else if (changeSpace && inStr.charAt(i) == '\t') {
  76. retStr.append(" &nbsp; &nbsp;");
  77. } else {
  78. retStr.append(inStr.charAt(i));
  79. }
  80. i++;
  81. }
  82. return retStr.toString();
  83. }
  84. /**
  85. * Decode html entities back into plain text characters.
  86. *
  87. * @param inStr
  88. * @return returns plain text from html
  89. */
  90. public static String decodeFromHtml(String inStr) {
  91. return inStr.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">")
  92. .replace("&quot;", "\"").replace("&nbsp;", " ");
  93. }
  94. /**
  95. * Encodes a url parameter by escaping troublesome characters.
  96. *
  97. * @param inStr
  98. * @return properly escaped url
  99. */
  100. public static String encodeURL(String inStr) {
  101. StringBuffer retStr = new StringBuffer();
  102. int i = 0;
  103. while (i < inStr.length()) {
  104. if (inStr.charAt(i) == '/') {
  105. retStr.append("%2F");
  106. } else if (inStr.charAt(i) == ' ') {
  107. retStr.append("%20");
  108. } else {
  109. retStr.append(inStr.charAt(i));
  110. }
  111. i++;
  112. }
  113. return retStr.toString();
  114. }
  115. /**
  116. * Flatten the list of strings into a single string with a space separator.
  117. *
  118. * @param values
  119. * @return flattened list
  120. */
  121. public static String flattenStrings(Collection<String> values) {
  122. return flattenStrings(values, " ");
  123. }
  124. /**
  125. * Flatten the list of strings into a single string with the specified
  126. * separator.
  127. *
  128. * @param values
  129. * @param separator
  130. * @return flattened list
  131. */
  132. public static String flattenStrings(Collection<String> values, String separator) {
  133. StringBuilder sb = new StringBuilder();
  134. for (String value : values) {
  135. sb.append(value).append(separator);
  136. }
  137. if (sb.length() > 0) {
  138. // truncate trailing separator
  139. sb.setLength(sb.length() - separator.length());
  140. }
  141. return sb.toString().trim();
  142. }
  143. /**
  144. * Returns a string trimmed to a maximum length with trailing ellipses. If
  145. * the string length is shorter than the max, the original string is
  146. * returned.
  147. *
  148. * @param value
  149. * @param max
  150. * @return trimmed string
  151. */
  152. public static String trimString(String value, int max) {
  153. if (value.length() <= max) {
  154. return value;
  155. }
  156. return value.substring(0, max - 3) + "...";
  157. }
  158. /**
  159. * Left pad a string with the specified character, if the string length is
  160. * less than the specified length.
  161. *
  162. * @param input
  163. * @param length
  164. * @param pad
  165. * @return left-padded string
  166. */
  167. public static String leftPad(String input, int length, char pad) {
  168. if (input.length() < length) {
  169. StringBuilder sb = new StringBuilder();
  170. for (int i = 0, len = length - input.length(); i < len; i++) {
  171. sb.append(pad);
  172. }
  173. sb.append(input);
  174. return sb.toString();
  175. }
  176. return input;
  177. }
  178. /**
  179. * Right pad a string with the specified character, if the string length is
  180. * less then the specified length.
  181. *
  182. * @param input
  183. * @param length
  184. * @param pad
  185. * @return right-padded string
  186. */
  187. public static String rightPad(String input, int length, char pad) {
  188. if (input.length() < length) {
  189. StringBuilder sb = new StringBuilder();
  190. sb.append(input);
  191. for (int i = 0, len = length - input.length(); i < len; i++) {
  192. sb.append(pad);
  193. }
  194. return sb.toString();
  195. }
  196. return input;
  197. }
  198. /**
  199. * Calculates the SHA1 of the string.
  200. *
  201. * @param text
  202. * @return sha1 of the string
  203. */
  204. public static String getSHA1(String text) {
  205. try {
  206. byte[] bytes = text.getBytes("iso-8859-1");
  207. return getSHA1(bytes);
  208. } catch (UnsupportedEncodingException u) {
  209. throw new RuntimeException(u);
  210. }
  211. }
  212. /**
  213. * Calculates the SHA1 of the byte array.
  214. *
  215. * @param bytes
  216. * @return sha1 of the byte array
  217. */
  218. public static String getSHA1(byte[] bytes) {
  219. try {
  220. MessageDigest md = MessageDigest.getInstance("SHA-1");
  221. md.update(bytes, 0, bytes.length);
  222. byte[] digest = md.digest();
  223. return toHex(digest);
  224. } catch (NoSuchAlgorithmException t) {
  225. throw new RuntimeException(t);
  226. }
  227. }
  228. /**
  229. * Calculates the MD5 of the string.
  230. *
  231. * @param string
  232. * @return md5 of the string
  233. */
  234. public static String getMD5(String string) {
  235. try {
  236. MessageDigest md = MessageDigest.getInstance("MD5");
  237. md.reset();
  238. md.update(string.getBytes("iso-8859-1"));
  239. byte[] digest = md.digest();
  240. return toHex(digest);
  241. } catch (UnsupportedEncodingException u) {
  242. throw new RuntimeException(u);
  243. } catch (NoSuchAlgorithmException t) {
  244. throw new RuntimeException(t);
  245. }
  246. }
  247. /**
  248. * Returns the hex representation of the byte array.
  249. *
  250. * @param bytes
  251. * @return byte array as hex string
  252. */
  253. private static String toHex(byte[] bytes) {
  254. StringBuilder sb = new StringBuilder(bytes.length * 2);
  255. for (int i = 0; i < bytes.length; i++) {
  256. if (((int) bytes[i] & 0xff) < 0x10) {
  257. sb.append('0');
  258. }
  259. sb.append(Long.toString((int) bytes[i] & 0xff, 16));
  260. }
  261. return sb.toString();
  262. }
  263. /**
  264. * Returns the root path of the specified path. Returns a blank string if
  265. * there is no root path.
  266. *
  267. * @param path
  268. * @return root path or blank
  269. */
  270. public static String getRootPath(String path) {
  271. if (path.indexOf('/') > -1) {
  272. return path.substring(0, path.lastIndexOf('/'));
  273. }
  274. return "";
  275. }
  276. /**
  277. * Returns the path remainder after subtracting the basePath from the
  278. * fullPath.
  279. *
  280. * @param basePath
  281. * @param fullPath
  282. * @return the relative path
  283. */
  284. public static String getRelativePath(String basePath, String fullPath) {
  285. String relativePath = fullPath.substring(basePath.length()).replace('\\', '/');
  286. if (relativePath.charAt(0) == '/') {
  287. relativePath = relativePath.substring(1);
  288. }
  289. return relativePath;
  290. }
  291. /**
  292. * Splits the space-separated string into a list of strings.
  293. *
  294. * @param value
  295. * @return list of strings
  296. */
  297. public static List<String> getStringsFromValue(String value) {
  298. return getStringsFromValue(value, " ");
  299. }
  300. /**
  301. * Splits the string into a list of string by the specified separator.
  302. *
  303. * @param value
  304. * @param separator
  305. * @return list of strings
  306. */
  307. public static List<String> getStringsFromValue(String value, String separator) {
  308. List<String> strings = new ArrayList<String>();
  309. try {
  310. String[] chunks = value.split(separator);
  311. for (String chunk : chunks) {
  312. chunk = chunk.trim();
  313. if (chunk.length() > 0) {
  314. strings.add(chunk);
  315. }
  316. }
  317. } catch (PatternSyntaxException e) {
  318. throw new RuntimeException(e);
  319. }
  320. return strings;
  321. }
  322. /**
  323. * Validates that a name is composed of letters, digits, or limited other
  324. * characters.
  325. *
  326. * @param name
  327. * @return the first invalid character found or null if string is acceptable
  328. */
  329. public static Character findInvalidCharacter(String name) {
  330. char[] validChars = { '/', '.', '_', '-' };
  331. for (char c : name.toCharArray()) {
  332. if (!Character.isLetterOrDigit(c)) {
  333. boolean ok = false;
  334. for (char vc : validChars) {
  335. ok |= c == vc;
  336. }
  337. if (!ok) {
  338. return c;
  339. }
  340. }
  341. }
  342. return null;
  343. }
  344. /**
  345. * Simple fuzzy string comparison. This is a case-insensitive check. A
  346. * single wildcard * value is supported.
  347. *
  348. * @param value
  349. * @param pattern
  350. * @return true if the value matches the pattern
  351. */
  352. public static boolean fuzzyMatch(String value, String pattern) {
  353. if (value.equalsIgnoreCase(pattern)) {
  354. return true;
  355. }
  356. if (pattern.contains("*")) {
  357. boolean prefixMatches = false;
  358. boolean suffixMatches = false;
  359. int wildcard = pattern.indexOf('*');
  360. String prefix = pattern.substring(0, wildcard).toLowerCase();
  361. prefixMatches = value.toLowerCase().startsWith(prefix);
  362. if (pattern.length() > (wildcard + 1)) {
  363. String suffix = pattern.substring(wildcard + 1).toLowerCase();
  364. suffixMatches = value.toLowerCase().endsWith(suffix);
  365. return prefixMatches && suffixMatches;
  366. }
  367. return prefixMatches || suffixMatches;
  368. }
  369. return false;
  370. }
  371. /**
  372. * Compare two repository names for proper group sorting.
  373. *
  374. * @param r1
  375. * @param r2
  376. * @return
  377. */
  378. public static int compareRepositoryNames(String r1, String r2) {
  379. // sort root repositories first, alphabetically
  380. // then sort grouped repositories, alphabetically
  381. int s1 = r1.indexOf('/');
  382. int s2 = r2.indexOf('/');
  383. if (s1 == -1 && s2 == -1) {
  384. // neither grouped
  385. return r1.compareTo(r2);
  386. } else if (s1 > -1 && s2 > -1) {
  387. // both grouped
  388. return r1.compareTo(r2);
  389. } else if (s1 == -1) {
  390. return -1;
  391. } else if (s2 == -1) {
  392. return 1;
  393. }
  394. return 0;
  395. }
  396. /**
  397. * Sort grouped repository names.
  398. *
  399. * @param list
  400. */
  401. public static void sortRepositorynames(List<String> list) {
  402. Collections.sort(list, new Comparator<String>() {
  403. @Override
  404. public int compare(String o1, String o2) {
  405. return compareRepositoryNames(o1, o2);
  406. }
  407. });
  408. }
  409. public static String getColor(String value) {
  410. int cs = 0;
  411. for (char c : getMD5(value.toLowerCase()).toCharArray()) {
  412. cs += c;
  413. }
  414. int n = (cs % 360);
  415. float hue = ((float) n) / 360;
  416. return hsvToRgb(hue, 0.90f, 0.65f);
  417. }
  418. public static String hsvToRgb(float hue, float saturation, float value) {
  419. int h = (int) (hue * 6);
  420. float f = hue * 6 - h;
  421. float p = value * (1 - saturation);
  422. float q = value * (1 - f * saturation);
  423. float t = value * (1 - (1 - f) * saturation);
  424. switch (h) {
  425. case 0:
  426. return rgbToString(value, t, p);
  427. case 1:
  428. return rgbToString(q, value, p);
  429. case 2:
  430. return rgbToString(p, value, t);
  431. case 3:
  432. return rgbToString(p, q, value);
  433. case 4:
  434. return rgbToString(t, p, value);
  435. case 5:
  436. return rgbToString(value, p, q);
  437. default:
  438. throw new RuntimeException(
  439. "Something went wrong when converting from HSV to RGB. Input was " + hue + ", "
  440. + saturation + ", " + value);
  441. }
  442. }
  443. public static String rgbToString(float r, float g, float b) {
  444. String rs = Integer.toHexString((int) (r * 256));
  445. String gs = Integer.toHexString((int) (g * 256));
  446. String bs = Integer.toHexString((int) (b * 256));
  447. return "#" + rs + gs + bs;
  448. }
  449. /**
  450. * Strips a trailing ".git" from the value.
  451. *
  452. * @param value
  453. * @return a stripped value or the original value if .git is not found
  454. */
  455. public static String stripDotGit(String value) {
  456. if (value.toLowerCase().endsWith(".git")) {
  457. return value.substring(0, value.length() - 4);
  458. }
  459. return value;
  460. }
  461. /**
  462. * Count the number of lines in a string.
  463. *
  464. * @param value
  465. * @return the line count
  466. */
  467. public static int countLines(String value) {
  468. if (isEmpty(value)) {
  469. return 0;
  470. }
  471. return value.split("\n").length;
  472. }
  473. /**
  474. * Returns the file extension of a path.
  475. *
  476. * @param path
  477. * @return a blank string or a file extension
  478. */
  479. public static String getFileExtension(String path) {
  480. int lastDot = path.lastIndexOf('.');
  481. if (lastDot > -1) {
  482. return path.substring(lastDot + 1);
  483. }
  484. return "";
  485. }
  486. /**
  487. * Replace all occurences of a substring within a string with
  488. * another string.
  489. *
  490. * From Spring StringUtils.
  491. *
  492. * @param inString String to examine
  493. * @param oldPattern String to replace
  494. * @param newPattern String to insert
  495. * @return a String with the replacements
  496. */
  497. public static String replace(String inString, String oldPattern, String newPattern) {
  498. StringBuilder sb = new StringBuilder();
  499. int pos = 0; // our position in the old string
  500. int index = inString.indexOf(oldPattern);
  501. // the index of an occurrence we've found, or -1
  502. int patLen = oldPattern.length();
  503. while (index >= 0) {
  504. sb.append(inString.substring(pos, index));
  505. sb.append(newPattern);
  506. pos = index + patLen;
  507. index = inString.indexOf(oldPattern, pos);
  508. }
  509. sb.append(inString.substring(pos));
  510. // remember to append any characters to the right of a match
  511. return sb.toString();
  512. }
  513. }