View Javadoc

1   package net.sf.jpkgmk.util;
2   
3   import java.io.UnsupportedEncodingException;
4   import java.nio.charset.Charset;
5   import java.util.Random;
6   
7   import net.sf.jpkgmk.ParseException;
8   import net.sf.jpkgmk.prototype.PrototypeParser;
9   
10  import org.apache.commons.logging.Log;
11  import org.apache.commons.logging.LogFactory;
12  
13  /**
14   * @author gommma (gommma AT users.sourceforge.net)
15   * @author Last changed by: $Author: gommma $
16   * @version $Revision: 2 $ $Date: 2008-08-20 21:14:19 +0200 (Mi, 20 Aug 2008) $
17   * @since 1.0
18   */
19  public class StringUtil 
20  {
21      /**
22       * Enumeration of supported truncation modes.
23       */
24      public static class TruncationMode {
25          /** 
26           * The mode that describes that a string is truncated at the beginning
27           * and the end string is returned. 
28           */
29          public static final TruncationMode START = new TruncationMode();
30          /** 
31           * The mode that describes that a string is truncated at the end
32           * and the start string is returned. 
33           */
34          public static final TruncationMode END = new TruncationMode();
35  
36      	private TruncationMode() {
37      	}
38      };
39  
40      /** The string literal for the UTF-8 character set */
41      public static final String UTF8_ENCODING = "UTF-8";
42  
43      /** Default encoding/charset */
44      public static final String DEFAULT_SYSTEM_CHARSET = Charset.defaultCharset().name();
45  
46  	/**
47  	 * All hexadecimal characters as bytes
48  	 */
49  	static final byte[] HEX_CHAR_TABLE = {
50  		(byte) '0', (byte) '1', (byte) '2',
51  		(byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
52  		(byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c',
53  		(byte) 'd', (byte) 'e', (byte) 'f' };
54  
55  	private static Log log = LogFactory.getLog(StringUtil.class);
56  	
57  	
58  	
59  	private StringUtil() {
60  		
61  	}
62  	
63  
64  	
65  	public static String getHexString(byte[] raw) throws UnsupportedEncodingException 
66  	{
67  	    byte[] hex = new byte[2 * raw.length];
68  	    int index = 0;
69  
70  	    for (int i=0; i<raw.length; i++) {
71  	    	byte b = raw[i];
72  	    	int v = b & 0xFF;
73  	    	hex[index++] = HEX_CHAR_TABLE[v >>> 4];
74  	    	hex[index++] = HEX_CHAR_TABLE[v & 0xF];
75  	    }
76  	    return new String(hex, "ASCII");
77  	}
78  
79  	
80  	/**
81  	 * @param string
82  	 * @return Returns true if the given string is null or an empty string
83  	 */
84  	public static final boolean isNullOrEmpty(String string) {
85  		return (string==null || string.trim().equals(""));
86  	}
87  
88  	
89  	/**
90  	 * Replaces the first occurrence of the given 'search' string by the 'replacement' string. 
91  	 * @param path The path on which the replacement should be done
92  	 * @param search search string
93  	 * @param replacement replacement string
94  	 * @return
95  	 */
96  	public static final String replace(String path, String search, String replacement) 
97      {
98          int index = path.indexOf(search);
99          if(index >= 0) {
100             String result = path.substring(0, index);
101             result += replacement;
102             result += path.substring(index + search.length());
103             return result;
104         }
105         else {
106             // Return the original if no replacement found
107             return path; 
108         }
109     }
110 
111 	
112 	/**
113 	 * Creates a random string with the given length
114 	 * @param byteSize
115 	 * @return
116 	 */
117 	public static String createRandomStringWithBytes(int byteSize)
118 	{
119 		if(byteSize <= 0) {
120 			throw new IllegalArgumentException("The given byteSize '" + byteSize + "' must be > 0");
121 		}
122 		
123 		StringBuffer sb = new StringBuffer();
124 		Random random = new Random();
125 		for (int i = 0; i < byteSize; i++) {
126 			// ASCII alphabet fits into 7 bits, 2^7=128 -> otherwise we could get into trouble with the unicode encodings...
127 			char oneByte = (char) (random.nextInt() % 128);
128 			sb.append(oneByte);
129 		}
130 		return sb.toString();
131 	}
132 
133 
134 	public static boolean isInteger(String value) {
135 		try {
136 			Integer.parseInt(value);
137 			return true;
138 		}
139 		catch(NumberFormatException e) {
140 			return false;
141 		}
142 	}
143 
144 	/**
145 	 * Converts the given array of strings into a single string using the given delimiter as separator.
146 	 * @param array
147 	 * @param delimiter
148 	 * @return
149 	 */
150 	public static String createString(String[] array, char delimiter) {
151 		return createString(array, 0, delimiter);
152 	}
153 
154 	/**
155 	 * Converts the given array of strings into a single string using the given delimiter as separator.
156 	 * @param array
157 	 * @param startIndex
158 	 * @param delimiter
159 	 * @return
160 	 */
161 	public static String createString(String[] array, int startIndex, char delimiter) {
162 		if(startIndex >= array.length) {
163 			throw new ArrayIndexOutOfBoundsException("The start index '" + startIndex + "' must be smaller than array length " + array.length);
164 		}
165 		
166 		StringBuffer sb = new StringBuffer();
167 		for(int i=startIndex; i<array.length; i++) {
168 			sb.append(array[i]);
169 			
170 			if(i < array.length-1) {
171 				sb.append(delimiter);
172 			}
173 		}
174 		return sb.toString();
175 	}
176 
177     /**
178      * Truncates the given string if it is longer than the maximum length specified. The symbols '...'
179      * replace the last three characters to indicate that truncation occurred. If the string's length is less than
180      * the specified maximum, it is returned unchanged. If the given string is null, null is returned.
181      * Maximum lengths of less than 1 will be ignored.
182      *
183      * @param inputString The string to truncate, if necessary.
184      * @param maxLength The maximum length of the string, after truncation.
185      *
186      * @return A string at most maxLength characters long taken from the input string, or the input string if it is
187      *         shorter than the specified maximum length.
188      */
189     public static String truncateToMaxLength(String inputString, int maxLength)
190     {
191         String outputString = inputString;
192 
193         if((inputString != null) && (maxLength > 0) && (inputString.length() > maxLength)) {
194             outputString = inputString.substring(0, maxLength - 3) + "...";
195         }
196 
197         return outputString;
198     }
199 
200     /**
201      * Returns the specified string truncated to the specified maximum length in bytes (when encoded in UTF-8
202      * format), with an ellipses (...) if need be.
203      *
204      * @param text The text to truncate.
205      * @param maxLengthInBytes The maximum length in bytes.
206      * @param mode The mode for executing the truncation. This can be one of 
207      * <ul>
208      * <li><code>{@link StringUtil.TruncationMode#END}</code> or</li> 
209      * <li><code>{@link StringUtil.TruncationMode#START}</code>. </li> 
210      * </ul> If the specified mode is any unknown mode, the
211      *        default mode <code>{@link StringUtil.TruncationMode#END}</code> is used.
212      *
213      * @return The specified string truncated to the specified maximum length in bytes.
214      *
215      * @throws IllegalArgumentException If maxLengthInBytes is less than 3 or if the given {@link TruncationMode} is
216      *         unknown.
217      */
218     public static String truncateUtf8String(String text, int maxLengthInBytes, TruncationMode mode)
219     {
220         if(text == null) {
221             throw new NullPointerException("The parameter 'text' must not be null");
222         }
223 
224         if(maxLengthInBytes < 3) {
225             throw new IllegalArgumentException("maxLengthInBytes < 3");
226         }
227 
228         int lengthInBytes = getLengthInBytes(text, UTF8_ENCODING);
229 
230         // Return the input of the length looks good.
231         if(lengthInBytes <= maxLengthInBytes) {
232             return text;
233         }
234 
235         // We're going to add "..." so we recompute this for good
236         maxLengthInBytes = maxLengthInBytes - 3;
237 
238         // If there's no special characters, then one char = one byte,
239         // and we do a normal truncation.
240         if(lengthInBytes == text.length()) {
241             if(mode == TruncationMode.START) {
242                 return "..." + text.substring(text.length() - maxLengthInBytes, text.length());
243             }
244             else {
245                 return text.substring(0, maxLengthInBytes) + "...";
246             }
247         }
248 
249         // We definitely have special characters at this point!
250         if(mode == TruncationMode.START) {
251         	return truncateStart(text, maxLengthInBytes);
252         }
253         else if(mode == TruncationMode.END) {
254         	return truncateEnd(text, maxLengthInBytes);
255         }
256         else {
257             throw new IllegalArgumentException("Unknown TruncationMode: " + mode);
258         }
259     }
260 
261     
262     private static String truncateEnd(String text, int maxLengthInBytes) {
263         // Truncate the end of the text so that we can append the "..."
264         text = text.substring(0, maxLengthInBytes);
265 
266         // We recalculate the length of what we have left.
267         int lengthInBytes = getLengthInBytes(text, UTF8_ENCODING);
268 
269         // It might be that we chopped off the special characters
270         // causing the overflow and can return what we have now.
271         if(lengthInBytes <= maxLengthInBytes) {
272             return text + "...";
273         }
274 
275         // Loop backwards through the string, removing the length in
276         // bytes of each character from the previously calculated length.
277         // When the length is under the allowable max, we return the
278         // candidate.
279         for(int i = text.length() - 1; i >= 0; i--) {
280             lengthInBytes -= ((text.charAt(i) > 127) ? 2 : 1);
281 
282             //		    	System.out.println("char at " + i + " = '" + text.charAt(i) +
283             //		        "', length of '" + text.substring(0, i) + "' is " + lengthInBytes);
284             if(lengthInBytes <= maxLengthInBytes) {
285                 return text.substring(0, i) + "...";
286             }
287         }
288 
289         // We should never arrive here
290         throw new IllegalStateException("Should never reach here");
291     }
292 
293 
294 
295 	private static String truncateStart(String text, int maxLengthInBytes) {
296         // Truncate the start of the text so that we can append the "..."
297         text = text.substring(text.length() - maxLengthInBytes, text.length());
298 
299         // We recalculate the length of what we have left.
300         int lengthInBytes = getLengthInBytes(text, UTF8_ENCODING);
301 
302         // It might be that we chopped off the special characters
303         // causing the overflow and can return what we have now.
304         if(lengthInBytes <= maxLengthInBytes) {
305             return "..." + text;
306         }
307 
308         // Loop forwards through the string, removing the length in
309         // bytes of each character from the previously calculated length.
310         // When the length is under the allowable max, we return the
311         // candidate.
312         for(int i = 0; i < text.length(); i++) {
313             lengthInBytes -= ((text.charAt(i) > 127) ? 2 : 1);
314 
315             //		    	System.out.println("char at " + i + " = '" + text.charAt(i) +
316             //		        "', length of '" + text.substring(0, i) + "' is " + lengthInBytes);
317             if(lengthInBytes <= maxLengthInBytes) {
318                 return "..." + text.substring(i, text.length());
319             }
320         }
321         
322         // We should never arrive here
323         throw new IllegalStateException("Should never reach here");
324 	}
325 
326 
327 
328 	/**
329      * Returns the length of the byte array for the specified <code>text</code>.
330      *
331      * @param text The text whose length in bytes should be computed.
332      * @param charset The encoding of the text which is needed for converting the text to a byte array. For available
333      *        encodings, refer to {@link java.nio.charset.Charset#availableCharsets()}.
334      *
335      * @return The length of the byte array for the text in the specified encoding. If the specified
336      *         <code>charset</code> is not supported, this method returns zero.
337      *
338      * @throws NullPointerException If the specified string is null.
339      */
340     public static int getLengthInBytes(String text, String charset)
341     {
342         if(text == null) {
343             throw new NullPointerException("The parameter 'text' must not be null!");
344         }
345 
346         if(charset == null) {
347             // get the default character set of the system
348             charset = DEFAULT_SYSTEM_CHARSET;
349         }
350 
351         try {
352             return text.getBytes(charset).length;
353         }
354         catch(UnsupportedEncodingException encodingException) {
355             log.warn("Unexpected encoding exception while converting string to " + charset + ": '"
356                 + encodingException.getMessage().trim() + "'.");
357             return 0;
358         }
359     }
360 
361     
362     
363     
364 	/**
365 	 * Resolves a key value pair from the given string by splitting it at the first found '=' character.
366 	 * @param someString String to resolve. If the string is null or empty, a runtime exception is thrown
367 	 * @return
368 	 */
369 	public static KeyValuePair resolveKeyValue(String someString)
370 	{
371 		if (someString == null) {
372 			throw new NullPointerException("The parameter 'someString' must not be null");
373 		}
374 		
375 		String key = null;
376 		String value = null;
377 		
378 		int idxEqualSymbol = someString.indexOf(KeyValuePair.ASSIGNMENT_CHAR);
379 		if(idxEqualSymbol != -1) {
380 			key = someString.substring(0, idxEqualSymbol);
381 			value = someString.substring(idxEqualSymbol+1);
382 		}
383 		else {
384 			key = someString;
385 		}
386 
387 		KeyValuePair result = new KeyValuePair(key, value);
388 		return result;
389 	}
390 	
391 	
392 	
393 	/**
394 	 * Simple object associating a key with a value
395 	 */
396 	public static class KeyValuePair
397 	{
398 		/**
399 		 * Constant for the '=' character
400 		 */
401 		public static final String ASSIGNMENT_CHAR = "=";
402 		
403 		private String key;
404 		private String value;
405 		
406 		/**
407 		 * @param key Key must not be null
408 		 * @param value value can be null
409 		 */
410 		public KeyValuePair(String key, String value) {
411 			super();
412 			if (key == null) {
413 				throw new NullPointerException("The parameter 'key' must not be null");
414 			}
415 			if(key.trim().length()<=0) {
416 				throw new IllegalArgumentException("The parameter 'key' must not be an empty string.");
417 			}
418 			this.key = key;
419 			this.value = value;
420 		}
421 		
422 		public String getKey() {
423 			return key;
424 		}
425 		public void setKey(String key) {
426 			this.key = key;
427 		}
428 		public String getValue() {
429 			return value;
430 		}
431 		public void setValue(String value) {
432 			this.value = value;
433 		}
434 		
435 		
436 	}
437 
438 
439 
440 	/**
441 	 * Checks if the given string is alphanumeric or not
442 	 * @param stringToCheck
443 	 * @return
444 	 */
445 	public static boolean isAlphaNumeric(String stringToCheck) {
446 		if (stringToCheck == null) {
447 			throw new NullPointerException(
448 					"The parameter 'stringToCheck' must not be null");
449 		}
450 
451 		char[] chr = stringToCheck.toCharArray();
452 		for (int i = 0; i < chr.length; i++) {
453 			if(!Character.isLetterOrDigit(chr[i])) {
454 				return false;
455 			}
456 		}
457 		return true;
458 		
459 //		boolean blnNumeric = false;
460 //		boolean blnAlpha = false;
461 //
462 //		char chr[] = null;
463 //		if(stringToCheck != null)
464 //		chr = stringToCheck.toCharArray();
465 //
466 //		for(int i=0; i<chr.length; i++){
467 //			if(chr[i] >= '0' && chr[i] <= '9'){
468 //				blnNumeric = true;
469 //				break;
470 //			}
471 //		}
472 //		for(int i=0; i<chr.length; i++){
473 //			if((chr[i] >= 'A' && chr[i] <= 'Z') || (chr[i] >= 'a' && chr[i] <= 'z')){
474 //				blnAlpha = true;
475 //				break;
476 //			}
477 //		}
478 //		return (blnNumeric && blnAlpha);
479 	}
480 
481 	public static int compare(String first, String second) {
482 		if(first == null && second == null) {
483 			return 0;
484 		}
485 		else if(first==null) {
486 			return -1;
487 		}
488 		else {
489 			return first.compareTo(second);
490 		}
491 	}
492 
493 
494 	public static void validateMode(String mode) {
495 		if (mode == null) {
496 			throw new NullPointerException(
497 					"The parameter 'mode' must not be null");
498 		}
499 		
500 		if(mode.equals("?")) {
501 			return;
502 		}
503 		else {
504 			String regexOctal = "[0-7]{3,4}";
505 			// Check if it is a valid 4 digit octal number
506 			if(!mode.matches(regexOctal)) {
507 				throw new ParseException("The given mode '" + mode + "' must consist of 4 valid octal numbers.");
508 			}
509 		}
510 	}
511 
512 
513 
514 	public static RemoveResult removePrefix(String string, String prefix) {
515 		// Cut off the key
516 		int indexOfKey = string.indexOf(prefix);
517 		boolean hasWhitespaceSeparator = false;
518 		if(string.length() > indexOfKey) {
519 			char nextChar = string.charAt(indexOfKey+1);
520 			if(nextChar == PrototypeParser.DEFAULT_LINE_DELIMITER) {
521 				hasWhitespaceSeparator = true;
522 				indexOfKey++;
523 			}
524 		}
525 		
526 		String resultString = null;
527 		if(string.length() > indexOfKey) {
528 			resultString = string.substring(indexOfKey+1);
529 		}
530 		
531 		return new RemoveResult(resultString, hasWhitespaceSeparator);
532 	}
533 	
534 	
535 	public static final class RemoveResult {
536 		private boolean hasWhitespaceSeparator;
537 		private String cleanedString;
538 		
539 		public RemoveResult(String cleanedString, boolean hasWhitespaceSeparator)
540 		{
541 			this.cleanedString = cleanedString;
542 			this.hasWhitespaceSeparator = hasWhitespaceSeparator;
543 		}
544 		
545 		public boolean isHasWhitespaceSeparator() {
546 			return hasWhitespaceSeparator;
547 		}
548 		public void setHasWhitespaceSeparator(boolean hasWhitespaceSeparator) {
549 			this.hasWhitespaceSeparator = hasWhitespaceSeparator;
550 		}
551 		public String getCleanedString() {
552 			return cleanedString;
553 		}
554 		public void setCleanedString(String cleanedString) {
555 			this.cleanedString = cleanedString;
556 		}
557 		
558 	}
559 
560 
561 }