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 }