001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008 Sun Microsystems, Inc.
015 * Portions Copyright 2015 ForgeRock AS.
016 */
017package org.opends.server.admin.client.spi;
018
019import java.util.Collection;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.SortedSet;
024import java.util.TreeSet;
025
026import org.opends.server.admin.PropertyException;
027import org.opends.server.admin.PropertyDefinition;
028import org.opends.server.admin.PropertyOption;
029
030/**
031 * A set of properties. Instances of this class can be used as the
032 * core of a managed object implementation.
033 */
034public final class PropertySet {
035
036  /**
037   * Internal property implementation.
038   *
039   * @param <T>
040   *          The type of the property.
041   */
042  private static final class MyProperty<T> implements Property<T> {
043
044    /** The active set of values. */
045    private final SortedSet<T> activeValues;
046
047    /** The definition associated with this property. */
048    private final PropertyDefinition<T> d;
049
050    /** The default set of values (read-only). */
051    private final SortedSet<T> defaultValues;
052
053    /** The pending set of values. */
054    private final SortedSet<T> pendingValues;
055
056
057
058    /**
059     * Create a property with the provided sets of pre-validated
060     * default and active values.
061     *
062     * @param pd
063     *          The property definition.
064     * @param defaultValues
065     *          The set of default values for the property.
066     * @param activeValues
067     *          The set of active values for the property.
068     */
069    public MyProperty(PropertyDefinition<T> pd, Collection<T> defaultValues,
070        Collection<T> activeValues) {
071      this.d = pd;
072
073      SortedSet<T> sortedDefaultValues = new TreeSet<>(pd);
074      sortedDefaultValues.addAll(defaultValues);
075      this.defaultValues = Collections
076          .unmodifiableSortedSet(sortedDefaultValues);
077
078      this.activeValues = new TreeSet<>(pd);
079      this.activeValues.addAll(activeValues);
080
081      // Initially the pending values is the same as the active values.
082      this.pendingValues = new TreeSet<>(this.activeValues);
083    }
084
085    /** Makes the pending values active. */
086    public void commit() {
087      activeValues.clear();
088      activeValues.addAll(pendingValues);
089    }
090
091    /** {@inheritDoc} */
092    public SortedSet<T> getActiveValues() {
093      return Collections.unmodifiableSortedSet(activeValues);
094    }
095
096    /** {@inheritDoc} */
097    public SortedSet<T> getDefaultValues() {
098      return defaultValues;
099    }
100
101    /** {@inheritDoc} */
102    public SortedSet<T> getEffectiveValues() {
103      SortedSet<T> values = getPendingValues();
104
105      if (values.isEmpty()) {
106        values = getDefaultValues();
107      }
108
109      return values;
110    }
111
112
113
114    /** {@inheritDoc} */
115    public SortedSet<T> getPendingValues() {
116      return Collections.unmodifiableSortedSet(pendingValues);
117    }
118
119
120
121    /** {@inheritDoc} */
122    public PropertyDefinition<T> getPropertyDefinition() {
123      return d;
124    }
125
126
127
128    /** {@inheritDoc} */
129    public boolean isEmpty() {
130      return pendingValues.isEmpty();
131    }
132
133
134
135    /** {@inheritDoc} */
136    public boolean isModified() {
137      return activeValues.size() != pendingValues.size()
138          || !activeValues.containsAll(pendingValues);
139    }
140
141
142
143    /**
144     * Replace all pending values of this property with the provided
145     * values.
146     *
147     * @param c
148     *          The new set of pending property values.
149     */
150    public void setPendingValues(Collection<T> c) {
151      pendingValues.clear();
152      pendingValues.addAll(c);
153    }
154
155
156
157    /** {@inheritDoc} */
158    @Override
159    public String toString() {
160      return getEffectiveValues().toString();
161    }
162
163
164
165    /** {@inheritDoc} */
166    public boolean wasEmpty() {
167      return activeValues.isEmpty();
168    }
169  }
170
171  /** The properties. */
172  private final Map<PropertyDefinition<?>, MyProperty<?>> properties = new HashMap<>();
173
174  /** Creates a new empty property set. */
175  public PropertySet() {
176  }
177
178  /**
179   * Creates a property with the provided sets of pre-validated
180   * default and active values.
181   *
182   * @param <T>
183   *          The type of the property.
184   * @param pd
185   *          The property definition.
186   * @param defaultValues
187   *          The set of default values for the property.
188   * @param activeValues
189   *          The set of active values for the property.
190   */
191  public <T> void addProperty(PropertyDefinition<T> pd,
192      Collection<T> defaultValues, Collection<T> activeValues) {
193    MyProperty<T> p = new MyProperty<>(pd, defaultValues, activeValues);
194    properties.put(pd, p);
195  }
196
197
198
199  /**
200   * Get the property associated with the specified property
201   * definition.
202   *
203   * @param <T>
204   *          The underlying type of the property.
205   * @param d
206   *          The Property definition.
207   * @return Returns the property associated with the specified
208   *         property definition.
209   * @throws IllegalArgumentException
210   *           If this property provider does not recognise the
211   *           requested property definition.
212   */
213  @SuppressWarnings("unchecked")
214  public <T> Property<T> getProperty(PropertyDefinition<T> d)
215      throws IllegalArgumentException {
216    if (!properties.containsKey(d)) {
217      throw new IllegalArgumentException("Unknown property " + d.getName());
218    }
219
220    return (Property<T>) properties.get(d);
221  }
222
223
224
225  /** {@inheritDoc} */
226  @Override
227  public String toString() {
228    StringBuilder builder = new StringBuilder();
229    builder.append('{');
230    for (Map.Entry<PropertyDefinition<?>, MyProperty<?>> entry : properties.entrySet()) {
231      builder.append(entry.getKey().getName());
232      builder.append('=');
233      builder.append(entry.getValue());
234      builder.append(' ');
235    }
236    builder.append('}');
237    return builder.toString();
238  }
239
240
241
242  /**
243   * Makes all pending values active.
244   */
245  void commit()
246  {
247    for (MyProperty<?> p : properties.values())
248    {
249      p.commit();
250    }
251  }
252
253
254
255  /**
256   * Set a new pending values for the specified property.
257   * <p>
258   * See the class description for more information regarding pending values.
259   *
260   * @param <T>
261   *          The type of the property to be modified.
262   * @param d
263   *          The property to be modified.
264   * @param values
265   *          A non-<code>null</code> set of new pending values for the property
266   *          (an empty set indicates that the property should be reset to its
267   *          default behavior). The set will not be referenced by this managed
268   *          object.
269   * @throws PropertyException
270   *           If a new pending value is deemed to be invalid according to the
271   *           property definition, or if an attempt was made to add multiple
272   *           pending values to a single-valued property, or if an attempt was
273   *           made to remove a mandatory property.
274   * @throws IllegalArgumentException
275   *           If the specified property definition is not associated with this
276   *           managed object.
277   */
278  <T> void setPropertyValues(PropertyDefinition<T> d, Collection<T> values)
279      throws PropertyException, IllegalArgumentException
280  {
281    MyProperty<T> property = (MyProperty<T>) getProperty(d);
282
283    if (values.size() > 1 && !d.hasOption(PropertyOption.MULTI_VALUED)) {
284      throw PropertyException.propertyIsSingleValuedException(d);
285    }
286
287    if (values.isEmpty()
288        && d.hasOption(PropertyOption.MANDATORY)
289        // But only if there are no default values.
290        && property.getDefaultValues().isEmpty()) {
291      throw PropertyException.propertyIsMandatoryException(d);
292    }
293
294    // Validate each value.
295    for (T e : values) {
296      if (e == null) {
297        throw new NullPointerException();
298      }
299
300      d.validateValue(e);
301    }
302
303    // Update the property.
304    property.setPendingValues(values);
305  }
306}