package com.hwlcn.ldap.ldap.sdk.schema;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Map;
import com.hwlcn.ldap.ldap.sdk.LDAPException;
import com.hwlcn.ldap.ldap.sdk.ResultCode;
import com.hwlcn.core.annotation.NotExtensible;
import com.hwlcn.core.annotation.ThreadSafety;
import com.hwlcn.ldap.util.ThreadSafetyLevel;
import static com.hwlcn.ldap.ldap.sdk.schema.SchemaMessages.*;
import static com.hwlcn.ldap.util.Debug.*;
import static com.hwlcn.ldap.util.StaticUtils.*;
@NotExtensible()
@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
public abstract class SchemaElement
implements Serializable
{
private static final long serialVersionUID = -8249972237068748580L;
static int skipSpaces(final String s, final int startPos, final int length)
throws LDAPException
{
int pos = startPos;
while ((pos < length) && (s.charAt(pos) == ' '))
{
pos++;
}
if (pos >= length)
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_SKIP_SPACES_NO_CLOSE_PAREN.get(
s));
}
return pos;
}
private static int readEscapedHexString(final String s, final int startPos,
final int length,
final StringBuilder buffer)
throws LDAPException
{
int pos = startPos;
final ByteBuffer byteBuffer = ByteBuffer.allocate(length - pos);
while (pos < length)
{
byte b;
switch (s.charAt(pos++))
{
case '0':
b = 0x00;
break;
case '1':
b = 0x10;
break;
case '2':
b = 0x20;
break;
case '3':
b = 0x30;
break;
case '4':
b = 0x40;
break;
case '5':
b = 0x50;
break;
case '6':
b = 0x60;
break;
case '7':
b = 0x70;
break;
case '8':
b = (byte) 0x80;
break;
case '9':
b = (byte) 0x90;
break;
case 'a':
case 'A':
b = (byte) 0xA0;
break;
case 'b':
case 'B':
b = (byte) 0xB0;
break;
case 'c':
case 'C':
b = (byte) 0xC0;
break;
case 'd':
case 'D':
b = (byte) 0xD0;
break;
case 'e':
case 'E':
b = (byte) 0xE0;
break;
case 'f':
case 'F':
b = (byte) 0xF0;
break;
default:
throw new LDAPException(ResultCode.INVALID_DN_SYNTAX,
ERR_SCHEMA_ELEM_INVALID_HEX_CHAR.get(s,
s.charAt(pos-1), (pos-1)));
}
if (pos >= length)
{
throw new LDAPException(ResultCode.INVALID_DN_SYNTAX,
ERR_SCHEMA_ELEM_MISSING_HEX_CHAR.get(s));
}
switch (s.charAt(pos++))
{
case '0':
// No action is required.
break;
case '1':
b |= 0x01;
break;
case '2':
b |= 0x02;
break;
case '3':
b |= 0x03;
break;
case '4':
b |= 0x04;
break;
case '5':
b |= 0x05;
break;
case '6':
b |= 0x06;
break;
case '7':
b |= 0x07;
break;
case '8':
b |= 0x08;
break;
case '9':
b |= 0x09;
break;
case 'a':
case 'A':
b |= 0x0A;
break;
case 'b':
case 'B':
b |= 0x0B;
break;
case 'c':
case 'C':
b |= 0x0C;
break;
case 'd':
case 'D':
b |= 0x0D;
break;
case 'e':
case 'E':
b |= 0x0E;
break;
case 'f':
case 'F':
b |= 0x0F;
break;
default:
throw new LDAPException(ResultCode.INVALID_DN_SYNTAX,
ERR_SCHEMA_ELEM_INVALID_HEX_CHAR.get(s,
s.charAt(pos-1), (pos-1)));
}
byteBuffer.put(b);
if (((pos+1) < length) && (s.charAt(pos) == '\\') &&
isHex(s.charAt(pos+1)))
{
pos++;
continue;
}
else
{
break;
}
}
byteBuffer.flip();
final byte[] byteArray = new byte[byteBuffer.limit()];
byteBuffer.get(byteArray);
try
{
buffer.append(toUTF8String(byteArray));
}
catch (final Exception e)
{
debugException(e);
buffer.append(new String(byteArray));
}
return pos;
}
static int readQDString(final String s, final int startPos, final int length,
final StringBuilder buffer)
throws LDAPException
{
if (s.charAt(startPos) != '\'')
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_EXPECTED_SINGLE_QUOTE.get(s,
startPos));
}
int pos = startPos + 1;
while (pos < length)
{
final char c = s.charAt(pos++);
if (c == '\'')
{
break;
}
else if (c == '\\')
{
if (pos >= length)
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_ENDS_WITH_BACKSLASH.get(s));
}
pos = readEscapedHexString(s, pos, length, buffer);
}
else
{
buffer.append(c);
}
}
if ((pos >= length) || ((s.charAt(pos) != ' ') && (s.charAt(pos) != ')')))
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_NO_CLOSING_PAREN.get(s));
}
if (buffer.length() == 0)
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_EMPTY_QUOTES.get(s));
}
return pos;
}
static int readQDStrings(final String s, final int startPos, final int length,
final ArrayList<String> valueList)
throws LDAPException
{
char c = s.charAt(startPos);
if (c == '\'')
{
final StringBuilder buffer = new StringBuilder();
final int returnPos = readQDString(s, startPos, length, buffer);
valueList.add(buffer.toString());
return returnPos;
}
else if (c == '(')
{
int pos = startPos + 1;
while (true)
{
pos = skipSpaces(s, pos, length);
c = s.charAt(pos);
if (c == ')')
{
pos++;
break;
}
else if (c == '\'')
{
final StringBuilder buffer = new StringBuilder();
pos = readQDString(s, pos, length, buffer);
valueList.add(buffer.toString());
}
else
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_EXPECTED_QUOTE_OR_PAREN.get(
s, startPos));
}
}
if (valueList.isEmpty())
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_EMPTY_STRING_LIST.get(s));
}
if ((pos >= length) ||
((s.charAt(pos) != ' ') && (s.charAt(pos) != ')')))
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_NO_SPACE_AFTER_QUOTE.get(s));
}
return pos;
}
else
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_EXPECTED_QUOTE_OR_PAREN.get(s,
startPos));
}
}
static int readOID(final String s, final int startPos, final int length,
final StringBuilder buffer)
throws LDAPException
{
int pos = startPos;
boolean lastWasQuote = false;
while (pos < length)
{
final char c = s.charAt(pos);
if ((c == ' ') || (c == '$') || (c == ')'))
{
if (buffer.length() == 0)
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_EMPTY_OID.get(s));
}
return pos;
}
else if (((c >= 'a') && (c <= 'z')) ||
((c >= 'A') && (c <= 'Z')) ||
((c >= '0') && (c <= '9')) ||
(c == '-') || (c == '.') || (c == '_') ||
(c == '{') || (c == '}'))
{
if (lastWasQuote)
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_UNEXPECTED_CHAR_IN_OID.get(s, (pos-1)));
}
buffer.append(c);
}
else if (c == '\'')
{
if (buffer.length() != 0)
{
lastWasQuote = true;
}
}
else
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_UNEXPECTED_CHAR_IN_OID.get(s,
pos));
}
pos++;
}
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_NO_SPACE_AFTER_OID.get(s));
}
static int readOIDs(final String s, final int startPos, final int length,
final ArrayList<String> valueList)
throws LDAPException
{
char c = s.charAt(startPos);
if (c == '(')
{
int pos = startPos + 1;
while (true)
{
pos = skipSpaces(s, pos, length);
c = s.charAt(pos);
if (c == ')')
{
pos++;
break;
}
else if (c == '$')
{
pos++;
pos = skipSpaces(s, pos, length);
final StringBuilder buffer = new StringBuilder();
pos = readOID(s, pos, length, buffer);
valueList.add(buffer.toString());
}
else if (valueList.isEmpty())
{
final StringBuilder buffer = new StringBuilder();
pos = readOID(s, pos, length, buffer);
valueList.add(buffer.toString());
}
else
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_UNEXPECTED_CHAR_IN_OID_LIST.get(s,
pos));
}
}
if (valueList.isEmpty())
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_EMPTY_OID_LIST.get(s));
}
if (pos >= length)
{
throw new LDAPException(ResultCode.DECODING_ERROR,
ERR_SCHEMA_ELEM_NO_SPACE_AFTER_OID_LIST.get(s));
}
return pos;
}
else
{
final StringBuilder buffer = new StringBuilder();
final int returnPos = readOID(s, startPos, length, buffer);
valueList.add(buffer.toString());
return returnPos;
}
}
static void encodeValue(final String value, final StringBuilder buffer)
{
final int length = value.length();
for (int i=0; i < length; i++)
{
final char c = value.charAt(i);
if ((c < ' ') || (c > '~') || (c == '\\') || (c == '\''))
{
hexEncode(c, buffer);
}
else
{
buffer.append(c);
}
}
}
public abstract int hashCode();
public abstract boolean equals(final Object o);
protected static boolean extensionsEqual(final Map<String,String[]> m1,
final Map<String,String[]> m2)
{
if (m1.isEmpty())
{
return m2.isEmpty();
}
if (m1.size() != m2.size())
{
return false;
}
for (final Map.Entry<String,String[]> e : m1.entrySet())
{
final String[] v1 = e.getValue();
final String[] v2 = m2.get(e.getKey());
if (! arraysEqualOrderIndependent(v1, v2))
{
return false;
}
}
return true;
}
@Override()
public abstract String toString();
}