001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.util;
028    
029    import static org.opends.messages.UtilityMessages.*;
030    import static org.opends.server.loggers.debug.DebugLogger.*;
031    import static org.opends.server.util.ServerConstants.*;
032    import org.opends.server.util.args.ArgumentException;
033    import org.opends.server.util.args.Argument;
034    
035    import java.io.BufferedReader;
036    import java.io.File;
037    import java.io.FileInputStream;
038    import java.io.FileOutputStream;
039    import java.io.IOException;
040    import java.io.InputStreamReader;
041    import java.io.InputStream;
042    import java.lang.reflect.InvocationTargetException;
043    import java.net.InetAddress;
044    import java.net.InetSocketAddress;
045    import java.net.ServerSocket;
046    import java.net.Socket;
047    import java.nio.ByteBuffer;
048    import java.text.ParseException;
049    import java.text.SimpleDateFormat;
050    import java.util.ArrayList;
051    import java.util.LinkedHashMap;
052    import java.util.LinkedHashSet;
053    import java.util.List;
054    import java.util.Map;
055    import java.util.RandomAccess;
056    import java.util.StringTokenizer;
057    import java.util.Date;
058    import java.util.TimeZone;
059    import java.util.Collection;
060    import java.util.Iterator;
061    
062    import org.opends.messages.Message;
063    import org.opends.messages.MessageBuilder;
064    import org.opends.messages.MessageDescriptor;
065    import org.opends.messages.ToolMessages;
066    import org.opends.server.core.DirectoryServer;
067    import org.opends.server.loggers.debug.DebugTracer;
068    import org.opends.server.types.Attribute;
069    import org.opends.server.types.AttributeType;
070    import org.opends.server.types.AttributeValue;
071    import org.opends.server.types.DN;
072    import org.opends.server.types.DebugLogLevel;
073    import org.opends.server.types.Entry;
074    import org.opends.server.types.IdentifiedException;
075    import org.opends.server.types.ObjectClass;
076    import org.opends.server.types.RDN;
077    
078    
079    /**
080     * This class defines a number of static utility methods that may be used
081     * throughout the server.  Note that because of the frequency with which these
082     * methods are expected to be used, very little debug logging will be performed
083     * to prevent the log from filling up with unimportant calls and to reduce the
084     * impact that debugging may have on performance.
085     */
086    @org.opends.server.types.PublicAPI(
087         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
088         mayInstantiate=false,
089         mayExtend=false,
090         mayInvoke=true)
091    public final class StaticUtils
092    {
093      /**
094       * The tracer object for the debug logger.
095       */
096      private static final DebugTracer TRACER = getTracer();
097    
098      /**
099       * Private constructor to prevent instantiation.
100       */
101      private StaticUtils() {
102        // No implementation required.
103      }
104    
105    
106    
107      /**
108       * Construct a byte array containing the UTF-8 encoding of the
109       * provided string. This is significantly faster
110       * than calling {@link String#getBytes(String)} for ASCII strings.
111       *
112       * @param s
113       *          The string to convert to a UTF-8 byte array.
114       * @return Returns a byte array containing the UTF-8 encoding of the
115       *         provided string.
116       */
117      public static byte[] getBytes(String s)
118      {
119        if (s == null) return null;
120    
121        try
122        {
123          char c;
124          int length = s.length();
125          byte[] returnArray = new byte[length];
126          for (int i=0; i < length; i++)
127          {
128            c = s.charAt(i);
129            returnArray[i] = (byte) (c & 0x0000007F);
130            if (c != returnArray[i])
131            {
132              return s.getBytes("UTF-8");
133            }
134          }
135    
136          return returnArray;
137        }
138        catch (Exception e)
139        {
140          if (debugEnabled())
141          {
142            TRACER.debugCaught(DebugLogLevel.ERROR, e);
143          }
144    
145          try
146          {
147            return s.getBytes("UTF-8");
148          }
149          catch (Exception e2)
150          {
151            if (debugEnabled())
152            {
153              TRACER.debugCaught(DebugLogLevel.ERROR, e2);
154            }
155    
156            return s.getBytes();
157          }
158        }
159      }
160    
161    
162    
163      /**
164       * Construct a byte array containing the UTF-8 encoding of the
165       * provided <code>char</code> array.
166       *
167       * @param chars
168       *          The character array to convert to a UTF-8 byte array.
169       * @return Returns a byte array containing the UTF-8 encoding of the
170       *         provided <code>char</code> array.
171       */
172      public static byte[] getBytes(char[] chars)
173      {
174        return getBytes(new String(chars));
175      }
176    
177    
178    
179      /**
180       * Retrieves a string representation of the provided byte in hexadecimal.
181       *
182       * @param  b  The byte for which to retrieve the hexadecimal string
183       *            representation.
184       *
185       * @return  The string representation of the provided byte in hexadecimal.
186       */
187      public static String byteToHex(byte b)
188      {
189        switch (b & 0xFF)
190        {
191          case 0x00:  return "00";
192          case 0x01:  return "01";
193          case 0x02:  return "02";
194          case 0x03:  return "03";
195          case 0x04:  return "04";
196          case 0x05:  return "05";
197          case 0x06:  return "06";
198          case 0x07:  return "07";
199          case 0x08:  return "08";
200          case 0x09:  return "09";
201          case 0x0A:  return "0A";
202          case 0x0B:  return "0B";
203          case 0x0C:  return "0C";
204          case 0x0D:  return "0D";
205          case 0x0E:  return "0E";
206          case 0x0F:  return "0F";
207          case 0x10:  return "10";
208          case 0x11:  return "11";
209          case 0x12:  return "12";
210          case 0x13:  return "13";
211          case 0x14:  return "14";
212          case 0x15:  return "15";
213          case 0x16:  return "16";
214          case 0x17:  return "17";
215          case 0x18:  return "18";
216          case 0x19:  return "19";
217          case 0x1A:  return "1A";
218          case 0x1B:  return "1B";
219          case 0x1C:  return "1C";
220          case 0x1D:  return "1D";
221          case 0x1E:  return "1E";
222          case 0x1F:  return "1F";
223          case 0x20:  return "20";
224          case 0x21:  return "21";
225          case 0x22:  return "22";
226          case 0x23:  return "23";
227          case 0x24:  return "24";
228          case 0x25:  return "25";
229          case 0x26:  return "26";
230          case 0x27:  return "27";
231          case 0x28:  return "28";
232          case 0x29:  return "29";
233          case 0x2A:  return "2A";
234          case 0x2B:  return "2B";
235          case 0x2C:  return "2C";
236          case 0x2D:  return "2D";
237          case 0x2E:  return "2E";
238          case 0x2F:  return "2F";
239          case 0x30:  return "30";
240          case 0x31:  return "31";
241          case 0x32:  return "32";
242          case 0x33:  return "33";
243          case 0x34:  return "34";
244          case 0x35:  return "35";
245          case 0x36:  return "36";
246          case 0x37:  return "37";
247          case 0x38:  return "38";
248          case 0x39:  return "39";
249          case 0x3A:  return "3A";
250          case 0x3B:  return "3B";
251          case 0x3C:  return "3C";
252          case 0x3D:  return "3D";
253          case 0x3E:  return "3E";
254          case 0x3F:  return "3F";
255          case 0x40:  return "40";
256          case 0x41:  return "41";
257          case 0x42:  return "42";
258          case 0x43:  return "43";
259          case 0x44:  return "44";
260          case 0x45:  return "45";
261          case 0x46:  return "46";
262          case 0x47:  return "47";
263          case 0x48:  return "48";
264          case 0x49:  return "49";
265          case 0x4A:  return "4A";
266          case 0x4B:  return "4B";
267          case 0x4C:  return "4C";
268          case 0x4D:  return "4D";
269          case 0x4E:  return "4E";
270          case 0x4F:  return "4F";
271          case 0x50:  return "50";
272          case 0x51:  return "51";
273          case 0x52:  return "52";
274          case 0x53:  return "53";
275          case 0x54:  return "54";
276          case 0x55:  return "55";
277          case 0x56:  return "56";
278          case 0x57:  return "57";
279          case 0x58:  return "58";
280          case 0x59:  return "59";
281          case 0x5A:  return "5A";
282          case 0x5B:  return "5B";
283          case 0x5C:  return "5C";
284          case 0x5D:  return "5D";
285          case 0x5E:  return "5E";
286          case 0x5F:  return "5F";
287          case 0x60:  return "60";
288          case 0x61:  return "61";
289          case 0x62:  return "62";
290          case 0x63:  return "63";
291          case 0x64:  return "64";
292          case 0x65:  return "65";
293          case 0x66:  return "66";
294          case 0x67:  return "67";
295          case 0x68:  return "68";
296          case 0x69:  return "69";
297          case 0x6A:  return "6A";
298          case 0x6B:  return "6B";
299          case 0x6C:  return "6C";
300          case 0x6D:  return "6D";
301          case 0x6E:  return "6E";
302          case 0x6F:  return "6F";
303          case 0x70:  return "70";
304          case 0x71:  return "71";
305          case 0x72:  return "72";
306          case 0x73:  return "73";
307          case 0x74:  return "74";
308          case 0x75:  return "75";
309          case 0x76:  return "76";
310          case 0x77:  return "77";
311          case 0x78:  return "78";
312          case 0x79:  return "79";
313          case 0x7A:  return "7A";
314          case 0x7B:  return "7B";
315          case 0x7C:  return "7C";
316          case 0x7D:  return "7D";
317          case 0x7E:  return "7E";
318          case 0x7F:  return "7F";
319          case 0x80:  return "80";
320          case 0x81:  return "81";
321          case 0x82:  return "82";
322          case 0x83:  return "83";
323          case 0x84:  return "84";
324          case 0x85:  return "85";
325          case 0x86:  return "86";
326          case 0x87:  return "87";
327          case 0x88:  return "88";
328          case 0x89:  return "89";
329          case 0x8A:  return "8A";
330          case 0x8B:  return "8B";
331          case 0x8C:  return "8C";
332          case 0x8D:  return "8D";
333          case 0x8E:  return "8E";
334          case 0x8F:  return "8F";
335          case 0x90:  return "90";
336          case 0x91:  return "91";
337          case 0x92:  return "92";
338          case 0x93:  return "93";
339          case 0x94:  return "94";
340          case 0x95:  return "95";
341          case 0x96:  return "96";
342          case 0x97:  return "97";
343          case 0x98:  return "98";
344          case 0x99:  return "99";
345          case 0x9A:  return "9A";
346          case 0x9B:  return "9B";
347          case 0x9C:  return "9C";
348          case 0x9D:  return "9D";
349          case 0x9E:  return "9E";
350          case 0x9F:  return "9F";
351          case 0xA0:  return "A0";
352          case 0xA1:  return "A1";
353          case 0xA2:  return "A2";
354          case 0xA3:  return "A3";
355          case 0xA4:  return "A4";
356          case 0xA5:  return "A5";
357          case 0xA6:  return "A6";
358          case 0xA7:  return "A7";
359          case 0xA8:  return "A8";
360          case 0xA9:  return "A9";
361          case 0xAA:  return "AA";
362          case 0xAB:  return "AB";
363          case 0xAC:  return "AC";
364          case 0xAD:  return "AD";
365          case 0xAE:  return "AE";
366          case 0xAF:  return "AF";
367          case 0xB0:  return "B0";
368          case 0xB1:  return "B1";
369          case 0xB2:  return "B2";
370          case 0xB3:  return "B3";
371          case 0xB4:  return "B4";
372          case 0xB5:  return "B5";
373          case 0xB6:  return "B6";
374          case 0xB7:  return "B7";
375          case 0xB8:  return "B8";
376          case 0xB9:  return "B9";
377          case 0xBA:  return "BA";
378          case 0xBB:  return "BB";
379          case 0xBC:  return "BC";
380          case 0xBD:  return "BD";
381          case 0xBE:  return "BE";
382          case 0xBF:  return "BF";
383          case 0xC0:  return "C0";
384          case 0xC1:  return "C1";
385          case 0xC2:  return "C2";
386          case 0xC3:  return "C3";
387          case 0xC4:  return "C4";
388          case 0xC5:  return "C5";
389          case 0xC6:  return "C6";
390          case 0xC7:  return "C7";
391          case 0xC8:  return "C8";
392          case 0xC9:  return "C9";
393          case 0xCA:  return "CA";
394          case 0xCB:  return "CB";
395          case 0xCC:  return "CC";
396          case 0xCD:  return "CD";
397          case 0xCE:  return "CE";
398          case 0xCF:  return "CF";
399          case 0xD0:  return "D0";
400          case 0xD1:  return "D1";
401          case 0xD2:  return "D2";
402          case 0xD3:  return "D3";
403          case 0xD4:  return "D4";
404          case 0xD5:  return "D5";
405          case 0xD6:  return "D6";
406          case 0xD7:  return "D7";
407          case 0xD8:  return "D8";
408          case 0xD9:  return "D9";
409          case 0xDA:  return "DA";
410          case 0xDB:  return "DB";
411          case 0xDC:  return "DC";
412          case 0xDD:  return "DD";
413          case 0xDE:  return "DE";
414          case 0xDF:  return "DF";
415          case 0xE0:  return "E0";
416          case 0xE1:  return "E1";
417          case 0xE2:  return "E2";
418          case 0xE3:  return "E3";
419          case 0xE4:  return "E4";
420          case 0xE5:  return "E5";
421          case 0xE6:  return "E6";
422          case 0xE7:  return "E7";
423          case 0xE8:  return "E8";
424          case 0xE9:  return "E9";
425          case 0xEA:  return "EA";
426          case 0xEB:  return "EB";
427          case 0xEC:  return "EC";
428          case 0xED:  return "ED";
429          case 0xEE:  return "EE";
430          case 0xEF:  return "EF";
431          case 0xF0:  return "F0";
432          case 0xF1:  return "F1";
433          case 0xF2:  return "F2";
434          case 0xF3:  return "F3";
435          case 0xF4:  return "F4";
436          case 0xF5:  return "F5";
437          case 0xF6:  return "F6";
438          case 0xF7:  return "F7";
439          case 0xF8:  return "F8";
440          case 0xF9:  return "F9";
441          case 0xFA:  return "FA";
442          case 0xFB:  return "FB";
443          case 0xFC:  return "FC";
444          case 0xFD:  return "FD";
445          case 0xFE:  return "FE";
446          case 0xFF:  return "FF";
447          default:    return "??";
448        }
449      }
450    
451    
452    
453      /**
454       * Retrieves a string representation of the provided byte in hexadecimal.
455       *
456       * @param  b  The byte for which to retrieve the hexadecimal string
457       *            representation.
458       *
459       * @return  The string representation of the provided byte in hexadecimal
460       *          using lowercase characters.
461       */
462      public static String byteToLowerHex(byte b)
463      {
464        switch (b & 0xFF)
465        {
466          case 0x00:  return "00";
467          case 0x01:  return "01";
468          case 0x02:  return "02";
469          case 0x03:  return "03";
470          case 0x04:  return "04";
471          case 0x05:  return "05";
472          case 0x06:  return "06";
473          case 0x07:  return "07";
474          case 0x08:  return "08";
475          case 0x09:  return "09";
476          case 0x0A:  return "0a";
477          case 0x0B:  return "0b";
478          case 0x0C:  return "0c";
479          case 0x0D:  return "0d";
480          case 0x0E:  return "0e";
481          case 0x0F:  return "0f";
482          case 0x10:  return "10";
483          case 0x11:  return "11";
484          case 0x12:  return "12";
485          case 0x13:  return "13";
486          case 0x14:  return "14";
487          case 0x15:  return "15";
488          case 0x16:  return "16";
489          case 0x17:  return "17";
490          case 0x18:  return "18";
491          case 0x19:  return "19";
492          case 0x1A:  return "1a";
493          case 0x1B:  return "1b";
494          case 0x1C:  return "1c";
495          case 0x1D:  return "1d";
496          case 0x1E:  return "1e";
497          case 0x1F:  return "1f";
498          case 0x20:  return "20";
499          case 0x21:  return "21";
500          case 0x22:  return "22";
501          case 0x23:  return "23";
502          case 0x24:  return "24";
503          case 0x25:  return "25";
504          case 0x26:  return "26";
505          case 0x27:  return "27";
506          case 0x28:  return "28";
507          case 0x29:  return "29";
508          case 0x2A:  return "2a";
509          case 0x2B:  return "2b";
510          case 0x2C:  return "2c";
511          case 0x2D:  return "2d";
512          case 0x2E:  return "2e";
513          case 0x2F:  return "2f";
514          case 0x30:  return "30";
515          case 0x31:  return "31";
516          case 0x32:  return "32";
517          case 0x33:  return "33";
518          case 0x34:  return "34";
519          case 0x35:  return "35";
520          case 0x36:  return "36";
521          case 0x37:  return "37";
522          case 0x38:  return "38";
523          case 0x39:  return "39";
524          case 0x3A:  return "3a";
525          case 0x3B:  return "3b";
526          case 0x3C:  return "3c";
527          case 0x3D:  return "3d";
528          case 0x3E:  return "3e";
529          case 0x3F:  return "3f";
530          case 0x40:  return "40";
531          case 0x41:  return "41";
532          case 0x42:  return "42";
533          case 0x43:  return "43";
534          case 0x44:  return "44";
535          case 0x45:  return "45";
536          case 0x46:  return "46";
537          case 0x47:  return "47";
538          case 0x48:  return "48";
539          case 0x49:  return "49";
540          case 0x4A:  return "4a";
541          case 0x4B:  return "4b";
542          case 0x4C:  return "4c";
543          case 0x4D:  return "4d";
544          case 0x4E:  return "4e";
545          case 0x4F:  return "4f";
546          case 0x50:  return "50";
547          case 0x51:  return "51";
548          case 0x52:  return "52";
549          case 0x53:  return "53";
550          case 0x54:  return "54";
551          case 0x55:  return "55";
552          case 0x56:  return "56";
553          case 0x57:  return "57";
554          case 0x58:  return "58";
555          case 0x59:  return "59";
556          case 0x5A:  return "5a";
557          case 0x5B:  return "5b";
558          case 0x5C:  return "5c";
559          case 0x5D:  return "5d";
560          case 0x5E:  return "5e";
561          case 0x5F:  return "5f";
562          case 0x60:  return "60";
563          case 0x61:  return "61";
564          case 0x62:  return "62";
565          case 0x63:  return "63";
566          case 0x64:  return "64";
567          case 0x65:  return "65";
568          case 0x66:  return "66";
569          case 0x67:  return "67";
570          case 0x68:  return "68";
571          case 0x69:  return "69";
572          case 0x6A:  return "6a";
573          case 0x6B:  return "6b";
574          case 0x6C:  return "6c";
575          case 0x6D:  return "6d";
576          case 0x6E:  return "6e";
577          case 0x6F:  return "6f";
578          case 0x70:  return "70";
579          case 0x71:  return "71";
580          case 0x72:  return "72";
581          case 0x73:  return "73";
582          case 0x74:  return "74";
583          case 0x75:  return "75";
584          case 0x76:  return "76";
585          case 0x77:  return "77";
586          case 0x78:  return "78";
587          case 0x79:  return "79";
588          case 0x7A:  return "7a";
589          case 0x7B:  return "7b";
590          case 0x7C:  return "7c";
591          case 0x7D:  return "7d";
592          case 0x7E:  return "7e";
593          case 0x7F:  return "7f";
594          case 0x80:  return "80";
595          case 0x81:  return "81";
596          case 0x82:  return "82";
597          case 0x83:  return "83";
598          case 0x84:  return "84";
599          case 0x85:  return "85";
600          case 0x86:  return "86";
601          case 0x87:  return "87";
602          case 0x88:  return "88";
603          case 0x89:  return "89";
604          case 0x8A:  return "8a";
605          case 0x8B:  return "8b";
606          case 0x8C:  return "8c";
607          case 0x8D:  return "8d";
608          case 0x8E:  return "8e";
609          case 0x8F:  return "8f";
610          case 0x90:  return "90";
611          case 0x91:  return "91";
612          case 0x92:  return "92";
613          case 0x93:  return "93";
614          case 0x94:  return "94";
615          case 0x95:  return "95";
616          case 0x96:  return "96";
617          case 0x97:  return "97";
618          case 0x98:  return "98";
619          case 0x99:  return "99";
620          case 0x9A:  return "9a";
621          case 0x9B:  return "9b";
622          case 0x9C:  return "9c";
623          case 0x9D:  return "9d";
624          case 0x9E:  return "9e";
625          case 0x9F:  return "9f";
626          case 0xA0:  return "a0";
627          case 0xA1:  return "a1";
628          case 0xA2:  return "a2";
629          case 0xA3:  return "a3";
630          case 0xA4:  return "a4";
631          case 0xA5:  return "a5";
632          case 0xA6:  return "a6";
633          case 0xA7:  return "a7";
634          case 0xA8:  return "a8";
635          case 0xA9:  return "a9";
636          case 0xAA:  return "aa";
637          case 0xAB:  return "ab";
638          case 0xAC:  return "ac";
639          case 0xAD:  return "ad";
640          case 0xAE:  return "ae";
641          case 0xAF:  return "af";
642          case 0xB0:  return "b0";
643          case 0xB1:  return "b1";
644          case 0xB2:  return "b2";
645          case 0xB3:  return "b3";
646          case 0xB4:  return "b4";
647          case 0xB5:  return "b5";
648          case 0xB6:  return "b6";
649          case 0xB7:  return "b7";
650          case 0xB8:  return "b8";
651          case 0xB9:  return "b9";
652          case 0xBA:  return "ba";
653          case 0xBB:  return "bb";
654          case 0xBC:  return "bc";
655          case 0xBD:  return "bd";
656          case 0xBE:  return "be";
657          case 0xBF:  return "bf";
658          case 0xC0:  return "c0";
659          case 0xC1:  return "c1";
660          case 0xC2:  return "c2";
661          case 0xC3:  return "c3";
662          case 0xC4:  return "c4";
663          case 0xC5:  return "c5";
664          case 0xC6:  return "c6";
665          case 0xC7:  return "c7";
666          case 0xC8:  return "c8";
667          case 0xC9:  return "c9";
668          case 0xCA:  return "ca";
669          case 0xCB:  return "cb";
670          case 0xCC:  return "cc";
671          case 0xCD:  return "cd";
672          case 0xCE:  return "ce";
673          case 0xCF:  return "cf";
674          case 0xD0:  return "d0";
675          case 0xD1:  return "d1";
676          case 0xD2:  return "d2";
677          case 0xD3:  return "d3";
678          case 0xD4:  return "d4";
679          case 0xD5:  return "d5";
680          case 0xD6:  return "d6";
681          case 0xD7:  return "d7";
682          case 0xD8:  return "d8";
683          case 0xD9:  return "d9";
684          case 0xDA:  return "da";
685          case 0xDB:  return "db";
686          case 0xDC:  return "dc";
687          case 0xDD:  return "dd";
688          case 0xDE:  return "de";
689          case 0xDF:  return "df";
690          case 0xE0:  return "e0";
691          case 0xE1:  return "e1";
692          case 0xE2:  return "e2";
693          case 0xE3:  return "e3";
694          case 0xE4:  return "e4";
695          case 0xE5:  return "e5";
696          case 0xE6:  return "e6";
697          case 0xE7:  return "e7";
698          case 0xE8:  return "e8";
699          case 0xE9:  return "e9";
700          case 0xEA:  return "ea";
701          case 0xEB:  return "eb";
702          case 0xEC:  return "ec";
703          case 0xED:  return "ed";
704          case 0xEE:  return "ee";
705          case 0xEF:  return "ef";
706          case 0xF0:  return "f0";
707          case 0xF1:  return "f1";
708          case 0xF2:  return "f2";
709          case 0xF3:  return "f3";
710          case 0xF4:  return "f4";
711          case 0xF5:  return "f5";
712          case 0xF6:  return "f6";
713          case 0xF7:  return "f7";
714          case 0xF8:  return "f8";
715          case 0xF9:  return "f9";
716          case 0xFA:  return "fa";
717          case 0xFB:  return "fb";
718          case 0xFC:  return "fc";
719          case 0xFD:  return "fd";
720          case 0xFE:  return "fe";
721          case 0xFF:  return "ff";
722          default:    return "??";
723        }
724      }
725    
726    
727    
728      /**
729       * Retrieves the printable ASCII representation of the provided byte.
730       *
731       * @param  b  The byte for which to retrieve the printable ASCII
732       *            representation.
733       *
734       * @return  The printable ASCII representation of the provided byte, or a
735       *          space if the provided byte does not have  printable ASCII
736       *          representation.
737       */
738      public static char byteToASCII(byte b)
739      {
740        if ((b >= 32) && (b <= 126))
741        {
742          return (char) b;
743        }
744    
745        return ' ';
746      }
747    
748    
749    
750      /**
751       * Retrieves a string representation of the contents of the provided byte
752       * array using hexadecimal characters with no space between each byte.
753       *
754       * @param  b  The byte array containing the data.
755       *
756       * @return  A string representation of the contents of the provided byte
757       *          array using hexadecimal characters.
758       */
759      public static String bytesToHexNoSpace(byte[] b)
760      {
761        if ((b == null) || (b.length == 0))
762        {
763          return "";
764        }
765    
766        int arrayLength = b.length;
767        StringBuilder buffer = new StringBuilder(arrayLength * 2);
768    
769        for (int i=0; i < arrayLength; i++)
770        {
771          buffer.append(byteToHex(b[i]));
772        }
773    
774        return buffer.toString();
775      }
776    
777    
778    
779      /**
780       * Retrieves a string representation of the contents of the provided byte
781       * array using hexadecimal characters and a space between each byte.
782       *
783       * @param  b  The byte array containing the data.
784       *
785       * @return  A string representation of the contents of the provided byte
786       *          array using hexadecimal characters.
787       */
788      public static String bytesToHex(byte[] b)
789      {
790        if ((b == null) || (b.length == 0))
791        {
792          return "";
793        }
794    
795        int arrayLength = b.length;
796        StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
797        buffer.append(byteToHex(b[0]));
798    
799        for (int i=1; i < arrayLength; i++)
800        {
801          buffer.append(" ");
802          buffer.append(byteToHex(b[i]));
803        }
804    
805        return buffer.toString();
806      }
807    
808    
809    
810      /**
811       * Retrieves a string representation of the contents of the provided byte
812       * array using hexadecimal characters and a colon between each byte.
813       *
814       * @param  b  The byte array containing the data.
815       *
816       * @return  A string representation of the contents of the provided byte
817       *          array using hexadecimal characters.
818       */
819      public static String bytesToColonDelimitedHex(byte[] b)
820      {
821        if ((b == null) || (b.length == 0))
822        {
823          return "";
824        }
825    
826        int arrayLength = b.length;
827        StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
828        buffer.append(byteToHex(b[0]));
829    
830        for (int i=1; i < arrayLength; i++)
831        {
832          buffer.append(":");
833          buffer.append(byteToHex(b[i]));
834        }
835    
836        return buffer.toString();
837      }
838    
839    
840    
841      /**
842       * Retrieves a string representation of the contents of the provided byte
843       * buffer using hexadecimal characters and a space between each byte.
844       *
845       * @param  b  The byte buffer containing the data.
846       *
847       * @return  A string representation of the contents of the provided byte
848       *          buffer using hexadecimal characters.
849       */
850      public static String bytesToHex(ByteBuffer b)
851      {
852        if (b == null)
853        {
854          return "";
855        }
856    
857        int position = b.position();
858        int limit    = b.limit();
859        int length   = limit - position;
860    
861        if (length == 0)
862        {
863          return "";
864        }
865    
866        StringBuilder buffer = new StringBuilder((length - 1) * 3 + 2);
867        buffer.append(byteToHex(b.get()));
868    
869        for (int i=1; i < length; i++)
870        {
871          buffer.append(" ");
872          buffer.append(byteToHex(b.get()));
873        }
874    
875        b.position(position);
876        b.limit(limit);
877    
878        return buffer.toString();
879      }
880    
881    
882    
883      /**
884       * Appends a string representation of the provided byte array to the given
885       * buffer using the specified indent.  The data will be formatted with sixteen
886       * hex bytes in a row followed by the ASCII representation, then wrapping to a
887       * new line as necessary.
888       *
889       * @param  buffer  The buffer to which the information is to be appended.
890       * @param  b       The byte array containing the data to write.
891       * @param  indent  The number of spaces to indent the output.
892       */
893      public static void byteArrayToHexPlusAscii(StringBuilder buffer, byte[] b,
894                                                 int indent)
895      {
896        StringBuilder indentBuf = new StringBuilder(indent);
897        for (int i=0 ; i < indent; i++)
898        {
899          indentBuf.append(' ');
900        }
901    
902    
903    
904        int length = b.length;
905        int pos    = 0;
906        while ((length - pos) >= 16)
907        {
908          StringBuilder asciiBuf = new StringBuilder(17);
909    
910          buffer.append(indentBuf);
911          buffer.append(byteToHex(b[pos]));
912          asciiBuf.append(byteToASCII(b[pos]));
913          pos++;
914    
915          for (int i=1; i < 16; i++, pos++)
916          {
917            buffer.append(' ');
918            buffer.append(byteToHex(b[pos]));
919            asciiBuf.append(byteToASCII(b[pos]));
920    
921            if (i == 7)
922            {
923              buffer.append("  ");
924              asciiBuf.append(' ');
925            }
926          }
927    
928          buffer.append("  ");
929          buffer.append(asciiBuf);
930          buffer.append(EOL);
931        }
932    
933    
934        int remaining = (length - pos);
935        if (remaining > 0)
936        {
937          StringBuilder asciiBuf = new StringBuilder(remaining+1);
938    
939          buffer.append(indentBuf);
940          buffer.append(byteToHex(b[pos]));
941          asciiBuf.append(byteToASCII(b[pos]));
942          pos++;
943    
944          for (int i=1; i < 16; i++)
945          {
946            buffer.append(' ');
947    
948            if (i < remaining)
949            {
950              buffer.append(byteToHex(b[pos]));
951              asciiBuf.append(byteToASCII(b[pos]));
952              pos++;
953            }
954            else
955            {
956              buffer.append("  ");
957            }
958    
959            if (i == 7)
960            {
961              buffer.append("  ");
962    
963              if (i < remaining)
964              {
965                asciiBuf.append(' ');
966              }
967            }
968          }
969    
970          buffer.append("  ");
971          buffer.append(asciiBuf);
972          buffer.append(EOL);
973        }
974      }
975    
976    
977    
978      /**
979       * Appends a string representation of the remaining unread data in the
980       * provided byte buffer to the given buffer using the specified indent.
981       * The data will be formatted with sixteen hex bytes in a row followed by
982       * the ASCII representation, then wrapping to a new line as necessary.
983       * The state of the byte buffer is not changed.
984       *
985       * @param  buffer  The buffer to which the information is to be appended.
986       * @param  b       The byte buffer containing the data to write.
987       *                 The data from the position to the limit is written.
988       * @param  indent  The number of spaces to indent the output.
989       */
990      public static void byteArrayToHexPlusAscii(StringBuilder buffer, ByteBuffer b,
991                                                 int indent)
992      {
993        StringBuilder indentBuf = new StringBuilder(indent);
994        for (int i=0 ; i < indent; i++)
995        {
996          indentBuf.append(' ');
997        }
998    
999    
1000        int position = b.position();
1001        int limit    = b.limit();
1002        int length   = limit - position;
1003        int pos      = 0;
1004        while ((length - pos) >= 16)
1005        {
1006          StringBuilder asciiBuf = new StringBuilder(17);
1007    
1008          byte currentByte = b.get();
1009          buffer.append(indentBuf);
1010          buffer.append(byteToHex(currentByte));
1011          asciiBuf.append(byteToASCII(currentByte));
1012          pos++;
1013    
1014          for (int i=1; i < 16; i++, pos++)
1015          {
1016            currentByte = b.get();
1017            buffer.append(' ');
1018            buffer.append(byteToHex(currentByte));
1019            asciiBuf.append(byteToASCII(currentByte));
1020    
1021            if (i == 7)
1022            {
1023              buffer.append("  ");
1024              asciiBuf.append(' ');
1025            }
1026          }
1027    
1028          buffer.append("  ");
1029          buffer.append(asciiBuf);
1030          buffer.append(EOL);
1031        }
1032    
1033    
1034        int remaining = (length - pos);
1035        if (remaining > 0)
1036        {
1037          StringBuilder asciiBuf = new StringBuilder(remaining+1);
1038    
1039          byte currentByte = b.get();
1040          buffer.append(indentBuf);
1041          buffer.append(byteToHex(currentByte));
1042          asciiBuf.append(byteToASCII(currentByte));
1043    
1044          for (int i=1; i < 16; i++)
1045          {
1046            buffer.append(' ');
1047    
1048            if (i < remaining)
1049            {
1050              currentByte = b.get();
1051              buffer.append(byteToHex(currentByte));
1052              asciiBuf.append(byteToASCII(currentByte));
1053            }
1054            else
1055            {
1056              buffer.append("  ");
1057            }
1058    
1059            if (i == 7)
1060            {
1061              buffer.append("  ");
1062    
1063              if (i < remaining)
1064              {
1065                asciiBuf.append(' ');
1066              }
1067            }
1068          }
1069    
1070          buffer.append("  ");
1071          buffer.append(asciiBuf);
1072          buffer.append(EOL);
1073        }
1074    
1075        b.position(position);
1076        b.limit(limit);
1077      }
1078    
1079    
1080    
1081      /**
1082       * Retrieves a binary representation of the provided byte.  It will always be
1083       * a sequence of eight zeros and/or ones.
1084       *
1085       * @param  b  The byte for which to retrieve the binary representation.
1086       *
1087       * @return  The binary representation for the provided byte.
1088       */
1089      public static String byteToBinary(byte b)
1090      {
1091        switch (b & 0xFF)
1092        {
1093          case 0x00:  return "00000000";
1094          case 0x01:  return "00000001";
1095          case 0x02:  return "00000010";
1096          case 0x03:  return "00000011";
1097          case 0x04:  return "00000100";
1098          case 0x05:  return "00000101";
1099          case 0x06:  return "00000110";
1100          case 0x07:  return "00000111";
1101          case 0x08:  return "00001000";
1102          case 0x09:  return "00001001";
1103          case 0x0A:  return "00001010";
1104          case 0x0B:  return "00001011";
1105          case 0x0C:  return "00001100";
1106          case 0x0D:  return "00001101";
1107          case 0x0E:  return "00001110";
1108          case 0x0F:  return "00001111";
1109          case 0x10:  return "00010000";
1110          case 0x11:  return "00010001";
1111          case 0x12:  return "00010010";
1112          case 0x13:  return "00010011";
1113          case 0x14:  return "00010100";
1114          case 0x15:  return "00010101";
1115          case 0x16:  return "00010110";
1116          case 0x17:  return "00010111";
1117          case 0x18:  return "00011000";
1118          case 0x19:  return "00011001";
1119          case 0x1A:  return "00011010";
1120          case 0x1B:  return "00011011";
1121          case 0x1C:  return "00011100";
1122          case 0x1D:  return "00011101";
1123          case 0x1E:  return "00011110";
1124          case 0x1F:  return "00011111";
1125          case 0x20:  return "00100000";
1126          case 0x21:  return "00100001";
1127          case 0x22:  return "00100010";
1128          case 0x23:  return "00100011";
1129          case 0x24:  return "00100100";
1130          case 0x25:  return "00100101";
1131          case 0x26:  return "00100110";
1132          case 0x27:  return "00100111";
1133          case 0x28:  return "00101000";
1134          case 0x29:  return "00101001";
1135          case 0x2A:  return "00101010";
1136          case 0x2B:  return "00101011";
1137          case 0x2C:  return "00101100";
1138          case 0x2D:  return "00101101";
1139          case 0x2E:  return "00101110";
1140          case 0x2F:  return "00101111";
1141          case 0x30:  return "00110000";
1142          case 0x31:  return "00110001";
1143          case 0x32:  return "00110010";
1144          case 0x33:  return "00110011";
1145          case 0x34:  return "00110100";
1146          case 0x35:  return "00110101";
1147          case 0x36:  return "00110110";
1148          case 0x37:  return "00110111";
1149          case 0x38:  return "00111000";
1150          case 0x39:  return "00111001";
1151          case 0x3A:  return "00111010";
1152          case 0x3B:  return "00111011";
1153          case 0x3C:  return "00111100";
1154          case 0x3D:  return "00111101";
1155          case 0x3E:  return "00111110";
1156          case 0x3F:  return "00111111";
1157          case 0x40:  return "01000000";
1158          case 0x41:  return "01000001";
1159          case 0x42:  return "01000010";
1160          case 0x43:  return "01000011";
1161          case 0x44:  return "01000100";
1162          case 0x45:  return "01000101";
1163          case 0x46:  return "01000110";
1164          case 0x47:  return "01000111";
1165          case 0x48:  return "01001000";
1166          case 0x49:  return "01001001";
1167          case 0x4A:  return "01001010";
1168          case 0x4B:  return "01001011";
1169          case 0x4C:  return "01001100";
1170          case 0x4D:  return "01001101";
1171          case 0x4E:  return "01001110";
1172          case 0x4F:  return "01001111";
1173          case 0x50:  return "01010000";
1174          case 0x51:  return "01010001";
1175          case 0x52:  return "01010010";
1176          case 0x53:  return "01010011";
1177          case 0x54:  return "01010100";
1178          case 0x55:  return "01010101";
1179          case 0x56:  return "01010110";
1180          case 0x57:  return "01010111";
1181          case 0x58:  return "01011000";
1182          case 0x59:  return "01011001";
1183          case 0x5A:  return "01011010";
1184          case 0x5B:  return "01011011";
1185          case 0x5C:  return "01011100";
1186          case 0x5D:  return "01011101";
1187          case 0x5E:  return "01011110";
1188          case 0x5F:  return "01011111";
1189          case 0x60:  return "01100000";
1190          case 0x61:  return "01100001";
1191          case 0x62:  return "01100010";
1192          case 0x63:  return "01100011";
1193          case 0x64:  return "01100100";
1194          case 0x65:  return "01100101";
1195          case 0x66:  return "01100110";
1196          case 0x67:  return "01100111";
1197          case 0x68:  return "01101000";
1198          case 0x69:  return "01101001";
1199          case 0x6A:  return "01101010";
1200          case 0x6B:  return "01101011";
1201          case 0x6C:  return "01101100";
1202          case 0x6D:  return "01101101";
1203          case 0x6E:  return "01101110";
1204          case 0x6F:  return "01101111";
1205          case 0x70:  return "01110000";
1206          case 0x71:  return "01110001";
1207          case 0x72:  return "01110010";
1208          case 0x73:  return "01110011";
1209          case 0x74:  return "01110100";
1210          case 0x75:  return "01110101";
1211          case 0x76:  return "01110110";
1212          case 0x77:  return "01110111";
1213          case 0x78:  return "01111000";
1214          case 0x79:  return "01111001";
1215          case 0x7A:  return "01111010";
1216          case 0x7B:  return "01111011";
1217          case 0x7C:  return "01111100";
1218          case 0x7D:  return "01111101";
1219          case 0x7E:  return "01111110";
1220          case 0x7F:  return "01111111";
1221          case 0x80:  return "10000000";
1222          case 0x81:  return "10000001";
1223          case 0x82:  return "10000010";
1224          case 0x83:  return "10000011";
1225          case 0x84:  return "10000100";
1226          case 0x85:  return "10000101";
1227          case 0x86:  return "10000110";
1228          case 0x87:  return "10000111";
1229          case 0x88:  return "10001000";
1230          case 0x89:  return "10001001";
1231          case 0x8A:  return "10001010";
1232          case 0x8B:  return "10001011";
1233          case 0x8C:  return "10001100";
1234          case 0x8D:  return "10001101";
1235          case 0x8E:  return "10001110";
1236          case 0x8F:  return "10001111";
1237          case 0x90:  return "10010000";
1238          case 0x91:  return "10010001";
1239          case 0x92:  return "10010010";
1240          case 0x93:  return "10010011";
1241          case 0x94:  return "10010100";
1242          case 0x95:  return "10010101";
1243          case 0x96:  return "10010110";
1244          case 0x97:  return "10010111";
1245          case 0x98:  return "10011000";
1246          case 0x99:  return "10011001";
1247          case 0x9A:  return "10011010";
1248          case 0x9B:  return "10011011";
1249          case 0x9C:  return "10011100";
1250          case 0x9D:  return "10011101";
1251          case 0x9E:  return "10011110";
1252          case 0x9F:  return "10011111";
1253          case 0xA0:  return "10100000";
1254          case 0xA1:  return "10100001";
1255          case 0xA2:  return "10100010";
1256          case 0xA3:  return "10100011";
1257          case 0xA4:  return "10100100";
1258          case 0xA5:  return "10100101";
1259          case 0xA6:  return "10100110";
1260          case 0xA7:  return "10100111";
1261          case 0xA8:  return "10101000";
1262          case 0xA9:  return "10101001";
1263          case 0xAA:  return "10101010";
1264          case 0xAB:  return "10101011";
1265          case 0xAC:  return "10101100";
1266          case 0xAD:  return "10101101";
1267          case 0xAE:  return "10101110";
1268          case 0xAF:  return "10101111";
1269          case 0xB0:  return "10110000";
1270          case 0xB1:  return "10110001";
1271          case 0xB2:  return "10110010";
1272          case 0xB3:  return "10110011";
1273          case 0xB4:  return "10110100";
1274          case 0xB5:  return "10110101";
1275          case 0xB6:  return "10110110";
1276          case 0xB7:  return "10110111";
1277          case 0xB8:  return "10111000";
1278          case 0xB9:  return "10111001";
1279          case 0xBA:  return "10111010";
1280          case 0xBB:  return "10111011";
1281          case 0xBC:  return "10111100";
1282          case 0xBD:  return "10111101";
1283          case 0xBE:  return "10111110";
1284          case 0xBF:  return "10111111";
1285          case 0xC0:  return "11000000";
1286          case 0xC1:  return "11000001";
1287          case 0xC2:  return "11000010";
1288          case 0xC3:  return "11000011";
1289          case 0xC4:  return "11000100";
1290          case 0xC5:  return "11000101";
1291          case 0xC6:  return "11000110";
1292          case 0xC7:  return "11000111";
1293          case 0xC8:  return "11001000";
1294          case 0xC9:  return "11001001";
1295          case 0xCA:  return "11001010";
1296          case 0xCB:  return "11001011";
1297          case 0xCC:  return "11001100";
1298          case 0xCD:  return "11001101";
1299          case 0xCE:  return "11001110";
1300          case 0xCF:  return "11001111";
1301          case 0xD0:  return "11010000";
1302          case 0xD1:  return "11010001";
1303          case 0xD2:  return "11010010";
1304          case 0xD3:  return "11010011";
1305          case 0xD4:  return "11010100";
1306          case 0xD5:  return "11010101";
1307          case 0xD6:  return "11010110";
1308          case 0xD7:  return "11010111";
1309          case 0xD8:  return "11011000";
1310          case 0xD9:  return "11011001";
1311          case 0xDA:  return "11011010";
1312          case 0xDB:  return "11011011";
1313          case 0xDC:  return "11011100";
1314          case 0xDD:  return "11011101";
1315          case 0xDE:  return "11011110";
1316          case 0xDF:  return "11011111";
1317          case 0xE0:  return "11100000";
1318          case 0xE1:  return "11100001";
1319          case 0xE2:  return "11100010";
1320          case 0xE3:  return "11100011";
1321          case 0xE4:  return "11100100";
1322          case 0xE5:  return "11100101";
1323          case 0xE6:  return "11100110";
1324          case 0xE7:  return "11100111";
1325          case 0xE8:  return "11101000";
1326          case 0xE9:  return "11101001";
1327          case 0xEA:  return "11101010";
1328          case 0xEB:  return "11101011";
1329          case 0xEC:  return "11101100";
1330          case 0xED:  return "11101101";
1331          case 0xEE:  return "11101110";
1332          case 0xEF:  return "11101111";
1333          case 0xF0:  return "11110000";
1334          case 0xF1:  return "11110001";
1335          case 0xF2:  return "11110010";
1336          case 0xF3:  return "11110011";
1337          case 0xF4:  return "11110100";
1338          case 0xF5:  return "11110101";
1339          case 0xF6:  return "11110110";
1340          case 0xF7:  return "11110111";
1341          case 0xF8:  return "11111000";
1342          case 0xF9:  return "11111001";
1343          case 0xFA:  return "11111010";
1344          case 0xFB:  return "11111011";
1345          case 0xFC:  return "11111100";
1346          case 0xFD:  return "11111101";
1347          case 0xFE:  return "11111110";
1348          case 0xFF:  return "11111111";
1349          default:    return "????????";
1350        }
1351      }
1352    
1353    
1354    
1355      /**
1356       * Compare two byte arrays for order. Returns a negative integer,
1357       * zero, or a positive integer as the first argument is less than,
1358       * equal to, or greater than the second.
1359       *
1360       * @param a
1361       *          The first byte array to be compared.
1362       * @param a2
1363       *          The second byte array to be compared.
1364       * @return Returns a negative integer, zero, or a positive integer
1365       *         if the first byte array is less than, equal to, or greater
1366       *         than the second.
1367       */
1368      public static int compare(byte[] a, byte[] a2) {
1369        if (a == a2) {
1370          return 0;
1371        }
1372    
1373        if (a == null) {
1374          return -1;
1375        }
1376    
1377        if (a2 == null) {
1378          return 1;
1379        }
1380    
1381        int minLength = Math.min(a.length, a2.length);
1382        for (int i = 0; i < minLength; i++) {
1383          if (a[i] != a2[i]) {
1384            if (a[i] < a2[i]) {
1385              return -1;
1386            } else if (a[i] > a2[i]) {
1387              return 1;
1388            }
1389          }
1390        }
1391    
1392        return (a.length - a2.length);
1393      }
1394    
1395    
1396    
1397      /**
1398       * Indicates whether the two array lists are equal. They will be
1399       * considered equal if they have the same number of elements, and
1400       * the corresponding elements between them are equal (in the same
1401       * order).
1402       *
1403       * @param list1
1404       *          The first list for which to make the determination.
1405       * @param list2
1406       *          The second list for which to make the determination.
1407       * @return <CODE>true</CODE> if the two array lists are equal, or
1408       *         <CODE>false</CODE> if they are not.
1409       */
1410      public static boolean listsAreEqual(List<?> list1, List<?> list2)
1411      {
1412        if (list1 == null)
1413        {
1414          return (list2 == null);
1415        }
1416        else if (list2 == null)
1417        {
1418          return false;
1419        }
1420    
1421        int numElements = list1.size();
1422        if (numElements != list2.size())
1423        {
1424          return false;
1425        }
1426    
1427        // If either of the lists doesn't support random access, then fall back
1428        // on their equals methods and go ahead and create some garbage with the
1429        // iterators.
1430        if (!(list1 instanceof RandomAccess) ||
1431            !(list2 instanceof RandomAccess))
1432        {
1433          return list1.equals(list2);
1434        }
1435    
1436        // Otherwise we can just retrieve the elements efficiently via their index.
1437        for (int i=0; i < numElements; i++)
1438        {
1439          Object o1 = list1.get(i);
1440          Object o2 = list2.get(i);
1441    
1442          if (o1 == null)
1443          {
1444            if (o2 != null)
1445            {
1446              return false;
1447            }
1448          }
1449          else if (! o1.equals(o2))
1450          {
1451            return false;
1452          }
1453        }
1454    
1455        return true;
1456      }
1457    
1458    
1459      /**
1460       * Return true if and only if o1 and o2 are both null or o1.equals(o2).
1461       *
1462       * @param o1 the first object to compare
1463       * @param o2 the second object to compare
1464       * @return true iff o1 and o2 are equal
1465       */
1466      public static boolean objectsAreEqual(Object o1, Object o2)
1467      {
1468        if (o1 == null)
1469        {
1470          return (o2 == null);
1471        }
1472        else
1473        {
1474          return o1.equals(o2);
1475        }
1476      }
1477    
1478    
1479    
1480      /**
1481       * Retrieves the best human-readable message for the provided exception.  For
1482       * exceptions defined in the OpenDS project, it will attempt to use the
1483       * message (combining it with the message ID if available).  For some
1484       * exceptions that use encapsulation (e.g., InvocationTargetException), it
1485       * will be unwrapped and the cause will be treated.  For all others, the
1486       *
1487       *
1488       * @param  t  The {@code Throwable} object for which to retrieve the message.
1489       *
1490       * @return  The human-readable message generated for the provided exception.
1491       */
1492      public static Message getExceptionMessage(Throwable t)
1493      {
1494        if (t instanceof IdentifiedException)
1495        {
1496          IdentifiedException ie = (IdentifiedException) t;
1497    
1498          StringBuilder message = new StringBuilder();
1499          message.append(ie.getMessage());
1500          message.append(" (id=");
1501          Message ieMsg = ie.getMessageObject();
1502          if (ieMsg != null) {
1503            message.append(ieMsg.getDescriptor().getId());
1504          } else {
1505            message.append(MessageDescriptor.NULL_ID);
1506          }
1507          message.append(")");
1508          return Message.raw(message.toString());
1509        }
1510        else if (t instanceof NullPointerException)
1511        {
1512          StackTraceElement[] stackElements = t.getStackTrace();
1513    
1514          MessageBuilder message = new MessageBuilder();
1515          message.append("NullPointerException(");
1516          message.append(stackElements[0].getFileName());
1517          message.append(":");
1518          message.append(stackElements[0].getLineNumber());
1519          message.append(")");
1520          return message.toMessage();
1521        }
1522        else if ((t instanceof InvocationTargetException) &&
1523                 (t.getCause() != null))
1524        {
1525          return getExceptionMessage(t.getCause());
1526        }
1527        else
1528        {
1529          StringBuilder message = new StringBuilder();
1530    
1531          String className = t.getClass().getName();
1532          int periodPos = className.lastIndexOf('.');
1533          if (periodPos > 0)
1534          {
1535            message.append(className.substring(periodPos+1));
1536          }
1537          else
1538          {
1539            message.append(className);
1540          }
1541    
1542          message.append("(");
1543          if (t.getMessage() == null)
1544          {
1545            StackTraceElement[] stackElements = t.getStackTrace();
1546            message.append(stackElements[0].getFileName());
1547            message.append(":");
1548            message.append(stackElements[0].getLineNumber());
1549    
1550            // FIXME Temporary to debug issue 2256.
1551            if (t instanceof IllegalStateException)
1552            {
1553              for (int i = 1; i < stackElements.length; i++)
1554              {
1555                message.append(' ');
1556                message.append(stackElements[i].getFileName());
1557                message.append(":");
1558                message.append(stackElements[i].getLineNumber());
1559              }
1560            }
1561          }
1562          else
1563          {
1564            message.append(t.getMessage());
1565          }
1566    
1567          message.append(")");
1568    
1569          return Message.raw(message.toString());
1570        }
1571      }
1572    
1573    
1574    
1575      /**
1576       * Retrieves a stack trace from the provided exception as a single-line
1577       * string.
1578       *
1579       * @param  t  The exception for which to retrieve the stack trace.
1580       *
1581       * @return  A stack trace from the provided exception as a single-line string.
1582       */
1583      public static String stackTraceToSingleLineString(Throwable t)
1584      {
1585        StringBuilder buffer = new StringBuilder();
1586        stackTraceToSingleLineString(buffer, t);
1587        return buffer.toString();
1588      }
1589    
1590    
1591    
1592      /**
1593       * Appends a single-line string representation of the provided exception to
1594       * the given buffer.
1595       *
1596       * @param  buffer  The buffer to which the information is to be appended.
1597       * @param  t       The exception for which to retrieve the stack trace.
1598       */
1599      public static void stackTraceToSingleLineString(StringBuilder buffer,
1600                                                      Throwable t)
1601      {
1602        if (t == null)
1603        {
1604          return;
1605        }
1606    
1607        if (DynamicConstants.DEBUG_BUILD)
1608        {
1609          buffer.append(t);
1610    
1611          for (StackTraceElement e : t.getStackTrace())
1612          {
1613            buffer.append(" / ");
1614            buffer.append(e.getFileName());
1615            buffer.append(":");
1616            buffer.append(e.getLineNumber());
1617          }
1618    
1619          while (t.getCause() != null)
1620          {
1621            t = t.getCause();
1622    
1623            buffer.append("; caused by ");
1624            buffer.append(t);
1625    
1626            for (StackTraceElement e : t.getStackTrace())
1627            {
1628              buffer.append(" / ");
1629              buffer.append(e.getFileName());
1630              buffer.append(":");
1631              buffer.append(e.getLineNumber());
1632            }
1633          }
1634        }
1635        else
1636        {
1637          if ((t instanceof InvocationTargetException) && (t.getCause() != null))
1638          {
1639            t = t.getCause();
1640          }
1641    
1642          String message = t.getMessage();
1643          if ((message == null) || (message.length() == 0))
1644          {
1645            String className = t.getClass().getName();
1646            try
1647            {
1648              className = className.substring(className.lastIndexOf('.') + 1);
1649            } catch (Exception e) { /* ignored */ }
1650            buffer.append(className);
1651          }
1652          else
1653          {
1654            buffer.append(message);
1655          }
1656    
1657          int i=0;
1658          buffer.append(" (");
1659          for (StackTraceElement e : t.getStackTrace())
1660          {
1661            if (i > 20)
1662            {
1663              buffer.append(" ...");
1664              break;
1665            }
1666            else if (i > 0)
1667            {
1668              buffer.append(" ");
1669            }
1670    
1671            buffer.append(e.getFileName());
1672            buffer.append(":");
1673            buffer.append(e.getLineNumber());
1674            i++;
1675          }
1676    
1677          buffer.append(")");
1678        }
1679      }
1680    
1681    
1682    
1683      /**
1684       * Retrieves a string representation of the stack trace for the provided
1685       * exception.
1686       *
1687       * @param  t  The exception for which to retrieve the stack trace.
1688       *
1689       * @return  A string representation of the stack trace for the provided
1690       *          exception.
1691       */
1692      public static String stackTraceToString(Throwable t)
1693      {
1694        StringBuilder buffer = new StringBuilder();
1695        stackTraceToString(buffer, t);
1696        return buffer.toString();
1697      }
1698    
1699    
1700    
1701      /**
1702       * Appends a string representation of the stack trace for the provided
1703       * exception to the given buffer.
1704       *
1705       * @param  buffer  The buffer to which the information is to be appended.
1706       * @param  t       The exception for which to retrieve the stack trace.
1707       */
1708      public static void stackTraceToString(StringBuilder buffer, Throwable t)
1709      {
1710        if (t == null)
1711        {
1712          return;
1713        }
1714    
1715        buffer.append(t);
1716    
1717        for (StackTraceElement e : t.getStackTrace())
1718        {
1719          buffer.append(EOL);
1720          buffer.append("  ");
1721          buffer.append(e.getClassName());
1722          buffer.append(".");
1723          buffer.append(e.getMethodName());
1724          buffer.append("(");
1725          buffer.append(e.getFileName());
1726          buffer.append(":");
1727          buffer.append(e.getLineNumber());
1728          buffer.append(")");
1729        }
1730    
1731        while (t.getCause() != null)
1732        {
1733          t = t.getCause();
1734          buffer.append(EOL);
1735          buffer.append("Caused by ");
1736          buffer.append(t);
1737    
1738          for (StackTraceElement e : t.getStackTrace())
1739          {
1740            buffer.append(EOL);
1741            buffer.append("  ");
1742            buffer.append(e.getClassName());
1743            buffer.append(".");
1744            buffer.append(e.getMethodName());
1745            buffer.append("(");
1746            buffer.append(e.getFileName());
1747            buffer.append(":");
1748            buffer.append(e.getLineNumber());
1749            buffer.append(")");
1750          }
1751        }
1752    
1753        buffer.append(EOL);
1754      }
1755    
1756    
1757    
1758      /**
1759       * Retrieves a backtrace for the current thread consisting only of filenames
1760       * and line numbers that may be useful in debugging the origin of problems
1761       * that should not have happened.  Note that this may be an expensive
1762       * operation to perform, so it should only be used for error conditions or
1763       * debugging.
1764       *
1765       * @return  A backtrace for the current thread.
1766       */
1767      public static String getBacktrace()
1768      {
1769        StringBuilder buffer = new StringBuilder();
1770    
1771        StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1772    
1773        if (elements.length > 1)
1774        {
1775          buffer.append(elements[1].getFileName());
1776          buffer.append(":");
1777          buffer.append(elements[1].getLineNumber());
1778    
1779          for (int i=2; i < elements.length; i++)
1780          {
1781            buffer.append(" ");
1782            buffer.append(elements[i].getFileName());
1783            buffer.append(":");
1784            buffer.append(elements[i].getLineNumber());
1785          }
1786        }
1787    
1788        return buffer.toString();
1789      }
1790    
1791    
1792    
1793      /**
1794       * Retrieves a backtrace for the provided exception consisting of only
1795       * filenames and line numbers that may be useful in debugging the origin of
1796       * problems.  This is less expensive than the call to
1797       * <CODE>getBacktrace</CODE> without any arguments if an exception has already
1798       * been thrown.
1799       *
1800       * @param  t  The exception for which to obtain the backtrace.
1801       *
1802       * @return  A backtrace from the provided exception.
1803       */
1804      public static String getBacktrace(Throwable t)
1805      {
1806        StringBuilder buffer = new StringBuilder();
1807    
1808        StackTraceElement[] elements = t.getStackTrace();
1809    
1810        if (elements.length > 0)
1811        {
1812          buffer.append(elements[0].getFileName());
1813          buffer.append(":");
1814          buffer.append(elements[0].getLineNumber());
1815    
1816          for (int i=1; i < elements.length; i++)
1817          {
1818            buffer.append(" ");
1819            buffer.append(elements[i].getFileName());
1820            buffer.append(":");
1821            buffer.append(elements[i].getLineNumber());
1822          }
1823        }
1824    
1825        return buffer.toString();
1826      }
1827    
1828    
1829    
1830      /**
1831       * Indicates whether the provided character is a numeric digit.
1832       *
1833       * @param  c  The character for which to make the determination.
1834       *
1835       * @return  <CODE>true</CODE> if the provided character represents a numeric
1836       *          digit, or <CODE>false</CODE> if not.
1837       */
1838      public static boolean isDigit(char c)
1839      {
1840        switch (c)
1841        {
1842          case '0':
1843          case '1':
1844          case '2':
1845          case '3':
1846          case '4':
1847          case '5':
1848          case '6':
1849          case '7':
1850          case '8':
1851          case '9':
1852            return true;
1853          default:
1854            return false;
1855        }
1856      }
1857    
1858    
1859    
1860      /**
1861       * Indicates whether the provided character is an ASCII alphabetic character.
1862       *
1863       * @param  c  The character for which to make the determination.
1864       *
1865       * @return  <CODE>true</CODE> if the provided value is an uppercase or
1866       *          lowercase ASCII alphabetic character, or <CODE>false</CODE> if it
1867       *          is not.
1868       */
1869      public static boolean isAlpha(char c)
1870      {
1871        switch (c)
1872        {
1873          case 'A':
1874          case 'B':
1875          case 'C':
1876          case 'D':
1877          case 'E':
1878          case 'F':
1879          case 'G':
1880          case 'H':
1881          case 'I':
1882          case 'J':
1883          case 'K':
1884          case 'L':
1885          case 'M':
1886          case 'N':
1887          case 'O':
1888          case 'P':
1889          case 'Q':
1890          case 'R':
1891          case 'S':
1892          case 'T':
1893          case 'U':
1894          case 'V':
1895          case 'W':
1896          case 'X':
1897          case 'Y':
1898          case 'Z':
1899            return true;
1900    
1901          case '[':
1902          case '\\':
1903          case ']':
1904          case '^':
1905          case '_':
1906          case '`':
1907            // Making sure all possible cases are present in one contiguous range
1908            // can result in a performance improvement.
1909            return false;
1910    
1911          case 'a':
1912          case 'b':
1913          case 'c':
1914          case 'd':
1915          case 'e':
1916          case 'f':
1917          case 'g':
1918          case 'h':
1919          case 'i':
1920          case 'j':
1921          case 'k':
1922          case 'l':
1923          case 'm':
1924          case 'n':
1925          case 'o':
1926          case 'p':
1927          case 'q':
1928          case 'r':
1929          case 's':
1930          case 't':
1931          case 'u':
1932          case 'v':
1933          case 'w':
1934          case 'x':
1935          case 'y':
1936          case 'z':
1937            return true;
1938          default:
1939            return false;
1940        }
1941      }
1942    
1943    
1944    
1945      /**
1946       * Indicates whether the provided character is a hexadecimal digit.
1947       *
1948       * @param  c  The character for which to make the determination.
1949       *
1950       * @return  <CODE>true</CODE> if the provided character represents a
1951       *          hexadecimal digit, or <CODE>false</CODE> if not.
1952       */
1953      public static boolean isHexDigit(char c)
1954      {
1955        switch (c)
1956        {
1957          case '0':
1958          case '1':
1959          case '2':
1960          case '3':
1961          case '4':
1962          case '5':
1963          case '6':
1964          case '7':
1965          case '8':
1966          case '9':
1967          case 'A':
1968          case 'B':
1969          case 'C':
1970          case 'D':
1971          case 'E':
1972          case 'F':
1973          case 'a':
1974          case 'b':
1975          case 'c':
1976          case 'd':
1977          case 'e':
1978          case 'f':
1979            return true;
1980          default:
1981            return false;
1982        }
1983      }
1984    
1985    
1986    
1987      /**
1988       * Indicates whether the provided byte represents a hexadecimal digit.
1989       *
1990       * @param  b  The byte for which to make the determination.
1991       *
1992       * @return  <CODE>true</CODE> if the provided byte represents a hexadecimal
1993       *          digit, or <CODE>false</CODE> if not.
1994       */
1995      public static boolean isHexDigit(byte b)
1996      {
1997        switch (b)
1998        {
1999          case '0':
2000          case '1':
2001          case '2':
2002          case '3':
2003          case '4':
2004          case '5':
2005          case '6':
2006          case '7':
2007          case '8':
2008          case '9':
2009          case 'A':
2010          case 'B':
2011          case 'C':
2012          case 'D':
2013          case 'E':
2014          case 'F':
2015          case 'a':
2016          case 'b':
2017          case 'c':
2018          case 'd':
2019          case 'e':
2020          case 'f':
2021            return true;
2022          default:
2023            return false;
2024        }
2025      }
2026    
2027    
2028    
2029      /**
2030       * Converts the provided hexadecimal string to a byte array.
2031       *
2032       * @param  hexString  The hexadecimal string to convert to a byte array.
2033       *
2034       * @return  The byte array containing the binary representation of the
2035       *          provided hex string.
2036       *
2037       * @throws  ParseException  If the provided string contains invalid
2038       *                          hexadecimal digits or does not contain an even
2039       *                          number of digits.
2040       */
2041      public static byte[] hexStringToByteArray(String hexString)
2042             throws ParseException
2043      {
2044        int length;
2045        if ((hexString == null) || ((length = hexString.length()) == 0))
2046        {
2047          return new byte[0];
2048        }
2049    
2050    
2051        if ((length % 2) == 1)
2052        {
2053          Message message = ERR_HEX_DECODE_INVALID_LENGTH.get(hexString);
2054          throw new ParseException(message.toString(), 0);
2055        }
2056    
2057    
2058        int pos = 0;
2059        int arrayLength = (length / 2);
2060        byte[] returnArray = new byte[arrayLength];
2061        for (int i=0; i < arrayLength; i++)
2062        {
2063          switch (hexString.charAt(pos++))
2064          {
2065            case '0':
2066              returnArray[i] = 0x00;
2067              break;
2068            case '1':
2069              returnArray[i] = 0x10;
2070              break;
2071            case '2':
2072              returnArray[i] = 0x20;
2073              break;
2074            case '3':
2075              returnArray[i] = 0x30;
2076              break;
2077            case '4':
2078              returnArray[i] = 0x40;
2079              break;
2080            case '5':
2081              returnArray[i] = 0x50;
2082              break;
2083            case '6':
2084              returnArray[i] = 0x60;
2085              break;
2086            case '7':
2087              returnArray[i] = 0x70;
2088              break;
2089            case '8':
2090              returnArray[i] = (byte) 0x80;
2091              break;
2092            case '9':
2093              returnArray[i] = (byte) 0x90;
2094              break;
2095            case 'A':
2096            case 'a':
2097              returnArray[i] = (byte) 0xA0;
2098              break;
2099            case 'B':
2100            case 'b':
2101              returnArray[i] = (byte) 0xB0;
2102              break;
2103            case 'C':
2104            case 'c':
2105              returnArray[i] = (byte) 0xC0;
2106              break;
2107            case 'D':
2108            case 'd':
2109              returnArray[i] = (byte) 0xD0;
2110              break;
2111            case 'E':
2112            case 'e':
2113              returnArray[i] = (byte) 0xE0;
2114              break;
2115            case 'F':
2116            case 'f':
2117              returnArray[i] = (byte) 0xF0;
2118              break;
2119            default:
2120              Message message = ERR_HEX_DECODE_INVALID_CHARACTER.get(
2121                  hexString, hexString.charAt(pos-1));
2122              throw new ParseException(message.toString(), 0);
2123          }
2124    
2125          switch (hexString.charAt(pos++))
2126          {
2127            case '0':
2128              // No action required.
2129              break;
2130            case '1':
2131              returnArray[i] |= 0x01;
2132              break;
2133            case '2':
2134              returnArray[i] |= 0x02;
2135              break;
2136            case '3':
2137              returnArray[i] |= 0x03;
2138              break;
2139            case '4':
2140              returnArray[i] |= 0x04;
2141              break;
2142            case '5':
2143              returnArray[i] |= 0x05;
2144              break;
2145            case '6':
2146              returnArray[i] |= 0x06;
2147              break;
2148            case '7':
2149              returnArray[i] |= 0x07;
2150              break;
2151            case '8':
2152              returnArray[i] |= 0x08;
2153              break;
2154            case '9':
2155              returnArray[i] |= 0x09;
2156              break;
2157            case 'A':
2158            case 'a':
2159              returnArray[i] |= 0x0A;
2160              break;
2161            case 'B':
2162            case 'b':
2163              returnArray[i] |= 0x0B;
2164              break;
2165            case 'C':
2166            case 'c':
2167              returnArray[i] |= 0x0C;
2168              break;
2169            case 'D':
2170            case 'd':
2171              returnArray[i] |= 0x0D;
2172              break;
2173            case 'E':
2174            case 'e':
2175              returnArray[i] |= 0x0E;
2176              break;
2177            case 'F':
2178            case 'f':
2179              returnArray[i] |= 0x0F;
2180              break;
2181            default:
2182              Message message = ERR_HEX_DECODE_INVALID_CHARACTER.get(
2183                  hexString, hexString.charAt(pos-1));
2184              throw new ParseException(message.toString(), 0);
2185          }
2186        }
2187    
2188        return returnArray;
2189      }
2190    
2191    
2192    
2193      /**
2194       * Indicates whether the provided value needs to be base64-encoded if it is
2195       * represented in LDIF form.
2196       *
2197       * @param  valueBytes  The binary representation of the attribute value for
2198       *                     which to make the determination.
2199       *
2200       * @return  <CODE>true</CODE> if the value needs to be base64-encoded if it is
2201       *          represented in LDIF form, or <CODE>false</CODE> if not.
2202       */
2203      public static boolean needsBase64Encoding(byte[] valueBytes)
2204      {
2205        int length;
2206        if ((valueBytes == null) || ((length = valueBytes.length) == 0))
2207        {
2208          return false;
2209        }
2210    
2211    
2212        // If the value starts with a space, colon, or less than, then it needs to
2213        // be base64-encoded.
2214        switch (valueBytes[0])
2215        {
2216          case 0x20: // Space
2217          case 0x3A: // Colon
2218          case 0x3C: // Less-than
2219            return true;
2220        }
2221    
2222    
2223        // If the value ends with a space, then it needs to be base64-encoded.
2224        if ((length > 1) && (valueBytes[length-1] == 0x20))
2225        {
2226          return true;
2227        }
2228    
2229    
2230        // If the value contains a null, newline, or return character, then it needs
2231        // to be base64-encoded.
2232        for (byte b : valueBytes)
2233        {
2234          if ((b > 127) || (b < 0))
2235          {
2236            return true;
2237          }
2238    
2239          switch (b)
2240          {
2241            case 0x00: // Null
2242            case 0x0A: // New line
2243            case 0x0D: // Carriage return
2244              return true;
2245          }
2246        }
2247    
2248    
2249        // If we've made it here, then there's no reason to base64-encode.
2250        return false;
2251      }
2252    
2253    
2254    
2255      /**
2256       * Indicates whether the provided value needs to be base64-encoded if it is
2257       * represented in LDIF form.
2258       *
2259       * @param  valueString  The string representation of the attribute value for
2260       *                      which to make the determination.
2261       *
2262       * @return  <CODE>true</CODE> if the value needs to be base64-encoded if it is
2263       *          represented in LDIF form, or <CODE>false</CODE> if not.
2264       */
2265      public static boolean needsBase64Encoding(String valueString)
2266      {
2267        int length;
2268        if ((valueString == null) || ((length = valueString.length()) == 0))
2269        {
2270          return false;
2271        }
2272    
2273    
2274        // If the value starts with a space, colon, or less than, then it needs to
2275        // be base64-encoded.
2276        switch (valueString.charAt(0))
2277        {
2278          case ' ':
2279          case ':':
2280          case '<':
2281            return true;
2282        }
2283    
2284    
2285        // If the value ends with a space, then it needs to be base64-encoded.
2286        if ((length > 1) && (valueString.charAt(length-1) == ' '))
2287        {
2288          return true;
2289        }
2290    
2291    
2292        // If the value contains a null, newline, or return character, then it needs
2293        // to be base64-encoded.
2294        for (int i=0; i < length; i++)
2295        {
2296          char c = valueString.charAt(i);
2297          if ((c <= 0) || (c == 0x0A) || (c == 0x0D) || (c > 127))
2298          {
2299            return true;
2300          }
2301        }
2302    
2303    
2304        // If we've made it here, then there's no reason to base64-encode.
2305        return false;
2306      }
2307    
2308    
2309    
2310      /**
2311       * Indicates whether the use of the exec method will be allowed on this
2312       * system.  It will be allowed by default, but that capability will be removed
2313       * if the org.opends.server.DisableExec system property is set and has any
2314       * value other than "false", "off", "no", or "0".
2315       *
2316       * @return  <CODE>true</CODE> if the use of the exec method should be allowed,
2317       *          or <CODE>false</CODE> if it should not be allowed.
2318       */
2319      public static boolean mayUseExec()
2320      {
2321        return (! DirectoryServer.getEnvironmentConfig().disableExec());
2322      }
2323    
2324    
2325    
2326      /**
2327       * Executes the specified command on the system and captures its output.  This
2328       * will not return until the specified process has completed.
2329       *
2330       * @param  command           The command to execute.
2331       * @param  args              The set of arguments to provide to the command.
2332       * @param  workingDirectory  The working directory to use for the command, or
2333       *                           <CODE>null</CODE> if the default directory
2334       *                           should be used.
2335       * @param  environment       The set of environment variables that should be
2336       *                           set when executing the command, or
2337       *                           <CODE>null</CODE> if none are needed.
2338       * @param  output            The output generated by the command while it was
2339       *                           running.  This will include both standard
2340       *                           output and standard error.  It may be
2341       *                           <CODE>null</CODE> if the output does not need to
2342       *                           be captured.
2343       *
2344       * @return  The exit code for the command.
2345       *
2346       * @throws  IOException  If an I/O problem occurs while trying to execute the
2347       *                       command.
2348       *
2349       * @throws  SecurityException  If the security policy will not allow the
2350       *                             command to be executed.
2351       *
2352       * @throws InterruptedException If the current thread is interrupted by
2353       *                              another thread while it is waiting, then
2354       *                              the wait is ended and an InterruptedException
2355       *                              is thrown.
2356       */
2357      public static int exec(String command, String[] args, File workingDirectory,
2358                             Map<String,String> environment, List<String> output)
2359             throws IOException, SecurityException, InterruptedException
2360      {
2361        // See whether we'll allow the use of exec on this system.  If not, then
2362        // throw an exception.
2363        if (! mayUseExec())
2364        {
2365          Message message = ERR_EXEC_DISABLED.get(String.valueOf(command));
2366          throw new SecurityException(message.toString());
2367        }
2368    
2369    
2370        ArrayList<String> commandAndArgs = new ArrayList<String>();
2371        commandAndArgs.add(command);
2372        if ((args != null) && (args.length > 0))
2373        {
2374          for (String arg : args)
2375          {
2376            commandAndArgs.add(arg);
2377          }
2378        }
2379    
2380        ProcessBuilder processBuilder = new ProcessBuilder(commandAndArgs);
2381        processBuilder.redirectErrorStream(true);
2382    
2383        if ((workingDirectory != null) && workingDirectory.isDirectory())
2384        {
2385          processBuilder.directory(workingDirectory);
2386        }
2387    
2388        if ((environment != null) && (! environment.isEmpty()))
2389        {
2390          processBuilder.environment().putAll(environment);
2391        }
2392    
2393        Process process = processBuilder.start();
2394    
2395        // We must exhaust stdout and stderr before calling waitfor. Since we
2396        // redirected the error stream, we just have to read from stdout.
2397        InputStream processStream =  process.getInputStream();
2398        BufferedReader reader =
2399            new BufferedReader(new InputStreamReader(processStream));
2400        String line = null;
2401    
2402        try
2403        {
2404          while((line = reader.readLine()) != null)
2405          {
2406            if(output != null)
2407            {
2408              output.add(line);
2409            }
2410          }
2411        }
2412        catch(IOException ioe)
2413        {
2414          // If this happens, then we have no choice but to forcefully terminate
2415          // the process.
2416          try
2417          {
2418            process.destroy();
2419          }
2420          catch (Exception e)
2421          {
2422            if (debugEnabled())
2423            {
2424              TRACER.debugCaught(DebugLogLevel.ERROR, e);
2425            }
2426          }
2427    
2428          throw ioe;
2429        }
2430        finally
2431        {
2432          try
2433          {
2434            reader.close();
2435          }
2436          catch(IOException e)
2437          {
2438            if (debugEnabled())
2439            {
2440              TRACER.debugCaught(DebugLogLevel.ERROR, e);
2441            }
2442          }
2443        }
2444    
2445        return process.waitFor();
2446      }
2447    
2448    
2449    
2450      /**
2451       * Indicates whether the provided string contains a name or OID for a schema
2452       * element like an attribute type or objectclass.
2453       *
2454       * @param  element        The string containing the substring for which to
2455       *                        make the determination.
2456       * @param  startPos       The position of the first character that is to be
2457       *                        checked.
2458       * @param  endPos         The position of the first character after the start
2459       *                        position that is not to be checked.
2460       * @param  invalidReason  The buffer to which the invalid reason is to be
2461       *                        appended if a problem is found.
2462       *
2463       * @return  <CODE>true</CODE> if the provided string contains a valid name or
2464       *          OID for a schema element, or <CODE>false</CODE> if it does not.
2465       */
2466      public static boolean isValidSchemaElement(String element, int startPos,
2467                                                 int endPos,
2468                                                 MessageBuilder invalidReason)
2469      {
2470        if ((element == null) || (startPos >= endPos))
2471        {
2472          invalidReason.append(ERR_SCHEMANAME_EMPTY_VALUE.get());
2473          return false;
2474        }
2475    
2476    
2477        char c = element.charAt(startPos);
2478        if (isAlpha(c))
2479        {
2480          // This can only be a name and not an OID.  The only remaining characters
2481          // must be letters, digits, dashes, and possibly the underscore.
2482          for (int i=startPos+1; i < endPos; i++)
2483          {
2484            c = element.charAt(i);
2485            if (! (isAlpha(c) || isDigit(c) || (c == '-') ||
2486                   ((c == '_') && DirectoryServer.allowAttributeNameExceptions())))
2487            {
2488              // This is an illegal character for an attribute name.
2489              invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(element, c, i));
2490              return false;
2491            }
2492          }
2493        }
2494        else if (isDigit(c))
2495        {
2496          // This should indicate an OID, but it may also be a name if name
2497          // exceptions are enabled.  Since we don't know for sure, we'll just
2498          // hold off until we know for sure.
2499          boolean isKnown    = (! DirectoryServer.allowAttributeNameExceptions());
2500          boolean isNumeric  = true;
2501          boolean lastWasDot = false;
2502    
2503          for (int i=startPos+1; i < endPos; i++)
2504          {
2505            c = element.charAt(i);
2506            if (c == '.')
2507            {
2508              if (isKnown)
2509              {
2510                if (isNumeric)
2511                {
2512                  // This is probably legal unless the last character was also a
2513                  // period.
2514                  if (lastWasDot)
2515                  {
2516                    invalidReason.append(ERR_SCHEMANAME_CONSECUTIVE_PERIODS.get(
2517                            element, i));
2518                    return false;
2519                  }
2520                  else
2521                  {
2522                    lastWasDot = true;
2523                  }
2524                }
2525                else
2526                {
2527                  // This is an illegal character.
2528                  invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
2529                          element, c, i));
2530                  return false;
2531                }
2532              }
2533              else
2534              {
2535                // Now we know that this must be a numeric OID and not an attribute
2536                // name with exceptions allowed.
2537                lastWasDot = true;
2538                isKnown    = true;
2539                isNumeric  = true;
2540              }
2541            }
2542            else
2543            {
2544              lastWasDot = false;
2545    
2546              if (isAlpha(c) || (c == '-') || (c == '_'))
2547              {
2548                if (isKnown)
2549                {
2550                  if (isNumeric)
2551                  {
2552                    // This is an illegal character for a numeric OID.
2553                    invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
2554                            element, c, i));
2555                    return false;
2556                  }
2557                }
2558                else
2559                {
2560                  // Now we know that this must be an attribute name with exceptions
2561                  // allowed and not a numeric OID.
2562                  isKnown   = true;
2563                  isNumeric = false;
2564                }
2565              }
2566              else if (! isDigit(c))
2567              {
2568                // This is an illegal character.
2569                invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
2570                        element, c, i));
2571                return false;
2572              }
2573            }
2574          }
2575        }
2576        else
2577        {
2578          // This is an illegal character.
2579          invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
2580                  element, c, startPos));
2581          return false;
2582        }
2583    
2584    
2585        // If we've gotten here, then the value is fine.
2586        return true;
2587      }
2588    
2589    
2590    
2591      /**
2592       * Indicates whether the provided TCP address is already in use.
2593       *
2594       * @param  address        IP address of the TCP address for which to make
2595       *                        the determination.
2596       * @param  port           TCP port number of the TCP address for which to
2597       *                        make the determination.
2598       * @param  allowReuse     Whether or not TCP address reuse is allowed when
2599       *                        making the determination.
2600       *
2601       * @return  <CODE>true</CODE> if the provided TCP address is already in
2602       *          use, or <CODE>false</CODE> otherwise.
2603       */
2604      public static boolean isAddressInUse(
2605        InetAddress address, int port,
2606        boolean allowReuse)
2607      {
2608        // Return pessimistic.
2609        boolean isInUse = true;
2610        Socket clientSocket = null;
2611        ServerSocket serverSocket = null;
2612        try {
2613          // HACK:
2614          // With dual stacks we can have a situation when INADDR_ANY/PORT
2615          // is bound in TCP4 space but available in TCP6 space and since
2616          // JavaServerSocket implemantation will always use TCP46 on dual
2617          // stacks the bind below will always succeed in such cases thus
2618          // shadowing anything that is already bound to INADDR_ANY/PORT.
2619          // While technically correct, with IPv4 and IPv6 being separate
2620          // address spaces, it presents a problem to end users because a
2621          // common case scenario is to have a single service serving both
2622          // address spaces ie listening to the same port in both spaces
2623          // on wildcard addresses 0 and ::. ServerSocket implemantation
2624          // does not provide any means of working with each address space
2625          // separately such as doing TCP4 or TCP6 only binds thus we have
2626          // to do a dummy connect to INADDR_ANY/PORT to check if it is
2627          // bound to something already. This is only needed for wildcard
2628          // addresses as specific IPv4 or IPv6 addresses will always be
2629          // handled in their respective address space.
2630          if (address.isAnyLocalAddress()) {
2631            clientSocket = new Socket();
2632            try {
2633              // This might fail on some stacks but this is the best we
2634              // can do. No need for explicit timeout since it is local
2635              // address and we have to know for sure unless it fails.
2636              clientSocket.connect(new InetSocketAddress(address, port));
2637            } catch (IOException e) {
2638            // Expected, ignore.
2639            }
2640            if (clientSocket.isConnected()) {
2641              isInUse = true;
2642            }
2643          }
2644          serverSocket = new ServerSocket();
2645          serverSocket.setReuseAddress(allowReuse);
2646          serverSocket.bind(new InetSocketAddress(address, port));
2647          isInUse = false;
2648        } catch (IOException e) {
2649          isInUse = true;
2650        } finally {
2651          try {
2652            if (serverSocket != null) {
2653              serverSocket.close();
2654            }
2655          } catch (Exception e) {}
2656          try {
2657            if (clientSocket != null) {
2658              clientSocket.close();
2659            }
2660          } catch (Exception e) {}
2661        }
2662        return isInUse;
2663      }
2664    
2665    
2666    
2667      /**
2668       * Retrieves a lowercase representation of the given string.  This
2669       * implementation presumes that the provided string will contain only ASCII
2670       * characters and is optimized for that case.  However, if a non-ASCII
2671       * character is encountered it will fall back on a more expensive algorithm
2672       * that will work properly for non-ASCII characters.
2673       *
2674       * @param  s  The string for which to obtain the lowercase representation.
2675       *
2676       * @return  The lowercase representation of the given string.
2677       */
2678      public static String toLowerCase(String s)
2679      {
2680        if (s == null)
2681        {
2682          return null;
2683        }
2684    
2685        StringBuilder buffer = new StringBuilder(s.length());
2686        toLowerCase(s, buffer);
2687        return buffer.toString();
2688      }
2689    
2690    
2691    
2692      /**
2693       * Appends a lowercase representation of the given string to the provided
2694       * buffer.  This implementation presumes that the provided string will contain
2695       * only ASCII characters and is optimized for that case.  However, if a
2696       * non-ASCII character is encountered it will fall back on a more expensive
2697       * algorithm that will work properly for non-ASCII characters.
2698       *
2699       * @param  s       The string for which to obtain the lowercase
2700       *                 representation.
2701       * @param  buffer  The buffer to which the lowercase form of the string should
2702       *                 be appended.
2703       */
2704      public static void toLowerCase(String s, StringBuilder buffer)
2705      {
2706        if (s == null)
2707        {
2708          return;
2709        }
2710    
2711        int length = s.length();
2712        for (int i=0; i < length; i++)
2713        {
2714          char c = s.charAt(i);
2715    
2716          if ((c & 0x7F) != c)
2717          {
2718            buffer.append(s.substring(i).toLowerCase());
2719            return;
2720          }
2721    
2722          switch (c)
2723          {
2724            case 'A':
2725              buffer.append('a');
2726              break;
2727            case 'B':
2728              buffer.append('b');
2729              break;
2730            case 'C':
2731              buffer.append('c');
2732              break;
2733            case 'D':
2734              buffer.append('d');
2735              break;
2736            case 'E':
2737              buffer.append('e');
2738              break;
2739            case 'F':
2740              buffer.append('f');
2741              break;
2742            case 'G':
2743              buffer.append('g');
2744              break;
2745            case 'H':
2746              buffer.append('h');
2747              break;
2748            case 'I':
2749              buffer.append('i');
2750              break;
2751            case 'J':
2752              buffer.append('j');
2753              break;
2754            case 'K':
2755              buffer.append('k');
2756              break;
2757            case 'L':
2758              buffer.append('l');
2759              break;
2760            case 'M':
2761              buffer.append('m');
2762              break;
2763            case 'N':
2764              buffer.append('n');
2765              break;
2766            case 'O':
2767              buffer.append('o');
2768              break;
2769            case 'P':
2770              buffer.append('p');
2771              break;
2772            case 'Q':
2773              buffer.append('q');
2774              break;
2775            case 'R':
2776              buffer.append('r');
2777              break;
2778            case 'S':
2779              buffer.append('s');
2780              break;
2781            case 'T':
2782              buffer.append('t');
2783              break;
2784            case 'U':
2785              buffer.append('u');
2786              break;
2787            case 'V':
2788              buffer.append('v');
2789              break;
2790            case 'W':
2791              buffer.append('w');
2792              break;
2793            case 'X':
2794              buffer.append('x');
2795              break;
2796            case 'Y':
2797              buffer.append('y');
2798              break;
2799            case 'Z':
2800              buffer.append('z');
2801              break;
2802            default:
2803              buffer.append(c);
2804          }
2805        }
2806      }
2807    
2808    
2809    
2810      /**
2811       * Appends a lowercase string representation of the contents of the given byte
2812       * array to the provided buffer, optionally trimming leading and trailing
2813       * spaces.  This implementation presumes that the provided string will contain
2814       * only ASCII characters and is optimized for that case.  However, if a
2815       * non-ASCII character is encountered it will fall back on a more expensive
2816       * algorithm that will work properly for non-ASCII characters.
2817       *
2818       * @param  b       The byte array for which to obtain the lowercase string
2819       *                 representation.
2820       * @param  buffer  The buffer to which the lowercase form of the string should
2821       *                 be appended.
2822       * @param  trim    Indicates whether leading and trailing spaces should be
2823       *                 omitted from the string representation.
2824       */
2825      public static void toLowerCase(byte[] b, StringBuilder buffer, boolean trim)
2826      {
2827        if (b == null)
2828        {
2829          return;
2830        }
2831    
2832        int length = b.length;
2833        for (int i=0; i < length; i++)
2834        {
2835          if ((b[i] & 0x7F) != b[i])
2836          {
2837            try
2838            {
2839              buffer.append(new String(b, i, (length-i), "UTF-8").toLowerCase());
2840            }
2841            catch (Exception e)
2842            {
2843              if (debugEnabled())
2844              {
2845                TRACER.debugCaught(DebugLogLevel.ERROR, e);
2846              }
2847              buffer.append(new String(b, i, (length - i)).toLowerCase());
2848            }
2849            break;
2850          }
2851    
2852          int bufferLength = buffer.length();
2853          switch (b[i])
2854          {
2855            case ' ':
2856              // If we don't care about trimming, then we can always append the
2857              // space.  Otherwise, only do so if there are other characters in the
2858              // value.
2859              if (trim && (bufferLength == 0))
2860              {
2861                break;
2862              }
2863    
2864              buffer.append(' ');
2865              break;
2866            case 'A':
2867              buffer.append('a');
2868              break;
2869            case 'B':
2870              buffer.append('b');
2871              break;
2872            case 'C':
2873              buffer.append('c');
2874              break;
2875            case 'D':
2876              buffer.append('d');
2877              break;
2878            case 'E':
2879              buffer.append('e');
2880              break;
2881            case 'F':
2882              buffer.append('f');
2883              break;
2884            case 'G':
2885              buffer.append('g');
2886              break;
2887            case 'H':
2888              buffer.append('h');
2889              break;
2890            case 'I':
2891              buffer.append('i');
2892              break;
2893            case 'J':
2894              buffer.append('j');
2895              break;
2896            case 'K':
2897              buffer.append('k');
2898              break;
2899            case 'L':
2900              buffer.append('l');
2901              break;
2902            case 'M':
2903              buffer.append('m');
2904              break;
2905            case 'N':
2906              buffer.append('n');
2907              break;
2908            case 'O':
2909              buffer.append('o');
2910              break;
2911            case 'P':
2912              buffer.append('p');
2913              break;
2914            case 'Q':
2915              buffer.append('q');
2916              break;
2917            case 'R':
2918              buffer.append('r');
2919              break;
2920            case 'S':
2921              buffer.append('s');
2922              break;
2923            case 'T':
2924              buffer.append('t');
2925              break;
2926            case 'U':
2927              buffer.append('u');
2928              break;
2929            case 'V':
2930              buffer.append('v');
2931              break;
2932            case 'W':
2933              buffer.append('w');
2934              break;
2935            case 'X':
2936              buffer.append('x');
2937              break;
2938            case 'Y':
2939              buffer.append('y');
2940              break;
2941            case 'Z':
2942              buffer.append('z');
2943              break;
2944            default:
2945              buffer.append((char) b[i]);
2946          }
2947        }
2948    
2949        if (trim)
2950        {
2951          // Strip off any trailing spaces.
2952          for (int i=buffer.length()-1; i > 0; i--)
2953          {
2954            if (buffer.charAt(i) == ' ')
2955            {
2956              buffer.delete(i, i+1);
2957            }
2958            else
2959            {
2960              break;
2961            }
2962          }
2963        }
2964      }
2965    
2966    
2967    
2968      /**
2969       * Retrieves an uppercase representation of the given string.  This
2970       * implementation presumes that the provided string will contain only ASCII
2971       * characters and is optimized for that case.  However, if a non-ASCII
2972       * character is encountered it will fall back on a more expensive algorithm
2973       * that will work properly for non-ASCII characters.
2974       *
2975       * @param  s  The string for which to obtain the uppercase representation.
2976       *
2977       * @return  The uppercase representation of the given string.
2978       */
2979      public static String toUpperCase(String s)
2980      {
2981        if (s == null)
2982        {
2983          return null;
2984        }
2985    
2986        StringBuilder buffer = new StringBuilder(s.length());
2987        toUpperCase(s, buffer);
2988        return buffer.toString();
2989      }
2990    
2991    
2992    
2993      /**
2994       * Appends an uppercase representation of the given string to the provided
2995       * buffer.  This implementation presumes that the provided string will contain
2996       * only ASCII characters and is optimized for that case.  However, if a
2997       * non-ASCII character is encountered it will fall back on a more expensive
2998       * algorithm that will work properly for non-ASCII characters.
2999       *
3000       * @param  s       The string for which to obtain the uppercase
3001       *                 representation.
3002       * @param  buffer  The buffer to which the uppercase form of the string should
3003       *                 be appended.
3004       */
3005      public static void toUpperCase(String s, StringBuilder buffer)
3006      {
3007        if (s == null)
3008        {
3009          return;
3010        }
3011    
3012        int length = s.length();
3013        for (int i=0; i < length; i++)
3014        {
3015          char c = s.charAt(i);
3016    
3017          if ((c & 0x7F) != c)
3018          {
3019            buffer.append(s.substring(i).toUpperCase());
3020            return;
3021          }
3022    
3023          switch (c)
3024          {
3025            case 'a':
3026              buffer.append('A');
3027              break;
3028            case 'b':
3029              buffer.append('B');
3030              break;
3031            case 'c':
3032              buffer.append('C');
3033              break;
3034            case 'd':
3035              buffer.append('D');
3036              break;
3037            case 'e':
3038              buffer.append('E');
3039              break;
3040            case 'f':
3041              buffer.append('F');
3042              break;
3043            case 'g':
3044              buffer.append('G');
3045              break;
3046            case 'h':
3047              buffer.append('H');
3048              break;
3049            case 'i':
3050              buffer.append('I');
3051              break;
3052            case 'j':
3053              buffer.append('J');
3054              break;
3055            case 'k':
3056              buffer.append('K');
3057              break;
3058            case 'l':
3059              buffer.append('L');
3060              break;
3061            case 'm':
3062              buffer.append('M');
3063              break;
3064            case 'n':
3065              buffer.append('N');
3066              break;
3067            case 'o':
3068              buffer.append('O');
3069              break;
3070            case 'p':
3071              buffer.append('P');
3072              break;
3073            case 'q':
3074              buffer.append('Q');
3075              break;
3076            case 'r':
3077              buffer.append('R');
3078              break;
3079            case 's':
3080              buffer.append('S');
3081              break;
3082            case 't':
3083              buffer.append('T');
3084              break;
3085            case 'u':
3086              buffer.append('U');
3087              break;
3088            case 'v':
3089              buffer.append('V');
3090              break;
3091            case 'w':
3092              buffer.append('W');
3093              break;
3094            case 'x':
3095              buffer.append('X');
3096              break;
3097            case 'y':
3098              buffer.append('Y');
3099              break;
3100            case 'z':
3101              buffer.append('Z');
3102              break;
3103            default:
3104              buffer.append(c);
3105          }
3106        }
3107      }
3108    
3109    
3110    
3111      /**
3112       * Appends an uppercase string representation of the contents of the given
3113       * byte array to the provided buffer, optionally trimming leading and trailing
3114       * spaces.  This implementation presumes that the provided string will contain
3115       * only ASCII characters and is optimized for that case.  However, if a
3116       * non-ASCII character is encountered it will fall back on a more expensive
3117       * algorithm that will work properly for non-ASCII characters.
3118       *
3119       * @param  b       The byte array for which to obtain the uppercase string
3120       *                 representation.
3121       * @param  buffer  The buffer to which the uppercase form of the string should
3122       *                 be appended.
3123       * @param  trim    Indicates whether leading and trailing spaces should be
3124       *                 omitted from the string representation.
3125       */
3126      public static void toUpperCase(byte[] b, StringBuilder buffer, boolean trim)
3127      {
3128        if (b == null)
3129        {
3130          return;
3131        }
3132    
3133        int length = b.length;
3134        for (int i=0; i < length; i++)
3135        {
3136          if ((b[i] & 0x7F) != b[i])
3137          {
3138            try
3139            {
3140              buffer.append(new String(b, i, (length-i), "UTF-8").toUpperCase());
3141            }
3142            catch (Exception e)
3143            {
3144              if (debugEnabled())
3145              {
3146                TRACER.debugCaught(DebugLogLevel.ERROR, e);
3147              }
3148              buffer.append(new String(b, i, (length - i)).toUpperCase());
3149            }
3150            break;
3151          }
3152    
3153          int bufferLength = buffer.length();
3154          switch (b[i])
3155          {
3156            case ' ':
3157              // If we don't care about trimming, then we can always append the
3158              // space.  Otherwise, only do so if there are other characters in the
3159              // value.
3160              if (trim && (bufferLength == 0))
3161              {
3162                break;
3163              }
3164    
3165              buffer.append(' ');
3166              break;
3167            case 'a':
3168              buffer.append('A');
3169              break;
3170            case 'b':
3171              buffer.append('B');
3172              break;
3173            case 'c':
3174              buffer.append('C');
3175              break;
3176            case 'd':
3177              buffer.append('D');
3178              break;
3179            case 'e':
3180              buffer.append('E');
3181              break;
3182            case 'f':
3183              buffer.append('F');
3184              break;
3185            case 'g':
3186              buffer.append('G');
3187              break;
3188            case 'h':
3189              buffer.append('H');
3190              break;
3191            case 'i':
3192              buffer.append('I');
3193              break;
3194            case 'j':
3195              buffer.append('J');
3196              break;
3197            case 'k':
3198              buffer.append('K');
3199              break;
3200            case 'l':
3201              buffer.append('L');
3202              break;
3203            case 'm':
3204              buffer.append('M');
3205              break;
3206            case 'n':
3207              buffer.append('N');
3208              break;
3209            case 'o':
3210              buffer.append('O');
3211              break;
3212            case 'p':
3213              buffer.append('P');
3214              break;
3215            case 'q':
3216              buffer.append('Q');
3217              break;
3218            case 'r':
3219              buffer.append('R');
3220              break;
3221            case 's':
3222              buffer.append('S');
3223              break;
3224            case 't':
3225              buffer.append('T');
3226              break;
3227            case 'u':
3228              buffer.append('U');
3229              break;
3230            case 'v':
3231              buffer.append('V');
3232              break;
3233            case 'w':
3234              buffer.append('W');
3235              break;
3236            case 'x':
3237              buffer.append('X');
3238              break;
3239            case 'y':
3240              buffer.append('Y');
3241              break;
3242            case 'z':
3243              buffer.append('Z');
3244              break;
3245            default:
3246              buffer.append((char) b[i]);
3247          }
3248        }
3249    
3250        if (trim)
3251        {
3252          // Strip off any trailing spaces.
3253          for (int i=buffer.length()-1; i > 0; i--)
3254          {
3255            if (buffer.charAt(i) == ' ')
3256            {
3257              buffer.delete(i, i+1);
3258            }
3259            else
3260            {
3261              break;
3262            }
3263          }
3264        }
3265      }
3266    
3267    
3268    
3269      /**
3270       * Append a string to a string builder, escaping any double quotes
3271       * according to the StringValue production in RFC 3641.
3272       * <p>
3273       * In RFC 3641 the StringValue production looks like this:
3274       *
3275       * <pre>
3276       *    StringValue       = dquote *SafeUTF8Character dquote
3277       *    dquote            = %x22 ; &quot; (double quote)
3278       *    SafeUTF8Character = %x00-21 / %x23-7F /   ; ASCII minus dquote
3279       *                        dquote dquote /       ; escaped double quote
3280       *                        %xC0-DF %x80-BF /     ; 2 byte UTF-8 character
3281       *                        %xE0-EF 2(%x80-BF) /  ; 3 byte UTF-8 character
3282       *                        %xF0-F7 3(%x80-BF)    ; 4 byte UTF-8 character
3283       * </pre>
3284       *
3285       * <p>
3286       * That is, strings are surrounded by double-quotes and any internal
3287       * double-quotes are doubled up.
3288       *
3289       * @param builder
3290       *          The string builder.
3291       * @param string
3292       *          The string to escape and append.
3293       * @return Returns the string builder.
3294       */
3295      public static StringBuilder toRFC3641StringValue(StringBuilder builder,
3296          String string)
3297      {
3298        // Initial double-quote.
3299        builder.append('"');
3300    
3301        for (char c : string.toCharArray())
3302        {
3303          if (c == '"')
3304          {
3305            // Internal double-quotes are escaped using a double-quote.
3306            builder.append('"');
3307          }
3308          builder.append(c);
3309        }
3310    
3311        // Trailing double-quote.
3312        builder.append('"');
3313    
3314        return builder;
3315      }
3316    
3317    
3318    
3319      /**
3320       * Retrieves a string array containing the contents of the provided
3321       * list of strings.
3322       *
3323       * @param stringList
3324       *          The string list to convert to an array.
3325       * @return A string array containing the contents of the provided list
3326       *         of strings.
3327       */
3328      public static String[] listToArray(List<String> stringList)
3329      {
3330        if (stringList == null)
3331        {
3332          return null;
3333        }
3334    
3335        String[] stringArray = new String[stringList.size()];
3336        stringList.toArray(stringArray);
3337        return stringArray;
3338      }
3339    
3340      /**
3341       * Creates a string representation of the elements in the
3342       * <code>list</code> separated by <code>separator</code>.
3343       *
3344       * @param list the list to print
3345       * @param separator to use between elements
3346       *
3347       * @return String representing the list
3348       */
3349      static public String listToString(List<?> list, String separator)
3350      {
3351        StringBuilder sb = new StringBuilder();
3352        for (int i = 0; i < list.size(); i++) {
3353          sb.append(list.get(i));
3354          if (i < list.size() - 1) {
3355            sb.append(separator);
3356          }
3357        }
3358        return sb.toString();
3359      }
3360    
3361      /**
3362       * Creates a string representation of the elements in the
3363       * <code>collection</code> separated by <code>separator</code>.
3364       *
3365       * @param collection to print
3366       * @param separator to use between elements
3367       *
3368       * @return String representing the collection
3369       */
3370      static public String collectionToString(Collection<?> collection,
3371                                              String separator)
3372      {
3373        StringBuilder sb = new StringBuilder();
3374        for (Iterator<?> iter = collection.iterator(); iter.hasNext();) {
3375          sb.append(iter.next());
3376          if (iter.hasNext()) {
3377            sb.append(separator);
3378          }
3379        }
3380        return sb.toString();
3381      }
3382    
3383    
3384      /**
3385       * Retrieves an array list containing the contents of the provided array.
3386       *
3387       * @param  stringArray  The string array to convert to an array list.
3388       *
3389       * @return  An array list containing the contents of the provided array.
3390       */
3391      public static ArrayList<String> arrayToList(String[] stringArray)
3392      {
3393        if (stringArray == null)
3394        {
3395          return null;
3396        }
3397    
3398        ArrayList<String> stringList = new ArrayList<String>(stringArray.length);
3399        for (String s : stringArray)
3400        {
3401          stringList.add(s);
3402        }
3403    
3404        return stringList;
3405      }
3406    
3407    
3408      /**
3409       * Attempts to delete the specified file or directory.  If it is a directory,
3410       * then any files or subdirectories that it contains will be recursively
3411       * deleted as well.
3412       *
3413       * @param  file  The file or directory to be removed.
3414       *
3415       * @return  <CODE>true</CODE> if the specified file and any subordinates are
3416       *          all successfully removed, or <CODE>false</CODE> if at least one
3417       *          element in the subtree could not be removed.
3418       */
3419      public static boolean recursiveDelete(File file)
3420      {
3421        boolean successful = true;
3422        if (file.isDirectory())
3423        {
3424          File[] childList = file.listFiles();
3425          if (childList != null)
3426          {
3427            for (File f : childList)
3428            {
3429              successful &= recursiveDelete(f);
3430            }
3431          }
3432        }
3433    
3434        return (successful & file.delete());
3435      }
3436    
3437    
3438    
3439      /**
3440       * Moves the indicated file to the specified directory by creating a new file
3441       * in the target directory, copying the contents of the existing file, and
3442       * removing the existing file.  The file to move must exist and must be a
3443       * file.  The target directory must exist, must be a directory, and must not
3444       * be the directory in which the file currently resides.
3445       *
3446       * @param  fileToMove       The file to move to the target directory.
3447       * @param  targetDirectory  The directory into which the file should be moved.
3448       *
3449       * @throws  IOException  If a problem occurs while attempting to move the
3450       *                       file.
3451       */
3452      public static void moveFile(File fileToMove, File targetDirectory)
3453             throws IOException
3454      {
3455        if (! fileToMove.exists())
3456        {
3457          Message message = ERR_MOVEFILE_NO_SUCH_FILE.get(fileToMove.getPath());
3458          throw new IOException(message.toString());
3459        }
3460    
3461        if (! fileToMove.isFile())
3462        {
3463          Message message = ERR_MOVEFILE_NOT_FILE.get(fileToMove.getPath());
3464          throw new IOException(message.toString());
3465        }
3466    
3467        if (! targetDirectory.exists())
3468        {
3469          Message message =
3470              ERR_MOVEFILE_NO_SUCH_DIRECTORY.get(targetDirectory.getPath());
3471          throw new IOException(message.toString());
3472        }
3473    
3474        if (! targetDirectory.isDirectory())
3475        {
3476          Message message =
3477              ERR_MOVEFILE_NOT_DIRECTORY.get(targetDirectory.getPath());
3478          throw new IOException(message.toString());
3479        }
3480    
3481        String newFilePath = targetDirectory.getPath() + File.separator +
3482                             fileToMove.getName();
3483        FileInputStream  inputStream  = new FileInputStream(fileToMove);
3484        FileOutputStream outputStream = new FileOutputStream(newFilePath, false);
3485        byte[] buffer = new byte[8192];
3486        while (true)
3487        {
3488          int bytesRead = inputStream.read(buffer);
3489          if (bytesRead < 0)
3490          {
3491            break;
3492          }
3493    
3494          outputStream.write(buffer, 0, bytesRead);
3495        }
3496    
3497        outputStream.flush();
3498        outputStream.close();
3499        inputStream.close();
3500        fileToMove.delete();
3501      }
3502    
3503      /**
3504       * Renames the source file to the target file.  If the target file exists
3505       * it is first deleted.  The rename and delete operation return values
3506       * are checked for success and if unsuccessful, this method throws an
3507       * exception.
3508       *
3509       * @param fileToRename The file to rename.
3510       * @param target       The file to which <code>fileToRename</code> will be
3511       *                     moved.
3512       * @throws IOException If a problem occurs while attempting to rename the
3513       *                     file.  On the Windows platform, this typically
3514       *                     indicates that the file is in use by this or another
3515       *                     application.
3516       */
3517      static public void renameFile(File fileToRename, File target)
3518              throws IOException {
3519        if (fileToRename != null && target != null)
3520        {
3521          synchronized(target)
3522          {
3523            if (target.exists())
3524            {
3525              if (!target.delete())
3526              {
3527                Message message =
3528                    ERR_RENAMEFILE_CANNOT_DELETE_TARGET.get(target.getPath());
3529                throw new IOException(message.toString());
3530              }
3531            }
3532          }
3533          if (!fileToRename.renameTo(target))
3534          {
3535            Message message = ERR_RENAMEFILE_CANNOT_RENAME.get(
3536                fileToRename.getPath(), target.getPath());
3537            throw new IOException(message.toString());
3538    
3539          }
3540        }
3541      }
3542    
3543    
3544      /**
3545       * Indicates whether the provided path refers to a relative path rather than
3546       * an absolute path.
3547       *
3548       * @param  path  The path string for which to make the determination.
3549       *
3550       * @return  <CODE>true</CODE> if the provided path is relative, or
3551       *          <CODE>false</CODE> if it is absolute.
3552       */
3553      public static boolean isRelativePath(String path)
3554      {
3555        File f = new File(path);
3556        return (! f.isAbsolute());
3557      }
3558    
3559    
3560    
3561      /**
3562       * Retrieves a <CODE>File</CODE> object corresponding to the specified path.
3563       * If the given path is an absolute path, then it will be used.  If the path
3564       * is relative, then it will be interpreted as if it were relative to the
3565       * Directory Server root.
3566       *
3567       * @param  path  The path string to be retrieved as a <CODE>File</CODE>
3568       *
3569       * @return  A <CODE>File</CODE> object that corresponds to the specified path.
3570       */
3571      public static File getFileForPath(String path)
3572      {
3573        File f = new File (path);
3574    
3575        if (f.isAbsolute())
3576        {
3577          return f;
3578        }
3579        else
3580        {
3581          return new File(DirectoryServer.getServerRoot() + File.separator + path);
3582        }
3583      }
3584    
3585    
3586    
3587      /**
3588       * Creates a new, blank entry with the given DN.  It will contain only the
3589       * attribute(s) contained in the RDN.  The choice of objectclasses will be
3590       * based on the RDN attribute.  If there is a single RDN attribute, then the
3591       * following mapping will be used:
3592       * <BR>
3593       * <UL>
3594       *   <LI>c attribute :: country objectclass</LI>
3595       *   <LI>dc attribute :: domain objectclass</LI>
3596       *   <LI>o attribute :: organization objectclass</LI>
3597       *   <LI>ou attribute :: organizationalUnit objectclass</LI>
3598       * </UL>
3599       * <BR>
3600       * Any other single RDN attribute types, or any case in which there are
3601       * multiple RDN attributes, will use the untypedObject objectclass.  If the
3602       * RDN includes one or more attributes that are not allowed in the
3603       * untypedObject objectclass, then the extensibleObject class will also be
3604       * added.  Note that this method cannot be used to generate an entry
3605       * with an empty or null DN.
3606       *
3607       * @param  dn  The DN to use for the entry.
3608       *
3609       * @return  The entry created with the provided DN.
3610       */
3611      public static Entry createEntry(DN dn)
3612      {
3613        // If the provided DN was null or empty, then return null because we don't
3614        // support it.
3615        if ((dn == null) || dn.isNullDN())
3616        {
3617          return null;
3618        }
3619    
3620    
3621        // Get the information about the RDN attributes.
3622        RDN rdn = dn.getRDN();
3623        int numAVAs = rdn.getNumValues();
3624    
3625        // If there is only one RDN attribute, then see which objectclass we should
3626        // use.
3627        ObjectClass structuralClass;
3628        if (numAVAs == 1)
3629        {
3630          AttributeType attrType = rdn.getAttributeType(0);
3631    
3632          if (attrType.hasName(ATTR_C))
3633          {
3634            structuralClass = DirectoryServer.getObjectClass(OC_COUNTRY, true);
3635          }
3636          else if (attrType.hasName(ATTR_DC))
3637          {
3638            structuralClass = DirectoryServer.getObjectClass(OC_DOMAIN, true);
3639          }
3640          else if (attrType.hasName(ATTR_O))
3641          {
3642            structuralClass = DirectoryServer.getObjectClass(OC_ORGANIZATION, true);
3643          }
3644          else if (attrType.hasName(ATTR_OU))
3645          {
3646            structuralClass =
3647                 DirectoryServer.getObjectClass(OC_ORGANIZATIONAL_UNIT_LC, true);
3648          }
3649          else
3650          {
3651            structuralClass =
3652                 DirectoryServer.getObjectClass(OC_UNTYPED_OBJECT_LC, true);
3653          }
3654        }
3655        else
3656        {
3657          structuralClass =
3658               DirectoryServer.getObjectClass(OC_UNTYPED_OBJECT_LC, true);
3659        }
3660    
3661    
3662        // Get the top and untypedObject classes to include in the entry.
3663        LinkedHashMap<ObjectClass,String> objectClasses =
3664             new LinkedHashMap<ObjectClass,String>(3);
3665    
3666        objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
3667        objectClasses.put(structuralClass, structuralClass.getNameOrOID());
3668    
3669    
3670        // Iterate through the RDN attributes and add them to the set of user or
3671        // operational attributes.
3672        LinkedHashMap<AttributeType,List<Attribute>> userAttributes =
3673             new LinkedHashMap<AttributeType,List<Attribute>>();
3674        LinkedHashMap<AttributeType,List<Attribute>> operationalAttributes =
3675             new LinkedHashMap<AttributeType,List<Attribute>>();
3676    
3677        boolean extensibleObjectAdded = false;
3678        for (int i=0; i < numAVAs; i++)
3679        {
3680          AttributeType attrType = rdn.getAttributeType(i);
3681          AttributeValue attrValue = rdn.getAttributeValue(i);
3682          String attrName = rdn.getAttributeName(i);
3683    
3684          // First, see if this type is allowed by the untypedObject class.  If not,
3685          // then we'll need to include the extensibleObject class.
3686          if ((! structuralClass.isRequiredOrOptional(attrType)) &&
3687              (! extensibleObjectAdded))
3688          {
3689            ObjectClass extensibleObjectOC =
3690                 DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC);
3691            if (extensibleObjectOC == null)
3692            {
3693              extensibleObjectOC =
3694                   DirectoryServer.getDefaultObjectClass(OC_EXTENSIBLE_OBJECT);
3695            }
3696            objectClasses.put(extensibleObjectOC, OC_EXTENSIBLE_OBJECT);
3697            extensibleObjectAdded = true;
3698          }
3699    
3700    
3701          // Create the attribute and add it to the appropriate map.
3702          LinkedHashSet<AttributeValue> valueSet =
3703               new LinkedHashSet<AttributeValue>(1);
3704          valueSet.add(attrValue);
3705    
3706          if (attrType.isOperational())
3707          {
3708            List<Attribute> attrList = operationalAttributes.get(attrType);
3709            if ((attrList == null) || attrList.isEmpty())
3710            {
3711              attrList = new ArrayList<Attribute>(1);
3712              attrList.add(new Attribute(attrType, attrName, valueSet));
3713              operationalAttributes.put(attrType, attrList);
3714            }
3715            else
3716            {
3717              Attribute attr = attrList.get(0);
3718              attr.getValues().add(attrValue);
3719            }
3720          }
3721          else
3722          {
3723            List<Attribute> attrList = userAttributes.get(attrType);
3724            if ((attrList == null) || attrList.isEmpty())
3725            {
3726              attrList = new ArrayList<Attribute>(1);
3727              attrList.add(new Attribute(attrType, attrName, valueSet));
3728              userAttributes.put(attrType, attrList);
3729            }
3730            else
3731            {
3732              Attribute attr = attrList.get(0);
3733              attr.getValues().add(attrValue);
3734            }
3735          }
3736        }
3737    
3738    
3739        // Create and return the entry.
3740        return new Entry(dn, objectClasses, userAttributes, operationalAttributes);
3741      }
3742    
3743    
3744    
3745      /**
3746       * Retrieves a user-friendly string that indicates the length of time (in
3747       * days, hours, minutes, and seconds) in the specified number of seconds.
3748       *
3749       * @param  numSeconds  The number of seconds to be converted to a more
3750       *                     user-friendly value.
3751       *
3752       * @return  The user-friendly representation of the specified number of
3753       *          seconds.
3754       */
3755      public static Message secondsToTimeString(int numSeconds)
3756      {
3757        if (numSeconds < 60)
3758        {
3759          // We can express it in seconds.
3760          return INFO_TIME_IN_SECONDS.get(numSeconds);
3761        }
3762        else if (numSeconds < 3600)
3763        {
3764          // We can express it in minutes and seconds.
3765          int m = numSeconds / 60;
3766          int s = numSeconds % 60;
3767          return INFO_TIME_IN_MINUTES_SECONDS.get(m, s);
3768        }
3769        else if (numSeconds < 86400)
3770        {
3771          // We can express it in hours, minutes, and seconds.
3772          int h = numSeconds / 3600;
3773          int m = (numSeconds % 3600) / 60;
3774          int s = numSeconds % 3600 % 60;
3775          return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s);
3776        }
3777        else
3778        {
3779          // We can express it in days, hours, minutes, and seconds.
3780          int d = numSeconds / 86400;
3781          int h = (numSeconds % 86400) / 3600;
3782          int m = (numSeconds % 86400 % 3600) / 60;
3783          int s = numSeconds % 86400 % 3600 % 60;
3784          return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s);
3785        }
3786      }
3787    
3788    
3789    
3790      /**
3791       * Inserts line breaks into the provided buffer to wrap text at no more than
3792       * the specified column width.  Wrapping will only be done at space boundaries
3793       * and if there are no spaces within the specified width, then wrapping will
3794       * be performed at the first space after the specified column.
3795       *
3796       * @param  message The message to be wrapped.
3797       * @param  width  The maximum number of characters to allow on a line if there
3798       *                is a suitable breaking point.
3799       *
3800       * @return  The wrapped text.
3801       */
3802      public static String wrapText(Message message, int width)
3803      {
3804        return wrapText(Message.toString(message), width, 0);
3805      }
3806    
3807    
3808    
3809      /**
3810       * Inserts line breaks into the provided buffer to wrap text at no more than
3811       * the specified column width.  Wrapping will only be done at space boundaries
3812       * and if there are no spaces within the specified width, then wrapping will
3813       * be performed at the first space after the specified column.
3814       *
3815       * @param  text   The text to be wrapped.
3816       * @param  width  The maximum number of characters to allow on a line if there
3817       *                is a suitable breaking point.
3818       *
3819       * @return  The wrapped text.
3820       */
3821      public static String wrapText(String text, int width) {
3822        return wrapText(text, width, 0);
3823      }
3824    
3825    
3826    
3827      /**
3828       * Inserts line breaks into the provided buffer to wrap text at no
3829       * more than the specified column width. Wrapping will only be done
3830       * at space boundaries and if there are no spaces within the
3831       * specified width, then wrapping will be performed at the first
3832       * space after the specified column. In addition each line will be
3833       * indented by the specified amount.
3834       *
3835       * @param message
3836       *          The message to be wrapped.
3837       * @param width
3838       *          The maximum number of characters to allow on a line if
3839       *          there is a suitable breaking point (including any
3840       *          indentation).
3841       * @param indent
3842       *          The number of columns to indent each line.
3843       * @return The wrapped text.
3844       */
3845      public static String wrapText(Message message, int width, int indent)
3846      {
3847        return wrapText(Message.toString(message), width, indent);
3848      }
3849    
3850    
3851    
3852      /**
3853       * Inserts line breaks into the provided buffer to wrap text at no
3854       * more than the specified column width. Wrapping will only be done
3855       * at space boundaries and if there are no spaces within the
3856       * specified width, then wrapping will be performed at the first
3857       * space after the specified column. In addition each line will be
3858       * indented by the specified amount.
3859       *
3860       * @param text
3861       *          The text to be wrapped.
3862       * @param width
3863       *          The maximum number of characters to allow on a line if
3864       *          there is a suitable breaking point (including any
3865       *          indentation).
3866       * @param indent
3867       *          The number of columns to indent each line.
3868       * @return The wrapped text.
3869       */
3870      public static String wrapText(String text, int width, int indent)
3871      {
3872        Validator.ensureTrue(indent >= 0 && indent < width);
3873    
3874        // Calculate the real width and indentation padding.
3875        width -= indent;
3876        StringBuilder pb = new StringBuilder();
3877        for (int i = 0; i < indent; i++) {
3878          pb.append(' ');
3879        }
3880        String padding = pb.toString();
3881    
3882        StringBuilder   buffer        = new StringBuilder();
3883        if (text != null) {
3884          StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
3885          while (lineTokenizer.hasMoreTokens())
3886          {
3887            String line = lineTokenizer.nextToken();
3888            if (line.equals("\r") || line.equals("\n"))
3889            {
3890              // It's an end-of-line character, so append it as-is.
3891              buffer.append(line);
3892            }
3893            else if (line.length() < width)
3894            {
3895              // The line fits in the specified width, so append it as-is.
3896              buffer.append(padding);
3897              buffer.append(line);
3898            }
3899            else
3900            {
3901              // The line doesn't fit in the specified width, so it needs to be
3902              // wrapped.  Do so at space boundaries.
3903              StringBuilder   lineBuffer    = new StringBuilder();
3904              StringBuilder   delimBuffer   = new StringBuilder();
3905              StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
3906              while (wordTokenizer.hasMoreTokens())
3907              {
3908                String word = wordTokenizer.nextToken();
3909                if (word.equals(" "))
3910                {
3911                  // It's a space, so add it to the delim buffer only if the line
3912                  // buffer is not empty.
3913                  if (lineBuffer.length() > 0)
3914                  {
3915                    delimBuffer.append(word);
3916                  }
3917                }
3918                else if (word.length() > width)
3919                {
3920                  // This is a long word that can't be wrapped, so we'll just have
3921                  // to make do.
3922                  if (lineBuffer.length() > 0)
3923                  {
3924                    buffer.append(padding);
3925                    buffer.append(lineBuffer);
3926                    buffer.append(EOL);
3927                    lineBuffer = new StringBuilder();
3928                  }
3929                  buffer.append(padding);
3930                  buffer.append(word);
3931    
3932                  if (wordTokenizer.hasMoreTokens())
3933                  {
3934                    // The next token must be a space, so remove it.  If there are
3935                    // still more tokens after that, then append an EOL.
3936                    wordTokenizer.nextToken();
3937                    if (wordTokenizer.hasMoreTokens())
3938                    {
3939                      buffer.append(EOL);
3940                    }
3941                  }
3942    
3943                  if (delimBuffer.length() > 0)
3944                  {
3945                    delimBuffer = new StringBuilder();
3946                  }
3947                }
3948                else
3949                {
3950                  // It's not a space, so see if we can fit it on the curent line.
3951                  int newLineLength = lineBuffer.length() + delimBuffer.length() +
3952                                      word.length();
3953                  if (newLineLength < width)
3954                  {
3955                    // It does fit on the line, so add it.
3956                    lineBuffer.append(delimBuffer).append(word);
3957    
3958                    if (delimBuffer.length() > 0)
3959                    {
3960                      delimBuffer = new StringBuilder();
3961                    }
3962                  }
3963                  else
3964                  {
3965                    // It doesn't fit on the line, so end the current line and start
3966                    // a new one.
3967                    buffer.append(padding);
3968                    buffer.append(lineBuffer);
3969                    buffer.append(EOL);
3970    
3971                    lineBuffer = new StringBuilder();
3972                    lineBuffer.append(word);
3973    
3974                    if (delimBuffer.length() > 0)
3975                    {
3976                      delimBuffer = new StringBuilder();
3977                    }
3978                  }
3979                }
3980              }
3981    
3982              // If there's anything left in the line buffer, then add it to the
3983              // final buffer.
3984              buffer.append(padding);
3985              buffer.append(lineBuffer);
3986            }
3987          }
3988        }
3989        return buffer.toString();
3990      }
3991    
3992    
3993    
3994      /**
3995       * Filters the provided value to ensure that it is appropriate for use as an
3996       * exit code.  Exit code values are generally only allowed to be between 0 and
3997       * 255, so any value outside of this range will be converted to 255, which is
3998       * the typical exit code used to indicate an overflow value.
3999       *
4000       * @param  exitCode  The exit code value to be processed.
4001       *
4002       * @return  An integer value between 0 and 255, inclusive.  If the provided
4003       *          exit code was already between 0 and 255, then the original value
4004       *          will be returned.  If the provided value was out of this range,
4005       *          then 255 will be returned.
4006       */
4007      public static int filterExitCode(int exitCode)
4008      {
4009        if (exitCode < 0)
4010        {
4011          return 255;
4012        }
4013        else if (exitCode > 255)
4014        {
4015          return 255;
4016        }
4017        else
4018        {
4019          return exitCode;
4020        }
4021      }
4022    
4023      /**
4024       * Checks that no more that one of a set of arguments is present.  This
4025       * utility should be used after argument parser has parsed a set of
4026       * arguments.
4027       *
4028       * @param  args to test for the presence of more than one
4029       * @throws ArgumentException if more than one of <code>args</code> is
4030       *         present and containing an error message identifying the
4031       *         arguments in violation
4032       */
4033      public static void checkOnlyOneArgPresent(Argument... args)
4034        throws ArgumentException
4035      {
4036        if (args != null) {
4037          for (Argument arg : args) {
4038            for (Argument otherArg : args) {
4039              if (arg != otherArg && arg.isPresent() && otherArg.isPresent()) {
4040                throw new ArgumentException(
4041                        ToolMessages.ERR_INCOMPATIBLE_ARGUMENTS.get(
4042                                arg.getName(), otherArg.getName()));
4043              }
4044            }
4045          }
4046        }
4047      }
4048    
4049      /**
4050       * Converts a string representing a time in "yyyyMMddHHmmss.SSS'Z'" or
4051       * "yyyyMMddHHmmss" to a <code>Date</code>.
4052       *
4053       * @param timeStr string formatted appropriately
4054       * @return Date object; null if <code>timeStr</code> is null
4055       * @throws ParseException if there was a problem converting the string to
4056       *         a <code>Date</code>.
4057       */
4058      static public Date parseDateTimeString(String timeStr) throws ParseException
4059      {
4060        Date dateTime = null;
4061        if (timeStr != null)
4062        {
4063          if (timeStr.endsWith("Z"))
4064          {
4065            SimpleDateFormat dateFormat =
4066                new SimpleDateFormat(DATE_FORMAT_GENERALIZED_TIME);
4067            dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
4068            dateFormat.setLenient(true);
4069            dateTime = dateFormat.parse(timeStr);
4070          }
4071          else
4072          {
4073            SimpleDateFormat dateFormat =
4074                new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME);
4075            dateFormat.setLenient(true);
4076            dateTime = dateFormat.parse(timeStr);
4077          }
4078        }
4079        return dateTime;
4080      }
4081    
4082      /**
4083       * Formats a Date to String representation in "yyyyMMddHHmmss'Z'".
4084       *
4085       * @param date to format; null if <code>date</code> is null
4086       * @return string representation of the date
4087       */
4088      static public String formatDateTimeString(Date date)
4089      {
4090        String timeStr = null;
4091        if (date != null)
4092        {
4093          SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
4094          dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
4095          timeStr = dateFormat.format(date);
4096        }
4097        return timeStr;
4098      }
4099    
4100      /**
4101       * Indicates whether or not a string represents a syntactically correct
4102       * email address.
4103       *
4104       * @param addr to validate
4105       * @return boolean where <code>true</code> indicates that the string is a
4106       *         syntactically correct email address
4107       */
4108      public static boolean isEmailAddress(String addr) {
4109    
4110        // This just does basic syntax checking.  Perhaps we
4111        // might want to be stricter about this.
4112        return addr != null && addr.contains("@") && addr.contains(".");
4113    
4114      }
4115    }
4116