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}