001/* 002 * Copyright 2011-2016 UnboundID Corp. 003 * 004 * This program is free software; you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License (GPLv2 only) 006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 007 * as published by the Free Software Foundation. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program; if not, see <http://www.gnu.org/licenses>. 016 */ 017 018package com.unboundid.scim.sdk; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Iterator; 023import java.util.List; 024 025/** 026 * This class provides a number of static utility functions. 027 */ 028public final class StaticUtils 029{ 030 /** 031 * Prevent this class from being instantiated. 032 */ 033 private StaticUtils() 034 { 035 // No implementation is required. 036 } 037 038 039 040 /** 041 * Retrieves an all-lowercase version of the provided string. 042 * 043 * @param s The string for which to retrieve the lowercase version. 044 * 045 * @return An all-lowercase version of the provided string. 046 */ 047 public static String toLowerCase(final String s) 048 { 049 if (s == null) 050 { 051 return null; 052 } 053 054 final int length = s.length(); 055 final char[] charArray = s.toCharArray(); 056 for (int i=0; i < length; i++) 057 { 058 switch (charArray[i]) 059 { 060 case 'A': 061 charArray[i] = 'a'; 062 break; 063 case 'B': 064 charArray[i] = 'b'; 065 break; 066 case 'C': 067 charArray[i] = 'c'; 068 break; 069 case 'D': 070 charArray[i] = 'd'; 071 break; 072 case 'E': 073 charArray[i] = 'e'; 074 break; 075 case 'F': 076 charArray[i] = 'f'; 077 break; 078 case 'G': 079 charArray[i] = 'g'; 080 break; 081 case 'H': 082 charArray[i] = 'h'; 083 break; 084 case 'I': 085 charArray[i] = 'i'; 086 break; 087 case 'J': 088 charArray[i] = 'j'; 089 break; 090 case 'K': 091 charArray[i] = 'k'; 092 break; 093 case 'L': 094 charArray[i] = 'l'; 095 break; 096 case 'M': 097 charArray[i] = 'm'; 098 break; 099 case 'N': 100 charArray[i] = 'n'; 101 break; 102 case 'O': 103 charArray[i] = 'o'; 104 break; 105 case 'P': 106 charArray[i] = 'p'; 107 break; 108 case 'Q': 109 charArray[i] = 'q'; 110 break; 111 case 'R': 112 charArray[i] = 'r'; 113 break; 114 case 'S': 115 charArray[i] = 's'; 116 break; 117 case 'T': 118 charArray[i] = 't'; 119 break; 120 case 'U': 121 charArray[i] = 'u'; 122 break; 123 case 'V': 124 charArray[i] = 'v'; 125 break; 126 case 'W': 127 charArray[i] = 'w'; 128 break; 129 case 'X': 130 charArray[i] = 'x'; 131 break; 132 case 'Y': 133 charArray[i] = 'y'; 134 break; 135 case 'Z': 136 charArray[i] = 'z'; 137 break; 138 default: 139 if (charArray[i] > 0x7F) 140 { 141 return s.toLowerCase(); 142 } 143 break; 144 } 145 } 146 147 return new String(charArray); 148 } 149 150 151 152 /** 153 * Creates a string representation of the elements in the 154 * <code>list</code> separated by <code>separator</code>. 155 * 156 * @param list the list to print 157 * @param separator to use between elements 158 * 159 * @return String representing the list 160 */ 161 public static String listToString(final List<?> list, final String separator) 162 { 163 StringBuilder sb = new StringBuilder(); 164 for (int i = 0; i < list.size(); i++) { 165 sb.append(list.get(i)); 166 if (i < list.size() - 1) { 167 sb.append(separator); 168 } 169 } 170 return sb.toString(); 171 } 172 173 174 175 /** 176 * Creates a string representation of the elements in the 177 * <code>collection</code> separated by <code>separator</code>. 178 * 179 * @param collection to print 180 * @param separator to use between elements 181 * 182 * @return String representing the collection 183 */ 184 public static String collectionToString(final Collection<?> collection, 185 final String separator) 186 { 187 StringBuilder sb = new StringBuilder(); 188 for (Iterator<?> iter = collection.iterator(); iter.hasNext();) { 189 sb.append(iter.next()); 190 if (iter.hasNext()) { 191 sb.append(separator); 192 } 193 } 194 return sb.toString(); 195 } 196 197 198 199 /** 200 * Retrieves a UTF-8 byte representation of the provided string. 201 * 202 * @param s The string for which to retrieve the UTF-8 byte representation. 203 * 204 * @return The UTF-8 byte representation for the provided string. 205 */ 206 public static byte[] getUTF8Bytes(final String s) 207 { 208 final int length; 209 if((s == null) || ((length = s.length()) == 0)) 210 { 211 return new byte[0]; 212 } 213 214 final byte[] b = new byte[length]; 215 for(int i = 0; i < length; i++) 216 { 217 final char c = s.charAt(i); 218 if(c <= 0x7F) 219 { 220 b[i] = (byte) (c & 0x7F); 221 } 222 else 223 { 224 try 225 { 226 return s.getBytes("UTF-8"); 227 } 228 catch(Exception e) 229 { 230 // This should never happen. 231 Debug.debugException(e); 232 return s.getBytes(); 233 } 234 } 235 } 236 return b; 237 } 238 239 240 241 /** 242 * Retrieves a single-line string representation of the stack trace for the 243 * provided {@code Throwable}. It will include the unqualified name of the 244 * {@code Throwable} class, a list of source files and line numbers (if 245 * available) for the stack trace, and will also include the stack trace for 246 * the cause (if present). 247 * 248 * @param t The {@code Throwable} for which to retrieve the stack trace. 249 * 250 * @return A single-line string representation of the stack trace for the 251 * provided {@code Throwable}. 252 */ 253 public static String getStackTrace(final Throwable t) 254 { 255 final StringBuilder buffer = new StringBuilder(); 256 getStackTrace(t, buffer); 257 return buffer.toString(); 258 } 259 260 261 262 /** 263 * Appends a single-line string representation of the stack trace for the 264 * provided {@code Throwable} to the given buffer. It will include the 265 * unqualified name of the {@code Throwable} class, a list of source files and 266 * line numbers (if available) for the stack trace, and will also include the 267 * stack trace for the cause (if present). 268 * 269 * @param t The {@code Throwable} for which to retrieve the stack 270 * trace. 271 * @param buffer The buffer to which the information should be appended. 272 */ 273 public static void getStackTrace(final Throwable t, 274 final StringBuilder buffer) 275 { 276 buffer.append(t.getClass().getSimpleName()); 277 buffer.append('('); 278 279 final String message = t.getMessage(); 280 if (message != null) 281 { 282 buffer.append("message='"); 283 buffer.append(message); 284 buffer.append("', "); 285 } 286 287 buffer.append("trace='"); 288 getStackTrace(t.getStackTrace(), buffer); 289 buffer.append('\''); 290 291 final Throwable cause = t.getCause(); 292 if (cause != null) 293 { 294 buffer.append(", cause="); 295 getStackTrace(cause, buffer); 296 } 297 buffer.append(", revision="); 298 buffer.append(Version.REVISION_ID); 299 buffer.append(')'); 300 } 301 302 303 304 /** 305 * Returns a single-line string representation of the stack trace. It will 306 * include a list of source files and line numbers (if available) for the 307 * stack trace. 308 * 309 * @param elements The stack trace. 310 * 311 * @return A single-line string representation of the stack trace. 312 */ 313 public static String getStackTrace(final StackTraceElement[] elements) 314 { 315 final StringBuilder buffer = new StringBuilder(); 316 getStackTrace(elements, buffer); 317 return buffer.toString(); 318 } 319 320 321 322 /** 323 * Appends a single-line string representation of the stack trace to the given 324 * buffer. It will include a list of source files and line numbers 325 * (if available) for the stack trace. 326 * 327 * @param elements The stack trace. 328 * @param buffer The buffer to which the information should be appended. 329 */ 330 public static void getStackTrace(final StackTraceElement[] elements, 331 final StringBuilder buffer) 332 { 333 for (int i=0; i < elements.length; i++) 334 { 335 if (i > 0) 336 { 337 buffer.append(" / "); 338 } 339 340 buffer.append(elements[i].getMethodName()); 341 buffer.append('('); 342 buffer.append(elements[i].getFileName()); 343 344 final int lineNumber = elements[i].getLineNumber(); 345 if (lineNumber > 0) 346 { 347 buffer.append(':'); 348 buffer.append(lineNumber); 349 } 350 buffer.append(')'); 351 } 352 } 353 354 355 356 /** 357 * Inspects the Throwable to obtain the root cause. This method walks through 358 * the exception chain to the last element (the "root" of the tree) and 359 * returns that exception. 360 * <p> 361 * This method handles recursive <code>cause</code> structures that 362 * might otherwise cause infinite loops. If the Throwable parameter has a 363 * <code>cause</code> of itself, then a reference to itself will be returned. 364 * If the <code>cause</code> chain loops, the last element in the chain before 365 * it loops is returned. 366 * 367 * @param t the Throwable to get the root cause for. This may be null. 368 * @return the root cause of the Throwable, or null if none is found or the 369 * input is null. 370 */ 371 public static Throwable getRootCause(final Throwable t) 372 { 373 if(t == null) 374 { 375 return null; 376 } 377 378 List<Throwable> chain = new ArrayList<Throwable>(10); 379 Throwable current = t; 380 while(current != null && !chain.contains(current)) 381 { 382 chain.add(current); 383 current = current.getCause(); 384 } 385 386 if(chain.size() < 2) 387 { 388 return t; 389 } 390 else 391 { 392 return chain.get(chain.size() - 1); 393 } 394 } 395}