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 }