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}