* This method is idempotent: if this attribute description does not contain
* the provided option then this attribute description will be returned.
*
* @param option
* The attribute option.
* @return The new attribute description excluding {@code option}.
* @throws NullPointerException
* If {@code attributeDescription} or {@code option} was
* {@code null}.
*/
public AttributeDescription withoutOption(final String option) {
Validator.ensureNotNull(option);
final String normalizedOption = toLowerCase(option);
if (!pimpl.hasOption(normalizedOption)) {
return this;
}
final String oldAttributeDescription = attributeDescription;
final StringBuilder builder =
new StringBuilder(oldAttributeDescription.length() - option.length() - 1);
final String normalizedOldAttributeDescription = toLowerCase(oldAttributeDescription);
final int index = normalizedOldAttributeDescription.indexOf(normalizedOption);
builder.append(oldAttributeDescription, 0, index - 1 /* to semi-colon */);
builder.append(oldAttributeDescription, index + option.length(), oldAttributeDescription
.length());
final String newAttributeDescription = builder.toString();
final Impl impl = pimpl;
if (impl instanceof ZeroOptionImpl) {
throw new IllegalStateException("ZeroOptionImpl unexpected");
} else if (impl instanceof SingleOptionImpl) {
return new AttributeDescription(newAttributeDescription, attributeType,
ZERO_OPTION_IMPL);
} else {
final MultiOptionImpl mimpl = (MultiOptionImpl) impl;
if (mimpl.options.length == 2) {
final String remainingOption;
final String remainingNormalizedOption;
if (toLowerCase(mimpl.options[0]).equals(normalizedOption)) {
remainingOption = mimpl.options[1];
} else {
remainingOption = mimpl.options[0];
}
if (mimpl.normalizedOptions[0].equals(normalizedOption)) {
remainingNormalizedOption = mimpl.normalizedOptions[1];
} else {
remainingNormalizedOption = mimpl.normalizedOptions[0];
}
return new AttributeDescription(newAttributeDescription, attributeType,
new SingleOptionImpl(remainingOption, remainingNormalizedOption));
} else {
final String[] newOptions = new String[mimpl.options.length - 1];
final String[] newNormalizedOptions =
new String[mimpl.normalizedOptions.length - 1];
for (int i = 0, j = 0; i < mimpl.options.length; i++) {
if (!toLowerCase(mimpl.options[i]).equals(normalizedOption)) {
newOptions[j++] = mimpl.options[i];
}
}
for (int i = 0, j = 0; i < mimpl.normalizedOptions.length; i++) {
if (!mimpl.normalizedOptions[i].equals(normalizedOption)) {
newNormalizedOptions[j++] = mimpl.normalizedOptions[i];
}
}
return new AttributeDescription(newAttributeDescription, attributeType,
new MultiOptionImpl(newOptions, newNormalizedOptions));
}
}
}
/**
* Creates an attribute description having the provided attribute type and
* no options.
*
* @param attributeType
* The attribute type.
* @return The attribute description.
* @throws NullPointerException
* If {@code attributeType} was {@code null}.
*/
public static AttributeDescription create(final AttributeType attributeType) {
Validator.ensureNotNull(attributeType);
// Use object identity in case attribute type does not come from
// core schema.
if (attributeType == OBJECT_CLASS.getAttributeType()) {
return OBJECT_CLASS;
} else {
return new AttributeDescription(attributeType.getNameOrOID(), attributeType,
ZERO_OPTION_IMPL);
}
}
/**
* Creates an attribute description having the provided attribute type and
* single option.
*
* @param attributeType
* The attribute type.
* @param option
* The attribute option.
* @return The attribute description.
* @throws NullPointerException
* If {@code attributeType} or {@code option} was {@code null}.
*/
public static AttributeDescription create(final AttributeType attributeType, final String option) {
Validator.ensureNotNull(attributeType, option);
final String oid = attributeType.getNameOrOID();
final StringBuilder builder = new StringBuilder(oid.length() + option.length() + 1);
builder.append(oid);
builder.append(';');
builder.append(option);
final String attributeDescription = builder.toString();
final String normalizedOption = toLowerCase(option);
return new AttributeDescription(attributeDescription, attributeType, new SingleOptionImpl(
option, normalizedOption));
}
/**
* Creates an attribute description having the provided attribute type and
* options.
*
* @param attributeType
* The attribute type.
* @param options
* The attribute options.
* @return The attribute description.
* @throws NullPointerException
* If {@code attributeType} or {@code options} was {@code null}.
*/
public static AttributeDescription create(final AttributeType attributeType,
final String... options) {
Validator.ensureNotNull(attributeType, options);
switch (options.length) {
case 0:
return create(attributeType);
case 1:
return create(attributeType, options[0]);
default:
final String[] optionsList = new String[options.length];
final String[] normalizedOptions = new String[options.length];
final String oid = attributeType.getNameOrOID();
final StringBuilder builder =
new StringBuilder(oid.length() + options[0].length() + options[1].length() + 2);
builder.append(oid);
int i = 0;
for (final String option : options) {
builder.append(';');
builder.append(option);
optionsList[i] = option;
final String normalizedOption = toLowerCase(option);
normalizedOptions[i++] = normalizedOption;
}
Arrays.sort(normalizedOptions);
final String attributeDescription = builder.toString();
return new AttributeDescription(attributeDescription, attributeType,
new MultiOptionImpl(optionsList, normalizedOptions));
}
}
/**
* Returns an attribute description representing the object class attribute
* type with no options.
*
* @return The object class attribute description.
*/
public static AttributeDescription objectClass() {
return OBJECT_CLASS;
}
/**
* Parses the provided LDAP string representation of an attribute
* description using the default schema.
*
* @param attributeDescription
* The LDAP string representation of an attribute description.
* @return The parsed attribute description.
* @throws UnknownSchemaElementException
* If {@code attributeDescription} contains an attribute type
* which is not contained in the default schema and the schema
* is strict.
* @throws LocalizedIllegalArgumentException
* If {@code attributeDescription} is not a valid LDAP string
* representation of an attribute description.
* @throws NullPointerException
* If {@code attributeDescription} was {@code null}.
*/
public static AttributeDescription valueOf(final String attributeDescription) {
return valueOf(attributeDescription, Schema.getDefaultSchema());
}
/**
* Parses the provided LDAP string representation of an attribute
* description using the provided schema.
*
* @param attributeDescription
* The LDAP string representation of an attribute description.
* @param schema
* The schema to use when parsing the attribute description.
* @return The parsed attribute description.
* @throws UnknownSchemaElementException
* If {@code attributeDescription} contains an attribute type
* which is not contained in the provided schema and the schema
* is strict.
* @throws LocalizedIllegalArgumentException
* If {@code attributeDescription} is not a valid LDAP string
* representation of an attribute description.
* @throws NullPointerException
* If {@code attributeDescription} or {@code schema} was
* {@code null}.
*/
@SuppressWarnings("serial")
public static AttributeDescription valueOf(final String attributeDescription,
final Schema schema) {
Validator.ensureNotNull(attributeDescription, schema);
// First look up the attribute description in the cache.
final WeakHashMap
* Place holder attribute descriptions have an attribute type whose OID is
* the normalized attribute name with the string {@code -oid} appended. In
* addition, they will use the directory string syntax and case ignore
* matching rule.
*
* @return {@code true} if this is a temporary place-holder attribute
* description allocated dynamically by a non-strict schema when no
* corresponding registered attribute type was found.
* @see Schema#getAttributeType(String)
* @see AttributeType#isPlaceHolder()
*/
public boolean isPlaceHolder() {
return attributeType.isPlaceHolder();
}
/**
* Indicates whether or not this attribute description is a sub-type of the
* provided attribute description as defined in RFC 4512 section 2.5.
* Specifically, this method will return {@code true} if and only if the
* following conditions are both {@code true}:
*
*
* Note that this method will return {@code true} if this attribute
* description is equal to the provided attribute description.
*
* @param other
* The attribute description for which to make the determination.
* @return {@code true} if this attribute description is a sub-type of the
* provided attribute description, or {@code false} if not.
* @throws NullPointerException
* If {@code name} was {@code null}.
*/
public boolean isSubTypeOf(final AttributeDescription other) {
if (!attributeType.isSubTypeOf(other.attributeType)) {
return false;
} else {
return pimpl.isSubTypeOf(other.pimpl);
}
}
/**
* Indicates whether or not this attribute description is a super-type of
* the provided attribute description as defined in RFC 4512 section 2.5.
* Specifically, this method will return {@code true} if and only if the
* following conditions are both {@code true}:
*
*
* Note that this method will return {@code true} if this attribute
* description is equal to the provided attribute description.
*
* @param other
* The attribute description for which to make the determination.
* @return {@code true} if this attribute description is a super-type of the
* provided attribute description, or {@code false} if not.
* @throws NullPointerException
* If {@code name} was {@code null}.
*/
public boolean isSuperTypeOf(final AttributeDescription other) {
if (!attributeType.isSuperTypeOf(other.attributeType)) {
return false;
} else {
return pimpl.isSuperTypeOf(other.pimpl);
}
}
/**
* Indicates whether the provided attribute description matches this
* attribute description. It will be considered a match if the attribute
* types {@link AttributeType#matches match} and the normalized sorted list
* of options are identical.
*
* @param other
* The attribute description for which to make the determination.
* @return {@code true} if the provided attribute description matches this
* attribute description, or {@code false} if not.
*/
public boolean matches(final AttributeDescription other) {
if (this == other) {
return true;
} else {
return attributeType.matches(other.attributeType) && pimpl.equals(other.pimpl);
}
}
/**
* Returns the string representation of this attribute description as
* defined in RFC4512 section 2.5.
*
* @return The string representation of this attribute description.
*/
@Override
public String toString() {
return attributeDescription;
}
}