| /* |
| * Copyright (c) 2014-2021 by Wen Yu |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the Eclipse |
| * Public License, v. 2.0 are satisfied: GNU General Public License, version 2 |
| * or any later version. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later |
| * |
| * Change History - most recent changes go on top of previous changes |
| * |
| * StringUtils.java |
| * |
| * Who Date Description |
| * ==== ========= ============================================================== |
| * WY 03May2015 Added rationalToString() |
| * WY 04Mar2015 Added toHexString() |
| * WY 04Mar2015 Added generateMD5() |
| * WY 07Feb201 Added decimalToDMS() and DMSToDecimal() |
| * WY 23Jan2015 Moved XML related methods to XMLUtils |
| * WY 10Jan2015 Added showXML() and printNode() to show XML document |
| * WY 28Dec2014 Added isInCharset() to test if a String can be encoded with |
| * certain character set. |
| */ |
| |
| package pixy.string; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.net.URLEncoder; |
| import java.nio.charset.Charset; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.text.DecimalFormat; |
| import java.util.Iterator; |
| import java.util.NoSuchElementException; |
| import java.util.regex.*; |
| |
| /** |
| * String utility class |
| * |
| * @author Wen Yu, yuwen_66@yahoo.com |
| * @version 1.0 09/18/2012 |
| */ |
| public class StringUtils { |
| |
| private static final char[] HEXES = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; |
| |
| /** |
| * Formats byte array. |
| * |
| * @param bytes an array of byte. |
| * @return a hex string representation of the byte array. |
| */ |
| public static String byteArrayToHexString(byte[] bytes) { |
| return byteArrayToHexString(bytes, 0, bytes.length); |
| } |
| |
| public static String byteArrayToHexString(byte[] bytes, int offset, int length) { |
| if ( bytes == null ) |
| return null; |
| |
| if(bytes.length == 0) return "[]"; |
| |
| if(offset < 0 || offset >= bytes.length) |
| throw new IllegalArgumentException("Offset out of array bound!"); |
| |
| int endOffset = offset + Math.min(length, bytes.length); |
| |
| if(endOffset > bytes.length) |
| length = bytes.length - offset; |
| |
| StringBuilder hex = new StringBuilder(5*length + 2); |
| hex.append("["); |
| |
| for (int i = offset; i < endOffset; i++) { |
| hex.append("0x").append(HEXES[(bytes[i] & 0xf0) >> 4]) |
| .append(HEXES[bytes[i] & 0x0f]).append(","); |
| } |
| |
| // Remove the last "," |
| if(hex.length() > 1) |
| hex.deleteCharAt(hex.length()-1); |
| |
| if(endOffset < bytes.length) |
| hex.append(" ..."); // Partial output |
| |
| hex.append("]"); |
| |
| return hex.toString(); |
| } |
| |
| public static String byteToHexString(byte b) { |
| return String.format("0x%02X ", b); |
| } |
| |
| /** |
| * Capitalizes the first character of the words in a string. |
| * |
| * @param s the input string |
| * @return a string with the first character of all words capitalized |
| */ |
| public static String capitalize(String s) { |
| StringBuffer myStringBuffer = new StringBuffer(); |
| Pattern p = Pattern.compile("\\b(\\w)(\\w*)"); |
| Matcher m = p.matcher(s); |
| |
| while (m.find()) { |
| if(!Character.isUpperCase(m.group().charAt(0))) |
| m.appendReplacement(myStringBuffer, m.group(1).toUpperCase()+"$2"); |
| } |
| |
| return m.appendTail(myStringBuffer).toString(); |
| } |
| |
| public static String capitalizeFully(String s) { |
| return capitalize(s.toLowerCase()); |
| } |
| |
| public static String concat(Iterable<? extends CharSequence> strings, String delimiter) { |
| int capacity = 0; |
| int delimLength = delimiter.length(); |
| |
| Iterator<? extends CharSequence> iter = strings.iterator(); |
| |
| while (iter.hasNext()) { |
| CharSequence next = iter.next(); |
| |
| if(!isNullOrEmpty(next)) |
| capacity += next.length() + delimLength; |
| } |
| |
| StringBuilder buffer = new StringBuilder(capacity); |
| iter = strings.iterator(); |
| |
| while (iter.hasNext()) { |
| CharSequence next = iter.next(); |
| |
| if(!isNullOrEmpty(next)) { |
| buffer.append(next); |
| buffer.append(delimiter); |
| } |
| } |
| |
| int lastIndexOfDelimiter = buffer.lastIndexOf(delimiter); |
| buffer.delete(lastIndexOfDelimiter, buffer.length()); |
| |
| return buffer.toString(); |
| } |
| |
| public static String concat(String first, String second) { |
| if(first == null) return second; |
| if(second == null) return first; |
| |
| StringBuilder sb = new StringBuilder(first.length() + second.length()); |
| sb.append(first); |
| sb.append(second); |
| |
| return sb.toString(); |
| } |
| |
| public static String concat(String first, String... strings) { |
| StringBuilder sb; |
| |
| if(first != null) sb = new StringBuilder(first); |
| else sb = new StringBuilder(); |
| |
| for (String s: strings) { |
| if(!isNullOrEmpty(s)) |
| sb.append(s); |
| } |
| |
| return sb.toString(); |
| } |
| |
| public static <T extends CharSequence> String concat(T[] strings, String delimiter) { |
| int capacity = 0; |
| int delimLength = delimiter.length(); |
| |
| for (T value : strings) { |
| if(!isNullOrEmpty(value)) |
| capacity += value.length() + delimLength; |
| } |
| |
| StringBuilder buffer = new StringBuilder(capacity); |
| |
| for (T value : strings) { |
| if(!isNullOrEmpty(value)) { |
| buffer.append(value); |
| buffer.append(delimiter); |
| } |
| } |
| |
| int lastIndexOfDelimiter = buffer.lastIndexOf(delimiter); |
| buffer.delete(lastIndexOfDelimiter, buffer.length()); |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Regular expression version of the String contains method. |
| * If used with a match from start or match from end regular expression, |
| * it becomes the regular expression version of the {@link String# |
| * startsWith(String prefix)} or {@link String#endsWith(String suffix)} |
| * methods. |
| * |
| * @param input the input string |
| * @param regex the regular expression to which this string is to be matched |
| * @return true if a match is found, otherwise false |
| */ |
| public static boolean contains(String input, String regex) { |
| Pattern p = Pattern.compile(regex); |
| Matcher m = p.matcher(input); |
| |
| if (m.find()) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // From http://stackoverflow.com/questions/15547329/how-to-prettily-format-gps-data-in-java-android |
| // Input a double latitude or longitude in the decimal format |
| public static String decimalToDMS(double coord) { |
| String output; |
| int degrees, minutes, seconds; |
| // gets the modulus the coordinate divided by one (MOD1). |
| // in other words gets all the numbers after the decimal point. |
| // e.g. mod = 87.728056 % 1 == 0.728056 |
| // |
| // next get the integer part of the coord. In other words the, whole number part. |
| // e.g. intPart = 87 |
| double mod = coord % 1; |
| degrees = (int)coord; |
| // next times the MOD1 of degrees by 60 so we can find the integer part for minutes. |
| // get the MOD1 of the new coord to find the numbers after the decimal point. |
| // e.g. coord = 0.728056 * 60 == 43.68336 |
| // mod = 43.68336 % 1 == 0.68336 |
| // |
| // next get the value of the integer part of the coord. |
| // e.g. intPart = 43 |
| coord = mod * 60; |
| mod = coord % 1; |
| minutes = (int)coord; |
| //do the same again for minutes |
| //e.g. coord = 0.68336 * 60 == 41.0016 |
| //e.g. intPart = 41 |
| coord = mod * 60; |
| seconds = (int)coord; |
| //Standard output of D°M'S" |
| output = degrees + '\u00B0' + minutes + "'" + seconds + "\""; |
| |
| return output; |
| } |
| |
| /** |
| * Converts DMS to decimal |
| * |
| * Input: latitude or longitude in the DMS format ( example: N 43° 36' 15.894") |
| * @param hemisphereOUmeridien => {W,E,S,N} |
| * @return latitude or longitude in decimal format |
| */ |
| public double DMSToDecimal(String hemisphereOUmeridien, double degrees, double minutes, double seconds) { |
| double LatOrLon = 0; |
| double sign = 1.0; |
| |
| if((hemisphereOUmeridien == "W")||(hemisphereOUmeridien == "S")) { |
| sign = -1.0; |
| } |
| |
| LatOrLon = sign*(Math.floor(degrees) + Math.floor(minutes)/60.0 + seconds/3600.0); |
| |
| return LatOrLon; |
| } |
| |
| /** |
| * From www.javapractices.com EscapeChars.java |
| * |
| * @param url URL string to be encoded |
| * @return a encoded URL string |
| */ |
| public static String encodeURL(String url) { |
| String result = null; |
| |
| try { |
| result = URLEncoder.encode(url, "UTF-8"); |
| } |
| catch (UnsupportedEncodingException ex){ |
| throw new RuntimeException("UTF-8 not supported", ex); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Escapes HTML reserved characters and other characters which might cause Cross Site Scripting |
| * (XSS) hacks |
| * |
| * The following table comes from www.javapractice.com EscapeChars.java |
| * |
| * <P>The following characters are replaced with corresponding HTML character entities: |
| * |
| * <table border='1' cellpadding='3' cellspacing='0'> |
| * <tr><th> Character </th><th>Replacement</th></tr> |
| * <tr><td> < </td><td> < </td></tr> |
| * <tr><td> > </td><td> > </td></tr> |
| * <tr><td> & </td><td> & </td></tr> |
| * <tr><td> " </td><td> "</td></tr> |
| * <tr><td> \t </td><td> 	</td></tr> |
| * <tr><td> ! </td><td> !</td></tr> |
| * <tr><td> # </td><td> #</td></tr> |
| * <tr><td> $ </td><td> $</td></tr> |
| * <tr><td> % </td><td> %</td></tr> |
| * <tr><td> ' </td><td> '</td></tr> |
| * <tr><td> ( </td><td> (</td></tr> |
| * <tr><td> ) </td><td> )</td></tr> |
| * <tr><td> * </td><td> *</td></tr> |
| * <tr><td> + </td><td> + </td></tr> |
| * <tr><td> , </td><td> , </td></tr> |
| * <tr><td> - </td><td> - </td></tr> |
| * <tr><td> . </td><td> . </td></tr> |
| * <tr><td> / </td><td> / </td></tr> |
| * <tr><td> : </td><td> :</td></tr> |
| * <tr><td> ; </td><td> ;</td></tr> |
| * <tr><td> = </td><td> =</td></tr> |
| * <tr><td> ? </td><td> ?</td></tr> |
| * <tr><td> @ </td><td> @</td></tr> |
| * <tr><td> [ </td><td> [</td></tr> |
| * <tr><td> \ </td><td> \</td></tr> |
| * <tr><td> ] </td><td> ]</td></tr> |
| * <tr><td> ^ </td><td> ^</td></tr> |
| * <tr><td> _ </td><td> _</td></tr> |
| * <tr><td> ` </td><td> `</td></tr> |
| * <tr><td> { </td><td> {</td></tr> |
| * <tr><td> | </td><td> |</td></tr> |
| * <tr><td> } </td><td> }</td></tr> |
| * <tr><td> ~ </td><td> ~</td></tr> |
| * </table> |
| * |
| * @return a string with the specified characters replaced by HTML entities |
| */ |
| public static String escapeHTML(String input) { |
| Iterator<Character> itr = stringIterator(input); |
| StringBuilder result = new StringBuilder(); |
| |
| while (itr.hasNext()) |
| { |
| Character c = itr.next(); |
| |
| switch (c) |
| { |
| case '<': |
| result.append("<"); |
| break; |
| case '>': |
| result.append(">"); |
| break; |
| case '&': |
| result.append("&"); |
| break; |
| case '"': |
| result.append("""); |
| break; |
| case '\t': |
| result.append("	"); |
| break; |
| case '!': |
| result.append("!"); |
| break; |
| case '#': |
| result.append("#"); |
| break; |
| case '$': |
| result.append("$"); |
| break; |
| case '%': |
| result.append("%"); |
| break; |
| case '\'': |
| result.append("'"); |
| break; |
| case '(': |
| result.append("("); |
| break; |
| case ')': |
| result.append(")"); |
| break; |
| case '*': |
| result.append("*"); |
| break; |
| case '+': |
| result.append("+"); |
| break; |
| case ',': |
| result.append(","); |
| break; |
| case '-': |
| result.append("-"); |
| break; |
| case '.': |
| result.append("."); |
| break; |
| case '/': |
| result.append("/"); |
| break; |
| case ':': |
| result.append(":"); |
| break; |
| case ';': |
| result.append(";"); |
| break; |
| case '=': |
| result.append("="); |
| break; |
| case '?': |
| result.append("?"); |
| break; |
| case '@': |
| result.append("@"); |
| break; |
| case '[': |
| result.append("["); |
| break; |
| case '\\': |
| result.append("\"); |
| break; |
| case ']': |
| result.append("]"); |
| break; |
| case '^': |
| result.append("^"); |
| break; |
| case '_': |
| result.append("_"); |
| break; |
| case '`': |
| result.append("`"); |
| break; |
| case '{': |
| result.append("{"); |
| break; |
| case '|': |
| result.append("|"); |
| break; |
| case '}': |
| result.append("}"); |
| break; |
| case '~': |
| result.append("~"); |
| break; |
| default: |
| result.append(c); |
| } |
| } |
| |
| return result.toString(); |
| } |
| |
| /** |
| * Replaces "&" with its entity "&" to make it a valid HTML link |
| * |
| * @param queryString a URL string with a query string attached |
| * @return a valid URL string to be used as a link |
| */ |
| public static String escapeQueryStringAmp(String queryString) { |
| return queryString.replace("&", "&"); |
| } |
| |
| public static String escapeRegex(String input) { |
| Iterator<Character> itr = stringIterator(input); |
| StringBuilder result = new StringBuilder(); |
| |
| while (itr.hasNext()) |
| { |
| Character c = itr.next(); |
| |
| switch (c) |
| { |
| case '.': |
| case '^': |
| case '$': |
| case '*': |
| case '+': |
| case '?': |
| case '(': |
| case ')': |
| case '[': |
| case '{': |
| result.append("\\").append(c); |
| break; |
| case '\\': |
| result.append("\\\\"); |
| break; |
| default: |
| result.append(c); |
| } |
| } |
| |
| return result.toString(); |
| } |
| |
| /** |
| * Generate MD5 digest from a byte array |
| * |
| * @param message byte array to generate MD5 |
| * @return MD5 string |
| */ |
| public static String generateMD5(byte[] message) { |
| MessageDigest md = null; |
| try { |
| md = MessageDigest.getInstance("MD5"); |
| } catch (NoSuchAlgorithmException e) { |
| throw new RuntimeException("No such algorithm: MD5"); |
| } |
| |
| return toHexString(md.digest(message)); |
| } |
| |
| public static String intToHexString(int value) { |
| StringBuilder buffer = new StringBuilder(10); |
| |
| buffer.append("0x"); |
| |
| buffer.append(HEXES[(value & 0x0000000F)]); |
| buffer.append(HEXES[(value & 0x000000F0) >>> 4]); |
| buffer.append(HEXES[(value & 0x00000F00) >>> 8]); |
| buffer.append(HEXES[(value & 0x0000F000) >>> 12]); |
| buffer.append(HEXES[(value & 0x000F0000) >>> 16]); |
| buffer.append(HEXES[(value & 0x00F00000) >>> 20]); |
| buffer.append(HEXES[(value & 0x0F000000) >>> 24]); |
| buffer.append(HEXES[(value & 0xF0000000) >>> 28]); |
| |
| return buffer.toString(); |
| } |
| |
| public static String intToHexStringMM(int value) { |
| |
| StringBuilder buffer = new StringBuilder(10); |
| |
| buffer.append("0x"); |
| |
| buffer.append(HEXES[(value & 0xF0000000) >>> 28]); |
| buffer.append(HEXES[(value & 0x0F000000) >>> 24]); |
| buffer.append(HEXES[(value & 0x00F00000) >>> 20]); |
| buffer.append(HEXES[(value & 0x000F0000) >>> 16]); |
| buffer.append(HEXES[(value & 0x0000F000) >>> 12]); |
| buffer.append(HEXES[(value & 0x00000F00) >>> 8]); |
| buffer.append(HEXES[(value & 0x000000F0) >>> 4]); |
| buffer.append(HEXES[(value & 0x0000000F)]); |
| |
| return buffer.toString(); |
| } |
| |
| public static boolean isInCharset(String input, String encoding) { |
| Charset charset = null; |
| try { |
| // May throw different unchecked exceptions |
| charset = Charset.forName(encoding); |
| } catch(Exception ex) { |
| ex.printStackTrace(); |
| return false; |
| } |
| // Convert input into byte array and encode it again using the |
| // same character set and see if we get the same string |
| String output = new String(input.getBytes(charset), charset); |
| |
| return output.equals(input); |
| } |
| |
| /** |
| * Checks if a string is null, empty, or consists only of white spaces |
| * |
| * @param str the input CharSequence to check |
| * @return true if the input string is null, empty, or contains only white |
| * spaces, otherwise false |
| */ |
| public static boolean isNullOrEmpty(CharSequence str) { |
| return ((str == null) || (str.length() == 0)); |
| } |
| |
| /** |
| * Formats TIFF long data field. |
| * |
| * @param data an array of int. |
| * @param unsigned true if the int value should be treated as unsigned, |
| * otherwise false |
| * @return a string representation of the int array. |
| */ |
| public static String longArrayToString(int[] data, boolean unsigned) { |
| |
| return longArrayToString(data, 0, data.length, unsigned); |
| } |
| |
| public static String longArrayToString(int[] data, int offset, int length, boolean unsigned) { |
| if ( data == null ) { |
| return null; |
| } |
| |
| if(data.length == 0) return "[]"; |
| |
| if(offset < 0 || offset >= data.length) |
| throw new IllegalArgumentException("Offset out of array bound!"); |
| |
| int endOffset = offset + Math.min(length, data.length); |
| |
| if(endOffset > data.length) |
| length = data.length - offset; |
| |
| StringBuilder longs = new StringBuilder(); |
| longs.append("["); |
| |
| for (int i = offset; i < endOffset; i++) |
| { |
| if(unsigned) { |
| // Convert it to unsigned integer |
| longs.append(data[i]&0xffffffffL); |
| } else { |
| longs.append(data[i]); |
| } |
| longs.append(","); |
| } |
| |
| // Remove the last "," |
| if(longs.length() > 1) |
| longs.deleteCharAt(longs.length()-1); |
| |
| if(endOffset < data.length) |
| longs.append(" ..."); // Partial output |
| |
| longs.append("]"); |
| |
| return longs.toString(); |
| } |
| |
| public static boolean parseBoolean(String s) { |
| return Boolean.parseBoolean(s); |
| } |
| |
| public static byte parseByte(String s) { |
| return Byte.parseByte(s); |
| } |
| |
| public static byte parseByte(String s, int radix) { |
| return Byte.parseByte(s, radix); |
| } |
| |
| public static double parseDouble(String s) { |
| return Double.parseDouble(s); |
| } |
| |
| public static float parseFloat(String s) { |
| return Float.parseFloat(s); |
| } |
| |
| public static int parseInt(String s) { |
| return Integer.parseInt(s); |
| } |
| |
| public static int parseInt(String s, int radix) { |
| return Integer.parseInt(s, radix); |
| } |
| |
| public static long parseLong(String s) { |
| return Long.parseLong(s); |
| } |
| |
| public static long parseLong(String s, int radix) { |
| return Long.parseLong(s, radix); |
| } |
| |
| public static short parseShort(String s) { |
| return Short.parseShort(s); |
| } |
| |
| public static short parseShort(String s, int radix) { |
| return Short.parseShort(s, radix); |
| } |
| |
| public static String quoteRegexReplacement(String replacement) |
| { |
| return Matcher.quoteReplacement(replacement); |
| } |
| |
| /** |
| * Formats TIFF rational data field. |
| * |
| * @param data an array of int. |
| * @param unsigned true if the int value should be treated as unsigned, |
| * otherwise false |
| * @return a string representation of the int array. |
| */ |
| public static String rationalArrayToString(int[] data, boolean unsigned) { |
| if(data.length%2 != 0) |
| throw new IllegalArgumentException("Data length is odd number, expect even!"); |
| |
| StringBuilder rational = new StringBuilder(); |
| rational.append("["); |
| |
| for (int i=0; i<data.length; i+=2) |
| { |
| long numerator = data[i], denominator = data[i+1]; |
| |
| //if(denominator == 0) throw new ArithmeticException("Divided by zero"); |
| |
| if (unsigned) { |
| // Converts it to unsigned integer |
| numerator = (data[i]&0xffffffffL); |
| denominator = (data[i+1]&0xffffffffL); |
| } |
| |
| rational.append(numerator); |
| rational.append("/"); |
| rational.append(denominator); |
| |
| rational.append(","); |
| } |
| |
| rational.deleteCharAt(rational.length()-1); |
| rational.append("]"); |
| |
| return rational.toString(); |
| } |
| |
| public static String rationalToString(DecimalFormat df, boolean unsigned, int ... rational) { |
| if(rational.length < 2) throw new IllegalArgumentException("Input data length is too short"); |
| if(rational[1] == 0) throw new ArithmeticException("Divided by zero"); |
| |
| long numerator = rational[0]; |
| long denominator= rational[1]; |
| |
| if (unsigned) { |
| // Converts it to unsigned integer |
| numerator = (numerator&0xffffffffL); |
| denominator = (denominator&0xffffffffL); |
| } |
| |
| return df.format(1.0*numerator/denominator); |
| } |
| |
| /** |
| * Replaces the last occurrence of the string represented by the regular expression |
| * |
| * @param input input string |
| * @param regex the regular expression to which this string is to be matched |
| * @param replacement the string to be substituted for the match |
| * @return the resulting String |
| */ |
| public static String replaceLast(String input, String regex, String replacement) { |
| return input.replaceAll(regex+"(?!.*"+regex+")", replacement); // Using negative look ahead |
| } |
| |
| public static String reverse(String s) { |
| if(s == null) return null; |
| |
| return new StringBuilder(s).reverse().toString(); |
| } |
| |
| // This method will not work for surrogate pairs |
| public static String reverse2(String s) { |
| if(s == null) return null; |
| |
| int i, len = s.length(); |
| StringBuilder dest = new StringBuilder(len); |
| |
| for (i = (len - 1); i >= 0; i--) |
| dest.append(s.charAt(i)); |
| |
| return dest.toString(); |
| } |
| |
| public static String reverse(String str, String delimiter) { |
| if(isNullOrEmpty(delimiter)) { |
| return str; |
| } |
| |
| StringBuilder sb = new StringBuilder(str.length()); |
| reverseIt(str, delimiter, sb); |
| |
| return sb.toString(); |
| } |
| |
| public static String reverse2(String str, String delimiter) { |
| if(isNullOrEmpty(delimiter) || isNullOrEmpty(str) || (str.trim().length() == 0) || (str.indexOf(delimiter) < 0)) { |
| return str; |
| } |
| |
| String escaptedDelimiter = escapeRegex(delimiter); |
| // Keep the trailing white spaces by setting limit to -1 |
| String[] stringArray = str.split(escaptedDelimiter, -1); |
| StringBuilder sb = new StringBuilder(str.length() + delimiter.length()); |
| |
| for (int i = stringArray.length-1; i >= 0; i--) |
| { |
| sb.append(stringArray[i]).append(delimiter); |
| } |
| |
| return sb.substring(0, sb.lastIndexOf(delimiter)); |
| } |
| |
| private static void reverseIt(String str, String delimiter, StringBuilder sb) { |
| if(isNullOrEmpty(str) || (str.trim().length() == 0) || str.indexOf(delimiter) < 0) { |
| sb.append(str); |
| return; |
| } |
| // Recursion |
| reverseIt(str.substring(str.indexOf(delimiter)+delimiter.length()), delimiter, sb); |
| sb.append(delimiter); |
| sb.append(str.substring(0, str.indexOf(delimiter))); |
| } |
| |
| public static String reverseWords(String s) { |
| String[] stringArray = s.split("\\b"); |
| StringBuilder sb = new StringBuilder(s.length()); |
| |
| for (int i = stringArray.length-1; i >= 0; i--) |
| { |
| sb.append(stringArray[i]); |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Formats TIFF short data field. |
| * |
| * @param data an array of short. |
| * @param unsigned true if the short value should be treated as unsigned, |
| * otherwise false |
| * @return a string representation of the short array. |
| */ |
| public static String shortArrayToString(short[] data, boolean unsigned) { |
| return shortArrayToString(data, 0, data.length, unsigned); |
| } |
| |
| public static String shortArrayToString(short[] data, int offset, int length, boolean unsigned) { |
| if ( data == null ) { |
| return null; |
| } |
| |
| if(data.length == 0) return "[]"; |
| |
| if(offset < 0 || offset >= data.length) |
| throw new IllegalArgumentException("Offset out of array bound!"); |
| |
| int endOffset = offset + Math.min(length, data.length); |
| |
| if(endOffset > data.length) |
| length = data.length - offset; |
| |
| StringBuilder shorts = new StringBuilder(); |
| shorts.append("["); |
| |
| for (int i = offset; i < endOffset; i++) |
| { |
| if(unsigned) { |
| // Convert it to unsigned short |
| shorts.append(data[i]&0xffff); |
| } else { |
| shorts.append(data[i]); |
| } |
| shorts.append(","); |
| } |
| |
| // Remove the last "," |
| if(shorts.length() > 1) |
| shorts.deleteCharAt(shorts.length()-1); |
| |
| if(endOffset < data.length) |
| shorts.append(" ..."); // Partial output |
| |
| shorts.append("]"); |
| |
| return shorts.toString(); |
| } |
| |
| public static String shortToHexString(short value) { |
| StringBuilder buffer = new StringBuilder(6); |
| |
| buffer.append("0x"); |
| |
| buffer.append(HEXES[(value & 0x000F)]); |
| buffer.append(HEXES[(value & 0x00F0) >>> 4]); |
| buffer.append(HEXES[(value & 0x0F00) >>> 8]); |
| buffer.append(HEXES[(value & 0xF000) >>> 12]); |
| |
| return buffer.toString(); |
| } |
| |
| public static String shortToHexStringMM(short value) { |
| |
| StringBuilder buffer = new StringBuilder(6); |
| |
| buffer.append("0x"); |
| |
| buffer.append(HEXES[(value & 0xF000) >>> 12]); |
| buffer.append(HEXES[(value & 0x0F00) >>> 8]); |
| buffer.append(HEXES[(value & 0x00F0) >>> 4]); |
| buffer.append(HEXES[(value & 0x000F)]); |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Converts stack trace to string |
| */ |
| public static String stackTraceToString(Throwable e) { |
| StringWriter sw = new StringWriter(); |
| e.printStackTrace(new PrintWriter(sw)); |
| |
| return sw.toString(); |
| } |
| |
| /** |
| * A read-only String iterator from stackoverflow.com |
| * |
| * @param string input string to be iterated |
| * @return an iterator for the input string |
| */ |
| public static Iterator<Character> stringIterator(final String string) { |
| // Ensure the error is found as soon as possible. |
| if (string == null) |
| throw new NullPointerException(); |
| |
| return new Iterator<Character>() { |
| private int index = 0; |
| |
| public boolean hasNext() { |
| return index < string.length(); |
| } |
| |
| public Character next() { |
| /* |
| * Throw NoSuchElementException as defined by the Iterator contract, |
| * not IndexOutOfBoundsException. |
| */ |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| return string.charAt(index++); |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| public static String toHexString(byte[] bytes) { |
| return toHexString(bytes, 0, bytes.length); |
| } |
| |
| /** |
| * Convert byte array to hex string |
| * |
| * @param bytes input byte array |
| * @param offset start offset |
| * @param length number of items to include |
| * |
| * @return a hex string representation for the byte array without 0x prefix |
| */ |
| public static String toHexString(byte[] bytes, int offset, int length) { |
| if ( bytes == null ) |
| return null; |
| |
| if(bytes.length == 0) return ""; |
| |
| if(offset < 0 || offset >= bytes.length) |
| throw new IllegalArgumentException("Offset out of array bound!"); |
| |
| int endOffset = offset + Math.min(length, bytes.length); |
| |
| if(endOffset > bytes.length) |
| length = bytes.length - offset; |
| |
| StringBuilder hex = new StringBuilder(5*length + 2); |
| |
| for (int i = offset; i < endOffset; i++) { |
| hex.append(HEXES[(bytes[i] & 0xf0) >> 4]) |
| .append(HEXES[bytes[i] & 0x0f]); |
| } |
| |
| return hex.toString(); |
| } |
| |
| public static String toUTF16BE(byte[] data, int start, int length) { |
| String retVal = ""; |
| |
| try { |
| retVal = new String(data, start, length, "UTF-16BE"); |
| } catch (UnsupportedEncodingException e) { |
| e.printStackTrace(); |
| } |
| |
| return retVal; |
| } |
| |
| private StringUtils(){} // Prevents instantiation |
| } |