PropertySet.java revision 998747bfaaa3c6b28bbfaf0e282e6c0ccbf46bc0
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008 Sun Microsystems, Inc.
* Portions Copyright 2015 ForgeRock AS
*/
package org.opends.server.admin.client.spi;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyOption;
/**
* A set of properties. Instances of this class can be used as the
* core of a managed object implementation.
*/
public final class PropertySet {
/**
* Internal property implementation.
*
* @param <T>
* The type of the property.
*/
private static final class MyProperty<T> implements Property<T> {
// The active set of values.
private final SortedSet<T> activeValues;
// The definition associated with this property.
private final PropertyDefinition<T> d;
// The default set of values (read-only).
private final SortedSet<T> defaultValues;
// The pending set of values.
private final SortedSet<T> pendingValues;
/**
* Create a property with the provided sets of pre-validated
* default and active values.
*
* @param pd
* The property definition.
* @param defaultValues
* The set of default values for the property.
* @param activeValues
* The set of active values for the property.
*/
public MyProperty(PropertyDefinition<T> pd, Collection<T> defaultValues,
Collection<T> activeValues) {
this.d = pd;
SortedSet<T> sortedDefaultValues = new TreeSet<T>(pd);
sortedDefaultValues.addAll(defaultValues);
this.defaultValues = Collections
.unmodifiableSortedSet(sortedDefaultValues);
this.activeValues = new TreeSet<T>(pd);
this.activeValues.addAll(activeValues);
// Initially the pending values is the same as the active
// values.
this.pendingValues = new TreeSet<T>(this.activeValues);
}
/**
* Makes the pending values active.
*/
public void commit() {
activeValues.clear();
activeValues.addAll(pendingValues);
}
/**
* {@inheritDoc}
*/
public SortedSet<T> getActiveValues() {
return Collections.unmodifiableSortedSet(activeValues);
}
/**
* {@inheritDoc}
*/
public SortedSet<T> getDefaultValues() {
return defaultValues;
}
/**
* {@inheritDoc}
*/
public SortedSet<T> getEffectiveValues() {
SortedSet<T> values = getPendingValues();
if (values.isEmpty()) {
values = getDefaultValues();
}
return values;
}
/**
* {@inheritDoc}
*/
public SortedSet<T> getPendingValues() {
return Collections.unmodifiableSortedSet(pendingValues);
}
/**
* {@inheritDoc}
*/
public PropertyDefinition<T> getPropertyDefinition() {
return d;
}
/**
* {@inheritDoc}
*/
public boolean isEmpty() {
return pendingValues.isEmpty();
}
/**
* {@inheritDoc}
*/
public boolean isModified() {
return activeValues.size() != pendingValues.size()
|| !activeValues.containsAll(pendingValues);
}
/**
* Replace all pending values of this property with the provided
* values.
*
* @param c
* The new set of pending property values.
*/
public void setPendingValues(Collection<T> c) {
pendingValues.clear();
pendingValues.addAll(c);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return getEffectiveValues().toString();
}
/**
* {@inheritDoc}
*/
public boolean wasEmpty() {
return activeValues.isEmpty();
}
}
// The properties.
private final Map<PropertyDefinition<?>, MyProperty<?>> properties;
/**
* Creates a new empty property set.
*/
public PropertySet() {
this.properties = new HashMap<PropertyDefinition<?>, MyProperty<?>>();
}
/**
* Creates a property with the provided sets of pre-validated
* default and active values.
*
* @param <T>
* The type of the property.
* @param pd
* The property definition.
* @param defaultValues
* The set of default values for the property.
* @param activeValues
* The set of active values for the property.
*/
public <T> void addProperty(PropertyDefinition<T> pd,
Collection<T> defaultValues, Collection<T> activeValues) {
MyProperty<T> p = new MyProperty<T>(pd, defaultValues, activeValues);
properties.put(pd, p);
}
/**
* Get the property associated with the specified property
* definition.
*
* @param <T>
* The underlying type of the property.
* @param d
* The Property definition.
* @return Returns the property associated with the specified
* property definition.
* @throws IllegalArgumentException
* If this property provider does not recognise the
* requested property definition.
*/
@SuppressWarnings("unchecked")
public <T> Property<T> getProperty(PropertyDefinition<T> d)
throws IllegalArgumentException {
if (!properties.containsKey(d)) {
throw new IllegalArgumentException("Unknown property " + d.getName());
}
return (Property<T>) properties.get(d);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append('{');
for (Map.Entry<PropertyDefinition<?>, MyProperty<?>> entry : properties.entrySet()) {
builder.append(entry.getKey().getName());
builder.append('=');
builder.append(entry.getValue());
builder.append(' ');
}
builder.append('}');
return builder.toString();
}
/**
* Makes all pending values active.
*/
void commit()
{
for (MyProperty<?> p : properties.values())
{
p.commit();
}
}
/**
* Set a new pending values for the specified property.
* <p>
* See the class description for more information regarding pending values.
*
* @param <T>
* The type of the property to be modified.
* @param d
* The property to be modified.
* @param values
* A non-<code>null</code> set of new pending values for the property
* (an empty set indicates that the property should be reset to its
* default behavior). The set will not be referenced by this managed
* object.
* @throws PropertyException
* If a new pending value is deemed to be invalid according to the
* property definition, or if an attempt was made to add multiple
* pending values to a single-valued property, or if an attempt was
* made to remove a mandatory property.
* @throws IllegalArgumentException
* If the specified property definition is not associated with this
* managed object.
*/
<T> void setPropertyValues(PropertyDefinition<T> d, Collection<T> values)
throws PropertyException, IllegalArgumentException
{
MyProperty<T> property = (MyProperty<T>) getProperty(d);
if (values.size() > 1 && !d.hasOption(PropertyOption.MULTI_VALUED)) {
throw PropertyException.propertyIsSingleValuedException(d);
}
if (values.isEmpty() && d.hasOption(PropertyOption.MANDATORY)) {
// But only if there are no default values.
if (property.getDefaultValues().isEmpty()) {
throw PropertyException.propertyIsMandatoryException(d);
}
}
// Validate each value.
for (T e : values) {
if (e == null) {
throw new NullPointerException();
}
d.validateValue(e);
}
// Update the property.
property.setPendingValues(values);
}
}