/*
|
* CDDL HEADER START
|
*
|
* The contents of this file are subject to the terms of the
|
* Common Development and Distribution License, Version 1.0 only
|
* (the "License"). You may not use this file except in compliance
|
* with the License.
|
*
|
* You can obtain a copy of the license at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
|
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
|
* See the License for the specific language governing permissions
|
* and limitations under the License.
|
*
|
* When distributing Covered Code, include this CDDL HEADER in each
|
* file and include the License file at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
|
* add the following below this CDDL HEADER, with the fields enclosed
|
* by brackets "[]" replaced with your own identifying information:
|
* Portions Copyright [yyyy] [name of copyright owner]
|
*
|
* CDDL HEADER END
|
*
|
*
|
* Copyright 2006-2010 Sun Microsystems, Inc.
|
* Portions Copyright 2011-2013 ForgeRock AS
|
*/
|
package org.opends.server.util;
|
|
import java.io.BufferedReader;
|
import java.io.Closeable;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.io.InputStreamReader;
|
import java.io.UnsupportedEncodingException;
|
import java.lang.reflect.InvocationTargetException;
|
import java.net.InetAddress;
|
import java.net.InetSocketAddress;
|
import java.net.NetworkInterface;
|
import java.net.ServerSocket;
|
import java.net.Socket;
|
import java.net.SocketException;
|
import java.net.UnknownHostException;
|
import java.nio.ByteBuffer;
|
import java.nio.channels.SelectionKey;
|
import java.nio.channels.Selector;
|
import java.nio.channels.SocketChannel;
|
import java.text.ParseException;
|
import java.text.SimpleDateFormat;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Collection;
|
import java.util.Date;
|
import java.util.Enumeration;
|
import java.util.HashSet;
|
import java.util.Iterator;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.RandomAccess;
|
import java.util.StringTokenizer;
|
import java.util.TimeZone;
|
|
import org.opends.messages.Message;
|
import org.opends.messages.MessageBuilder;
|
import org.opends.messages.MessageDescriptor;
|
import org.opends.messages.ToolMessages;
|
import org.opends.server.api.ClientConnection;
|
import org.opends.server.core.DirectoryServer;
|
import org.opends.server.loggers.debug.DebugTracer;
|
import org.opends.server.types.Attribute;
|
import org.opends.server.types.AttributeBuilder;
|
import org.opends.server.types.AttributeType;
|
import org.opends.server.types.AttributeValue;
|
import org.opends.server.types.ByteSequence;
|
import org.opends.server.types.DN;
|
import org.opends.server.types.DebugLogLevel;
|
import org.opends.server.types.DirectoryException;
|
import org.opends.server.types.Entry;
|
import org.opends.server.types.IdentifiedException;
|
import org.opends.server.types.ObjectClass;
|
import org.opends.server.types.RDN;
|
import org.opends.server.types.ResultCode;
|
import org.opends.server.util.args.Argument;
|
import org.opends.server.util.args.ArgumentException;
|
|
import static org.opends.messages.CoreMessages.*;
|
import static org.opends.messages.UtilityMessages.*;
|
import static org.opends.server.loggers.debug.DebugLogger.*;
|
import static org.opends.server.util.ServerConstants.*;
|
|
|
/**
|
* This class defines a number of static utility methods that may be used
|
* throughout the server. Note that because of the frequency with which these
|
* methods are expected to be used, very little debug logging will be performed
|
* to prevent the log from filling up with unimportant calls and to reduce the
|
* impact that debugging may have on performance.
|
*/
|
@org.opends.server.types.PublicAPI(
|
stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
|
mayInstantiate=false,
|
mayExtend=false,
|
mayInvoke=true)
|
public final class StaticUtils
|
{
|
/**
|
* The tracer object for the debug logger.
|
*/
|
private static final DebugTracer TRACER = getTracer();
|
|
/**
|
* Private constructor to prevent instantiation.
|
*/
|
private StaticUtils() {
|
// No implementation required.
|
}
|
|
|
|
/**
|
* Construct a byte array containing the UTF-8 encoding of the
|
* provided string. This is significantly faster
|
* than calling {@link String#getBytes(String)} for ASCII strings.
|
*
|
* @param s
|
* The string to convert to a UTF-8 byte array.
|
* @return Returns a byte array containing the UTF-8 encoding of the
|
* provided string.
|
*/
|
public static byte[] getBytes(String s)
|
{
|
if (s == null) return null;
|
|
try
|
{
|
char c;
|
int length = s.length();
|
byte[] returnArray = new byte[length];
|
for (int i=0; i < length; i++)
|
{
|
c = s.charAt(i);
|
returnArray[i] = (byte) (c & 0x0000007F);
|
if (c != returnArray[i])
|
{
|
return s.getBytes("UTF-8");
|
}
|
}
|
|
return returnArray;
|
}
|
catch (Exception e)
|
{
|
if (debugEnabled())
|
{
|
TRACER.debugCaught(DebugLogLevel.ERROR, e);
|
}
|
|
try
|
{
|
return s.getBytes("UTF-8");
|
}
|
catch (Exception e2)
|
{
|
if (debugEnabled())
|
{
|
TRACER.debugCaught(DebugLogLevel.ERROR, e2);
|
}
|
|
return s.getBytes();
|
}
|
}
|
}
|
|
|
|
/**
|
* Returns the provided byte array decoded as a UTF-8 string without throwing
|
* an UnsupportedEncodingException. This method is equivalent to:
|
*
|
* <pre>
|
* try
|
* {
|
* return new String(bytes, "UTF-8");
|
* }
|
* catch (UnsupportedEncodingException e)
|
* {
|
* // Should never happen: UTF-8 is always supported.
|
* throw new RuntimeException(e);
|
* }
|
* </pre>
|
*
|
* @param bytes
|
* The byte array to be decoded as a UTF-8 string.
|
* @return The decoded string.
|
*/
|
public static String decodeUTF8(final byte[] bytes)
|
{
|
Validator.ensureNotNull(bytes);
|
|
if (bytes.length == 0)
|
{
|
return "".intern();
|
}
|
|
final StringBuilder builder = new StringBuilder(bytes.length);
|
final int sz = bytes.length;
|
|
for (int i = 0; i < sz; i++)
|
{
|
final byte b = bytes[i];
|
if ((b & 0x7f) != b)
|
{
|
try
|
{
|
builder.append(new String(bytes, i, (sz - i), "UTF-8"));
|
}
|
catch (UnsupportedEncodingException e)
|
{
|
// Should never happen: UTF-8 is always supported.
|
throw new RuntimeException(e);
|
}
|
break;
|
}
|
builder.append((char) b);
|
}
|
return builder.toString();
|
}
|
|
|
|
/**
|
* Construct a byte array containing the UTF-8 encoding of the
|
* provided <code>char</code> array.
|
*
|
* @param chars
|
* The character array to convert to a UTF-8 byte array.
|
* @return Returns a byte array containing the UTF-8 encoding of the
|
* provided <code>char</code> array.
|
*/
|
public static byte[] getBytes(char[] chars)
|
{
|
return getBytes(new String(chars));
|
}
|
|
|
|
/**
|
* Retrieves a string representation of the provided byte in hexadecimal.
|
*
|
* @param b The byte for which to retrieve the hexadecimal string
|
* representation.
|
*
|
* @return The string representation of the provided byte in hexadecimal.
|
*/
|
public static String byteToHex(byte b)
|
{
|
switch (b & 0xFF)
|
{
|
case 0x00: return "00";
|
case 0x01: return "01";
|
case 0x02: return "02";
|
case 0x03: return "03";
|
case 0x04: return "04";
|
case 0x05: return "05";
|
case 0x06: return "06";
|
case 0x07: return "07";
|
case 0x08: return "08";
|
case 0x09: return "09";
|
case 0x0A: return "0A";
|
case 0x0B: return "0B";
|
case 0x0C: return "0C";
|
case 0x0D: return "0D";
|
case 0x0E: return "0E";
|
case 0x0F: return "0F";
|
case 0x10: return "10";
|
case 0x11: return "11";
|
case 0x12: return "12";
|
case 0x13: return "13";
|
case 0x14: return "14";
|
case 0x15: return "15";
|
case 0x16: return "16";
|
case 0x17: return "17";
|
case 0x18: return "18";
|
case 0x19: return "19";
|
case 0x1A: return "1A";
|
case 0x1B: return "1B";
|
case 0x1C: return "1C";
|
case 0x1D: return "1D";
|
case 0x1E: return "1E";
|
case 0x1F: return "1F";
|
case 0x20: return "20";
|
case 0x21: return "21";
|
case 0x22: return "22";
|
case 0x23: return "23";
|
case 0x24: return "24";
|
case 0x25: return "25";
|
case 0x26: return "26";
|
case 0x27: return "27";
|
case 0x28: return "28";
|
case 0x29: return "29";
|
case 0x2A: return "2A";
|
case 0x2B: return "2B";
|
case 0x2C: return "2C";
|
case 0x2D: return "2D";
|
case 0x2E: return "2E";
|
case 0x2F: return "2F";
|
case 0x30: return "30";
|
case 0x31: return "31";
|
case 0x32: return "32";
|
case 0x33: return "33";
|
case 0x34: return "34";
|
case 0x35: return "35";
|
case 0x36: return "36";
|
case 0x37: return "37";
|
case 0x38: return "38";
|
case 0x39: return "39";
|
case 0x3A: return "3A";
|
case 0x3B: return "3B";
|
case 0x3C: return "3C";
|
case 0x3D: return "3D";
|
case 0x3E: return "3E";
|
case 0x3F: return "3F";
|
case 0x40: return "40";
|
case 0x41: return "41";
|
case 0x42: return "42";
|
case 0x43: return "43";
|
case 0x44: return "44";
|
case 0x45: return "45";
|
case 0x46: return "46";
|
case 0x47: return "47";
|
case 0x48: return "48";
|
case 0x49: return "49";
|
case 0x4A: return "4A";
|
case 0x4B: return "4B";
|
case 0x4C: return "4C";
|
case 0x4D: return "4D";
|
case 0x4E: return "4E";
|
case 0x4F: return "4F";
|
case 0x50: return "50";
|
case 0x51: return "51";
|
case 0x52: return "52";
|
case 0x53: return "53";
|
case 0x54: return "54";
|
case 0x55: return "55";
|
case 0x56: return "56";
|
case 0x57: return "57";
|
case 0x58: return "58";
|
case 0x59: return "59";
|
case 0x5A: return "5A";
|
case 0x5B: return "5B";
|
case 0x5C: return "5C";
|
case 0x5D: return "5D";
|
case 0x5E: return "5E";
|
case 0x5F: return "5F";
|
case 0x60: return "60";
|
case 0x61: return "61";
|
case 0x62: return "62";
|
case 0x63: return "63";
|
case 0x64: return "64";
|
case 0x65: return "65";
|
case 0x66: return "66";
|
case 0x67: return "67";
|
case 0x68: return "68";
|
case 0x69: return "69";
|
case 0x6A: return "6A";
|
case 0x6B: return "6B";
|
case 0x6C: return "6C";
|
case 0x6D: return "6D";
|
case 0x6E: return "6E";
|
case 0x6F: return "6F";
|
case 0x70: return "70";
|
case 0x71: return "71";
|
case 0x72: return "72";
|
case 0x73: return "73";
|
case 0x74: return "74";
|
case 0x75: return "75";
|
case 0x76: return "76";
|
case 0x77: return "77";
|
case 0x78: return "78";
|
case 0x79: return "79";
|
case 0x7A: return "7A";
|
case 0x7B: return "7B";
|
case 0x7C: return "7C";
|
case 0x7D: return "7D";
|
case 0x7E: return "7E";
|
case 0x7F: return "7F";
|
case 0x80: return "80";
|
case 0x81: return "81";
|
case 0x82: return "82";
|
case 0x83: return "83";
|
case 0x84: return "84";
|
case 0x85: return "85";
|
case 0x86: return "86";
|
case 0x87: return "87";
|
case 0x88: return "88";
|
case 0x89: return "89";
|
case 0x8A: return "8A";
|
case 0x8B: return "8B";
|
case 0x8C: return "8C";
|
case 0x8D: return "8D";
|
case 0x8E: return "8E";
|
case 0x8F: return "8F";
|
case 0x90: return "90";
|
case 0x91: return "91";
|
case 0x92: return "92";
|
case 0x93: return "93";
|
case 0x94: return "94";
|
case 0x95: return "95";
|
case 0x96: return "96";
|
case 0x97: return "97";
|
case 0x98: return "98";
|
case 0x99: return "99";
|
case 0x9A: return "9A";
|
case 0x9B: return "9B";
|
case 0x9C: return "9C";
|
case 0x9D: return "9D";
|
case 0x9E: return "9E";
|
case 0x9F: return "9F";
|
case 0xA0: return "A0";
|
case 0xA1: return "A1";
|
case 0xA2: return "A2";
|
case 0xA3: return "A3";
|
case 0xA4: return "A4";
|
case 0xA5: return "A5";
|
case 0xA6: return "A6";
|
case 0xA7: return "A7";
|
case 0xA8: return "A8";
|
case 0xA9: return "A9";
|
case 0xAA: return "AA";
|
case 0xAB: return "AB";
|
case 0xAC: return "AC";
|
case 0xAD: return "AD";
|
case 0xAE: return "AE";
|
case 0xAF: return "AF";
|
case 0xB0: return "B0";
|
case 0xB1: return "B1";
|
case 0xB2: return "B2";
|
case 0xB3: return "B3";
|
case 0xB4: return "B4";
|
case 0xB5: return "B5";
|
case 0xB6: return "B6";
|
case 0xB7: return "B7";
|
case 0xB8: return "B8";
|
case 0xB9: return "B9";
|
case 0xBA: return "BA";
|
case 0xBB: return "BB";
|
case 0xBC: return "BC";
|
case 0xBD: return "BD";
|
case 0xBE: return "BE";
|
case 0xBF: return "BF";
|
case 0xC0: return "C0";
|
case 0xC1: return "C1";
|
case 0xC2: return "C2";
|
case 0xC3: return "C3";
|
case 0xC4: return "C4";
|
case 0xC5: return "C5";
|
case 0xC6: return "C6";
|
case 0xC7: return "C7";
|
case 0xC8: return "C8";
|
case 0xC9: return "C9";
|
case 0xCA: return "CA";
|
case 0xCB: return "CB";
|
case 0xCC: return "CC";
|
case 0xCD: return "CD";
|
case 0xCE: return "CE";
|
case 0xCF: return "CF";
|
case 0xD0: return "D0";
|
case 0xD1: return "D1";
|
case 0xD2: return "D2";
|
case 0xD3: return "D3";
|
case 0xD4: return "D4";
|
case 0xD5: return "D5";
|
case 0xD6: return "D6";
|
case 0xD7: return "D7";
|
case 0xD8: return "D8";
|
case 0xD9: return "D9";
|
case 0xDA: return "DA";
|
case 0xDB: return "DB";
|
case 0xDC: return "DC";
|
case 0xDD: return "DD";
|
case 0xDE: return "DE";
|
case 0xDF: return "DF";
|
case 0xE0: return "E0";
|
case 0xE1: return "E1";
|
case 0xE2: return "E2";
|
case 0xE3: return "E3";
|
case 0xE4: return "E4";
|
case 0xE5: return "E5";
|
case 0xE6: return "E6";
|
case 0xE7: return "E7";
|
case 0xE8: return "E8";
|
case 0xE9: return "E9";
|
case 0xEA: return "EA";
|
case 0xEB: return "EB";
|
case 0xEC: return "EC";
|
case 0xED: return "ED";
|
case 0xEE: return "EE";
|
case 0xEF: return "EF";
|
case 0xF0: return "F0";
|
case 0xF1: return "F1";
|
case 0xF2: return "F2";
|
case 0xF3: return "F3";
|
case 0xF4: return "F4";
|
case 0xF5: return "F5";
|
case 0xF6: return "F6";
|
case 0xF7: return "F7";
|
case 0xF8: return "F8";
|
case 0xF9: return "F9";
|
case 0xFA: return "FA";
|
case 0xFB: return "FB";
|
case 0xFC: return "FC";
|
case 0xFD: return "FD";
|
case 0xFE: return "FE";
|
case 0xFF: return "FF";
|
default: return "??";
|
}
|
}
|
|
|
|
/**
|
* Retrieves a string representation of the provided byte in hexadecimal.
|
*
|
* @param b The byte for which to retrieve the hexadecimal string
|
* representation.
|
*
|
* @return The string representation of the provided byte in hexadecimal
|
* using lowercase characters.
|
*/
|
public static String byteToLowerHex(byte b)
|
{
|
switch (b & 0xFF)
|
{
|
case 0x00: return "00";
|
case 0x01: return "01";
|
case 0x02: return "02";
|
case 0x03: return "03";
|
case 0x04: return "04";
|
case 0x05: return "05";
|
case 0x06: return "06";
|
case 0x07: return "07";
|
case 0x08: return "08";
|
case 0x09: return "09";
|
case 0x0A: return "0a";
|
case 0x0B: return "0b";
|
case 0x0C: return "0c";
|
case 0x0D: return "0d";
|
case 0x0E: return "0e";
|
case 0x0F: return "0f";
|
case 0x10: return "10";
|
case 0x11: return "11";
|
case 0x12: return "12";
|
case 0x13: return "13";
|
case 0x14: return "14";
|
case 0x15: return "15";
|
case 0x16: return "16";
|
case 0x17: return "17";
|
case 0x18: return "18";
|
case 0x19: return "19";
|
case 0x1A: return "1a";
|
case 0x1B: return "1b";
|
case 0x1C: return "1c";
|
case 0x1D: return "1d";
|
case 0x1E: return "1e";
|
case 0x1F: return "1f";
|
case 0x20: return "20";
|
case 0x21: return "21";
|
case 0x22: return "22";
|
case 0x23: return "23";
|
case 0x24: return "24";
|
case 0x25: return "25";
|
case 0x26: return "26";
|
case 0x27: return "27";
|
case 0x28: return "28";
|
case 0x29: return "29";
|
case 0x2A: return "2a";
|
case 0x2B: return "2b";
|
case 0x2C: return "2c";
|
case 0x2D: return "2d";
|
case 0x2E: return "2e";
|
case 0x2F: return "2f";
|
case 0x30: return "30";
|
case 0x31: return "31";
|
case 0x32: return "32";
|
case 0x33: return "33";
|
case 0x34: return "34";
|
case 0x35: return "35";
|
case 0x36: return "36";
|
case 0x37: return "37";
|
case 0x38: return "38";
|
case 0x39: return "39";
|
case 0x3A: return "3a";
|
case 0x3B: return "3b";
|
case 0x3C: return "3c";
|
case 0x3D: return "3d";
|
case 0x3E: return "3e";
|
case 0x3F: return "3f";
|
case 0x40: return "40";
|
case 0x41: return "41";
|
case 0x42: return "42";
|
case 0x43: return "43";
|
case 0x44: return "44";
|
case 0x45: return "45";
|
case 0x46: return "46";
|
case 0x47: return "47";
|
case 0x48: return "48";
|
case 0x49: return "49";
|
case 0x4A: return "4a";
|
case 0x4B: return "4b";
|
case 0x4C: return "4c";
|
case 0x4D: return "4d";
|
case 0x4E: return "4e";
|
case 0x4F: return "4f";
|
case 0x50: return "50";
|
case 0x51: return "51";
|
case 0x52: return "52";
|
case 0x53: return "53";
|
case 0x54: return "54";
|
case 0x55: return "55";
|
case 0x56: return "56";
|
case 0x57: return "57";
|
case 0x58: return "58";
|
case 0x59: return "59";
|
case 0x5A: return "5a";
|
case 0x5B: return "5b";
|
case 0x5C: return "5c";
|
case 0x5D: return "5d";
|
case 0x5E: return "5e";
|
case 0x5F: return "5f";
|
case 0x60: return "60";
|
case 0x61: return "61";
|
case 0x62: return "62";
|
case 0x63: return "63";
|
case 0x64: return "64";
|
case 0x65: return "65";
|
case 0x66: return "66";
|
case 0x67: return "67";
|
case 0x68: return "68";
|
case 0x69: return "69";
|
case 0x6A: return "6a";
|
case 0x6B: return "6b";
|
case 0x6C: return "6c";
|
case 0x6D: return "6d";
|
case 0x6E: return "6e";
|
case 0x6F: return "6f";
|
case 0x70: return "70";
|
case 0x71: return "71";
|
case 0x72: return "72";
|
case 0x73: return "73";
|
case 0x74: return "74";
|
case 0x75: return "75";
|
case 0x76: return "76";
|
case 0x77: return "77";
|
case 0x78: return "78";
|
case 0x79: return "79";
|
case 0x7A: return "7a";
|
case 0x7B: return "7b";
|
case 0x7C: return "7c";
|
case 0x7D: return "7d";
|
case 0x7E: return "7e";
|
case 0x7F: return "7f";
|
case 0x80: return "80";
|
case 0x81: return "81";
|
case 0x82: return "82";
|
case 0x83: return "83";
|
case 0x84: return "84";
|
case 0x85: return "85";
|
case 0x86: return "86";
|
case 0x87: return "87";
|
case 0x88: return "88";
|
case 0x89: return "89";
|
case 0x8A: return "8a";
|
case 0x8B: return "8b";
|
case 0x8C: return "8c";
|
case 0x8D: return "8d";
|
case 0x8E: return "8e";
|
case 0x8F: return "8f";
|
case 0x90: return "90";
|
case 0x91: return "91";
|
case 0x92: return "92";
|
case 0x93: return "93";
|
case 0x94: return "94";
|
case 0x95: return "95";
|
case 0x96: return "96";
|
case 0x97: return "97";
|
case 0x98: return "98";
|
case 0x99: return "99";
|
case 0x9A: return "9a";
|
case 0x9B: return "9b";
|
case 0x9C: return "9c";
|
case 0x9D: return "9d";
|
case 0x9E: return "9e";
|
case 0x9F: return "9f";
|
case 0xA0: return "a0";
|
case 0xA1: return "a1";
|
case 0xA2: return "a2";
|
case 0xA3: return "a3";
|
case 0xA4: return "a4";
|
case 0xA5: return "a5";
|
case 0xA6: return "a6";
|
case 0xA7: return "a7";
|
case 0xA8: return "a8";
|
case 0xA9: return "a9";
|
case 0xAA: return "aa";
|
case 0xAB: return "ab";
|
case 0xAC: return "ac";
|
case 0xAD: return "ad";
|
case 0xAE: return "ae";
|
case 0xAF: return "af";
|
case 0xB0: return "b0";
|
case 0xB1: return "b1";
|
case 0xB2: return "b2";
|
case 0xB3: return "b3";
|
case 0xB4: return "b4";
|
case 0xB5: return "b5";
|
case 0xB6: return "b6";
|
case 0xB7: return "b7";
|
case 0xB8: return "b8";
|
case 0xB9: return "b9";
|
case 0xBA: return "ba";
|
case 0xBB: return "bb";
|
case 0xBC: return "bc";
|
case 0xBD: return "bd";
|
case 0xBE: return "be";
|
case 0xBF: return "bf";
|
case 0xC0: return "c0";
|
case 0xC1: return "c1";
|
case 0xC2: return "c2";
|
case 0xC3: return "c3";
|
case 0xC4: return "c4";
|
case 0xC5: return "c5";
|
case 0xC6: return "c6";
|
case 0xC7: return "c7";
|
case 0xC8: return "c8";
|
case 0xC9: return "c9";
|
case 0xCA: return "ca";
|
case 0xCB: return "cb";
|
case 0xCC: return "cc";
|
case 0xCD: return "cd";
|
case 0xCE: return "ce";
|
case 0xCF: return "cf";
|
case 0xD0: return "d0";
|
case 0xD1: return "d1";
|
case 0xD2: return "d2";
|
case 0xD3: return "d3";
|
case 0xD4: return "d4";
|
case 0xD5: return "d5";
|
case 0xD6: return "d6";
|
case 0xD7: return "d7";
|
case 0xD8: return "d8";
|
case 0xD9: return "d9";
|
case 0xDA: return "da";
|
case 0xDB: return "db";
|
case 0xDC: return "dc";
|
case 0xDD: return "dd";
|
case 0xDE: return "de";
|
case 0xDF: return "df";
|
case 0xE0: return "e0";
|
case 0xE1: return "e1";
|
case 0xE2: return "e2";
|
case 0xE3: return "e3";
|
case 0xE4: return "e4";
|
case 0xE5: return "e5";
|
case 0xE6: return "e6";
|
case 0xE7: return "e7";
|
case 0xE8: return "e8";
|
case 0xE9: return "e9";
|
case 0xEA: return "ea";
|
case 0xEB: return "eb";
|
case 0xEC: return "ec";
|
case 0xED: return "ed";
|
case 0xEE: return "ee";
|
case 0xEF: return "ef";
|
case 0xF0: return "f0";
|
case 0xF1: return "f1";
|
case 0xF2: return "f2";
|
case 0xF3: return "f3";
|
case 0xF4: return "f4";
|
case 0xF5: return "f5";
|
case 0xF6: return "f6";
|
case 0xF7: return "f7";
|
case 0xF8: return "f8";
|
case 0xF9: return "f9";
|
case 0xFA: return "fa";
|
case 0xFB: return "fb";
|
case 0xFC: return "fc";
|
case 0xFD: return "fd";
|
case 0xFE: return "fe";
|
case 0xFF: return "ff";
|
default: return "??";
|
}
|
}
|
|
|
|
/**
|
* Retrieves the printable ASCII representation of the provided byte.
|
*
|
* @param b The byte for which to retrieve the printable ASCII
|
* representation.
|
*
|
* @return The printable ASCII representation of the provided byte, or a
|
* space if the provided byte does not have printable ASCII
|
* representation.
|
*/
|
public static char byteToASCII(byte b)
|
{
|
if ((b >= 32) && (b <= 126))
|
{
|
return (char) b;
|
}
|
|
return ' ';
|
}
|
|
|
|
/**
|
* Retrieves a string representation of the contents of the provided byte
|
* array using hexadecimal characters with no space between each byte.
|
*
|
* @param b The byte array containing the data.
|
*
|
* @return A string representation of the contents of the provided byte
|
* array using hexadecimal characters.
|
*/
|
public static String bytesToHexNoSpace(byte[] b)
|
{
|
if ((b == null) || (b.length == 0))
|
{
|
return "";
|
}
|
|
int arrayLength = b.length;
|
StringBuilder buffer = new StringBuilder(arrayLength * 2);
|
|
for (int i=0; i < arrayLength; i++)
|
{
|
buffer.append(byteToHex(b[i]));
|
}
|
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Retrieves a string representation of the contents of the provided byte
|
* array using hexadecimal characters and a space between each byte.
|
*
|
* @param b The byte array containing the data.
|
*
|
* @return A string representation of the contents of the provided byte
|
* array using hexadecimal characters.
|
*/
|
public static String bytesToHex(byte[] b)
|
{
|
if ((b == null) || (b.length == 0))
|
{
|
return "";
|
}
|
|
int arrayLength = b.length;
|
StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
|
buffer.append(byteToHex(b[0]));
|
|
for (int i=1; i < arrayLength; i++)
|
{
|
buffer.append(" ");
|
buffer.append(byteToHex(b[i]));
|
}
|
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Retrieves a string representation of the contents of the provided byte
|
* array using hexadecimal characters and a colon between each byte.
|
*
|
* @param b The byte array containing the data.
|
*
|
* @return A string representation of the contents of the provided byte
|
* array using hexadecimal characters.
|
*/
|
public static String bytesToColonDelimitedHex(byte[] b)
|
{
|
if ((b == null) || (b.length == 0))
|
{
|
return "";
|
}
|
|
int arrayLength = b.length;
|
StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
|
buffer.append(byteToHex(b[0]));
|
|
for (int i=1; i < arrayLength; i++)
|
{
|
buffer.append(":");
|
buffer.append(byteToHex(b[i]));
|
}
|
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Retrieves a string representation of the contents of the provided byte
|
* buffer using hexadecimal characters and a space between each byte.
|
*
|
* @param b The byte buffer containing the data.
|
*
|
* @return A string representation of the contents of the provided byte
|
* buffer using hexadecimal characters.
|
*/
|
public static String bytesToHex(ByteBuffer b)
|
{
|
if (b == null)
|
{
|
return "";
|
}
|
|
int position = b.position();
|
int limit = b.limit();
|
int length = limit - position;
|
|
if (length == 0)
|
{
|
return "";
|
}
|
|
StringBuilder buffer = new StringBuilder((length - 1) * 3 + 2);
|
buffer.append(byteToHex(b.get()));
|
|
for (int i=1; i < length; i++)
|
{
|
buffer.append(" ");
|
buffer.append(byteToHex(b.get()));
|
}
|
|
b.position(position);
|
b.limit(limit);
|
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Appends a string representation of the provided byte array to the given
|
* buffer using the specified indent. The data will be formatted with sixteen
|
* hex bytes in a row followed by the ASCII representation, then wrapping to a
|
* new line as necessary.
|
*
|
* @param buffer The buffer to which the information is to be appended.
|
* @param b The byte array containing the data to write.
|
* @param indent The number of spaces to indent the output.
|
*/
|
public static void byteArrayToHexPlusAscii(StringBuilder buffer, byte[] b,
|
int indent)
|
{
|
StringBuilder indentBuf = new StringBuilder(indent);
|
for (int i=0 ; i < indent; i++)
|
{
|
indentBuf.append(' ');
|
}
|
|
|
|
int length = b.length;
|
int pos = 0;
|
while ((length - pos) >= 16)
|
{
|
StringBuilder asciiBuf = new StringBuilder(17);
|
|
buffer.append(indentBuf);
|
buffer.append(byteToHex(b[pos]));
|
asciiBuf.append(byteToASCII(b[pos]));
|
pos++;
|
|
for (int i=1; i < 16; i++, pos++)
|
{
|
buffer.append(' ');
|
buffer.append(byteToHex(b[pos]));
|
asciiBuf.append(byteToASCII(b[pos]));
|
|
if (i == 7)
|
{
|
buffer.append(" ");
|
asciiBuf.append(' ');
|
}
|
}
|
|
buffer.append(" ");
|
buffer.append(asciiBuf);
|
buffer.append(EOL);
|
}
|
|
|
int remaining = (length - pos);
|
if (remaining > 0)
|
{
|
StringBuilder asciiBuf = new StringBuilder(remaining+1);
|
|
buffer.append(indentBuf);
|
buffer.append(byteToHex(b[pos]));
|
asciiBuf.append(byteToASCII(b[pos]));
|
pos++;
|
|
for (int i=1; i < 16; i++)
|
{
|
buffer.append(' ');
|
|
if (i < remaining)
|
{
|
buffer.append(byteToHex(b[pos]));
|
asciiBuf.append(byteToASCII(b[pos]));
|
pos++;
|
}
|
else
|
{
|
buffer.append(" ");
|
}
|
|
if (i == 7)
|
{
|
buffer.append(" ");
|
|
if (i < remaining)
|
{
|
asciiBuf.append(' ');
|
}
|
}
|
}
|
|
buffer.append(" ");
|
buffer.append(asciiBuf);
|
buffer.append(EOL);
|
}
|
}
|
|
|
|
/**
|
* Appends a string representation of the remaining unread data in the
|
* provided byte buffer to the given buffer using the specified indent.
|
* The data will be formatted with sixteen hex bytes in a row followed by
|
* the ASCII representation, then wrapping to a new line as necessary.
|
* The state of the byte buffer is not changed.
|
*
|
* @param buffer The buffer to which the information is to be appended.
|
* @param b The byte buffer containing the data to write.
|
* The data from the position to the limit is written.
|
* @param indent The number of spaces to indent the output.
|
*/
|
public static void byteArrayToHexPlusAscii(StringBuilder buffer, ByteBuffer b,
|
int indent)
|
{
|
StringBuilder indentBuf = new StringBuilder(indent);
|
for (int i=0 ; i < indent; i++)
|
{
|
indentBuf.append(' ');
|
}
|
|
|
int position = b.position();
|
int limit = b.limit();
|
int length = limit - position;
|
int pos = 0;
|
while ((length - pos) >= 16)
|
{
|
StringBuilder asciiBuf = new StringBuilder(17);
|
|
byte currentByte = b.get();
|
buffer.append(indentBuf);
|
buffer.append(byteToHex(currentByte));
|
asciiBuf.append(byteToASCII(currentByte));
|
pos++;
|
|
for (int i=1; i < 16; i++, pos++)
|
{
|
currentByte = b.get();
|
buffer.append(' ');
|
buffer.append(byteToHex(currentByte));
|
asciiBuf.append(byteToASCII(currentByte));
|
|
if (i == 7)
|
{
|
buffer.append(" ");
|
asciiBuf.append(' ');
|
}
|
}
|
|
buffer.append(" ");
|
buffer.append(asciiBuf);
|
buffer.append(EOL);
|
}
|
|
|
int remaining = (length - pos);
|
if (remaining > 0)
|
{
|
StringBuilder asciiBuf = new StringBuilder(remaining+1);
|
|
byte currentByte = b.get();
|
buffer.append(indentBuf);
|
buffer.append(byteToHex(currentByte));
|
asciiBuf.append(byteToASCII(currentByte));
|
|
for (int i=1; i < 16; i++)
|
{
|
buffer.append(' ');
|
|
if (i < remaining)
|
{
|
currentByte = b.get();
|
buffer.append(byteToHex(currentByte));
|
asciiBuf.append(byteToASCII(currentByte));
|
}
|
else
|
{
|
buffer.append(" ");
|
}
|
|
if (i == 7)
|
{
|
buffer.append(" ");
|
|
if (i < remaining)
|
{
|
asciiBuf.append(' ');
|
}
|
}
|
}
|
|
buffer.append(" ");
|
buffer.append(asciiBuf);
|
buffer.append(EOL);
|
}
|
|
b.position(position);
|
b.limit(limit);
|
}
|
|
|
|
/**
|
* Retrieves a binary representation of the provided byte. It will always be
|
* a sequence of eight zeros and/or ones.
|
*
|
* @param b The byte for which to retrieve the binary representation.
|
*
|
* @return The binary representation for the provided byte.
|
*/
|
public static String byteToBinary(byte b)
|
{
|
switch (b & 0xFF)
|
{
|
case 0x00: return "00000000";
|
case 0x01: return "00000001";
|
case 0x02: return "00000010";
|
case 0x03: return "00000011";
|
case 0x04: return "00000100";
|
case 0x05: return "00000101";
|
case 0x06: return "00000110";
|
case 0x07: return "00000111";
|
case 0x08: return "00001000";
|
case 0x09: return "00001001";
|
case 0x0A: return "00001010";
|
case 0x0B: return "00001011";
|
case 0x0C: return "00001100";
|
case 0x0D: return "00001101";
|
case 0x0E: return "00001110";
|
case 0x0F: return "00001111";
|
case 0x10: return "00010000";
|
case 0x11: return "00010001";
|
case 0x12: return "00010010";
|
case 0x13: return "00010011";
|
case 0x14: return "00010100";
|
case 0x15: return "00010101";
|
case 0x16: return "00010110";
|
case 0x17: return "00010111";
|
case 0x18: return "00011000";
|
case 0x19: return "00011001";
|
case 0x1A: return "00011010";
|
case 0x1B: return "00011011";
|
case 0x1C: return "00011100";
|
case 0x1D: return "00011101";
|
case 0x1E: return "00011110";
|
case 0x1F: return "00011111";
|
case 0x20: return "00100000";
|
case 0x21: return "00100001";
|
case 0x22: return "00100010";
|
case 0x23: return "00100011";
|
case 0x24: return "00100100";
|
case 0x25: return "00100101";
|
case 0x26: return "00100110";
|
case 0x27: return "00100111";
|
case 0x28: return "00101000";
|
case 0x29: return "00101001";
|
case 0x2A: return "00101010";
|
case 0x2B: return "00101011";
|
case 0x2C: return "00101100";
|
case 0x2D: return "00101101";
|
case 0x2E: return "00101110";
|
case 0x2F: return "00101111";
|
case 0x30: return "00110000";
|
case 0x31: return "00110001";
|
case 0x32: return "00110010";
|
case 0x33: return "00110011";
|
case 0x34: return "00110100";
|
case 0x35: return "00110101";
|
case 0x36: return "00110110";
|
case 0x37: return "00110111";
|
case 0x38: return "00111000";
|
case 0x39: return "00111001";
|
case 0x3A: return "00111010";
|
case 0x3B: return "00111011";
|
case 0x3C: return "00111100";
|
case 0x3D: return "00111101";
|
case 0x3E: return "00111110";
|
case 0x3F: return "00111111";
|
case 0x40: return "01000000";
|
case 0x41: return "01000001";
|
case 0x42: return "01000010";
|
case 0x43: return "01000011";
|
case 0x44: return "01000100";
|
case 0x45: return "01000101";
|
case 0x46: return "01000110";
|
case 0x47: return "01000111";
|
case 0x48: return "01001000";
|
case 0x49: return "01001001";
|
case 0x4A: return "01001010";
|
case 0x4B: return "01001011";
|
case 0x4C: return "01001100";
|
case 0x4D: return "01001101";
|
case 0x4E: return "01001110";
|
case 0x4F: return "01001111";
|
case 0x50: return "01010000";
|
case 0x51: return "01010001";
|
case 0x52: return "01010010";
|
case 0x53: return "01010011";
|
case 0x54: return "01010100";
|
case 0x55: return "01010101";
|
case 0x56: return "01010110";
|
case 0x57: return "01010111";
|
case 0x58: return "01011000";
|
case 0x59: return "01011001";
|
case 0x5A: return "01011010";
|
case 0x5B: return "01011011";
|
case 0x5C: return "01011100";
|
case 0x5D: return "01011101";
|
case 0x5E: return "01011110";
|
case 0x5F: return "01011111";
|
case 0x60: return "01100000";
|
case 0x61: return "01100001";
|
case 0x62: return "01100010";
|
case 0x63: return "01100011";
|
case 0x64: return "01100100";
|
case 0x65: return "01100101";
|
case 0x66: return "01100110";
|
case 0x67: return "01100111";
|
case 0x68: return "01101000";
|
case 0x69: return "01101001";
|
case 0x6A: return "01101010";
|
case 0x6B: return "01101011";
|
case 0x6C: return "01101100";
|
case 0x6D: return "01101101";
|
case 0x6E: return "01101110";
|
case 0x6F: return "01101111";
|
case 0x70: return "01110000";
|
case 0x71: return "01110001";
|
case 0x72: return "01110010";
|
case 0x73: return "01110011";
|
case 0x74: return "01110100";
|
case 0x75: return "01110101";
|
case 0x76: return "01110110";
|
case 0x77: return "01110111";
|
case 0x78: return "01111000";
|
case 0x79: return "01111001";
|
case 0x7A: return "01111010";
|
case 0x7B: return "01111011";
|
case 0x7C: return "01111100";
|
case 0x7D: return "01111101";
|
case 0x7E: return "01111110";
|
case 0x7F: return "01111111";
|
case 0x80: return "10000000";
|
case 0x81: return "10000001";
|
case 0x82: return "10000010";
|
case 0x83: return "10000011";
|
case 0x84: return "10000100";
|
case 0x85: return "10000101";
|
case 0x86: return "10000110";
|
case 0x87: return "10000111";
|
case 0x88: return "10001000";
|
case 0x89: return "10001001";
|
case 0x8A: return "10001010";
|
case 0x8B: return "10001011";
|
case 0x8C: return "10001100";
|
case 0x8D: return "10001101";
|
case 0x8E: return "10001110";
|
case 0x8F: return "10001111";
|
case 0x90: return "10010000";
|
case 0x91: return "10010001";
|
case 0x92: return "10010010";
|
case 0x93: return "10010011";
|
case 0x94: return "10010100";
|
case 0x95: return "10010101";
|
case 0x96: return "10010110";
|
case 0x97: return "10010111";
|
case 0x98: return "10011000";
|
case 0x99: return "10011001";
|
case 0x9A: return "10011010";
|
case 0x9B: return "10011011";
|
case 0x9C: return "10011100";
|
case 0x9D: return "10011101";
|
case 0x9E: return "10011110";
|
case 0x9F: return "10011111";
|
case 0xA0: return "10100000";
|
case 0xA1: return "10100001";
|
case 0xA2: return "10100010";
|
case 0xA3: return "10100011";
|
case 0xA4: return "10100100";
|
case 0xA5: return "10100101";
|
case 0xA6: return "10100110";
|
case 0xA7: return "10100111";
|
case 0xA8: return "10101000";
|
case 0xA9: return "10101001";
|
case 0xAA: return "10101010";
|
case 0xAB: return "10101011";
|
case 0xAC: return "10101100";
|
case 0xAD: return "10101101";
|
case 0xAE: return "10101110";
|
case 0xAF: return "10101111";
|
case 0xB0: return "10110000";
|
case 0xB1: return "10110001";
|
case 0xB2: return "10110010";
|
case 0xB3: return "10110011";
|
case 0xB4: return "10110100";
|
case 0xB5: return "10110101";
|
case 0xB6: return "10110110";
|
case 0xB7: return "10110111";
|
case 0xB8: return "10111000";
|
case 0xB9: return "10111001";
|
case 0xBA: return "10111010";
|
case 0xBB: return "10111011";
|
case 0xBC: return "10111100";
|
case 0xBD: return "10111101";
|
case 0xBE: return "10111110";
|
case 0xBF: return "10111111";
|
case 0xC0: return "11000000";
|
case 0xC1: return "11000001";
|
case 0xC2: return "11000010";
|
case 0xC3: return "11000011";
|
case 0xC4: return "11000100";
|
case 0xC5: return "11000101";
|
case 0xC6: return "11000110";
|
case 0xC7: return "11000111";
|
case 0xC8: return "11001000";
|
case 0xC9: return "11001001";
|
case 0xCA: return "11001010";
|
case 0xCB: return "11001011";
|
case 0xCC: return "11001100";
|
case 0xCD: return "11001101";
|
case 0xCE: return "11001110";
|
case 0xCF: return "11001111";
|
case 0xD0: return "11010000";
|
case 0xD1: return "11010001";
|
case 0xD2: return "11010010";
|
case 0xD3: return "11010011";
|
case 0xD4: return "11010100";
|
case 0xD5: return "11010101";
|
case 0xD6: return "11010110";
|
case 0xD7: return "11010111";
|
case 0xD8: return "11011000";
|
case 0xD9: return "11011001";
|
case 0xDA: return "11011010";
|
case 0xDB: return "11011011";
|
case 0xDC: return "11011100";
|
case 0xDD: return "11011101";
|
case 0xDE: return "11011110";
|
case 0xDF: return "11011111";
|
case 0xE0: return "11100000";
|
case 0xE1: return "11100001";
|
case 0xE2: return "11100010";
|
case 0xE3: return "11100011";
|
case 0xE4: return "11100100";
|
case 0xE5: return "11100101";
|
case 0xE6: return "11100110";
|
case 0xE7: return "11100111";
|
case 0xE8: return "11101000";
|
case 0xE9: return "11101001";
|
case 0xEA: return "11101010";
|
case 0xEB: return "11101011";
|
case 0xEC: return "11101100";
|
case 0xED: return "11101101";
|
case 0xEE: return "11101110";
|
case 0xEF: return "11101111";
|
case 0xF0: return "11110000";
|
case 0xF1: return "11110001";
|
case 0xF2: return "11110010";
|
case 0xF3: return "11110011";
|
case 0xF4: return "11110100";
|
case 0xF5: return "11110101";
|
case 0xF6: return "11110110";
|
case 0xF7: return "11110111";
|
case 0xF8: return "11111000";
|
case 0xF9: return "11111001";
|
case 0xFA: return "11111010";
|
case 0xFB: return "11111011";
|
case 0xFC: return "11111100";
|
case 0xFD: return "11111101";
|
case 0xFE: return "11111110";
|
case 0xFF: return "11111111";
|
default: return "????????";
|
}
|
}
|
|
|
|
/**
|
* Compare two byte arrays for order. Returns a negative integer,
|
* zero, or a positive integer as the first argument is less than,
|
* equal to, or greater than the second.
|
*
|
* @param a
|
* The first byte array to be compared.
|
* @param a2
|
* The second byte array to be compared.
|
* @return Returns a negative integer, zero, or a positive integer
|
* if the first byte array is less than, equal to, or greater
|
* than the second.
|
*/
|
public static int compare(byte[] a, byte[] a2) {
|
if (a == a2) {
|
return 0;
|
}
|
|
if (a == null) {
|
return -1;
|
}
|
|
if (a2 == null) {
|
return 1;
|
}
|
|
int minLength = Math.min(a.length, a2.length);
|
for (int i = 0; i < minLength; i++) {
|
int firstByte = 0xFF & a[i];
|
int secondByte = 0xFF & a2[i];
|
if (firstByte != secondByte) {
|
if (firstByte < secondByte) {
|
return -1;
|
} else if (firstByte > secondByte) {
|
return 1;
|
}
|
}
|
}
|
|
return (a.length - a2.length);
|
}
|
|
|
|
/**
|
* Indicates whether the two array lists are equal. They will be
|
* considered equal if they have the same number of elements, and
|
* the corresponding elements between them are equal (in the same
|
* order).
|
*
|
* @param list1
|
* The first list for which to make the determination.
|
* @param list2
|
* The second list for which to make the determination.
|
* @return <CODE>true</CODE> if the two array lists are equal, or
|
* <CODE>false</CODE> if they are not.
|
*/
|
public static boolean listsAreEqual(List<?> list1, List<?> list2)
|
{
|
if (list1 == null)
|
{
|
return (list2 == null);
|
}
|
else if (list2 == null)
|
{
|
return false;
|
}
|
|
int numElements = list1.size();
|
if (numElements != list2.size())
|
{
|
return false;
|
}
|
|
// If either of the lists doesn't support random access, then fall back
|
// on their equals methods and go ahead and create some garbage with the
|
// iterators.
|
if (!(list1 instanceof RandomAccess) ||
|
!(list2 instanceof RandomAccess))
|
{
|
return list1.equals(list2);
|
}
|
|
// Otherwise we can just retrieve the elements efficiently via their index.
|
for (int i=0; i < numElements; i++)
|
{
|
Object o1 = list1.get(i);
|
Object o2 = list2.get(i);
|
|
if (o1 == null)
|
{
|
if (o2 != null)
|
{
|
return false;
|
}
|
}
|
else if (! o1.equals(o2))
|
{
|
return false;
|
}
|
}
|
|
return true;
|
}
|
|
|
/**
|
* Return true if and only if o1 and o2 are both null or o1.equals(o2).
|
*
|
* @param o1 the first object to compare
|
* @param o2 the second object to compare
|
* @return true iff o1 and o2 are equal
|
*/
|
public static boolean objectsAreEqual(Object o1, Object o2)
|
{
|
if (o1 == null)
|
{
|
return (o2 == null);
|
}
|
else
|
{
|
return o1.equals(o2);
|
}
|
}
|
|
|
|
/**
|
* Retrieves the best human-readable message for the provided exception. For
|
* exceptions defined in the OpenDJ project, it will attempt to use the
|
* message (combining it with the message ID if available). For some
|
* exceptions that use encapsulation (e.g., InvocationTargetException), it
|
* will be unwrapped and the cause will be treated. For all others, the
|
*
|
*
|
* @param t The {@code Throwable} object for which to retrieve the message.
|
*
|
* @return The human-readable message generated for the provided exception.
|
*/
|
public static Message getExceptionMessage(Throwable t)
|
{
|
if (t instanceof IdentifiedException)
|
{
|
IdentifiedException ie = (IdentifiedException) t;
|
|
StringBuilder message = new StringBuilder();
|
message.append(ie.getMessage());
|
message.append(" (id=");
|
Message ieMsg = ie.getMessageObject();
|
if (ieMsg != null) {
|
message.append(ieMsg.getDescriptor().getId());
|
} else {
|
message.append(MessageDescriptor.NULL_ID);
|
}
|
message.append(")");
|
return Message.raw(message.toString());
|
}
|
else if (t instanceof NullPointerException)
|
{
|
MessageBuilder message = new MessageBuilder();
|
message.append("NullPointerException(");
|
|
StackTraceElement[] stackElements = t.getStackTrace();
|
if (stackElements.length > 0)
|
{
|
message.append(stackElements[0].getFileName());
|
message.append(":");
|
message.append(stackElements[0].getLineNumber());
|
}
|
|
message.append(")");
|
return message.toMessage();
|
}
|
else if ((t instanceof InvocationTargetException) &&
|
(t.getCause() != null))
|
{
|
return getExceptionMessage(t.getCause());
|
}
|
else
|
{
|
StringBuilder message = new StringBuilder();
|
|
String className = t.getClass().getName();
|
int periodPos = className.lastIndexOf('.');
|
if (periodPos > 0)
|
{
|
message.append(className.substring(periodPos+1));
|
}
|
else
|
{
|
message.append(className);
|
}
|
|
message.append("(");
|
if (t.getMessage() == null)
|
{
|
StackTraceElement[] stackElements = t.getStackTrace();
|
|
if (stackElements.length > 0)
|
{
|
message.append(stackElements[0].getFileName());
|
message.append(":");
|
message.append(stackElements[0].getLineNumber());
|
|
// FIXME Temporary to debug issue 2256.
|
if (t instanceof IllegalStateException)
|
{
|
for (int i = 1; i < stackElements.length; i++)
|
{
|
message.append(' ');
|
message.append(stackElements[i].getFileName());
|
message.append(":");
|
message.append(stackElements[i].getLineNumber());
|
}
|
}
|
}
|
}
|
else
|
{
|
message.append(t.getMessage());
|
}
|
|
message.append(")");
|
|
return Message.raw(message.toString());
|
}
|
}
|
|
|
|
/**
|
* Retrieves a stack trace from the provided exception as a single-line
|
* string.
|
*
|
* @param t The exception for which to retrieve the stack trace.
|
*
|
* @return A stack trace from the provided exception as a single-line string.
|
*/
|
public static String stackTraceToSingleLineString(Throwable t)
|
{
|
StringBuilder buffer = new StringBuilder();
|
stackTraceToSingleLineString(buffer, t);
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Appends a single-line string representation of the provided exception to
|
* the given buffer.
|
*
|
* @param buffer The buffer to which the information is to be appended.
|
* @param t The exception for which to retrieve the stack trace.
|
*/
|
public static void stackTraceToSingleLineString(StringBuilder buffer,
|
Throwable t)
|
{
|
if (t == null)
|
{
|
return;
|
}
|
|
if (DynamicConstants.DEBUG_BUILD)
|
{
|
buffer.append(t);
|
|
for (StackTraceElement e : t.getStackTrace())
|
{
|
buffer.append(" / ");
|
buffer.append(e.getFileName());
|
buffer.append(":");
|
buffer.append(e.getLineNumber());
|
}
|
|
while (t.getCause() != null)
|
{
|
t = t.getCause();
|
|
buffer.append("; caused by ");
|
buffer.append(t);
|
|
for (StackTraceElement e : t.getStackTrace())
|
{
|
buffer.append(" / ");
|
buffer.append(e.getFileName());
|
buffer.append(":");
|
buffer.append(e.getLineNumber());
|
}
|
}
|
}
|
else
|
{
|
if ((t instanceof InvocationTargetException) && (t.getCause() != null))
|
{
|
t = t.getCause();
|
}
|
|
String message = t.getMessage();
|
if ((message == null) || (message.length() == 0))
|
{
|
String className = t.getClass().getName();
|
try
|
{
|
className = className.substring(className.lastIndexOf('.') + 1);
|
} catch (Exception e) { /* ignored */ }
|
buffer.append(className);
|
}
|
else
|
{
|
buffer.append(message);
|
}
|
|
int i=0;
|
buffer.append(" (");
|
for (StackTraceElement e : t.getStackTrace())
|
{
|
if (i > 20)
|
{
|
buffer.append(" ...");
|
break;
|
}
|
else if (i > 0)
|
{
|
buffer.append(" ");
|
}
|
|
buffer.append(e.getFileName());
|
buffer.append(":");
|
buffer.append(e.getLineNumber());
|
i++;
|
}
|
|
buffer.append(")");
|
}
|
}
|
|
|
|
/**
|
* Retrieves a string representation of the stack trace for the provided
|
* exception.
|
*
|
* @param t The exception for which to retrieve the stack trace.
|
*
|
* @return A string representation of the stack trace for the provided
|
* exception.
|
*/
|
public static String stackTraceToString(Throwable t)
|
{
|
StringBuilder buffer = new StringBuilder();
|
stackTraceToString(buffer, t);
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Appends a string representation of the stack trace for the provided
|
* exception to the given buffer.
|
*
|
* @param buffer The buffer to which the information is to be appended.
|
* @param t The exception for which to retrieve the stack trace.
|
*/
|
public static void stackTraceToString(StringBuilder buffer, Throwable t)
|
{
|
if (t == null)
|
{
|
return;
|
}
|
|
buffer.append(t);
|
|
for (StackTraceElement e : t.getStackTrace())
|
{
|
buffer.append(EOL);
|
buffer.append(" ");
|
buffer.append(e.getClassName());
|
buffer.append(".");
|
buffer.append(e.getMethodName());
|
buffer.append("(");
|
buffer.append(e.getFileName());
|
buffer.append(":");
|
buffer.append(e.getLineNumber());
|
buffer.append(")");
|
}
|
|
while (t.getCause() != null)
|
{
|
t = t.getCause();
|
buffer.append(EOL);
|
buffer.append("Caused by ");
|
buffer.append(t);
|
|
for (StackTraceElement e : t.getStackTrace())
|
{
|
buffer.append(EOL);
|
buffer.append(" ");
|
buffer.append(e.getClassName());
|
buffer.append(".");
|
buffer.append(e.getMethodName());
|
buffer.append("(");
|
buffer.append(e.getFileName());
|
buffer.append(":");
|
buffer.append(e.getLineNumber());
|
buffer.append(")");
|
}
|
}
|
|
buffer.append(EOL);
|
}
|
|
|
|
/**
|
* Retrieves a backtrace for the current thread consisting only of filenames
|
* and line numbers that may be useful in debugging the origin of problems
|
* that should not have happened. Note that this may be an expensive
|
* operation to perform, so it should only be used for error conditions or
|
* debugging.
|
*
|
* @return A backtrace for the current thread.
|
*/
|
public static String getBacktrace()
|
{
|
StringBuilder buffer = new StringBuilder();
|
|
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
|
|
if (elements.length > 1)
|
{
|
buffer.append(elements[1].getFileName());
|
buffer.append(":");
|
buffer.append(elements[1].getLineNumber());
|
|
for (int i=2; i < elements.length; i++)
|
{
|
buffer.append(" ");
|
buffer.append(elements[i].getFileName());
|
buffer.append(":");
|
buffer.append(elements[i].getLineNumber());
|
}
|
}
|
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Retrieves a backtrace for the provided exception consisting of only
|
* filenames and line numbers that may be useful in debugging the origin of
|
* problems. This is less expensive than the call to
|
* <CODE>getBacktrace</CODE> without any arguments if an exception has already
|
* been thrown.
|
*
|
* @param t The exception for which to obtain the backtrace.
|
*
|
* @return A backtrace from the provided exception.
|
*/
|
public static String getBacktrace(Throwable t)
|
{
|
StringBuilder buffer = new StringBuilder();
|
|
StackTraceElement[] elements = t.getStackTrace();
|
|
if (elements.length > 0)
|
{
|
buffer.append(elements[0].getFileName());
|
buffer.append(":");
|
buffer.append(elements[0].getLineNumber());
|
|
for (int i=1; i < elements.length; i++)
|
{
|
buffer.append(" ");
|
buffer.append(elements[i].getFileName());
|
buffer.append(":");
|
buffer.append(elements[i].getLineNumber());
|
}
|
}
|
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Indicates whether the provided character is a numeric digit.
|
*
|
* @param c The character for which to make the determination.
|
*
|
* @return <CODE>true</CODE> if the provided character represents a numeric
|
* digit, or <CODE>false</CODE> if not.
|
*/
|
public static boolean isDigit(char c)
|
{
|
switch (c)
|
{
|
case '0':
|
case '1':
|
case '2':
|
case '3':
|
case '4':
|
case '5':
|
case '6':
|
case '7':
|
case '8':
|
case '9':
|
return true;
|
default:
|
return false;
|
}
|
}
|
|
|
|
/**
|
* Indicates whether the provided character is an ASCII alphabetic character.
|
*
|
* @param c The character for which to make the determination.
|
*
|
* @return <CODE>true</CODE> if the provided value is an uppercase or
|
* lowercase ASCII alphabetic character, or <CODE>false</CODE> if it
|
* is not.
|
*/
|
public static boolean isAlpha(char c)
|
{
|
switch (c)
|
{
|
case 'A':
|
case 'B':
|
case 'C':
|
case 'D':
|
case 'E':
|
case 'F':
|
case 'G':
|
case 'H':
|
case 'I':
|
case 'J':
|
case 'K':
|
case 'L':
|
case 'M':
|
case 'N':
|
case 'O':
|
case 'P':
|
case 'Q':
|
case 'R':
|
case 'S':
|
case 'T':
|
case 'U':
|
case 'V':
|
case 'W':
|
case 'X':
|
case 'Y':
|
case 'Z':
|
return true;
|
|
case '[':
|
case '\\':
|
case ']':
|
case '^':
|
case '_':
|
case '`':
|
// Making sure all possible cases are present in one contiguous range
|
// can result in a performance improvement.
|
return false;
|
|
case 'a':
|
case 'b':
|
case 'c':
|
case 'd':
|
case 'e':
|
case 'f':
|
case 'g':
|
case 'h':
|
case 'i':
|
case 'j':
|
case 'k':
|
case 'l':
|
case 'm':
|
case 'n':
|
case 'o':
|
case 'p':
|
case 'q':
|
case 'r':
|
case 's':
|
case 't':
|
case 'u':
|
case 'v':
|
case 'w':
|
case 'x':
|
case 'y':
|
case 'z':
|
return true;
|
default:
|
return false;
|
}
|
}
|
|
|
|
/**
|
* Indicates whether the provided character is a hexadecimal digit.
|
*
|
* @param c The character for which to make the determination.
|
*
|
* @return <CODE>true</CODE> if the provided character represents a
|
* hexadecimal digit, or <CODE>false</CODE> if not.
|
*/
|
public static boolean isHexDigit(char c)
|
{
|
switch (c)
|
{
|
case '0':
|
case '1':
|
case '2':
|
case '3':
|
case '4':
|
case '5':
|
case '6':
|
case '7':
|
case '8':
|
case '9':
|
case 'A':
|
case 'B':
|
case 'C':
|
case 'D':
|
case 'E':
|
case 'F':
|
case 'a':
|
case 'b':
|
case 'c':
|
case 'd':
|
case 'e':
|
case 'f':
|
return true;
|
default:
|
return false;
|
}
|
}
|
|
|
|
/**
|
* Indicates whether the provided byte represents a hexadecimal digit.
|
*
|
* @param b The byte for which to make the determination.
|
*
|
* @return <CODE>true</CODE> if the provided byte represents a hexadecimal
|
* digit, or <CODE>false</CODE> if not.
|
*/
|
public static boolean isHexDigit(byte b)
|
{
|
switch (b)
|
{
|
case '0':
|
case '1':
|
case '2':
|
case '3':
|
case '4':
|
case '5':
|
case '6':
|
case '7':
|
case '8':
|
case '9':
|
case 'A':
|
case 'B':
|
case 'C':
|
case 'D':
|
case 'E':
|
case 'F':
|
case 'a':
|
case 'b':
|
case 'c':
|
case 'd':
|
case 'e':
|
case 'f':
|
return true;
|
default:
|
return false;
|
}
|
}
|
|
|
|
/**
|
* Converts the provided hexadecimal string to a byte array.
|
*
|
* @param hexString The hexadecimal string to convert to a byte array.
|
*
|
* @return The byte array containing the binary representation of the
|
* provided hex string.
|
*
|
* @throws ParseException If the provided string contains invalid
|
* hexadecimal digits or does not contain an even
|
* number of digits.
|
*/
|
public static byte[] hexStringToByteArray(String hexString)
|
throws ParseException
|
{
|
int length;
|
if ((hexString == null) || ((length = hexString.length()) == 0))
|
{
|
return new byte[0];
|
}
|
|
|
if ((length % 2) == 1)
|
{
|
Message message = ERR_HEX_DECODE_INVALID_LENGTH.get(hexString);
|
throw new ParseException(message.toString(), 0);
|
}
|
|
|
int pos = 0;
|
int arrayLength = (length / 2);
|
byte[] returnArray = new byte[arrayLength];
|
for (int i=0; i < arrayLength; i++)
|
{
|
switch (hexString.charAt(pos++))
|
{
|
case '0':
|
returnArray[i] = 0x00;
|
break;
|
case '1':
|
returnArray[i] = 0x10;
|
break;
|
case '2':
|
returnArray[i] = 0x20;
|
break;
|
case '3':
|
returnArray[i] = 0x30;
|
break;
|
case '4':
|
returnArray[i] = 0x40;
|
break;
|
case '5':
|
returnArray[i] = 0x50;
|
break;
|
case '6':
|
returnArray[i] = 0x60;
|
break;
|
case '7':
|
returnArray[i] = 0x70;
|
break;
|
case '8':
|
returnArray[i] = (byte) 0x80;
|
break;
|
case '9':
|
returnArray[i] = (byte) 0x90;
|
break;
|
case 'A':
|
case 'a':
|
returnArray[i] = (byte) 0xA0;
|
break;
|
case 'B':
|
case 'b':
|
returnArray[i] = (byte) 0xB0;
|
break;
|
case 'C':
|
case 'c':
|
returnArray[i] = (byte) 0xC0;
|
break;
|
case 'D':
|
case 'd':
|
returnArray[i] = (byte) 0xD0;
|
break;
|
case 'E':
|
case 'e':
|
returnArray[i] = (byte) 0xE0;
|
break;
|
case 'F':
|
case 'f':
|
returnArray[i] = (byte) 0xF0;
|
break;
|
default:
|
Message message = ERR_HEX_DECODE_INVALID_CHARACTER.get(
|
hexString, hexString.charAt(pos-1));
|
throw new ParseException(message.toString(), 0);
|
}
|
|
switch (hexString.charAt(pos++))
|
{
|
case '0':
|
// No action required.
|
break;
|
case '1':
|
returnArray[i] |= 0x01;
|
break;
|
case '2':
|
returnArray[i] |= 0x02;
|
break;
|
case '3':
|
returnArray[i] |= 0x03;
|
break;
|
case '4':
|
returnArray[i] |= 0x04;
|
break;
|
case '5':
|
returnArray[i] |= 0x05;
|
break;
|
case '6':
|
returnArray[i] |= 0x06;
|
break;
|
case '7':
|
returnArray[i] |= 0x07;
|
break;
|
case '8':
|
returnArray[i] |= 0x08;
|
break;
|
case '9':
|
returnArray[i] |= 0x09;
|
break;
|
case 'A':
|
case 'a':
|
returnArray[i] |= 0x0A;
|
break;
|
case 'B':
|
case 'b':
|
returnArray[i] |= 0x0B;
|
break;
|
case 'C':
|
case 'c':
|
returnArray[i] |= 0x0C;
|
break;
|
case 'D':
|
case 'd':
|
returnArray[i] |= 0x0D;
|
break;
|
case 'E':
|
case 'e':
|
returnArray[i] |= 0x0E;
|
break;
|
case 'F':
|
case 'f':
|
returnArray[i] |= 0x0F;
|
break;
|
default:
|
Message message = ERR_HEX_DECODE_INVALID_CHARACTER.get(
|
hexString, hexString.charAt(pos-1));
|
throw new ParseException(message.toString(), 0);
|
}
|
}
|
|
return returnArray;
|
}
|
|
|
|
/**
|
* Indicates whether the provided value needs to be base64-encoded if it is
|
* represented in LDIF form.
|
*
|
* @param valueBytes The binary representation of the attribute value for
|
* which to make the determination.
|
*
|
* @return <CODE>true</CODE> if the value needs to be base64-encoded if it is
|
* represented in LDIF form, or <CODE>false</CODE> if not.
|
*/
|
public static boolean needsBase64Encoding(ByteSequence valueBytes)
|
{
|
int length;
|
if ((valueBytes == null) || ((length = valueBytes.length()) == 0))
|
{
|
return false;
|
}
|
|
|
// If the value starts with a space, colon, or less than, then it needs to
|
// be base64-encoded.
|
switch (valueBytes.byteAt(0))
|
{
|
case 0x20: // Space
|
case 0x3A: // Colon
|
case 0x3C: // Less-than
|
return true;
|
}
|
|
|
// If the value ends with a space, then it needs to be base64-encoded.
|
if ((length > 1) && (valueBytes.byteAt(length-1) == 0x20))
|
{
|
return true;
|
}
|
|
|
// If the value contains a null, newline, or return character, then it needs
|
// to be base64-encoded.
|
byte b;
|
for (int i = 0; i < valueBytes.length(); i++)
|
{
|
b = valueBytes.byteAt(i);
|
if ((b > 127) || (b < 0))
|
{
|
return true;
|
}
|
|
switch (b)
|
{
|
case 0x00: // Null
|
case 0x0A: // New line
|
case 0x0D: // Carriage return
|
return true;
|
}
|
}
|
|
|
// If we've made it here, then there's no reason to base64-encode.
|
return false;
|
}
|
|
|
|
/**
|
* Indicates whether the provided value needs to be base64-encoded if it is
|
* represented in LDIF form.
|
*
|
* @param valueString The string representation of the attribute value for
|
* which to make the determination.
|
*
|
* @return <CODE>true</CODE> if the value needs to be base64-encoded if it is
|
* represented in LDIF form, or <CODE>false</CODE> if not.
|
*/
|
public static boolean needsBase64Encoding(String valueString)
|
{
|
int length;
|
if ((valueString == null) || ((length = valueString.length()) == 0))
|
{
|
return false;
|
}
|
|
|
// If the value starts with a space, colon, or less than, then it needs to
|
// be base64-encoded.
|
switch (valueString.charAt(0))
|
{
|
case ' ':
|
case ':':
|
case '<':
|
return true;
|
}
|
|
|
// If the value ends with a space, then it needs to be base64-encoded.
|
if ((length > 1) && (valueString.charAt(length-1) == ' '))
|
{
|
return true;
|
}
|
|
|
// If the value contains a null, newline, or return character, then it needs
|
// to be base64-encoded.
|
for (int i=0; i < length; i++)
|
{
|
char c = valueString.charAt(i);
|
if ((c <= 0) || (c == 0x0A) || (c == 0x0D) || (c > 127))
|
{
|
return true;
|
}
|
}
|
|
|
// If we've made it here, then there's no reason to base64-encode.
|
return false;
|
}
|
|
|
|
/**
|
* Indicates whether the use of the exec method will be allowed on this
|
* system. It will be allowed by default, but that capability will be removed
|
* if the org.opends.server.DisableExec system property is set and has any
|
* value other than "false", "off", "no", or "0".
|
*
|
* @return <CODE>true</CODE> if the use of the exec method should be allowed,
|
* or <CODE>false</CODE> if it should not be allowed.
|
*/
|
public static boolean mayUseExec()
|
{
|
return (! DirectoryServer.getEnvironmentConfig().disableExec());
|
}
|
|
|
|
/**
|
* Executes the specified command on the system and captures its output. This
|
* will not return until the specified process has completed.
|
*
|
* @param command The command to execute.
|
* @param args The set of arguments to provide to the command.
|
* @param workingDirectory The working directory to use for the command, or
|
* <CODE>null</CODE> if the default directory
|
* should be used.
|
* @param environment The set of environment variables that should be
|
* set when executing the command, or
|
* <CODE>null</CODE> if none are needed.
|
* @param output The output generated by the command while it was
|
* running. This will include both standard
|
* output and standard error. It may be
|
* <CODE>null</CODE> if the output does not need to
|
* be captured.
|
*
|
* @return The exit code for the command.
|
*
|
* @throws IOException If an I/O problem occurs while trying to execute the
|
* command.
|
*
|
* @throws SecurityException If the security policy will not allow the
|
* command to be executed.
|
*
|
* @throws InterruptedException If the current thread is interrupted by
|
* another thread while it is waiting, then
|
* the wait is ended and an InterruptedException
|
* is thrown.
|
*/
|
public static int exec(String command, String[] args, File workingDirectory,
|
Map<String,String> environment, List<String> output)
|
throws IOException, SecurityException, InterruptedException
|
{
|
// See whether we'll allow the use of exec on this system. If not, then
|
// throw an exception.
|
if (! mayUseExec())
|
{
|
Message message = ERR_EXEC_DISABLED.get(String.valueOf(command));
|
throw new SecurityException(message.toString());
|
}
|
|
|
ArrayList<String> commandAndArgs = new ArrayList<String>();
|
commandAndArgs.add(command);
|
if ((args != null) && (args.length > 0))
|
{
|
for (String arg : args)
|
{
|
commandAndArgs.add(arg);
|
}
|
}
|
|
ProcessBuilder processBuilder = new ProcessBuilder(commandAndArgs);
|
processBuilder.redirectErrorStream(true);
|
|
if ((workingDirectory != null) && workingDirectory.isDirectory())
|
{
|
processBuilder.directory(workingDirectory);
|
}
|
|
if ((environment != null) && (! environment.isEmpty()))
|
{
|
processBuilder.environment().putAll(environment);
|
}
|
|
Process process = processBuilder.start();
|
|
// We must exhaust stdout and stderr before calling waitfor. Since we
|
// redirected the error stream, we just have to read from stdout.
|
InputStream processStream = process.getInputStream();
|
BufferedReader reader =
|
new BufferedReader(new InputStreamReader(processStream));
|
String line = null;
|
|
try
|
{
|
while((line = reader.readLine()) != null)
|
{
|
if(output != null)
|
{
|
output.add(line);
|
}
|
}
|
}
|
catch(IOException ioe)
|
{
|
// If this happens, then we have no choice but to forcefully terminate
|
// the process.
|
try
|
{
|
process.destroy();
|
}
|
catch (Exception e)
|
{
|
if (debugEnabled())
|
{
|
TRACER.debugCaught(DebugLogLevel.ERROR, e);
|
}
|
}
|
|
throw ioe;
|
}
|
finally
|
{
|
try
|
{
|
reader.close();
|
}
|
catch(IOException e)
|
{
|
if (debugEnabled())
|
{
|
TRACER.debugCaught(DebugLogLevel.ERROR, e);
|
}
|
}
|
}
|
|
return process.waitFor();
|
}
|
|
|
|
/**
|
* Indicates whether the provided string contains a name or OID for a schema
|
* element like an attribute type or objectclass.
|
*
|
* @param element The string containing the substring for which to
|
* make the determination.
|
* @param startPos The position of the first character that is to be
|
* checked.
|
* @param endPos The position of the first character after the start
|
* position that is not to be checked.
|
* @param invalidReason The buffer to which the invalid reason is to be
|
* appended if a problem is found.
|
*
|
* @return <CODE>true</CODE> if the provided string contains a valid name or
|
* OID for a schema element, or <CODE>false</CODE> if it does not.
|
*/
|
public static boolean isValidSchemaElement(String element, int startPos,
|
int endPos,
|
MessageBuilder invalidReason)
|
{
|
if ((element == null) || (startPos >= endPos))
|
{
|
invalidReason.append(ERR_SCHEMANAME_EMPTY_VALUE.get());
|
return false;
|
}
|
|
|
char c = element.charAt(startPos);
|
if (isAlpha(c))
|
{
|
// This can only be a name and not an OID. The only remaining characters
|
// must be letters, digits, dashes, and possibly the underscore.
|
for (int i=startPos+1; i < endPos; i++)
|
{
|
c = element.charAt(i);
|
if (! (isAlpha(c) || isDigit(c) || (c == '-') ||
|
((c == '_') && DirectoryServer.allowAttributeNameExceptions())))
|
{
|
// This is an illegal character for an attribute name.
|
invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(element, c, i));
|
return false;
|
}
|
}
|
}
|
else if (isDigit(c))
|
{
|
// This should indicate an OID, but it may also be a name if name
|
// exceptions are enabled. Since we don't know for sure, we'll just
|
// hold off until we know for sure.
|
boolean isKnown = (! DirectoryServer.allowAttributeNameExceptions());
|
boolean isNumeric = true;
|
boolean lastWasDot = false;
|
|
for (int i=startPos+1; i < endPos; i++)
|
{
|
c = element.charAt(i);
|
if (c == '.')
|
{
|
if (isKnown)
|
{
|
if (isNumeric)
|
{
|
// This is probably legal unless the last character was also a
|
// period.
|
if (lastWasDot)
|
{
|
invalidReason.append(ERR_SCHEMANAME_CONSECUTIVE_PERIODS.get(
|
element, i));
|
return false;
|
}
|
else
|
{
|
lastWasDot = true;
|
}
|
}
|
else
|
{
|
// This is an illegal character.
|
invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
|
element, c, i));
|
return false;
|
}
|
}
|
else
|
{
|
// Now we know that this must be a numeric OID and not an attribute
|
// name with exceptions allowed.
|
lastWasDot = true;
|
isKnown = true;
|
isNumeric = true;
|
}
|
}
|
else
|
{
|
lastWasDot = false;
|
|
if (isAlpha(c) || (c == '-') || (c == '_'))
|
{
|
if (isKnown)
|
{
|
if (isNumeric)
|
{
|
// This is an illegal character for a numeric OID.
|
invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
|
element, c, i));
|
return false;
|
}
|
}
|
else
|
{
|
// Now we know that this must be an attribute name with exceptions
|
// allowed and not a numeric OID.
|
isKnown = true;
|
isNumeric = false;
|
}
|
}
|
else if (! isDigit(c))
|
{
|
// This is an illegal character.
|
invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
|
element, c, i));
|
return false;
|
}
|
}
|
}
|
}
|
else
|
{
|
// This is an illegal character.
|
invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
|
element, c, startPos));
|
return false;
|
}
|
|
|
// If we've gotten here, then the value is fine.
|
return true;
|
}
|
|
|
|
/**
|
* Indicates whether the provided TCP address is already in use.
|
*
|
* @param address IP address of the TCP address for which to make
|
* the determination.
|
* @param port TCP port number of the TCP address for which to
|
* make the determination.
|
* @param allowReuse Whether or not TCP address reuse is allowed when
|
* making the determination.
|
*
|
* @return <CODE>true</CODE> if the provided TCP address is already in
|
* use, or <CODE>false</CODE> otherwise.
|
*/
|
public static boolean isAddressInUse(
|
InetAddress address, int port,
|
boolean allowReuse)
|
{
|
// Return pessimistic.
|
boolean isInUse = true;
|
Socket clientSocket = null;
|
ServerSocket serverSocket = null;
|
try {
|
// HACK:
|
// With dual stacks we can have a situation when INADDR_ANY/PORT
|
// is bound in TCP4 space but available in TCP6 space and since
|
// JavaServerSocket implemantation will always use TCP46 on dual
|
// stacks the bind below will always succeed in such cases thus
|
// shadowing anything that is already bound to INADDR_ANY/PORT.
|
// While technically correct, with IPv4 and IPv6 being separate
|
// address spaces, it presents a problem to end users because a
|
// common case scenario is to have a single service serving both
|
// address spaces ie listening to the same port in both spaces
|
// on wildcard addresses 0 and ::. ServerSocket implemantation
|
// does not provide any means of working with each address space
|
// separately such as doing TCP4 or TCP6 only binds thus we have
|
// to do a dummy connect to INADDR_ANY/PORT to check if it is
|
// bound to something already. This is only needed for wildcard
|
// addresses as specific IPv4 or IPv6 addresses will always be
|
// handled in their respective address space.
|
if (address.isAnyLocalAddress()) {
|
clientSocket = new Socket();
|
try {
|
// This might fail on some stacks but this is the best we
|
// can do. No need for explicit timeout since it is local
|
// address and we have to know for sure unless it fails.
|
clientSocket.connect(new InetSocketAddress(address, port));
|
} catch (IOException e) {
|
// Expected, ignore.
|
}
|
if (clientSocket.isConnected()) {
|
return true;
|
}
|
}
|
serverSocket = new ServerSocket();
|
serverSocket.setReuseAddress(allowReuse);
|
serverSocket.bind(new InetSocketAddress(address, port));
|
isInUse = false;
|
} catch (IOException e) {
|
isInUse = true;
|
} finally {
|
try {
|
if (serverSocket != null) {
|
serverSocket.close();
|
}
|
} catch (Exception e) {}
|
try {
|
if (clientSocket != null) {
|
clientSocket.close();
|
}
|
} catch (Exception e) {}
|
}
|
return isInUse;
|
}
|
|
|
|
/**
|
* Retrieves a lowercase representation of the given string. This
|
* implementation presumes that the provided string will contain only ASCII
|
* characters and is optimized for that case. However, if a non-ASCII
|
* character is encountered it will fall back on a more expensive algorithm
|
* that will work properly for non-ASCII characters.
|
*
|
* @param s The string for which to obtain the lowercase representation.
|
*
|
* @return The lowercase representation of the given string.
|
*/
|
public static String toLowerCase(String s)
|
{
|
if (s == null)
|
{
|
return null;
|
}
|
|
StringBuilder buffer = new StringBuilder(s.length());
|
toLowerCase(s, buffer);
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Appends a lowercase representation of the given string to the provided
|
* buffer. This implementation presumes that the provided string will contain
|
* only ASCII characters and is optimized for that case. However, if a
|
* non-ASCII character is encountered it will fall back on a more expensive
|
* algorithm that will work properly for non-ASCII characters.
|
*
|
* @param s The string for which to obtain the lowercase
|
* representation.
|
* @param buffer The buffer to which the lowercase form of the string should
|
* be appended.
|
*/
|
public static void toLowerCase(String s, StringBuilder buffer)
|
{
|
if (s == null)
|
{
|
return;
|
}
|
|
int length = s.length();
|
for (int i=0; i < length; i++)
|
{
|
char c = s.charAt(i);
|
|
if ((c & 0x7F) != c)
|
{
|
buffer.append(s.substring(i).toLowerCase());
|
return;
|
}
|
|
switch (c)
|
{
|
case 'A':
|
buffer.append('a');
|
break;
|
case 'B':
|
buffer.append('b');
|
break;
|
case 'C':
|
buffer.append('c');
|
break;
|
case 'D':
|
buffer.append('d');
|
break;
|
case 'E':
|
buffer.append('e');
|
break;
|
case 'F':
|
buffer.append('f');
|
break;
|
case 'G':
|
buffer.append('g');
|
break;
|
case 'H':
|
buffer.append('h');
|
break;
|
case 'I':
|
buffer.append('i');
|
break;
|
case 'J':
|
buffer.append('j');
|
break;
|
case 'K':
|
buffer.append('k');
|
break;
|
case 'L':
|
buffer.append('l');
|
break;
|
case 'M':
|
buffer.append('m');
|
break;
|
case 'N':
|
buffer.append('n');
|
break;
|
case 'O':
|
buffer.append('o');
|
break;
|
case 'P':
|
buffer.append('p');
|
break;
|
case 'Q':
|
buffer.append('q');
|
break;
|
case 'R':
|
buffer.append('r');
|
break;
|
case 'S':
|
buffer.append('s');
|
break;
|
case 'T':
|
buffer.append('t');
|
break;
|
case 'U':
|
buffer.append('u');
|
break;
|
case 'V':
|
buffer.append('v');
|
break;
|
case 'W':
|
buffer.append('w');
|
break;
|
case 'X':
|
buffer.append('x');
|
break;
|
case 'Y':
|
buffer.append('y');
|
break;
|
case 'Z':
|
buffer.append('z');
|
break;
|
default:
|
buffer.append(c);
|
}
|
}
|
}
|
|
|
|
/**
|
* Appends a lowercase string representation of the contents of the given byte
|
* array to the provided buffer, optionally trimming leading and trailing
|
* spaces. This implementation presumes that the provided string will contain
|
* only ASCII characters and is optimized for that case. However, if a
|
* non-ASCII character is encountered it will fall back on a more expensive
|
* algorithm that will work properly for non-ASCII characters.
|
*
|
* @param b The byte array for which to obtain the lowercase string
|
* representation.
|
* @param buffer The buffer to which the lowercase form of the string should
|
* be appended.
|
* @param trim Indicates whether leading and trailing spaces should be
|
* omitted from the string representation.
|
*/
|
public static void toLowerCase(ByteSequence b, StringBuilder buffer,
|
boolean trim)
|
{
|
if (b == null)
|
{
|
return;
|
}
|
|
int origBufferLen = buffer.length();
|
int length = b.length();
|
for (int i=0; i < length; i++)
|
{
|
if ((b.byteAt(i) & 0x7F) != b.byteAt(i))
|
{
|
buffer.replace(origBufferLen, buffer.length(),
|
b.toString().toLowerCase());
|
break;
|
}
|
|
int bufferLength = buffer.length();
|
switch (b.byteAt(i))
|
{
|
case ' ':
|
// If we don't care about trimming, then we can always append the
|
// space. Otherwise, only do so if there are other characters in the
|
// value.
|
if (trim && (bufferLength == 0))
|
{
|
break;
|
}
|
|
buffer.append(' ');
|
break;
|
case 'A':
|
buffer.append('a');
|
break;
|
case 'B':
|
buffer.append('b');
|
break;
|
case 'C':
|
buffer.append('c');
|
break;
|
case 'D':
|
buffer.append('d');
|
break;
|
case 'E':
|
buffer.append('e');
|
break;
|
case 'F':
|
buffer.append('f');
|
break;
|
case 'G':
|
buffer.append('g');
|
break;
|
case 'H':
|
buffer.append('h');
|
break;
|
case 'I':
|
buffer.append('i');
|
break;
|
case 'J':
|
buffer.append('j');
|
break;
|
case 'K':
|
buffer.append('k');
|
break;
|
case 'L':
|
buffer.append('l');
|
break;
|
case 'M':
|
buffer.append('m');
|
break;
|
case 'N':
|
buffer.append('n');
|
break;
|
case 'O':
|
buffer.append('o');
|
break;
|
case 'P':
|
buffer.append('p');
|
break;
|
case 'Q':
|
buffer.append('q');
|
break;
|
case 'R':
|
buffer.append('r');
|
break;
|
case 'S':
|
buffer.append('s');
|
break;
|
case 'T':
|
buffer.append('t');
|
break;
|
case 'U':
|
buffer.append('u');
|
break;
|
case 'V':
|
buffer.append('v');
|
break;
|
case 'W':
|
buffer.append('w');
|
break;
|
case 'X':
|
buffer.append('x');
|
break;
|
case 'Y':
|
buffer.append('y');
|
break;
|
case 'Z':
|
buffer.append('z');
|
break;
|
default:
|
buffer.append((char) b.byteAt(i));
|
}
|
}
|
|
if (trim)
|
{
|
// Strip off any trailing spaces.
|
for (int i=buffer.length()-1; i > 0; i--)
|
{
|
if (buffer.charAt(i) == ' ')
|
{
|
buffer.delete(i, i+1);
|
}
|
else
|
{
|
break;
|
}
|
}
|
}
|
}
|
|
|
|
/**
|
* Retrieves an uppercase representation of the given string. This
|
* implementation presumes that the provided string will contain only ASCII
|
* characters and is optimized for that case. However, if a non-ASCII
|
* character is encountered it will fall back on a more expensive algorithm
|
* that will work properly for non-ASCII characters.
|
*
|
* @param s The string for which to obtain the uppercase representation.
|
*
|
* @return The uppercase representation of the given string.
|
*/
|
public static String toUpperCase(String s)
|
{
|
if (s == null)
|
{
|
return null;
|
}
|
|
StringBuilder buffer = new StringBuilder(s.length());
|
toUpperCase(s, buffer);
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Appends an uppercase representation of the given string to the provided
|
* buffer. This implementation presumes that the provided string will contain
|
* only ASCII characters and is optimized for that case. However, if a
|
* non-ASCII character is encountered it will fall back on a more expensive
|
* algorithm that will work properly for non-ASCII characters.
|
*
|
* @param s The string for which to obtain the uppercase
|
* representation.
|
* @param buffer The buffer to which the uppercase form of the string should
|
* be appended.
|
*/
|
public static void toUpperCase(String s, StringBuilder buffer)
|
{
|
if (s == null)
|
{
|
return;
|
}
|
|
int length = s.length();
|
for (int i=0; i < length; i++)
|
{
|
char c = s.charAt(i);
|
|
if ((c & 0x7F) != c)
|
{
|
buffer.append(s.substring(i).toUpperCase());
|
return;
|
}
|
|
switch (c)
|
{
|
case 'a':
|
buffer.append('A');
|
break;
|
case 'b':
|
buffer.append('B');
|
break;
|
case 'c':
|
buffer.append('C');
|
break;
|
case 'd':
|
buffer.append('D');
|
break;
|
case 'e':
|
buffer.append('E');
|
break;
|
case 'f':
|
buffer.append('F');
|
break;
|
case 'g':
|
buffer.append('G');
|
break;
|
case 'h':
|
buffer.append('H');
|
break;
|
case 'i':
|
buffer.append('I');
|
break;
|
case 'j':
|
buffer.append('J');
|
break;
|
case 'k':
|
buffer.append('K');
|
break;
|
case 'l':
|
buffer.append('L');
|
break;
|
case 'm':
|
buffer.append('M');
|
break;
|
case 'n':
|
buffer.append('N');
|
break;
|
case 'o':
|
buffer.append('O');
|
break;
|
case 'p':
|
buffer.append('P');
|
break;
|
case 'q':
|
buffer.append('Q');
|
break;
|
case 'r':
|
buffer.append('R');
|
break;
|
case 's':
|
buffer.append('S');
|
break;
|
case 't':
|
buffer.append('T');
|
break;
|
case 'u':
|
buffer.append('U');
|
break;
|
case 'v':
|
buffer.append('V');
|
break;
|
case 'w':
|
buffer.append('W');
|
break;
|
case 'x':
|
buffer.append('X');
|
break;
|
case 'y':
|
buffer.append('Y');
|
break;
|
case 'z':
|
buffer.append('Z');
|
break;
|
default:
|
buffer.append(c);
|
}
|
}
|
}
|
|
|
|
/**
|
* Appends an uppercase string representation of the contents of the given
|
* byte array to the provided buffer, optionally trimming leading and trailing
|
* spaces. This implementation presumes that the provided string will contain
|
* only ASCII characters and is optimized for that case. However, if a
|
* non-ASCII character is encountered it will fall back on a more expensive
|
* algorithm that will work properly for non-ASCII characters.
|
*
|
* @param b The byte array for which to obtain the uppercase string
|
* representation.
|
* @param buffer The buffer to which the uppercase form of the string should
|
* be appended.
|
* @param trim Indicates whether leading and trailing spaces should be
|
* omitted from the string representation.
|
*/
|
public static void toUpperCase(byte[] b, StringBuilder buffer, boolean trim)
|
{
|
if (b == null)
|
{
|
return;
|
}
|
|
int length = b.length;
|
for (int i=0; i < length; i++)
|
{
|
if ((b[i] & 0x7F) != b[i])
|
{
|
try
|
{
|
buffer.append(new String(b, i, (length-i), "UTF-8").toUpperCase());
|
}
|
catch (Exception e)
|
{
|
if (debugEnabled())
|
{
|
TRACER.debugCaught(DebugLogLevel.ERROR, e);
|
}
|
buffer.append(new String(b, i, (length - i)).toUpperCase());
|
}
|
break;
|
}
|
|
int bufferLength = buffer.length();
|
switch (b[i])
|
{
|
case ' ':
|
// If we don't care about trimming, then we can always append the
|
// space. Otherwise, only do so if there are other characters in the
|
// value.
|
if (trim && (bufferLength == 0))
|
{
|
break;
|
}
|
|
buffer.append(' ');
|
break;
|
case 'a':
|
buffer.append('A');
|
break;
|
case 'b':
|
buffer.append('B');
|
break;
|
case 'c':
|
buffer.append('C');
|
break;
|
case 'd':
|
buffer.append('D');
|
break;
|
case 'e':
|
buffer.append('E');
|
break;
|
case 'f':
|
buffer.append('F');
|
break;
|
case 'g':
|
buffer.append('G');
|
break;
|
case 'h':
|
buffer.append('H');
|
break;
|
case 'i':
|
buffer.append('I');
|
break;
|
case 'j':
|
buffer.append('J');
|
break;
|
case 'k':
|
buffer.append('K');
|
break;
|
case 'l':
|
buffer.append('L');
|
break;
|
case 'm':
|
buffer.append('M');
|
break;
|
case 'n':
|
buffer.append('N');
|
break;
|
case 'o':
|
buffer.append('O');
|
break;
|
case 'p':
|
buffer.append('P');
|
break;
|
case 'q':
|
buffer.append('Q');
|
break;
|
case 'r':
|
buffer.append('R');
|
break;
|
case 's':
|
buffer.append('S');
|
break;
|
case 't':
|
buffer.append('T');
|
break;
|
case 'u':
|
buffer.append('U');
|
break;
|
case 'v':
|
buffer.append('V');
|
break;
|
case 'w':
|
buffer.append('W');
|
break;
|
case 'x':
|
buffer.append('X');
|
break;
|
case 'y':
|
buffer.append('Y');
|
break;
|
case 'z':
|
buffer.append('Z');
|
break;
|
default:
|
buffer.append((char) b[i]);
|
}
|
}
|
|
if (trim)
|
{
|
// Strip off any trailing spaces.
|
for (int i=buffer.length()-1; i > 0; i--)
|
{
|
if (buffer.charAt(i) == ' ')
|
{
|
buffer.delete(i, i+1);
|
}
|
else
|
{
|
break;
|
}
|
}
|
}
|
}
|
|
|
|
/**
|
* Append a string to a string builder, escaping any double quotes
|
* according to the StringValue production in RFC 3641.
|
* <p>
|
* In RFC 3641 the StringValue production looks like this:
|
*
|
* <pre>
|
* StringValue = dquote *SafeUTF8Character dquote
|
* dquote = %x22 ; " (double quote)
|
* SafeUTF8Character = %x00-21 / %x23-7F / ; ASCII minus dquote
|
* dquote dquote / ; escaped double quote
|
* %xC0-DF %x80-BF / ; 2 byte UTF-8 character
|
* %xE0-EF 2(%x80-BF) / ; 3 byte UTF-8 character
|
* %xF0-F7 3(%x80-BF) ; 4 byte UTF-8 character
|
* </pre>
|
*
|
* <p>
|
* That is, strings are surrounded by double-quotes and any internal
|
* double-quotes are doubled up.
|
*
|
* @param builder
|
* The string builder.
|
* @param string
|
* The string to escape and append.
|
* @return Returns the string builder.
|
*/
|
public static StringBuilder toRFC3641StringValue(StringBuilder builder,
|
String string)
|
{
|
// Initial double-quote.
|
builder.append('"');
|
|
for (char c : string.toCharArray())
|
{
|
if (c == '"')
|
{
|
// Internal double-quotes are escaped using a double-quote.
|
builder.append('"');
|
}
|
builder.append(c);
|
}
|
|
// Trailing double-quote.
|
builder.append('"');
|
|
return builder;
|
}
|
|
|
|
/**
|
* Retrieves a string array containing the contents of the provided
|
* list of strings.
|
*
|
* @param stringList
|
* The string list to convert to an array.
|
* @return A string array containing the contents of the provided list
|
* of strings.
|
*/
|
public static String[] listToArray(List<String> stringList)
|
{
|
if (stringList == null)
|
{
|
return null;
|
}
|
|
String[] stringArray = new String[stringList.size()];
|
stringList.toArray(stringArray);
|
return stringArray;
|
}
|
|
/**
|
* Creates a string representation of the elements in the
|
* <code>list</code> separated by <code>separator</code>.
|
*
|
* @param list the list to print
|
* @param separator to use between elements
|
*
|
* @return String representing the list
|
*/
|
static public String listToString(List<?> list, String separator)
|
{
|
StringBuilder sb = new StringBuilder();
|
for (int i = 0; i < list.size(); i++) {
|
sb.append(list.get(i));
|
if (i < list.size() - 1) {
|
sb.append(separator);
|
}
|
}
|
return sb.toString();
|
}
|
|
/**
|
* Creates a string representation of the elements in the
|
* <code>collection</code> separated by <code>separator</code>.
|
*
|
* @param collection to print
|
* @param separator to use between elements
|
*
|
* @return String representing the collection
|
*/
|
static public String collectionToString(Collection<?> collection,
|
String separator)
|
{
|
StringBuilder sb = new StringBuilder();
|
for (Iterator<?> iter = collection.iterator(); iter.hasNext();) {
|
sb.append(iter.next());
|
if (iter.hasNext()) {
|
sb.append(separator);
|
}
|
}
|
return sb.toString();
|
}
|
|
|
/**
|
* Retrieves an array list containing the contents of the provided array.
|
*
|
* @param stringArray The string array to convert to an array list.
|
*
|
* @return An array list containing the contents of the provided array.
|
*/
|
public static ArrayList<String> arrayToList(String[] stringArray)
|
{
|
if (stringArray == null)
|
{
|
return null;
|
}
|
|
ArrayList<String> stringList = new ArrayList<String>(stringArray.length);
|
for (String s : stringArray)
|
{
|
stringList.add(s);
|
}
|
|
return stringList;
|
}
|
|
|
/**
|
* Attempts to delete the specified file or directory. If it is a directory,
|
* then any files or subdirectories that it contains will be recursively
|
* deleted as well.
|
*
|
* @param file The file or directory to be removed.
|
*
|
* @return <CODE>true</CODE> if the specified file and any subordinates are
|
* all successfully removed, or <CODE>false</CODE> if at least one
|
* element in the subtree could not be removed.
|
*/
|
public static boolean recursiveDelete(File file)
|
{
|
boolean successful = true;
|
if (file.isDirectory())
|
{
|
File[] childList = file.listFiles();
|
if (childList != null)
|
{
|
for (File f : childList)
|
{
|
successful &= recursiveDelete(f);
|
}
|
}
|
}
|
|
return (successful & file.delete());
|
}
|
|
|
|
/**
|
* Moves the indicated file to the specified directory by creating a new file
|
* in the target directory, copying the contents of the existing file, and
|
* removing the existing file. The file to move must exist and must be a
|
* file. The target directory must exist, must be a directory, and must not
|
* be the directory in which the file currently resides.
|
*
|
* @param fileToMove The file to move to the target directory.
|
* @param targetDirectory The directory into which the file should be moved.
|
*
|
* @throws IOException If a problem occurs while attempting to move the
|
* file.
|
*/
|
public static void moveFile(File fileToMove, File targetDirectory)
|
throws IOException
|
{
|
if (! fileToMove.exists())
|
{
|
Message message = ERR_MOVEFILE_NO_SUCH_FILE.get(fileToMove.getPath());
|
throw new IOException(message.toString());
|
}
|
|
if (! fileToMove.isFile())
|
{
|
Message message = ERR_MOVEFILE_NOT_FILE.get(fileToMove.getPath());
|
throw new IOException(message.toString());
|
}
|
|
if (! targetDirectory.exists())
|
{
|
Message message =
|
ERR_MOVEFILE_NO_SUCH_DIRECTORY.get(targetDirectory.getPath());
|
throw new IOException(message.toString());
|
}
|
|
if (! targetDirectory.isDirectory())
|
{
|
Message message =
|
ERR_MOVEFILE_NOT_DIRECTORY.get(targetDirectory.getPath());
|
throw new IOException(message.toString());
|
}
|
|
String newFilePath = targetDirectory.getPath() + File.separator +
|
fileToMove.getName();
|
FileInputStream inputStream = new FileInputStream(fileToMove);
|
FileOutputStream outputStream = new FileOutputStream(newFilePath, false);
|
byte[] buffer = new byte[8192];
|
while (true)
|
{
|
int bytesRead = inputStream.read(buffer);
|
if (bytesRead < 0)
|
{
|
break;
|
}
|
|
outputStream.write(buffer, 0, bytesRead);
|
}
|
|
outputStream.flush();
|
outputStream.close();
|
inputStream.close();
|
fileToMove.delete();
|
}
|
|
/**
|
* Renames the source file to the target file. If the target file exists
|
* it is first deleted. The rename and delete operation return values
|
* are checked for success and if unsuccessful, this method throws an
|
* exception.
|
*
|
* @param fileToRename The file to rename.
|
* @param target The file to which <code>fileToRename</code> will be
|
* moved.
|
* @throws IOException If a problem occurs while attempting to rename the
|
* file. On the Windows platform, this typically
|
* indicates that the file is in use by this or another
|
* application.
|
*/
|
static public void renameFile(File fileToRename, File target)
|
throws IOException {
|
if (fileToRename != null && target != null)
|
{
|
synchronized(target)
|
{
|
if (target.exists())
|
{
|
if (!target.delete())
|
{
|
Message message =
|
ERR_RENAMEFILE_CANNOT_DELETE_TARGET.get(target.getPath());
|
throw new IOException(message.toString());
|
}
|
}
|
}
|
if (!fileToRename.renameTo(target))
|
{
|
Message message = ERR_RENAMEFILE_CANNOT_RENAME.get(
|
fileToRename.getPath(), target.getPath());
|
throw new IOException(message.toString());
|
|
}
|
}
|
}
|
|
|
/**
|
* Indicates whether the provided path refers to a relative path rather than
|
* an absolute path.
|
*
|
* @param path The path string for which to make the determination.
|
*
|
* @return <CODE>true</CODE> if the provided path is relative, or
|
* <CODE>false</CODE> if it is absolute.
|
*/
|
public static boolean isRelativePath(String path)
|
{
|
File f = new File(path);
|
return (! f.isAbsolute());
|
}
|
|
|
|
/**
|
* Retrieves a <CODE>File</CODE> object corresponding to the specified path.
|
* If the given path is an absolute path, then it will be used. If the path
|
* is relative, then it will be interpreted as if it were relative to the
|
* Directory Server root.
|
*
|
* @param path The path string to be retrieved as a <CODE>File</CODE>
|
*
|
* @return A <CODE>File</CODE> object that corresponds to the specified path.
|
*/
|
public static File getFileForPath(String path)
|
{
|
File f = new File (path);
|
|
if (f.isAbsolute())
|
{
|
return f;
|
}
|
else
|
{
|
return new File(DirectoryServer.getInstanceRoot() + File.separator +
|
path);
|
}
|
}
|
|
|
|
/**
|
* Creates a new, blank entry with the given DN. It will contain only the
|
* attribute(s) contained in the RDN. The choice of objectclasses will be
|
* based on the RDN attribute. If there is a single RDN attribute, then the
|
* following mapping will be used:
|
* <BR>
|
* <UL>
|
* <LI>c attribute :: country objectclass</LI>
|
* <LI>dc attribute :: domain objectclass</LI>
|
* <LI>o attribute :: organization objectclass</LI>
|
* <LI>ou attribute :: organizationalUnit objectclass</LI>
|
* </UL>
|
* <BR>
|
* Any other single RDN attribute types, or any case in which there are
|
* multiple RDN attributes, will use the untypedObject objectclass. If the
|
* RDN includes one or more attributes that are not allowed in the
|
* untypedObject objectclass, then the extensibleObject class will also be
|
* added. Note that this method cannot be used to generate an entry
|
* with an empty or null DN.
|
*
|
* @param dn The DN to use for the entry.
|
*
|
* @return The entry created with the provided DN.
|
*/
|
public static Entry createEntry(DN dn)
|
{
|
// If the provided DN was null or empty, then return null because we don't
|
// support it.
|
if ((dn == null) || dn.isNullDN())
|
{
|
return null;
|
}
|
|
|
// Get the information about the RDN attributes.
|
RDN rdn = dn.getRDN();
|
int numAVAs = rdn.getNumValues();
|
|
// If there is only one RDN attribute, then see which objectclass we should
|
// use.
|
ObjectClass structuralClass;
|
if (numAVAs == 1)
|
{
|
AttributeType attrType = rdn.getAttributeType(0);
|
|
if (attrType.hasName(ATTR_C))
|
{
|
structuralClass = DirectoryServer.getObjectClass(OC_COUNTRY, true);
|
}
|
else if (attrType.hasName(ATTR_DC))
|
{
|
structuralClass = DirectoryServer.getObjectClass(OC_DOMAIN, true);
|
}
|
else if (attrType.hasName(ATTR_O))
|
{
|
structuralClass = DirectoryServer.getObjectClass(OC_ORGANIZATION, true);
|
}
|
else if (attrType.hasName(ATTR_OU))
|
{
|
structuralClass =
|
DirectoryServer.getObjectClass(OC_ORGANIZATIONAL_UNIT_LC, true);
|
}
|
else
|
{
|
structuralClass =
|
DirectoryServer.getObjectClass(OC_UNTYPED_OBJECT_LC, true);
|
}
|
}
|
else
|
{
|
structuralClass =
|
DirectoryServer.getObjectClass(OC_UNTYPED_OBJECT_LC, true);
|
}
|
|
|
// Get the top and untypedObject classes to include in the entry.
|
LinkedHashMap<ObjectClass,String> objectClasses =
|
new LinkedHashMap<ObjectClass,String>(3);
|
|
objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
|
objectClasses.put(structuralClass, structuralClass.getNameOrOID());
|
|
|
// Iterate through the RDN attributes and add them to the set of user or
|
// operational attributes.
|
LinkedHashMap<AttributeType,List<Attribute>> userAttributes =
|
new LinkedHashMap<AttributeType,List<Attribute>>();
|
LinkedHashMap<AttributeType,List<Attribute>> operationalAttributes =
|
new LinkedHashMap<AttributeType,List<Attribute>>();
|
|
boolean extensibleObjectAdded = false;
|
for (int i=0; i < numAVAs; i++)
|
{
|
AttributeType attrType = rdn.getAttributeType(i);
|
AttributeValue attrValue = rdn.getAttributeValue(i);
|
String attrName = rdn.getAttributeName(i);
|
|
// First, see if this type is allowed by the untypedObject class. If not,
|
// then we'll need to include the extensibleObject class.
|
if ((! structuralClass.isRequiredOrOptional(attrType)) &&
|
(! extensibleObjectAdded))
|
{
|
ObjectClass extensibleObjectOC =
|
DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC);
|
if (extensibleObjectOC == null)
|
{
|
extensibleObjectOC =
|
DirectoryServer.getDefaultObjectClass(OC_EXTENSIBLE_OBJECT);
|
}
|
objectClasses.put(extensibleObjectOC, OC_EXTENSIBLE_OBJECT);
|
extensibleObjectAdded = true;
|
}
|
|
|
// Create the attribute and add it to the appropriate map.
|
if (attrType.isOperational())
|
{
|
List<Attribute> attrList = operationalAttributes.get(attrType);
|
if ((attrList == null) || attrList.isEmpty())
|
{
|
AttributeBuilder builder = new AttributeBuilder(attrType, attrName);
|
builder.add(attrValue);
|
attrList = new ArrayList<Attribute>(1);
|
attrList.add(builder.toAttribute());
|
operationalAttributes.put(attrType, attrList);
|
}
|
else
|
{
|
AttributeBuilder builder = new AttributeBuilder(attrList.get(0));
|
builder.add(attrValue);
|
attrList.set(0, builder.toAttribute());
|
}
|
}
|
else
|
{
|
List<Attribute> attrList = userAttributes.get(attrType);
|
if ((attrList == null) || attrList.isEmpty())
|
{
|
AttributeBuilder builder = new AttributeBuilder(attrType, attrName);
|
builder.add(attrValue);
|
attrList = new ArrayList<Attribute>(1);
|
attrList.add(builder.toAttribute());
|
userAttributes.put(attrType, attrList);
|
}
|
else
|
{
|
AttributeBuilder builder = new AttributeBuilder(attrList.get(0));
|
builder.add(attrValue);
|
attrList.set(0, builder.toAttribute());
|
}
|
}
|
}
|
|
|
// Create and return the entry.
|
return new Entry(dn, objectClasses, userAttributes, operationalAttributes);
|
}
|
|
|
|
/**
|
* Retrieves a user-friendly string that indicates the length of time (in
|
* days, hours, minutes, and seconds) in the specified number of seconds.
|
*
|
* @param numSeconds The number of seconds to be converted to a more
|
* user-friendly value.
|
*
|
* @return The user-friendly representation of the specified number of
|
* seconds.
|
*/
|
public static Message secondsToTimeString(long numSeconds)
|
{
|
if (numSeconds < 60)
|
{
|
// We can express it in seconds.
|
return INFO_TIME_IN_SECONDS.get(numSeconds);
|
}
|
else if (numSeconds < 3600)
|
{
|
// We can express it in minutes and seconds.
|
long m = numSeconds / 60;
|
long s = numSeconds % 60;
|
return INFO_TIME_IN_MINUTES_SECONDS.get(m, s);
|
}
|
else if (numSeconds < 86400)
|
{
|
// We can express it in hours, minutes, and seconds.
|
long h = numSeconds / 3600;
|
long m = (numSeconds % 3600) / 60;
|
long s = numSeconds % 3600 % 60;
|
return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s);
|
}
|
else
|
{
|
// We can express it in days, hours, minutes, and seconds.
|
long d = numSeconds / 86400;
|
long h = (numSeconds % 86400) / 3600;
|
long m = (numSeconds % 86400 % 3600) / 60;
|
long s = numSeconds % 86400 % 3600 % 60;
|
return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s);
|
}
|
}
|
|
|
|
/**
|
* Inserts line breaks into the provided buffer to wrap text at no more than
|
* the specified column width. Wrapping will only be done at space boundaries
|
* and if there are no spaces within the specified width, then wrapping will
|
* be performed at the first space after the specified column.
|
*
|
* @param message The message to be wrapped.
|
* @param width The maximum number of characters to allow on a line if there
|
* is a suitable breaking point.
|
*
|
* @return The wrapped text.
|
*/
|
public static String wrapText(Message message, int width)
|
{
|
return wrapText(Message.toString(message), width, 0);
|
}
|
|
|
|
/**
|
* Inserts line breaks into the provided buffer to wrap text at no more than
|
* the specified column width. Wrapping will only be done at space boundaries
|
* and if there are no spaces within the specified width, then wrapping will
|
* be performed at the first space after the specified column.
|
*
|
* @param text The text to be wrapped.
|
* @param width The maximum number of characters to allow on a line if there
|
* is a suitable breaking point.
|
*
|
* @return The wrapped text.
|
*/
|
public static String wrapText(String text, int width) {
|
return wrapText(text, width, 0);
|
}
|
|
|
|
/**
|
* Inserts line breaks into the provided buffer to wrap text at no
|
* more than the specified column width. Wrapping will only be done
|
* at space boundaries and if there are no spaces within the
|
* specified width, then wrapping will be performed at the first
|
* space after the specified column. In addition each line will be
|
* indented by the specified amount.
|
*
|
* @param message
|
* The message to be wrapped.
|
* @param width
|
* The maximum number of characters to allow on a line if
|
* there is a suitable breaking point (including any
|
* indentation).
|
* @param indent
|
* The number of columns to indent each line.
|
* @return The wrapped text.
|
*/
|
public static String wrapText(Message message, int width, int indent)
|
{
|
return wrapText(Message.toString(message), width, indent);
|
}
|
|
|
|
/**
|
* Inserts line breaks into the provided buffer to wrap text at no
|
* more than the specified column width. Wrapping will only be done
|
* at space boundaries and if there are no spaces within the
|
* specified width, then wrapping will be performed at the first
|
* space after the specified column. In addition each line will be
|
* indented by the specified amount.
|
*
|
* @param text
|
* The text to be wrapped.
|
* @param width
|
* The maximum number of characters to allow on a line if
|
* there is a suitable breaking point (including any
|
* indentation).
|
* @param indent
|
* The number of columns to indent each line.
|
* @return The wrapped text.
|
*/
|
public static String wrapText(String text, int width, int indent)
|
{
|
Validator.ensureTrue(indent >= 0 && indent < width);
|
|
// Calculate the real width and indentation padding.
|
width -= indent;
|
StringBuilder pb = new StringBuilder();
|
for (int i = 0; i < indent; i++) {
|
pb.append(' ');
|
}
|
String padding = pb.toString();
|
|
StringBuilder buffer = new StringBuilder();
|
if (text != null) {
|
StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
|
while (lineTokenizer.hasMoreTokens())
|
{
|
String line = lineTokenizer.nextToken();
|
if (line.equals("\r") || line.equals("\n"))
|
{
|
// It's an end-of-line character, so append it as-is.
|
buffer.append(line);
|
}
|
else if (line.length() < width)
|
{
|
// The line fits in the specified width, so append it as-is.
|
buffer.append(padding);
|
buffer.append(line);
|
}
|
else
|
{
|
// The line doesn't fit in the specified width, so it needs to be
|
// wrapped. Do so at space boundaries.
|
StringBuilder lineBuffer = new StringBuilder();
|
StringBuilder delimBuffer = new StringBuilder();
|
StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
|
while (wordTokenizer.hasMoreTokens())
|
{
|
String word = wordTokenizer.nextToken();
|
if (word.equals(" "))
|
{
|
// It's a space, so add it to the delim buffer only if the line
|
// buffer is not empty.
|
if (lineBuffer.length() > 0)
|
{
|
delimBuffer.append(word);
|
}
|
}
|
else if (word.length() > width)
|
{
|
// This is a long word that can't be wrapped, so we'll just have
|
// to make do.
|
if (lineBuffer.length() > 0)
|
{
|
buffer.append(padding);
|
buffer.append(lineBuffer);
|
buffer.append(EOL);
|
lineBuffer = new StringBuilder();
|
}
|
buffer.append(padding);
|
buffer.append(word);
|
|
if (wordTokenizer.hasMoreTokens())
|
{
|
// The next token must be a space, so remove it. If there are
|
// still more tokens after that, then append an EOL.
|
wordTokenizer.nextToken();
|
if (wordTokenizer.hasMoreTokens())
|
{
|
buffer.append(EOL);
|
}
|
}
|
|
if (delimBuffer.length() > 0)
|
{
|
delimBuffer = new StringBuilder();
|
}
|
}
|
else
|
{
|
// It's not a space, so see if we can fit it on the curent line.
|
int newLineLength = lineBuffer.length() + delimBuffer.length() +
|
word.length();
|
if (newLineLength < width)
|
{
|
// It does fit on the line, so add it.
|
lineBuffer.append(delimBuffer).append(word);
|
|
if (delimBuffer.length() > 0)
|
{
|
delimBuffer = new StringBuilder();
|
}
|
}
|
else
|
{
|
// It doesn't fit on the line, so end the current line and start
|
// a new one.
|
buffer.append(padding);
|
buffer.append(lineBuffer);
|
buffer.append(EOL);
|
|
lineBuffer = new StringBuilder();
|
lineBuffer.append(word);
|
|
if (delimBuffer.length() > 0)
|
{
|
delimBuffer = new StringBuilder();
|
}
|
}
|
}
|
}
|
|
// If there's anything left in the line buffer, then add it to the
|
// final buffer.
|
buffer.append(padding);
|
buffer.append(lineBuffer);
|
}
|
}
|
}
|
return buffer.toString();
|
}
|
|
|
|
/**
|
* Filters the provided value to ensure that it is appropriate for use as an
|
* exit code. Exit code values are generally only allowed to be between 0 and
|
* 255, so any value outside of this range will be converted to 255, which is
|
* the typical exit code used to indicate an overflow value.
|
*
|
* @param exitCode The exit code value to be processed.
|
*
|
* @return An integer value between 0 and 255, inclusive. If the provided
|
* exit code was already between 0 and 255, then the original value
|
* will be returned. If the provided value was out of this range,
|
* then 255 will be returned.
|
*/
|
public static int filterExitCode(int exitCode)
|
{
|
if (exitCode < 0)
|
{
|
return 255;
|
}
|
else if (exitCode > 255)
|
{
|
return 255;
|
}
|
else
|
{
|
return exitCode;
|
}
|
}
|
|
/**
|
* Checks that no more that one of a set of arguments is present. This
|
* utility should be used after argument parser has parsed a set of
|
* arguments.
|
*
|
* @param args to test for the presence of more than one
|
* @throws ArgumentException if more than one of <code>args</code> is
|
* present and containing an error message identifying the
|
* arguments in violation
|
*/
|
public static void checkOnlyOneArgPresent(Argument... args)
|
throws ArgumentException
|
{
|
if (args != null) {
|
for (Argument arg : args) {
|
for (Argument otherArg : args) {
|
if (arg != otherArg && arg.isPresent() && otherArg.isPresent()) {
|
throw new ArgumentException(
|
ToolMessages.ERR_INCOMPATIBLE_ARGUMENTS.get(
|
arg.getName(), otherArg.getName()));
|
}
|
}
|
}
|
}
|
}
|
|
/**
|
* Converts a string representing a time in "yyyyMMddHHmmss.SSS'Z'" or
|
* "yyyyMMddHHmmss" to a <code>Date</code>.
|
*
|
* @param timeStr string formatted appropriately
|
* @return Date object; null if <code>timeStr</code> is null
|
* @throws ParseException if there was a problem converting the string to
|
* a <code>Date</code>.
|
*/
|
static public Date parseDateTimeString(String timeStr) throws ParseException
|
{
|
Date dateTime = null;
|
if (timeStr != null)
|
{
|
if (timeStr.endsWith("Z"))
|
{
|
try
|
{
|
SimpleDateFormat dateFormat =
|
new SimpleDateFormat(DATE_FORMAT_GENERALIZED_TIME);
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
dateFormat.setLenient(true);
|
dateTime = dateFormat.parse(timeStr);
|
}
|
catch (ParseException pe)
|
{
|
// Best effort: try with GMT time.
|
SimpleDateFormat dateFormat =
|
new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
dateFormat.setLenient(true);
|
dateTime = dateFormat.parse(timeStr);
|
}
|
}
|
else
|
{
|
SimpleDateFormat dateFormat =
|
new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME);
|
dateFormat.setLenient(true);
|
dateTime = dateFormat.parse(timeStr);
|
}
|
}
|
return dateTime;
|
}
|
|
/**
|
* Formats a Date to String representation in "yyyyMMddHHmmss'Z'".
|
*
|
* @param date to format; null if <code>date</code> is null
|
* @return string representation of the date
|
*/
|
static public String formatDateTimeString(Date date)
|
{
|
String timeStr = null;
|
if (date != null)
|
{
|
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
timeStr = dateFormat.format(date);
|
}
|
return timeStr;
|
}
|
|
/**
|
* Indicates whether or not a string represents a syntactically correct
|
* email address.
|
*
|
* @param addr to validate
|
* @return boolean where <code>true</code> indicates that the string is a
|
* syntactically correct email address
|
*/
|
public static boolean isEmailAddress(String addr) {
|
|
// This just does basic syntax checking. Perhaps we
|
// might want to be stricter about this.
|
return addr != null && addr.contains("@") && addr.contains(".");
|
|
}
|
|
|
|
/**
|
* Writes the contents of the provided buffer to the client,
|
* terminating the connection if the write is unsuccessful for too
|
* long (e.g., if the client is unresponsive or there is a network
|
* problem). If possible, it will attempt to use the selector returned
|
* by the {@code ClientConnection.getWriteSelector} method, but it is
|
* capable of working even if that method returns {@code null}. <BR>
|
*
|
* Note that the original position and limit values will not be
|
* preserved, so if that is important to the caller, then it should
|
* record them before calling this method and restore them after it
|
* returns.
|
*
|
* @param clientConnection
|
* The client connection to which the data is to be written.
|
* @param buffer
|
* The data to be written to the client.
|
* @return <CODE>true</CODE> if all the data in the provided buffer was
|
* written to the client and the connection may remain
|
* established, or <CODE>false</CODE> if a problem occurred
|
* and the client connection is no longer valid. Note that if
|
* this method does return <CODE>false</CODE>, then it must
|
* have already disconnected the client.
|
* @throws IOException
|
* If a problem occurs while attempting to write data to the
|
* client. The caller will be responsible for catching this
|
* and terminating the client connection.
|
*/
|
public static boolean writeWithTimeout(ClientConnection clientConnection,
|
ByteBuffer buffer) throws IOException
|
{
|
SocketChannel socketChannel = clientConnection.getSocketChannel();
|
long startTime = System.currentTimeMillis();
|
long waitTime = clientConnection.getMaxBlockedWriteTimeLimit();
|
if (waitTime <= 0)
|
{
|
// We won't support an infinite time limit, so fall back to using
|
// five minutes, which is a very long timeout given that we're
|
// blocking a worker thread.
|
waitTime = 300000L;
|
}
|
|
long stopTime = startTime + waitTime;
|
|
Selector selector = clientConnection.getWriteSelector();
|
if (selector == null)
|
{
|
// The client connection does not provide a selector, so we'll
|
// fall back
|
// to a more inefficient way that will work without a selector.
|
while (buffer.hasRemaining()
|
&& (System.currentTimeMillis() < stopTime))
|
{
|
if (socketChannel.write(buffer) < 0)
|
{
|
// The client connection has been closed.
|
return false;
|
}
|
}
|
|
if (buffer.hasRemaining())
|
{
|
// If we've gotten here, then the write timed out.
|
return false;
|
}
|
|
return true;
|
}
|
|
// Register with the selector for handling write operations.
|
SelectionKey key =
|
socketChannel.register(selector, SelectionKey.OP_WRITE);
|
|
try
|
{
|
selector.select(waitTime);
|
while (buffer.hasRemaining())
|
{
|
long currentTime = System.currentTimeMillis();
|
if (currentTime >= stopTime)
|
{
|
// We've been blocked for too long.
|
return false;
|
}
|
else
|
{
|
waitTime = stopTime - currentTime;
|
}
|
|
Iterator<SelectionKey> iterator =
|
selector.selectedKeys().iterator();
|
while (iterator.hasNext())
|
{
|
SelectionKey k = iterator.next();
|
if (k.isWritable())
|
{
|
int bytesWritten = socketChannel.write(buffer);
|
if (bytesWritten < 0)
|
{
|
// The client connection has been closed.
|
return false;
|
}
|
|
iterator.remove();
|
}
|
}
|
|
if (buffer.hasRemaining())
|
{
|
selector.select(waitTime);
|
}
|
}
|
|
return true;
|
}
|
finally
|
{
|
if (key.isValid())
|
{
|
key.cancel();
|
selector.selectNow();
|
}
|
}
|
}
|
|
|
|
/**
|
* Evaluates and converts 2 consequetive characters of the provided
|
* string starting at startPos and converts them into a single escaped
|
* char.
|
*
|
* @param hexString
|
* The hexadecimal string containing the escape sequence.
|
* @param startPos
|
* The starting position of the hexadecimal escape sequence.
|
* @return The escaped character
|
* @throws DirectoryException
|
* If the provided string contains invalid hexadecimal
|
* digits .
|
*/
|
public static char hexToEscapedChar(String hexString, int startPos)
|
throws DirectoryException
|
{
|
// The two positions must be the hex characters that
|
// comprise the escaped value.
|
if ((startPos + 1) >= hexString.length())
|
{
|
Message message =
|
ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString,
|
startPos + 1);
|
|
throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
|
}
|
byte byteValue = 0;
|
switch (hexString.charAt(startPos))
|
{
|
case 0x30: // '0'
|
break;
|
case 0x31: // '1'
|
byteValue = (byte) 0x10;
|
break;
|
case 0x32: // '2'
|
byteValue = (byte) 0x20;
|
break;
|
case 0x33: // '3'
|
byteValue = (byte) 0x30;
|
break;
|
case 0x34: // '4'
|
byteValue = (byte) 0x40;
|
break;
|
case 0x35: // '5'
|
byteValue = (byte) 0x50;
|
break;
|
case 0x36: // '6'
|
byteValue = (byte) 0x60;
|
break;
|
case 0x37: // '7'
|
byteValue = (byte) 0x70;
|
break;
|
case 0x38: // '8'
|
byteValue = (byte) 0x80;
|
break;
|
case 0x39: // '9'
|
byteValue = (byte) 0x90;
|
break;
|
case 0x41: // 'A'
|
case 0x61: // 'a'
|
byteValue = (byte) 0xA0;
|
break;
|
case 0x42: // 'B'
|
case 0x62: // 'b'
|
byteValue = (byte) 0xB0;
|
break;
|
case 0x43: // 'C'
|
case 0x63: // 'c'
|
byteValue = (byte) 0xC0;
|
break;
|
case 0x44: // 'D'
|
case 0x64: // 'd'
|
byteValue = (byte) 0xD0;
|
break;
|
case 0x45: // 'E'
|
case 0x65: // 'e'
|
byteValue = (byte) 0xE0;
|
break;
|
case 0x46: // 'F'
|
case 0x66: // 'f'
|
byteValue = (byte) 0xF0;
|
break;
|
default:
|
Message message =
|
ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString,
|
startPos);
|
throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
|
}
|
|
switch (hexString.charAt(++startPos))
|
{
|
case 0x30: // '0'
|
break;
|
case 0x31: // '1'
|
byteValue |= (byte) 0x01;
|
break;
|
case 0x32: // '2'
|
byteValue |= (byte) 0x02;
|
break;
|
case 0x33: // '3'
|
byteValue |= (byte) 0x03;
|
break;
|
case 0x34: // '4'
|
byteValue |= (byte) 0x04;
|
break;
|
case 0x35: // '5'
|
byteValue |= (byte) 0x05;
|
break;
|
case 0x36: // '6'
|
byteValue |= (byte) 0x06;
|
break;
|
case 0x37: // '7'
|
byteValue |= (byte) 0x07;
|
break;
|
case 0x38: // '8'
|
byteValue |= (byte) 0x08;
|
break;
|
case 0x39: // '9'
|
byteValue |= (byte) 0x09;
|
break;
|
case 0x41: // 'A'
|
case 0x61: // 'a'
|
byteValue |= (byte) 0x0A;
|
break;
|
case 0x42: // 'B'
|
case 0x62: // 'b'
|
byteValue |= (byte) 0x0B;
|
break;
|
case 0x43: // 'C'
|
case 0x63: // 'c'
|
byteValue |= (byte) 0x0C;
|
break;
|
case 0x44: // 'D'
|
case 0x64: // 'd'
|
byteValue |= (byte) 0x0D;
|
break;
|
case 0x45: // 'E'
|
case 0x65: // 'e'
|
byteValue |= (byte) 0x0E;
|
break;
|
case 0x46: // 'F'
|
case 0x66: // 'f'
|
byteValue |= (byte) 0x0F;
|
break;
|
default:
|
Message message =
|
ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString,
|
startPos);
|
throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
|
}
|
return (char) byteValue;
|
}
|
|
/**
|
* Add all of the superior objectclasses to the specified objectclass
|
* map if they don't already exist. Used by add and import-ldif to
|
* add missing superior objectclasses to entries that don't have them.
|
*
|
* @param objectClasses A Map of objectclasses.
|
*/
|
public static void addSuperiorObjectClasses(Map<ObjectClass,
|
String> objectClasses) {
|
HashSet<ObjectClass> additionalClasses = null;
|
for (ObjectClass oc : objectClasses.keySet())
|
{
|
for(ObjectClass superiorClass : oc.getSuperiorClasses())
|
{
|
if (! objectClasses.containsKey(superiorClass))
|
{
|
if (additionalClasses == null)
|
{
|
additionalClasses = new HashSet<ObjectClass>();
|
}
|
|
additionalClasses.add(superiorClass);
|
}
|
}
|
}
|
|
if (additionalClasses != null)
|
{
|
for (ObjectClass oc : additionalClasses)
|
{
|
addObjectClassChain(oc, objectClasses);
|
}
|
}
|
}
|
|
private static void addObjectClassChain(ObjectClass objectClass,
|
Map<ObjectClass, String> objectClasses)
|
{
|
if (objectClasses != null){
|
if (! objectClasses.containsKey(objectClass))
|
{
|
objectClasses.put(objectClass, objectClass.getNameOrOID());
|
}
|
|
for(ObjectClass superiorClass : objectClass.getSuperiorClasses())
|
{
|
if (! objectClasses.containsKey(superiorClass))
|
{
|
addObjectClassChain(superiorClass, objectClasses);
|
}
|
}
|
}
|
}
|
|
|
|
/**
|
* Returns {@code true} if the provided IPv4 or IPv6 address or host name
|
* represents the address of one of the interfaces on the current host
|
* machine.
|
*
|
* @param addressString
|
* The IPv4 or IPv6 address or host name.
|
* @return {@code true} if the provided IPv4 or IPv6 address or host name
|
* represents the address of one of the interfaces on the current host
|
* machine.
|
*/
|
public static boolean isLocalAddress(String addressString)
|
{
|
try
|
{
|
return isLocalAddress(InetAddress.getByName(addressString));
|
}
|
catch (UnknownHostException e)
|
{
|
return false;
|
}
|
}
|
|
|
|
/**
|
* Returns {@code true} if the provided {@code InetAddress} represents the
|
* address of one of the interfaces on the current host machine.
|
*
|
* @param address
|
* The network address.
|
* @return {@code true} if the provided {@code InetAddress} represents the
|
* address of one of the interfaces on the current host machine.
|
*/
|
public static boolean isLocalAddress(InetAddress address)
|
{
|
if (address.isLoopbackAddress())
|
{
|
return true;
|
}
|
else
|
{
|
Enumeration<NetworkInterface> i;
|
try
|
{
|
i = NetworkInterface.getNetworkInterfaces();
|
}
|
catch (SocketException e)
|
{
|
// Unable to determine whether the address is local.
|
TRACER.debugCaught(DebugLogLevel.WARNING, e);
|
return false;
|
}
|
|
while (i.hasMoreElements())
|
{
|
NetworkInterface n = i.nextElement();
|
Enumeration<InetAddress> j = n.getInetAddresses();
|
while (j.hasMoreElements())
|
{
|
InetAddress localAddress = j.nextElement();
|
if (localAddress.equals(address))
|
{
|
return true;
|
}
|
}
|
}
|
return false;
|
}
|
}
|
|
/**
|
* Closes the provided {@link Closeable}'s ignoring any errors which
|
* occurred.
|
*
|
* @param closeables The closeables to be closed, which may be
|
* <code>null</code>.
|
*/
|
public static void close(Closeable... closeables)
|
{
|
if (closeables == null)
|
{
|
return;
|
}
|
close(Arrays.asList(closeables));
|
}
|
|
/**
|
* Closes the provided {@link Closeable}'s ignoring any errors which occurred.
|
*
|
* @param closeables
|
* The closeables to be closed, which may be <code>null</code>.
|
*/
|
public static void close(Collection<Closeable> closeables)
|
{
|
if (closeables == null)
|
{
|
return;
|
}
|
for (Closeable closeable : closeables)
|
{
|
if (closeable != null)
|
{
|
try
|
{
|
closeable.close();
|
}
|
catch (IOException ignored)
|
{
|
// Ignore.
|
}
|
}
|
}
|
}
|
|
/**
|
* Closes the provided {@link Selector}'s ignoring any errors which occurred.
|
* <p>
|
* With java 7 we will be able to use {@link StaticUtils#close(Closeable...)}
|
* </p>
|
*
|
* @param selectors
|
* The selectors to be closed, which may be <code>null</code>.
|
*/
|
public static void close(Selector... selectors)
|
{
|
for (Selector selector : selectors)
|
{
|
if (selector != null)
|
{
|
try
|
{
|
selector.close();
|
}
|
catch (IOException ignored)
|
{
|
// Ignore.
|
}
|
}
|
}
|
}
|
|
/**
|
* Closes the provided {@link Socket}s ignoring any errors which occurred.
|
* <p>
|
* With java 7 we will be able to use {@link StaticUtils#close(Closeable...)}
|
* </p>
|
*
|
* @param sockets
|
* The sockets to be closed, which may be <code>null</code>.
|
*/
|
public static void close(Socket... sockets)
|
{
|
for (Socket socket : sockets)
|
{
|
if (socket != null)
|
{
|
try
|
{
|
socket.close();
|
}
|
catch (IOException ignored)
|
{
|
// Ignore.
|
}
|
}
|
}
|
}
|
|
/**
|
* Calls {@link Thread#sleep(long)}, surrounding it with the mandatory
|
* <code>try</code> / <code>catch(InterruptedException)</code> block.
|
*
|
* @param millis
|
* the length of time to sleep in milliseconds
|
*/
|
public static void sleep(long millis)
|
{
|
try
|
{
|
Thread.sleep(millis);
|
}
|
catch (InterruptedException wokenUp)
|
{
|
}
|
}
|
|
/**
|
* Returns an {@link Iterable} returning the passed in {@link Iterator}. THis
|
* allows using methods returning Iterators with foreach statements.
|
* <p>
|
* For example, consider a method with this signature:
|
* <p>
|
* <code>public Iterator<String> myIteratorMethod();</code>
|
* <p>
|
* Classical use with for or while loop:
|
*
|
* <pre>
|
* for (Iterator<String> it = myIteratorMethod(); it.hasNext();)
|
* {
|
* String s = it.next();
|
* // use it
|
* }
|
*
|
* Iterator<String> it = myIteratorMethod();
|
* while(it.hasNext();)
|
* {
|
* String s = it.next();
|
* // use it
|
* }
|
* </pre>
|
*
|
* Improved use with foreach:
|
*
|
* <pre>
|
* for (String s : StaticUtils.toIterable(myIteratorMethod()))
|
* {
|
* }
|
* </pre>
|
*
|
* </p>
|
*
|
* @param <T>
|
* the generic type of the passed in Iterator and for the returned
|
* Iterable.
|
* @param iterator
|
* the Iterator that will be returned by the Iterable.
|
* @return an Iterable returning the passed in Iterator
|
*/
|
public static <T> Iterable<T> toIterable(final Iterator<T> iterator)
|
{
|
return new Iterable<T>()
|
{
|
|
@Override
|
public Iterator<T> iterator()
|
{
|
return iterator;
|
}
|
};
|
}
|
}
|