001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util; 017 018import static org.apache.commons.lang3.StringUtils.replace; 019import static org.apache.commons.lang3.StringUtils.trim; 020import static org.kuali.common.util.Encodings.ASCII; 021 022import java.io.UnsupportedEncodingException; 023import java.util.List; 024 025import org.apache.commons.lang3.StringUtils; 026 027/** 028 * Operations on <code>String</code> that are <code>null</code> safe 029 */ 030public class Str { 031 032 public static final String EMPTY_STRING = ""; 033 public static final String UTF8 = Encodings.UTF8; 034 public static final String COMMA = ","; 035 public static final String SPACE = " "; 036 public static final String CR = "\r"; 037 public static final String LF = "\n"; 038 public static final String DOT = "."; 039 public static final String COLON = ":"; 040 public static final String FORWARD_SLASH = "/"; 041 public static final char DOUBLE_QUOTE = '"'; 042 public static final String CDATA_PREFIX = "<![CDATA["; 043 public static final String CDATA_SUFFIX = "]]>"; 044 public static final String[] EMPTY_ARRAY = new String[0]; 045 046 private static final String CONCEALED_PREFIX = "cnc--"; 047 048 /** 049 * <p> 050 * A trivial way to conceal <code>text</code>. Can be reversed using <code>reveal()</code>. Do <b>NOT</b> use this method in an attempt to obscure sensitive data. The algorithm 051 * is completely trivial and exceedingly simple to reverse engineer. Not to mention, the <code>reveal()</code> method can reproduce the original string without requiring any 052 * secret knowledge. 053 * </p> 054 * 055 * <p> 056 * The use case here is to help prevent someone with otherwise mostly good intentions from altering a piece of information in a way they should not. This is <b>NOT</b> intended 057 * to defeat any serious attempt at discovering the original text. 058 * </p> 059 * 060 * <p> 061 * Think a hungry sales or marketing rep who stumbles across a config file with the entry <code>vending.machine.refill.day=wed</code> in it and tries to change that to 062 * <code>mon</code> in order to beat a case of the munchies. :) 063 * </p> 064 * 065 * <p> 066 * If the entry says <code>vending.machine.refill.day=cnc--jrq</code> instead of <code>vending.machine.refill.day=wed</code> they are far more likely to ask around before they 067 * change it <b>OR</b> just give up and head out to lunch instead. 068 * </p> 069 * 070 * @see reveal 071 */ 072 public static final String conceal(String text) { 073 if (text == null) { 074 return null; 075 } 076 Assert.noBlanks(text); 077 if (isConcealed(text)) { 078 return text; 079 } 080 Assert.notConcealed(text); 081 char[] chars = text.toCharArray(); 082 StringBuilder sb = new StringBuilder(); 083 sb.append(CONCEALED_PREFIX); 084 for (char c : chars) { 085 sb.append(Ascii.flip(c)); 086 } 087 return sb.toString(); 088 } 089 090 /** 091 * Reveal the original contents of a string concealed by the <code>conceal</code> method. 092 * 093 * @see conceal 094 */ 095 public static final String reveal(String text) { 096 if (text == null) { 097 return null; 098 } 099 Assert.noBlanks(text); 100 if (!isConcealed(text)) { 101 return text; 102 } 103 Assert.concealed(text); 104 String substring = removePrefix(text, CONCEALED_PREFIX); 105 char[] chars = substring.toCharArray(); 106 StringBuilder sb = new StringBuilder(); 107 for (char c : chars) { 108 sb.append(Ascii.flip(c)); 109 } 110 return sb.toString(); 111 } 112 113 /** 114 * Return true if <code>text</code> is concealed 115 */ 116 public static final boolean isConcealed(String text) { 117 return StringUtils.startsWith(text, CONCEALED_PREFIX); 118 } 119 120 /** 121 * If <code>strings</code> are <code>null</code> return <code>EMPTY_ARRAY</code>, otherwise return <code>strings</code>. 122 */ 123 public static final String[] toEmptyArray(String[] strings) { 124 if (strings == null) { 125 return EMPTY_ARRAY; 126 } else { 127 return strings; 128 } 129 } 130 131 /** 132 * Convert the tokens into a string delimited by the colon "<code>:</code>" character 133 * 134 * <pre> 135 * "foo","bar" ,"baz" -> foo:bar:baz 136 * "foo", null ,"baz" -> foo::baz 137 * "foo", "" ,"baz" -> foo::baz 138 * "foo", null , null -> foo:: 139 * null,"bar" , null -> :bar: 140 * </pre> 141 */ 142 public static final String getId(String... tokens) { 143 if (tokens == null) { 144 return null; 145 } 146 StringBuilder sb = new StringBuilder(); 147 for (int i = 0; i < tokens.length; i++) { 148 if (i != 0) { 149 sb.append(COLON); 150 } 151 sb.append(StringUtils.trimToEmpty(tokens[i])); 152 } 153 return sb.toString(); 154 } 155 156 /** 157 * Turn the string into CDATA - http://en.wikipedia.org/wiki/CDATA 158 */ 159 public static final String cdata(String s) { 160 if (s == null) { 161 return null; 162 } else { 163 return CDATA_PREFIX + s + CDATA_SUFFIX; 164 } 165 } 166 167 /** 168 * If <code>s</code> ends with <code>suffix</code>, remove it 169 */ 170 public static final String removeSuffix(String s, String suffix) { 171 if (StringUtils.endsWith(s, suffix)) { 172 int end = StringUtils.length(s) - StringUtils.length(suffix); 173 return StringUtils.substring(s, 0, end); 174 } else { 175 return s; 176 } 177 } 178 179 /** 180 * If <code>s</code> starts with <code>prefix</code>, remove it 181 */ 182 public static final String removePrefix(String s, String prefix) { 183 if (StringUtils.startsWith(s, prefix)) { 184 int beginIndex = StringUtils.length(prefix); 185 return StringUtils.substring(s, beginIndex); 186 } else { 187 return s; 188 } 189 } 190 191 /** 192 * Return true if <code>s</code> starts with <code>prefix</code> and ends with <code>suffix</code> 193 */ 194 public static final boolean matches(String s, String prefix, String suffix) { 195 return StringUtils.startsWith(s, prefix) && StringUtils.endsWith(s, suffix); 196 } 197 198 public static final String remove(String s, String prefix, String suffix) { 199 String returnValue = s; 200 returnValue = removePrefix(returnValue, prefix); 201 returnValue = removeSuffix(returnValue, suffix); 202 return returnValue; 203 } 204 205 /** 206 * If s is null return "" otherwise return s 207 */ 208 public static final String toEmpty(String s) { 209 if (s == null) { 210 return ""; 211 } else { 212 return s; 213 } 214 } 215 216 public static final String getAsciiString(byte[] bytes) { 217 return getString(bytes, ASCII); 218 } 219 220 public static final String getUTF8String(byte[] bytes) { 221 return getString(bytes, UTF8); 222 } 223 224 public static final String getString(byte[] bytes, String encoding) { 225 if (bytes == null) { 226 return null; 227 } 228 if (encoding == null) { 229 return new String(bytes); 230 } 231 try { 232 return new String(bytes, encoding); 233 } catch (UnsupportedEncodingException e) { 234 throw new IllegalArgumentException(e); 235 } 236 } 237 238 public static final byte[] getAsciiBytes(String s) { 239 if (s == null) { 240 return null; 241 } else { 242 return getBytes(s, ASCII); 243 } 244 } 245 246 public static final byte[] getUTF8Bytes(String s) { 247 if (s == null) { 248 return null; 249 } else { 250 return getBytes(s, UTF8); 251 } 252 } 253 254 public static final byte[] getBytes(String s, String encoding) { 255 if (s == null) { 256 return null; 257 } 258 if (encoding == null) { 259 return s.getBytes(); 260 } 261 try { 262 return s.getBytes(encoding); 263 } catch (UnsupportedEncodingException e) { 264 throw new IllegalArgumentException(e); 265 } 266 } 267 268 public static final boolean contains(List<String> tokens, String value, boolean caseSensitive) { 269 for (String token : tokens) { 270 if (equals(token, value, caseSensitive)) { 271 return true; 272 } 273 } 274 return false; 275 } 276 277 public static final boolean equals(String s1, String s2, boolean caseSensitive) { 278 if (caseSensitive) { 279 return StringUtils.equals(s1, s2); 280 } else { 281 return StringUtils.equalsIgnoreCase(s1, s2); 282 } 283 } 284 285 /** 286 * Combine <code>tokens</code> into a <code>String</code> 287 */ 288 public static final String toString(String[] tokens) { 289 if (tokens == null) { 290 return null; 291 } 292 StringBuilder sb = new StringBuilder(); 293 for (String token : tokens) { 294 sb.append(token); 295 } 296 return sb.toString(); 297 } 298 299 /** 300 * Convert dots to forward slashes and trim. 301 */ 302 public static final String getPath(String s) { 303 return trim(replace(s, DOT, FORWARD_SLASH)); 304 } 305 306 /** 307 * Surround the string with double quotes. 308 */ 309 public static final String quote(String s) { 310 return s == null ? null : DOUBLE_QUOTE + s + DOUBLE_QUOTE; 311 } 312 313 /** 314 * Split comma separated values into tokens, optionally trimming the tokens. 315 */ 316 public static final String[] splitCSV(String csv, boolean trim) { 317 return split(csv, COMMA, trim); 318 } 319 320 /** 321 * Split comma separated values into tokens, trimming as we go. 322 */ 323 public static final String[] splitAndTrimCSV(String csv) { 324 return splitCSV(csv, true); 325 } 326 327 /** 328 * Split the string into tokens using the indicated separator, trimming as we go. 329 */ 330 public static final String[] splitAndTrim(String s, String separatorChars) { 331 return split(s, separatorChars, true); 332 } 333 334 /** 335 * Split the string into tokens using the indicated separator chars, optionally trimming the tokens. 336 */ 337 public static final String[] split(String s, String separatorChars, boolean trim) { 338 String[] tokens = StringUtils.split(s, separatorChars); 339 if (tokens == null) { 340 return null; 341 } 342 for (int i = 0; i < tokens.length; i++) { 343 tokens[i] = trim ? StringUtils.trim(tokens[i]) : tokens[i]; 344 } 345 return tokens; 346 } 347 348 /** 349 * Replace carriage returns and linefeeds with a space 350 */ 351 public static final String flatten(String s) { 352 return flatten(s, SPACE, SPACE); 353 } 354 355 /** 356 * Replace carriage returns with <code>cr</code> and linefeeds with <code>lf</code>. 357 */ 358 public static final String flatten(String s, String cr, String lf) { 359 return StringUtils.replace(StringUtils.replace(s, CR, cr), LF, lf); 360 } 361 362 /** 363 * Replace <code>cr</code> with carriage return and <code>lf</code> with linefeed. 364 */ 365 public static final String inflate(String s, String cr, String lf) { 366 return StringUtils.replace(StringUtils.replace(s, cr, CR), lf, LF); 367 } 368}