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