001/** 002 * 003 * Copyright 2003-2007 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smack.util; 019 020import java.io.UnsupportedEncodingException; 021import java.util.Collection; 022import java.util.Random; 023 024/** 025 * A collection of utility methods for String objects. 026 */ 027public class StringUtils { 028 029 public static final String MD5 = "MD5"; 030 public static final String SHA1 = "SHA-1"; 031 public static final String UTF8 = "UTF-8"; 032 public static final String USASCII = "US-ASCII"; 033 034 public static final String QUOTE_ENCODE = """; 035 public static final String APOS_ENCODE = "'"; 036 public static final String AMP_ENCODE = "&"; 037 public static final String LT_ENCODE = "<"; 038 public static final String GT_ENCODE = ">"; 039 040 public static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); 041 042 /** 043 * Escapes all necessary characters in the String so that it can be used 044 * in an XML doc. 045 * 046 * @param string the string to escape. 047 * @return the string with appropriate characters escaped. 048 */ 049 public static CharSequence escapeForXML(final String string) { 050 if (string == null) { 051 return null; 052 } 053 final char[] input = string.toCharArray(); 054 final int len = input.length; 055 final StringBuilder out = new StringBuilder((int)(len*1.3)); 056 CharSequence toAppend; 057 char ch; 058 int last = 0; 059 int i = 0; 060 while (i < len) { 061 toAppend = null; 062 ch = input[i]; 063 switch(ch) { 064 case '<': 065 toAppend = LT_ENCODE; 066 break; 067 case '>': 068 toAppend = GT_ENCODE; 069 break; 070 case '&': 071 toAppend = AMP_ENCODE; 072 break; 073 case '"': 074 toAppend = QUOTE_ENCODE; 075 break; 076 case '\'': 077 toAppend = APOS_ENCODE; 078 break; 079 default: 080 break; 081 } 082 if (toAppend != null) { 083 if (i > last) { 084 out.append(input, last, i - last); 085 } 086 out.append(toAppend); 087 last = ++i; 088 } else { 089 i++; 090 } 091 } 092 if (last == 0) { 093 return string; 094 } 095 if (i > last) { 096 out.append(input, last, i - last); 097 } 098 return out; 099 } 100 101 /** 102 * Hashes a String using the SHA-1 algorithm and returns the result as a 103 * String of hexadecimal numbers. This method is synchronized to avoid 104 * excessive MessageDigest object creation. If calling this method becomes 105 * a bottleneck in your code, you may wish to maintain a pool of 106 * MessageDigest objects instead of using this method. 107 * <p> 108 * A hash is a one-way function -- that is, given an 109 * input, an output is easily computed. However, given the output, the 110 * input is almost impossible to compute. This is useful for passwords 111 * since we can store the hash and a hacker will then have a very hard time 112 * determining the original password. 113 * 114 * @param data the String to compute the hash of. 115 * @return a hashed version of the passed-in String 116 * @deprecated use {@link org.jivesoftware.smack.util.SHA1#hex(String)} instead. 117 */ 118 @Deprecated 119 public synchronized static String hash(String data) { 120 return org.jivesoftware.smack.util.SHA1.hex(data); 121 } 122 123 /** 124 * Encodes an array of bytes as String representation of hexadecimal. 125 * 126 * @param bytes an array of bytes to convert to a hex string. 127 * @return generated hex string. 128 */ 129 public static String encodeHex(byte[] bytes) { 130 char[] hexChars = new char[bytes.length * 2]; 131 for ( int j = 0; j < bytes.length; j++ ) { 132 int v = bytes[j] & 0xFF; 133 hexChars[j * 2] = HEX_CHARS[v >>> 4]; 134 hexChars[j * 2 + 1] = HEX_CHARS[v & 0x0F]; 135 } 136 return new String(hexChars); 137 } 138 139 public static byte[] toBytes(String string) { 140 try { 141 return string.getBytes(StringUtils.UTF8); 142 } 143 catch (UnsupportedEncodingException e) { 144 throw new IllegalStateException("UTF-8 encoding not supported by platform", e); 145 } 146 } 147 148 /** 149 * Pseudo-random number generator object for use with randomString(). 150 * The Random class is not considered to be cryptographically secure, so 151 * only use these random Strings for low to medium security applications. 152 */ 153 private static Random randGen = new Random(); 154 155 /** 156 * Array of numbers and letters of mixed case. Numbers appear in the list 157 * twice so that there is a more equal chance that a number will be picked. 158 * We can use the array to get a random number or letter by picking a random 159 * array index. 160 */ 161 private static char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" + 162 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); 163 164 /** 165 * Returns a random String of numbers and letters (lower and upper case) 166 * of the specified length. The method uses the Random class that is 167 * built-in to Java which is suitable for low to medium grade security uses. 168 * This means that the output is only pseudo random, i.e., each number is 169 * mathematically generated so is not truly random.<p> 170 * 171 * The specified length must be at least one. If not, the method will return 172 * null. 173 * 174 * @param length the desired length of the random String to return. 175 * @return a random String of numbers and letters of the specified length. 176 */ 177 public static String randomString(int length) { 178 if (length < 1) { 179 return null; 180 } 181 // Create a char buffer to put random letters and numbers in. 182 char [] randBuffer = new char[length]; 183 for (int i=0; i<randBuffer.length; i++) { 184 randBuffer[i] = numbersAndLetters[randGen.nextInt(numbersAndLetters.length)]; 185 } 186 return new String(randBuffer); 187 } 188 189 /** 190 * Returns true if CharSequence is not null and is not empty, false otherwise 191 * Examples: 192 * isNotEmpty(null) - false 193 * isNotEmpty("") - false 194 * isNotEmpty(" ") - true 195 * isNotEmpty("empty") - true 196 * 197 * @param cs checked CharSequence 198 * @return true if string is not null and is not empty, false otherwise 199 */ 200 public static boolean isNotEmpty(CharSequence cs) { 201 return !isNullOrEmpty(cs); 202 } 203 204 /** 205 * Returns true if the given CharSequence is null or empty. 206 * 207 * @param cs 208 * @return true if the given CharSequence is null or empty 209 */ 210 public static boolean isNullOrEmpty(CharSequence cs) { 211 return cs == null || isEmpty(cs); 212 } 213 214 /** 215 * Returns true if the given CharSequence is empty 216 * 217 * @param cs 218 * @return true if the given CharSequence is empty 219 */ 220 public static boolean isEmpty(CharSequence cs) { 221 return cs.length() == 0; 222 } 223 224 public static String collectionToString(Collection<String> collection) { 225 StringBuilder sb = new StringBuilder(); 226 for (String s : collection) { 227 sb.append(s); 228 sb.append(" "); 229 } 230 String res = sb.toString(); 231 // Remove the trailing whitespace 232 res = res.substring(0, res.length() - 1); 233 return res; 234 } 235 236 public static String returnIfNotEmptyTrimmed(String string) { 237 if (string == null) 238 return null; 239 String trimmedString = string.trim(); 240 if (trimmedString.length() > 0) { 241 return trimmedString; 242 } else { 243 return null; 244 } 245 } 246 247 public static boolean nullSafeCharSequenceEquals(CharSequence csOne, CharSequence csTwo) { 248 return nullSafeCharSequenceComperator(csOne, csTwo) == 0; 249 } 250 251 public static int nullSafeCharSequenceComperator(CharSequence csOne, CharSequence csTwo) { 252 if (csOne == null ^ csTwo == null) { 253 return (csOne == null) ? -1 : 1; 254 } 255 if (csOne == null && csTwo == null) { 256 return 0; 257 } 258 return csOne.toString().compareTo(csTwo.toString()); 259 } 260 261 public static <CS extends CharSequence> CS requireNotNullOrEmpty(CS cs, String message) { 262 if (isNullOrEmpty(cs)) { 263 throw new IllegalArgumentException(message); 264 } 265 return cs; 266 } 267 268 /** 269 * Return the String representation of the given char sequence if it is not null. 270 * 271 * @param cs the char sequence or null. 272 * @return the String representation of <code>cs</code> or null. 273 */ 274 public static String maybeToString(CharSequence cs) { 275 if (cs == null) { 276 return null; 277 } 278 return cs.toString(); 279 } 280}