AttributeBuilder.java revision e8cead474d5ce2b933d931f0c4743a78e68d9cfc
/*
* 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 legal-notices/CDDLv1_0.txt
* 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 legal-notices/CDDLv1_0.txt.
* 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 2012-2015 ForgeRock AS
*/
/**
* This class provides an interface for creating new non-virtual
* {@link Attribute}s, or "real" attributes.
* <p>
* An attribute can be created incrementally using either
* {@link #AttributeBuilder(AttributeType)} or
* {@link #AttributeBuilder(AttributeType, String)}. The caller is
* then free to add new options using {@link #setOption(String)} and
* new values using {@link #add(ByteString)} or
* {@link #addAll(Collection)}. Once all the options and values have
* been added, the attribute can be retrieved using the
* {@link #toAttribute()} method.
* <p>
* A real attribute can also be created based on the values taken from
* another attribute using the {@link #AttributeBuilder(Attribute)}
* constructor. The caller is then free to modify the values within
* the attribute before retrieving the updated attribute using the
* {@link #toAttribute()} method.
* <p>
* The {@link org.opends.server.types.Attributes} class contains
* convenience factory methods,
* e.g. {@link org.opends.server.types.Attributes#empty(String)} for
* creating empty attributes, and
* {@link org.opends.server.types.Attributes#create(String, String)}
* for creating single-valued attributes.
* <p>
* <code>AttributeBuilder</code>s can be re-used. Once an
* <code>AttributeBuilder</code> has been converted to an
* {@link Attribute} using {@link #toAttribute()}, its state is reset
* so that its attribute type, user-provided name, options, and values
* are all undefined:
*
* <pre>
* AttributeBuilder builder = new AttributeBuilder();
* for (int i = 0; i < 10; i++)
* {
* builder.setAttributeType("myAttribute" + i);
* builder.setOption("an-option");
* builder.add("a value");
* Attribute attribute = builder.toAttribute();
* // Do something with attribute...
* }
* </pre>
* <p>
* <b>Implementation Note:</b> this class is optimized for the common
* case where there is only a single value. By doing so, we avoid
* using unnecessary storage space and also performing any unnecessary
* normalization. In addition, this class is optimized for the common
* cases where there are zero or one attribute type options.
*/
mayInstantiate = true,
mayExtend = false,
mayInvoke = true)
{
/**
* A real attribute - options handled by sub-classes.
*/
private static abstract class RealAttribute
extends AbstractAttribute
{
/** The attribute type for this attribute. */
private final AttributeType attributeType;
/** The name of this attribute as provided by the end user. */
/**
* The unmodifiable set of attribute values, which are lazily normalized.
* <p>
* When required, the attribute values are normalized according to the equality
* matching rule.
*/
/**
* Creates a new real attribute.
*
* @param attributeType
* The attribute type.
* @param name
* The user-provided attribute name.
* @param values
* The attribute values.
*/
{
this.attributeType = attributeType;
}
{
if (matchingRule == null)
{
return ConditionResult.UNDEFINED;
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
return ConditionResult.UNDEFINED;
}
for (AttributeValue v : values)
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
// We couldn't normalize one of the attribute values. If we
// can't find a definite match, then we should return
// "undefined".
}
}
return result;
}
{
}
{
try
{
{
{
return ConditionResult.TRUE;
}
}
return ConditionResult.FALSE;
}
catch (DecodeException e)
{
return ConditionResult.UNDEFINED;
}
}
public final AttributeType getAttributeType()
{
return attributeType;
}
{
return name;
}
{
if (matchingRule == null)
{
return ConditionResult.UNDEFINED;
}
try
{
}
catch (DecodeException e)
{
logger.traceException(e);
return ConditionResult.UNDEFINED;
}
for (AttributeValue v : values)
{
try
{
{
return ConditionResult.TRUE;
}
}
catch (Exception e)
{
logger.traceException(e);
// We couldn't normalize one of the attribute values. If we
// can't find a definite match, then we should return "undefined".
}
}
return result;
}
public final boolean isVirtual()
{
return false;
}
{
return getUnmodifiableIterator(values);
}
{
if (matchingRule == null)
{
return ConditionResult.UNDEFINED;
}
try
{
}
catch (DecodeException e)
{
logger.traceException(e);
return ConditionResult.UNDEFINED;
}
for (AttributeValue v : values)
{
try
{
{
return ConditionResult.TRUE;
}
}
catch (Exception e)
{
logger.traceException(e);
// We couldn't normalize one of the attribute values. If we
// can't find a definite match, then we should return "undefined".
}
}
return result;
}
public final ConditionResult matchesSubstring(ByteString subInitial, List<ByteString> subAny, ByteString subFinal)
{
if (matchingRule == null)
{
return ConditionResult.UNDEFINED;
}
try
{
}
catch (DecodeException e)
{
logger.traceException(e);
return ConditionResult.UNDEFINED;
}
{
try
{
{
return ConditionResult.TRUE;
}
}
catch (Exception e)
{
logger.traceException(e);
// The value couldn't be normalized. If we can't find a
// definite match, then we should return "undefined".
}
}
return result;
}
public final int size()
{
}
public int hashCode()
{
{
}
return hashCode;
}
{
}
}
/**
* A real attribute with a many options.
*/
private static final class RealAttributeManyOptions
extends RealAttribute
{
/** The normalized options. */
/** The options. */
/**
* Creates a new real attribute that has multiple options.
*
* @param attributeType
* The attribute type.
* @param name
* The user-provided attribute name.
* @param values
* The attribute values.
* @param options
* The attribute options.
* @param normalizedOptions
* The normalized attribute options.
*/
private RealAttributeManyOptions(
{
this.normalizedOptions = normalizedOptions;
}
{
return options;
}
{
return normalizedOptions.contains(s);
}
public boolean hasOptions()
{
return true;
}
}
/**
* A real attribute with no options.
*/
private static final class RealAttributeNoOptions extends RealAttribute
{
/**
* Creates a new real attribute that has no options.
*
* @param attributeType
* The attribute type.
* @param name
* The user-provided attribute name.
* @param values
* The attribute values.
*/
private RealAttributeNoOptions(AttributeType attributeType, String name, Set<AttributeValue> values)
{
}
public String getNameWithOptions()
{
return getName();
}
{
return Collections.emptySet();
}
{
}
{
return false;
}
public boolean hasOptions()
{
return false;
}
{
}
}
/**
* A real attribute with a single option.
*/
private static final class RealAttributeSingleOption
extends RealAttribute
{
/** The normalized single option. */
private final String normalizedOption;
/** A singleton set containing the single option. */
/**
* Creates a new real attribute that has a single option.
*
* @param attributeType
* The attribute type.
* @param name
* The user-provided attribute name.
* @param values
* The attribute values.
* @param option
* The attribute option.
*/
private RealAttributeSingleOption(
{
}
{
return option;
}
{
return normalizedOption.equals(s);
}
public boolean hasOptions()
{
return true;
}
}
/**
* A small set of values. This set implementation is optimized to
* use as little memory as possible in the case where there zero or
* one elements. In addition, any normalization of elements is
* delayed until the second element is added (normalization may be
* triggered by invoking {@link Object#hashCode()} or
* {@link Object#equals(Object)}.
*
* @param <T>
* The type of elements to be contained in this small set.
*/
private static final class SmallSet<T> extends AbstractSet<T>
{
/** The set of elements if there are more than one. */
private LinkedHashSet<T> elements;
/** The first element. */
private T firstElement;
/**
* Creates a new small set which is initially empty.
*/
public SmallSet()
{
// No implementation required.
}
/**
* Creates a new small set with an initial capacity.
*
* @param initialCapacity
* The capacity of the set
*/
public SmallSet(int initialCapacity)
{
if (initialCapacity > 1)
{
}
}
public boolean add(T e)
{
// Special handling for the first value. This avoids potentially
// expensive normalization.
{
firstElement = e;
return true;
}
// Create the value set if necessary.
{
if (firstElement.equals(e))
{
return false;
}
// Move the first value into the set.
firstElement = null;
}
}
public boolean addAll(Collection<? extends T> c)
{
{
}
if (firstElement != null)
{
firstElement = null;
}
// Initially empty.
switch (c.size())
{
case 0:
// Do nothing.
return false;
case 1:
return true;
default:
elements = new LinkedHashSet<>(c);
return true;
}
}
public void clear()
{
firstElement = null;
}
{
{
}
else if (firstElement != null)
{
return new Iterator<T>()
{
private boolean hasNext = true;
public boolean hasNext()
{
return hasNext;
}
public T next()
{
if (!hasNext)
{
throw new NoSuchElementException();
}
hasNext = false;
return firstElement;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
else
{
}
}
{
{
// Note: if there is one or zero values left we could stop
// using the set. However, lets assume that if the set
// was multi-valued before then it may become multi-valued
// again.
}
{
firstElement = null;
return true;
}
return false;
}
{
{
}
}
/**
* Sets the initial capacity of this small set. If this small set
* already contains elements or if its capacity has already been
* defined then an {@link IllegalStateException} is thrown.
*
* @param initialCapacity
* The initial capacity of this small set.
* @throws IllegalStateException
* If this small set already contains elements or if its
* capacity has already been defined.
*/
public void setInitialCapacity(int initialCapacity)
throws IllegalStateException
{
{
throw new IllegalStateException();
}
if (initialCapacity > 1)
{
}
}
public int size()
{
{
}
else if (firstElement != null)
{
return 1;
}
else
{
return 0;
}
}
}
/**
* An attribute value which is lazily normalized.
* <p>
* Stores the value in user-provided form and a reference to the associated
* attribute type. The normalized form of the value will be initialized upon
* first request. The normalized form of the value should only be used in
* cases where equality matching between two values can be performed with
* byte-for-byte comparisons of the normalized values.
*/
private static final class AttributeValue
{
private final AttributeType attributeType;
/** User-provided value. */
private final ByteString value;
/** Normalized value, which is {@code null} until computation is required. */
private ByteString normalizedValue;
/**
* Construct a new attribute value.
*
* @param attributeType
* The attribute type.
* @param value
* The value of the attribute.
*/
{
this.attributeType = attributeType;
}
/**
* Retrieves the normalized form of this attribute value.
*
* @return The normalized form of this attribute value.
*/
public ByteString getNormalizedValue()
{
if (normalizedValue == null)
{
}
return normalizedValue;
}
boolean isNormalized()
{
return normalizedValue != null;
}
/**
* Retrieves the user-defined form of this attribute value.
*
* @return The user-defined form of this attribute value.
*/
public ByteString getValue()
{
return value;
}
/**
* Indicates whether the provided object is an attribute value that is equal
* to this attribute value. It will be considered equal if the normalized
* representations of both attribute values are equal.
*
* @param o
* The object for which to make the determination.
* @return <CODE>true</CODE> if the provided object is an attribute value
* that is equal to this attribute value, or <CODE>false</CODE> if
* not.
*/
{
if (this == o)
{
return true;
}
else if (o instanceof AttributeValue)
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
return false;
}
/**
* Retrieves the hash code for this attribute value. It will be calculated
* using the normalized representation of the value.
*
* @return The hash code for this attribute value.
*/
public int hashCode()
{
try
{
return getNormalizedValue().hashCode();
}
catch (Exception e)
{
logger.traceException(e);
}
}
{
}
}
/**
* Creates an attribute that has no options.
* <p>
* This method is only intended for use by the {@link Attributes}
* class.
*
* @param attributeType
* The attribute type.
* @param name
* The user-provided attribute name.
* @param values
* The attribute values.
* @return The new attribute.
*/
{
return builder.toAttribute();
}
/**
* Gets the named attribute type, creating a default attribute if
* necessary.
*
* @param attributeName
* The name of the attribute type.
* @return The attribute type associated with the provided attribute
* name.
*/
{
}
/** The attribute type for this attribute. */
private AttributeType attributeType;
/** The name of this attribute as provided by the end user. */
/** The normalized set of options if there are more than one. */
/** The set of options. */
/** The set of attribute values, which are lazily normalized. */
/**
* Creates a new attribute builder with an undefined attribute type
* and user-provided name. The attribute type, and optionally the
* user-provided name, must be defined using
* {@link #setAttributeType(AttributeType)} before the attribute
* builder can be converted to an {@link Attribute}. Failure to do
* so will yield an {@link IllegalStateException}.
*/
public AttributeBuilder()
{
// No implementation required.
}
/**
* Creates a new attribute builder from an existing attribute.
* <p>
* Modifications to the attribute builder will not impact the
* provided attribute.
*
* @param attribute
* The attribute to be copied.
*/
{
this(attribute, false);
}
/**
* Creates a new attribute builder from an existing attribute,
* optionally omitting the values contained in the provided
* attribute.
* <p>
* Modifications to the attribute builder will not impact the
* provided attribute.
*
* @param attribute
* The attribute to be copied.
* @param omitValues
* <CODE>true</CODE> if the values should be omitted.
*/
{
{
}
if (!omitValues)
{
}
}
/**
* Creates a new attribute builder with the specified type and no
* options and no values.
*
* @param attributeType
* The attribute type for this attribute builder.
*/
{
}
/**
* Creates a new attribute builder with the specified type and
* user-provided name and no options and no values.
*
* @param attributeType
* The attribute type for this attribute builder.
* @param name
* The user-provided name for this attribute builder.
*/
{
this.attributeType = attributeType;
}
/**
* Creates a new attribute builder with the specified attribute name
* and no options and no values.
* <p>
* If the attribute name cannot be found in the schema, a new
* attribute type is created using the default attribute syntax.
*
* @param attributeName
* The attribute name for this attribute builder.
*/
{
}
/**
* Adds the specified attribute value to this attribute builder if
* it is not already present.
*
* @param valueString
* The string representation of the attribute value to be
* added to this attribute builder.
* @return <code>true</code> if this attribute builder did not
* already contain the specified attribute value.
*/
{
}
/**
* Adds the specified attribute value to this attribute builder if it is not
* already present.
*
* @param attributeValue
* The {@link ByteString} representation of the attribute value to be
* added to this attribute builder.
* @return <code>true</code> if this attribute builder did not already contain
* the specified attribute value.
*/
{
if (!isNewValue)
{
// AttributeValue is already present, but the user-provided value may be different
// There is no direct way to check this, so remove and add to ensure
// the last user-provided value is recorded
}
return isNewValue;
}
/** Creates an attribute value with delayed normalization. */
private static AttributeValue createAttributeValue(AttributeType attributeType, ByteString attributeValue)
{
}
{
try
{
if (attributeType != null)
{
}
}
catch (DecodeException e)
{
// nothing to do here
}
return attributeValue;
}
/**
* Adds all the values from the specified attribute to this
* attribute builder if they are not already present.
*
* @param attribute
* The attribute containing the values to be added to this
* attribute builder.
* @return <code>true</code> if this attribute builder was
* modified.
*/
{
boolean wasModified = false;
for (ByteString v : attribute)
{
wasModified |= add(v);
}
return wasModified;
}
/**
* Adds the specified attribute values to this attribute builder if
* they are not already present.
*
* @param values
* The attribute values to be added to this attribute builder.
* @return <code>true</code> if this attribute builder was modified.
*/
{
boolean wasModified = false;
for (ByteString v : values)
{
wasModified |= add(v);
}
return wasModified;
}
/**
* Adds the specified attribute values to this attribute builder
* if they are not already present.
*
* @param values
* The attribute values to be added to this attribute builder.
* @return <code>true</code> if this attribute builder was modified.
* @throws NullPointerException if any of the values is null
*/
{
boolean wasModified = false;
{
}
return wasModified;
}
/**
* Removes all attribute values from this attribute builder.
*/
public void clear()
{
}
/**
* Indicates whether this attribute builder contains the specified
* value.
*
* @param value
* The value for which to make the determination.
* @return <CODE>true</CODE> if this attribute builder has the
* specified value, or <CODE>false</CODE> if not.
*/
{
}
/**
* Indicates whether this attribute builder contains all the values
* in the collection.
*
* @param values
* The set of values for which to make the determination.
* @return <CODE>true</CODE> if this attribute builder contains
* all the values in the provided collection, or
* <CODE>false</CODE> if it does not contain at least one
* of them.
*/
{
for (ByteString v : values)
{
if (!contains(v))
{
return false;
}
}
return true;
}
/**
* Retrieves the attribute type for this attribute builder.
*
* @return The attribute type for this attribute builder, or
* <code>null</code> if one has not yet been specified.
*/
public AttributeType getAttributeType()
{
return attributeType;
}
/**
* Returns <code>true</code> if this attribute builder contains no
* attribute values.
*
* @return <CODE>true</CODE> if this attribute builder contains no
* attribute values.
*/
public boolean isEmpty()
{
}
/**
* Returns an iterator over the attribute values in this attribute
* builder. The attribute values are returned in the order in which
* they were added to this attribute builder. The returned iterator
* supports attribute value removals via its <code>remove</code>
* method.
*
* @return An iterator over the attribute values in this attribute builder.
*/
{
return getUnmodifiableIterator(values);
}
/**
* Removes the specified attribute value from this attribute builder
* if it is present.
*
* @param value
* The attribute value to be removed from this attribute
* builder.
* @return <code>true</code> if this attribute builder contained
* the specified attribute value.
*/
{
}
/**
* Removes the specified attribute value from this attribute builder
* if it is present.
*
* @param valueString
* The string representation of the attribute value to be
* removed from this attribute builder.
* @return <code>true</code> if this attribute builder contained
* the specified attribute value.
*/
{
}
/**
* Removes all the values from the specified attribute from this
* attribute builder if they are not already present.
*
* @param attribute
* The attribute containing the values to be removed from
* this attribute builder.
* @return <code>true</code> if this attribute builder was
* modified.
*/
{
boolean wasModified = false;
for (ByteString v : attribute)
{
wasModified |= remove(v);
}
return wasModified;
}
/**
* Removes the specified attribute values from this attribute
* builder if they are present.
*
* @param values
* The attribute values to be removed from this attribute
* builder.
* @return <code>true</code> if this attribute builder was
* modified.
*/
{
boolean wasModified = false;
for (ByteString v : values)
{
wasModified |= remove(v);
}
return wasModified;
}
/**
* Replaces all the values in this attribute value with the
* specified attribute value.
*
* @param value
* The attribute value to replace all existing values.
*/
{
clear();
}
/**
* Replaces all the values in this attribute value with the
* specified attribute value.
*
* @param valueString
* The string representation of the attribute value to
* replace all existing values.
*/
{
}
/**
* Replaces all the values in this attribute value with the
* attributes from the specified attribute.
*
* @param attribute
* The attribute containing the values to replace all
* existing values.
*/
{
clear();
}
/**
* Replaces all the values in this attribute value with the
* specified attribute values.
*
* @param values
* The attribute values to replace all existing values.
*/
{
clear();
}
/**
* Sets the attribute type associated with this attribute builder.
*
* @param attributeType
* The attribute type for this attribute builder.
*/
{
}
/**
* Sets the attribute type and user-provided name associated with
* this attribute builder.
*
* @param attributeType
* The attribute type for this attribute builder.
* @param name
* The user-provided name for this attribute builder.
*/
public void setAttributeType(
{
this.attributeType = attributeType;
}
/**
* Sets the attribute type associated with this attribute builder
* using the provided attribute type name.
* <p>
* If the attribute name cannot be found in the schema, a new
* attribute type is created using the default attribute syntax.
*
* @param attributeName
* The attribute name for this attribute builder.
*/
{
}
/**
* Adds the specified option to this attribute builder if it is not
* already present.
*
* @param option
* The option to be added to this attribute builder.
* @return <code>true</code> if this attribute builder did not
* already contain the specified option.
*/
{
{
case 0:
case 1:
// Normalize and add the first option to normalized set.
normalizedOptions = new TreeSet<>();
{
return true;
}
break;
default:
{
return true;
}
break;
}
return false;
}
/**
* Adds the specified options to this attribute builder if they are
* not already present.
*
* @param options
* The options to be added to this attribute builder.
* @return <code>true</code> if this attribute builder was
* modified.
*/
{
boolean isModified = false;
{
}
return isModified;
}
/**
* Indicates whether this attribute builder has exactly the
* specified set of options.
*
* This implementation returns
* {@link java.util.AbstractCollection#isEmpty()}
* if the provided set of options is <code>null</code>.
* Otherwise it checks that the size of the provided
* set of options is equal to the size of this attribute
* builder options, returns <code>false</code> if the
* sizes differ. If the sizes are the same then each
* option in the provided set is checked and if all the
* provided options are present <code>true</code> is
* returned.
*
* @param options
* The set of options for which to make the
* determination (may be <code>null</code>).
* @return <code>true</code> if this attribute
* builder has exactly the specified
* set of options.
*/
{
{
}
{
return false;
}
{
{
return false;
}
}
return true;
}
/**
* Returns the number of attribute values in this attribute builder.
*
* @return The number of attribute values in this attribute builder.
*/
public int size()
{
}
/** Returns an iterator on values corresponding to the provided attribute values set. */
{
return new Iterator<ByteString>()
{
public boolean hasNext()
{
}
public ByteString next()
{
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
/**
* Indicates if the values for this attribute have been normalized.
* <p>
* This method is intended for tests.
*/
boolean isNormalized()
{
{
if (attrValue.isNormalized())
{
return true;
}
}
return false;
}
/**
* Returns an attribute representing the content of this attribute builder.
* <p>
* For efficiency purposes this method resets the content of this
* attribute builder so that it no longer contains any options or
* values and its attribute type is <code>null</code>.
*
* @return An attribute representing the content of this attribute builder.
* @throws IllegalStateException
* If this attribute builder has an undefined attribute type or name.
*/
{
if (attributeType == null)
{
throw new IllegalStateException("Undefined attribute type or name");
}
// Now create the appropriate attribute based on the options.
// Reset the state of this builder.
return attribute;
}
private Attribute toAttribute0()
{
{
case 0:
case 1:
default:
Collections.unmodifiableSet(options.elements), Collections.unmodifiableSortedSet(normalizedOptions));
}
}
/**
* Returns a List with a single attribute representing the content of this attribute builder.
* <p>
* For efficiency purposes this method resets the content of this
* attribute builder so that it no longer contains any options or
* values and its attribute type is <code>null</code>.
*
* @return A List with a single attribute representing the content of this attribute builder.
* @throws IllegalStateException
* If this attribute builder has an undefined attribute type or name.
*/
{
}
{
{
}
}
}