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