mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

matthew_swift
28.31.2007 45359adc09ac1d9e48206c549e667ed6965c7cd3
opends/src/server/org/opends/server/admin/client/PropertySet.java
@@ -32,41 +32,23 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.DefaultBehaviorPropertyValueException;
import org.opends.server.admin.DefaultBehaviorProviderVisitor;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
import org.opends.server.admin.IllegalPropertyValueException;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.InheritedDefaultValueException;
import org.opends.server.admin.InheritedDefaultValueProvider;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OperationsException;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.StringPropertyProvider;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
/**
 * A set of properties. Instances of this class can be used as the core of a
 * managed object implementation.
 * A set of properties. Instances of this class can be used as the
 * core of a managed object implementation.
 */
public final class PropertySet implements PropertyProvider {
@@ -78,50 +60,65 @@
   */
  private static final class MyProperty<T> implements Property<T> {
    // The active set of values.
    private final SortedSet<T> activeValues;
    // The definition associated with this property.
    private final PropertyDefinition<T> d;
    // The default set of values (read-only).
    private final SortedSet<T> defaultValues;
    // The active set of values (read-only).
    private final SortedSet<T> activeValues;
    // The pending set of values.
    private final SortedSet<T> pendingValues;
    /**
     * Create a property with the provided sets of pre-validated default and
     * active values.
     * <p>
     * This constructor takes ownership of the provided value sets.
     * Create a property with the provided sets of pre-validated
     * default and active values.
     *
     * @param d
     * @param pd
     *          The property definition.
     * @param defaultValues
     *          The set of default values for the property.
     * @param activeValues
     *          The set of active values for the property.
     */
    public MyProperty(PropertyDefinition<T> d, SortedSet<T> defaultValues,
        SortedSet<T> activeValues) {
      this.d = d;
      this.defaultValues = Collections.unmodifiableSortedSet(defaultValues);
      this.activeValues = Collections.unmodifiableSortedSet(activeValues);
    public MyProperty(PropertyDefinition<T> pd, Collection<T> defaultValues,
        Collection<T> activeValues) {
      this.d = pd;
      // Initially the pending values is the same as the active values.
      SortedSet<T> sortedDefaultValues = new TreeSet<T>(pd);
      sortedDefaultValues.addAll(defaultValues);
      this.defaultValues = Collections
          .unmodifiableSortedSet(sortedDefaultValues);
      this.activeValues = new TreeSet<T>(pd);
      this.activeValues.addAll(activeValues);
      // Initially the pending values is the same as the active
      // values.
      this.pendingValues = new TreeSet<T>(this.activeValues);
    }
    /**
     * Makes the pending values active.
     */
    public void commit() {
      activeValues.clear();
      activeValues.addAll(pendingValues);
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> getActiveValues() {
      return activeValues;
      return Collections.unmodifiableSortedSet(activeValues);
    }
@@ -181,21 +178,18 @@
     * {@inheritDoc}
     */
    public boolean isModified() {
      if (activeValues == pendingValues) {
      if (activeValues.size() == pendingValues.size()
          && activeValues.containsAll(pendingValues)) {
        return false;
      } else if (activeValues.size() != pendingValues.size()) {
        return true;
      } else if (activeValues.containsAll(pendingValues)) {
        return false;
      } else {
        return true;
      }
      return true;
    }
    /**
     * Replace all pending values of this property with the provided values.
     * Replace all pending values of this property with the provided
     * values.
     *
     * @param c
     *          The new set of pending property values.
@@ -210,376 +204,19 @@
    /**
     * {@inheritDoc}
     */
    public boolean wasEmpty() {
      return activeValues.isEmpty();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
      return getEffectiveValues().toString();
    }
  }
  /**
   * Internal default behavior visitor implementation.
   *
   * @param <T>
   *          The type of the default property values.
   */
  private static final class DefaultVisitor<T> implements
      DefaultBehaviorProviderVisitor<T, SortedSet<T>,
      Collection<PropertyException>> {
    // The property definition.
    private final PropertyDefinition<T> pd;
    // Used to retrieve inherited properties.
    private final InheritedDefaultValueProvider provider;
    // Private constructor.
    private DefaultVisitor(PropertyDefinition<T> pd,
        InheritedDefaultValueProvider provider) {
      this.pd = pd;
      this.provider = provider;
    }
    // Cast a set of objects to the required type.
    private Collection<T> castValues(Collection<?> values,
        Collection<PropertyException> exceptions) {
      List<T> castValues = new LinkedList<T>();
      for (Object value : values) {
        try {
          castValues.add(pd.castValue(value));
        } catch (ClassCastException e) {
          exceptions.add(new IllegalPropertyValueException(pd, value));
        }
      }
      return castValues;
    }
    // Build set of default values and validate them.
    private SortedSet<T> validateStrings(Collection<String> values,
        Collection<PropertyException> exceptions) {
      TreeSet<T> defaultValues = new TreeSet<T>(pd);
      for (String value : values) {
        try {
          defaultValues.add(pd.decodeValue(value));
        } catch (IllegalPropertyValueStringException e) {
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      if (!pd.hasOption(PropertyOption.MULTI_VALUED)) {
        if (defaultValues.size() > 1) {
          PropertyException e = new PropertyIsSingleValuedException(pd);
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      return defaultValues;
    }
    // Build set of default values and validate them.
    private SortedSet<T> validate(Collection<T> values,
        Collection<PropertyException> exceptions) {
      TreeSet<T> defaultValues = new TreeSet<T>(pd);
      for (T value : values) {
        try {
          pd.validateValue(value);
          defaultValues.add(value);
        } catch (IllegalPropertyValueException e) {
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      if (!pd.hasOption(PropertyOption.MULTI_VALUED)) {
        if (defaultValues.size() > 1) {
          PropertyException e = new PropertyIsSingleValuedException(pd);
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      return defaultValues;
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      // Get the values from the managed object at the specified path.
      try {
        // Get the property values/defaults.
        ManagedObjectPath path = d.getManagedObjectPath();
        Collection<?> values = provider.getDefaultPropertyValues(path, d
            .getPropertyName());
        return validate(castValues(values, p), p);
      } catch (OperationsException e) {
        p.add(new InheritedDefaultValueException(pd, e));
        return new TreeSet<T>(pd);
      }
    public boolean wasEmpty() {
      return activeValues.isEmpty();
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitAlias(AliasDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      // No values applicable - just return the empty set.
      return new TreeSet<T>(pd);
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      return validateStrings(d.getDefaultValues(), p);
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      if (d.getRelativeOffset() == 0) {
        // TODO: we're inheriting default values from another property in this
        // property set. Logging is a good use-case: there is a general logging
        // level for all categories and then category specific levels which can
        // override. Should the default values be determined dynamically every
        // time they are accessed? If dynamically, how will decoding errors be
        // handled? Dynamically: we could return a SortedSet<T> which is lazily
        // computed.
        return new TreeSet<T>(pd);
      } else {
        // Inheriting default values from a parent managed object.
        try {
          ManagedObjectPath base = provider.getManagedObjectPath();
          ManagedObjectPath path = d.getManagedObjectPath(base);
          Collection<?> values = provider.getDefaultPropertyValues(path, d
              .getPropertyName());
          return validate(castValues(values, p), p);
        } catch (OperationsException e) {
          p.add(new InheritedDefaultValueException(pd, e));
          return new TreeSet<T>(pd);
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      // No values applicable - just return the empty set.
      return new TreeSet<T>(pd);
    }
  }
  /**
   * Create a new property set using a property provider to supply the active
   * property values. This constructor takes care of validation of the property
   * values and retrieval of any default values.
   * <p>
   * Any exceptions that occurred whilst processing the properties will be
   * placed in the provided exception collection. Properties that caused the
   * exceptions will be created with an empty set of values (note that this
   * could mean that the resulting property set might contain empty mandatory
   * properties).
   *
   * @param d
   *          The managed object definition.
   * @param p
   *          The property provider.
   * @param i
   *          An inherited managed object provider for retrieving inherited
   *          properties.
   * @param exceptions
   *          A collection in which any property exceptions can be placed.
   * @return Returns the new property set.
   */
  public static PropertySet create(ManagedObjectDefinition<?, ?> d,
      PropertyProvider p, InheritedDefaultValueProvider i,
      Collection<PropertyException> exceptions) {
    Map<PropertyDefinition, MyProperty> properties =
      new HashMap<PropertyDefinition, MyProperty>();
    // Copy the properties from the provider.
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
      createProperty(pd, p, i, properties, exceptions);
    }
    return new PropertySet(properties);
  }
  /**
   * Create a new property set using a string property provider to supply the
   * active property values. This constructor takes care of validation of the
   * property values and retrieval of any default values.
   * <p>
   * Any exceptions that occurred whilst processing the properties will be
   * placed in the provided exception collection. Properties that caused the
   * exceptions will be created with an empty set of values (note that this
   * could mean that the resulting property set might contain empty mandatory
   * properties).
   *
   * @param d
   *          The managed object definition.
   * @param p
   *          The string property provider.
   * @param i
   *          An inherited managed object provider for retrieving inherited
   *          properties.
   * @param exceptions
   *          A collection in which any property exceptions can be placed.
   * @return Returns the new property set.
   */
  public static PropertySet create(ManagedObjectDefinition<?, ?> d,
      StringPropertyProvider p, InheritedDefaultValueProvider i,
      Collection<PropertyException> exceptions) {
    Map<PropertyDefinition, MyProperty> properties =
      new HashMap<PropertyDefinition, MyProperty>();
    // Copy the properties from the provider.
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
      createProperty(pd, p, i, properties, exceptions);
    }
    return new PropertySet(properties);
  }
  // Create new property using string values taken from a property provider.
  private static <T> void createProperty(PropertyDefinition<T> pd,
      StringPropertyProvider p, InheritedDefaultValueProvider i,
      Map<PropertyDefinition, MyProperty> properties,
      Collection<PropertyException> exceptions) {
    // Get the active values for this property.
    Collection<String> activeStringValues;
    try {
      activeStringValues = p.getPropertyValues(pd);
    } catch (IllegalArgumentException e) {
      // Default to empty set of values.
      activeStringValues = Collections.<String> emptySet();
    }
    SortedSet<T> activeValues = new TreeSet<T>(pd);
    boolean gotException = false;
    for (String stringValue : activeStringValues) {
      try {
        activeValues.add(pd.decodeValue(stringValue));
      } catch (IllegalPropertyValueStringException e) {
        exceptions.add(e);
        gotException = true;
      }
    }
    if (gotException == false) {
      if (pd.hasOption(PropertyOption.MANDATORY)) {
        if (activeValues.isEmpty()) {
          exceptions.add(new PropertyIsMandatoryException(pd));
        }
      }
    }
    createProperty(pd, activeValues, i, properties, exceptions);
  }
  // Create new property using values taken from a property provider.
  private static <T> void createProperty(PropertyDefinition<T> pd,
      PropertyProvider p, InheritedDefaultValueProvider i,
      Map<PropertyDefinition, MyProperty> properties,
      Collection<PropertyException> exceptions) {
    // Get the active values for this property.
    Collection<T> activeValues;
    try {
      activeValues = p.getPropertyValues(pd);
    } catch (IllegalArgumentException e) {
      // Default to empty set of values.
      activeValues = Collections.<T> emptySet();
    }
    SortedSet<T> validActiveValues = new TreeSet<T>(pd);
    boolean gotException = false;
    for (T value : activeValues) {
      try {
        pd.validateValue(value);
        validActiveValues.add(value);
      } catch (IllegalPropertyValueException e) {
        exceptions.add(e);
        gotException = true;
      }
    }
    if (gotException == false) {
      if (pd.hasOption(PropertyOption.MANDATORY)) {
        if (validActiveValues.isEmpty()) {
          exceptions.add(new PropertyIsMandatoryException(pd));
        }
      }
    }
    createProperty(pd, validActiveValues, i, properties, exceptions);
  }
  // Create new property using the provided validated values.
  private static <T> void createProperty(PropertyDefinition<T> pd,
      SortedSet<T> activeValues, InheritedDefaultValueProvider i,
      Map<PropertyDefinition, MyProperty> properties,
      Collection<PropertyException> exceptions) {
    // Do remaining validation of active values.
    if (!pd.hasOption(PropertyOption.MULTI_VALUED)) {
      if (activeValues.size() > 1) {
        exceptions.add(new PropertyIsSingleValuedException(pd));
      }
    }
    // Get the default values for this property.
    DefaultVisitor<T> visitor = new DefaultVisitor<T>(pd, i);
    SortedSet<T> defaultValues = pd.getDefaultBehaviorProvider().accept(
        visitor, exceptions);
    // Create the property.
    properties.put(pd, new MyProperty<T>(pd, defaultValues, activeValues));
  }
  // The properties.
@@ -587,25 +224,60 @@
  // Private constructor.
  private PropertySet(Map<PropertyDefinition, MyProperty> properties) {
    this.properties = properties;
  /**
   * Creates a new empty property set.
   */
  public PropertySet() {
    this.properties = new HashMap<PropertyDefinition, MyProperty>();
  }
  /**
   * Get the property associated with the specified property definition.
   * Creates a property with the provided sets of pre-validated
   * default and active values.
   *
   * @param <T>
   *          The type of the property.
   * @param pd
   *          The property definition.
   * @param defaultValues
   *          The set of default values for the property.
   * @param activeValues
   *          The set of active values for the property.
   */
  public <T> void addProperty(PropertyDefinition<T> pd,
      Collection<T> defaultValues, Collection<T> activeValues) {
    MyProperty<T> p = new MyProperty<T>(pd, defaultValues, activeValues);
    properties.put(pd, p);
  }
  /**
   * Makes all pending values active.
   */
  public void commit() {
    for (MyProperty<?> p : properties.values()) {
      p.commit();
    }
  }
  /**
   * Get the property associated with the specified property
   * definition.
   *
   * @param <T>
   *          The underlying type of the property.
   * @param d
   *          The Property definition.
   * @return Returns the property associated with the specified property
   *         definition.
   * @return Returns the property associated with the specified
   *         property definition.
   * @throws IllegalArgumentException
   *           If this property provider does not recognise the requested
   *           property definition.
   *           If this property provider does not recognise the
   *           requested property definition.
   */
  @SuppressWarnings("unchecked")
  public <T> Property<T> getProperty(PropertyDefinition<T> d)
@@ -622,18 +294,19 @@
  /**
   * Get the effective value of the specified property.
   * <p>
   * See the class description for more information about how the effective
   * property value is derived.
   * See the class description for more information about how the
   * effective property value is derived.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns the property's effective value, or <code>null</code> if
   *         there is no effective value defined.
   * @return Returns the property's effective value, or
   *         <code>null</code> if there is no effective value
   *         defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this managed
   *           object's definition.
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  public <T> T getPropertyValue(PropertyDefinition<T> d)
      throws IllegalArgumentException {
@@ -650,18 +323,18 @@
  /**
   * Get the effective values of the specified property.
   * <p>
   * See the class description for more information about how the effective
   * property values are derived.
   * See the class description for more information about how the
   * effective property values are derived.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns the property's effective values, or an empty set if there
   *         are no effective values defined.
   * @return Returns the property's effective values, or an empty set
   *         if there are no effective values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this managed
   *           object's definition.
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
      throws IllegalArgumentException {
@@ -674,29 +347,29 @@
  /**
   * Set a new pending value for the specified property.
   * <p>
   * See the class description for more information regarding pending values.
   * See the class description for more information regarding pending
   * values.
   *
   * @param <T>
   *          The type of the property to be modified.
   * @param d
   *          The property to be modified.
   * @param value
   *          The new pending value for the property, or <code>null</code> if
   *          the property should be reset to its default behavior.
   *          The new pending value for the property, or
   *          <code>null</code> if the property should be reset to
   *          its default behavior.
   * @throws IllegalPropertyValueException
   *           If the new pending value is deemed to be invalid according to the
   *           property definition.
   * @throws PropertyIsReadOnlyException
   *           If an attempt was made to modify a read-only property.
   *           If the new pending value is deemed to be invalid
   *           according to the property definition.
   * @throws PropertyIsMandatoryException
   *           If an attempt was made to remove a mandatory property.
   * @throws IllegalArgumentException
   *           If the specified property definition is not associated with this
   *           managed object.
   *           If the specified property definition is not associated
   *           with this managed object.
   */
  public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
      throws IllegalPropertyValueException, PropertyIsMandatoryException,
      IllegalArgumentException {
    if (value == null) {
      setPropertyValues(d, Collections.<T> emptySet());
    } else {
@@ -709,51 +382,42 @@
  /**
   * Set a new pending values for the specified property.
   * <p>
   * See the class description for more information regarding pending values.
   * See the class description for more information regarding pending
   * values.
   *
   * @param <T>
   *          The type of the property to be modified.
   * @param d
   *          The property to be modified.
   * @param values
   *          A non-<code>null</code> set of new pending values for the
   *          property (an empty set indicates that the property should be reset
   *          to its default behavior). The set will not be referenced by this
   *          managed object.
   *          A non-<code>null</code> set of new pending values for
   *          the property (an empty set indicates that the property
   *          should be reset to its default behavior). The set will
   *          not be referenced by this managed object.
   * @throws IllegalPropertyValueException
   *           If a new pending value is deemed to be invalid according to the
   *           property definition.
   *           If a new pending value is deemed to be invalid
   *           according to the property definition.
   * @throws PropertyIsSingleValuedException
   *           If an attempt was made to add multiple pending values to a
   *           single-valued property.
   * @throws PropertyIsReadOnlyException
   *           If an attempt was made to modify a read-only property.
   *           If an attempt was made to add multiple pending values
   *           to a single-valued property.
   * @throws PropertyIsMandatoryException
   *           If an attempt was made to remove a mandatory property.
   * @throws IllegalArgumentException
   *           If the specified property definition is not associated with this
   *           managed object.
   *           If the specified property definition is not associated
   *           with this managed object.
   */
  public <T> void setPropertyValues(PropertyDefinition<T> d,
      Collection<T> values) throws IllegalPropertyValueException,
      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
      PropertyIsSingleValuedException, PropertyIsMandatoryException,
      IllegalArgumentException {
    MyProperty<T> property = (MyProperty<T>) getProperty(d);
    if (d.hasOption(PropertyOption.READ_ONLY)) {
      throw new PropertyIsReadOnlyException(d);
    if (values.size() > 1 && !d.hasOption(PropertyOption.MULTI_VALUED)) {
      throw new PropertyIsSingleValuedException(d);
    }
    if (!d.hasOption(PropertyOption.MULTI_VALUED)) {
      if (values.size() > 1) {
        throw new PropertyIsSingleValuedException(d);
      }
    }
    if (d.hasOption(PropertyOption.MANDATORY)) {
      if (values.isEmpty()) {
        throw new PropertyIsMandatoryException(d);
      }
    if (values.isEmpty() && d.hasOption(PropertyOption.MANDATORY)) {
      throw new PropertyIsMandatoryException(d);
    }
    // Validate each value.