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