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