// for everything else we might need to know.
String primaryName = oid;
List<String> typeNames = new LinkedList<String>();
String description = null;
AttributeType superiorType = null;
AttributeSyntax syntax = DirectoryServer.getDefaultAttributeSyntax();
ApproximateMatchingRule approximateMatchingRule = null;
EqualityMatchingRule equalityMatchingRule = null;
OrderingMatchingRule orderingMatchingRule = null;
SubstringMatchingRule substringMatchingRule = null;
AttributeUsage attributeUsage = AttributeUsage.USER_APPLICATIONS;
boolean isCollective = false;
boolean isNoUserModification = false;
boolean isObsolete = false;
boolean isSingleValue = false;
HashMap<String,List<String>> extraProperties =
new LinkedHashMap<String,List<String>>();
while (true)
{
StringBuilder tokenNameBuffer = new StringBuilder();
pos = readTokenName(valueStr, tokenNameBuffer, pos);
String tokenName = tokenNameBuffer.toString();
String lowerTokenName = toLowerCase(tokenName);
if (tokenName.equals(")"))
{
// We must be at the end of the value. If not, then that's a problem.
if (pos < length)
{
Message message =
ERR_ATTR_SYNTAX_ATTRTYPE_UNEXPECTED_CLOSE_PARENTHESIS.
get(valueStr, (pos-1));
throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
message);
}
break;
}
else if (lowerTokenName.equals("name"))
{
// This specifies the set of names for the attribute type. It may be a
// single name in single quotes, or it may be an open parenthesis
// followed by one or more names in single quotes separated by spaces.
c = valueStr.charAt(pos++);
if (c == '\'')
{
StringBuilder userBuffer = new StringBuilder();
StringBuilder lowerBuffer = new StringBuilder();
pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer,
(pos-1));
primaryName = userBuffer.toString();
typeNames.add(primaryName);
}
else if (c == '(')
{
StringBuilder userBuffer = new StringBuilder();
StringBuilder lowerBuffer = new StringBuilder();
pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer,
pos);
primaryName = userBuffer.toString();
typeNames.add(primaryName);
while (true)
{
if (valueStr.charAt(pos) == ')')
{
// Skip over any spaces after the parenthesis.
pos++;
while ((pos < length) && ((c = valueStr.charAt(pos)) == ' '))
{
pos++;
}
break;
}
else
{
userBuffer = new StringBuilder();
lowerBuffer = new StringBuilder();
pos = readQuotedString(valueStr, lowerStr, userBuffer,
lowerBuffer, pos);
typeNames.add(userBuffer.toString());
}
}
}
else
{
// This is an illegal character.
Message message =
ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR.get(
valueStr, String.valueOf(c), (pos-1));
throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
message);
}
//RFC 2251: A specification may also assign one or more textual names
//for an attribute type. These names MUST begin with a letter, and
//only contain ASCII letters, digit characters and hyphens.
//The global config hasn't been read so far. Allow the name exceptions
//during startup.
boolean allowExceptions = DirectoryServer.isRunning()?
DirectoryServer.allowAttributeNameExceptions():true;
//Iterate over all the names and throw an exception if it is invalid.
for(String name : typeNames)
{
for(int index=0; index < name.length(); index++)
{
char ch = name.charAt(index);
switch(ch)
{
case '-':
//hyphen is allowed but not as the first byte.
if (index==0)
{
Message msg = ERR_ATTR_SYNTAX_ATTR_ILLEGAL_INITIAL_DASH.
get(value.toString());
throw new DirectoryException(
ResultCode.INVALID_ATTRIBUTE_SYNTAX,
msg);
}
break;
case '_':
// This will never be allowed as the first character. It
// may be allowed for subsequent characters if the attribute
// name exceptions option is enabled.
if (index==0)
{
Message msg =
ERR_ATTR_SYNTAX_ATTR_ILLEGAL_INITIAL_UNDERSCORE.
get(value.toString(),
ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
throw new DirectoryException(
ResultCode.INVALID_ATTRIBUTE_SYNTAX,
msg);
}
else if (!allowExceptions)
{
Message msg = ERR_ATTR_SYNTAX_ATTR_ILLEGAL_UNDERSCORE_CHAR.
get(value.toString(),
ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
throw new DirectoryException(
ResultCode.INVALID_ATTRIBUTE_SYNTAX,
msg);
}
break;
default:
//Only digits and ascii letters are allowed but the first byte
//can not be a digit.
if(index ==0 && isDigit(ch) && !allowExceptions)
{
Message message = ERR_ATTR_SYNTAX_ATTR_ILLEGAL_INITIAL_DIGIT.
get(value.toString(), ch,
ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
throw new DirectoryException(
ResultCode.INVALID_ATTRIBUTE_SYNTAX,
message);
}
else if(!((ch>='0' && ch<='9') || (ch>='A' && ch<='Z') ||
(ch>='a' && ch<='z')))
{
Message msg = ERR_ATTR_SYNTAX_ATTR_ILLEGAL_CHAR.get(
value.toString(), ch, index);
throw new DirectoryException(
ResultCode.INVALID_ATTRIBUTE_SYNTAX,
msg);
}
break;
}
}
}
}
else if (lowerTokenName.equals("desc"))
{
// This specifies the description for the attribute type. It is an
// arbitrary string of characters enclosed in single quotes.
StringBuilder descriptionBuffer = new StringBuilder();
pos = readQuotedString(valueStr, descriptionBuffer, pos);
description = descriptionBuffer.toString();
}
else if (lowerTokenName.equals("obsolete"))
{
// This indicates whether the attribute type should be considered
// obsolete. We do not need to do any more parsing for this token.
isObsolete = true;
}
else if (lowerTokenName.equals("sup"))
{
// This specifies the name or OID of the superior attribute type from
// which this attribute type should inherit its properties.
StringBuilder woidBuffer = new StringBuilder();
pos = readWOID(lowerStr, woidBuffer, pos);
superiorType = schema.getAttributeType(woidBuffer.toString());
if (superiorType == null)
{
if (allowUnknownElements)
{
superiorType = DirectoryServer.getDefaultAttributeType(
woidBuffer.toString());
}
else
{
// This is bad because we don't know what the superior attribute
// type is so we can't base this attribute type on it.
Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SUPERIOR_TYPE.
get(String.valueOf(oid), String.valueOf(woidBuffer));
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
message);
}
}
// Use the information in the superior type to provide defaults for the
// rest of the components in this attribute type description.
// Technically, the definition of the superior type should be provided
// before the matching rule, syntax, single-value, collective,
// no-user-modification, and usage components, and in that case we won't
// undo something else that has already been set by an earlier
// definition. However, if the information is provided out-of-order,
// then it is possible that this could overwrite some desired setting
// that is different from that of the supertype.
approximateMatchingRule = superiorType.getApproximateMatchingRule();
equalityMatchingRule = superiorType.getEqualityMatchingRule();
orderingMatchingRule = superiorType.getOrderingMatchingRule();
substringMatchingRule = superiorType.getSubstringMatchingRule();
syntax = superiorType.getSyntax();
isSingleValue = superiorType.isSingleValue();
isCollective = superiorType.isCollective();
isNoUserModification = superiorType.isNoUserModification();
attributeUsage = superiorType.getUsage();
}
else if (lowerTokenName.equals("equality"))
{
// This specifies the name or OID of the equality matching rule to use
// for this attribute type.
StringBuilder woidBuffer = new StringBuilder();
pos = readWOID(lowerStr, woidBuffer, pos);
EqualityMatchingRule emr =
schema.getEqualityMatchingRule(woidBuffer.toString());
if (emr == null)
{
// This is bad because we have no idea what the equality matching
// rule should be.
Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_EQUALITY_MR.get(
String.valueOf(oid), String.valueOf(woidBuffer));
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
message);
}
else
{
equalityMatchingRule = emr;
}
}
else if (lowerTokenName.equals("ordering"))
{
// This specifies the name or OID of the ordering matching rule to use
// for this attribute type.
StringBuilder woidBuffer = new StringBuilder();
pos = readWOID(lowerStr, woidBuffer, pos);
OrderingMatchingRule omr =
schema.getOrderingMatchingRule(woidBuffer.toString());
if (omr == null)
{
// This is bad because we have no idea what the ordering matching
// rule should be.
Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_ORDERING_MR.get(
String.valueOf(oid), String.valueOf(woidBuffer));
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
message);
}
else
{
orderingMatchingRule = omr;
}
}
else if (lowerTokenName.equals("substr"))
{
// This specifies the name or OID of the substring matching rule to use
// for this attribute type.
StringBuilder woidBuffer = new StringBuilder();
pos = readWOID(lowerStr, woidBuffer, pos);
SubstringMatchingRule smr =
schema.getSubstringMatchingRule(woidBuffer.toString());
if (smr == null)
{
// This is bad because we have no idea what the substring matching
// rule should be.
Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SUBSTRING_MR.get(
String.valueOf(oid), String.valueOf(woidBuffer));
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
message);
}
else
{
substringMatchingRule = smr;
}
}
else if (lowerTokenName.equals("syntax"))
{
// This specifies the numeric OID of the syntax for this matching rule.
// It may optionally be immediately followed by an open curly brace, an
// integer value, and a close curly brace to suggest the minimum number
// of characters that should be allowed in values of that type. This
// implementation will ignore any such length because it does not
// impose any practical limit on the length of attribute values.
boolean inBrace = false;
boolean lastWasPeriod = false;
StringBuilder oidBuffer = new StringBuilder();
while (pos < length)
{
c = lowerStr.charAt(pos++);
if (inBrace)
{
// The only thing we'll allow here will be numeric digits and the
// closing curly brace.
if (c == '}')
{
// The next character must be a space or a closing parenthesis.
if ((c = lowerStr.charAt(pos)) != ' '
&& (c = lowerStr.charAt(pos)) != ')')
{
Message message =
ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
get(valueStr, String.valueOf(c), (pos-1));
throw new DirectoryException(
ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
}
break;
}
else if (! isDigit(c))
{
Message message =
ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
get(valueStr, String.valueOf(c), (pos-1));
throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
message);
}
}
else
{
if (isDigit(c))
{
oidBuffer.append(c);
lastWasPeriod = false;
}
else if (c == '.')
{
if (lastWasPeriod)
{
Message message =
ERR_ATTR_SYNTAX_ATTRTYPE_DOUBLE_PERIOD_IN_NUMERIC_OID.
get(valueStr, (pos-1));
throw new DirectoryException(
ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
}
else
{
oidBuffer.append(c);
lastWasPeriod = true;
}
}
else if (c == '{')
{
// It's the start of the length specification.
inBrace = true;
}
else if (c == ' ')
{
// It's the end of the value.
break;
}
else if(c == ')')
{
// As per RFC 4512 (4.1.2) it is end of the value.
--pos;
break;
}
else
{
Message message =
ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
get(valueStr, String.valueOf(c), (pos-1));
throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
message);
}
}
}
syntax = schema.getSyntax(oidBuffer.toString());
if (syntax == null)
{
Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SYNTAX.get(
String.valueOf(oid), String.valueOf(oidBuffer));
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
message);
}
if (approximateMatchingRule == null)
{
approximateMatchingRule = syntax.getApproximateMatchingRule();
}
if (equalityMatchingRule == null)
{
equalityMatchingRule = syntax.getEqualityMatchingRule();
}
if (orderingMatchingRule == null)
{
orderingMatchingRule = syntax.getOrderingMatchingRule();
}
if (substringMatchingRule == null)
{
substringMatchingRule = syntax.getSubstringMatchingRule();
}
}
else if (lowerTokenName.equals("single-value"))
{
// This indicates that attributes of this type are allowed to have at