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 */
016
017package org.forgerock.opendj.config;
018
019import org.forgerock.util.Reject;
020
021import java.util.EnumSet;
022import java.util.Locale;
023import java.util.MissingResourceException;
024
025import org.forgerock.i18n.LocalizableMessage;
026
027/**
028 * Integer property definition.
029 * <p>
030 * All values must be zero or positive and within the lower/upper limit
031 * constraints. Support is provided for "unlimited" values. These are
032 * represented using a negative value or using the string "unlimited".
033 */
034public final class IntegerPropertyDefinition extends PropertyDefinition<Integer> {
035
036    /** String used to represent unlimited. */
037    private static final String UNLIMITED = "unlimited";
038
039    /** The lower limit of the property value. */
040    private final int lowerLimit;
041
042    /** The optional upper limit of the property value. */
043    private final Integer upperLimit;
044
045    /**
046     * Indicates whether this property allows the use of the "unlimited" value
047     * (represented using a -1 or the string "unlimited").
048     */
049    private final boolean allowUnlimited;
050
051    /**
052     * An interface for incrementally constructing integer property definitions.
053     */
054    public static final class Builder extends AbstractBuilder<Integer, IntegerPropertyDefinition> {
055
056        /** The lower limit of the property value. */
057        private int lowerLimit;
058
059        /** The optional upper limit of the property value. */
060        private Integer upperLimit;
061
062        /**
063         * Indicates whether this property allows the use of the "unlimited" value
064         * (represented using a -1 or the string "unlimited").
065         */
066        private boolean allowUnlimited;
067
068        /** Private constructor. */
069        private Builder(AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
070            super(d, propertyName);
071        }
072
073        /**
074         * Set the lower limit.
075         *
076         * @param lowerLimit
077         *            The new lower limit (must be >= 0).
078         * @throws IllegalArgumentException
079         *             If a negative lower limit was specified or the lower
080         *             limit is greater than the upper limit.
081         */
082        public final void setLowerLimit(int lowerLimit) {
083            if (lowerLimit < 0) {
084                throw new IllegalArgumentException("Negative lower limit");
085            }
086            if (upperLimit != null && lowerLimit > upperLimit) {
087                throw new IllegalArgumentException("Lower limit greater than upper limit");
088            }
089            this.lowerLimit = lowerLimit;
090        }
091
092        /**
093         * Set the upper limit.
094         *
095         * @param upperLimit
096         *            The new upper limit or <code>null</code> if there is no
097         *            upper limit.
098         */
099        public final void setUpperLimit(Integer upperLimit) {
100            if (upperLimit != null) {
101                if (upperLimit < 0) {
102                    throw new IllegalArgumentException("Negative lower limit");
103                }
104                if (lowerLimit > upperLimit) {
105                    throw new IllegalArgumentException("Lower limit greater than upper limit");
106                }
107            }
108            this.upperLimit = upperLimit;
109        }
110
111        /**
112         * Specify whether or not this property definition will allow unlimited
113         * values (default is false).
114         *
115         * @param allowUnlimited
116         *            <code>true</code> if the property will allow unlimited
117         *            values, or <code>false</code> otherwise.
118         */
119        public final void setAllowUnlimited(boolean allowUnlimited) {
120            this.allowUnlimited = allowUnlimited;
121        }
122
123        /** {@inheritDoc} */
124        @Override
125        protected IntegerPropertyDefinition buildInstance(AbstractManagedObjectDefinition<?, ?> d,
126            String propertyName, EnumSet<PropertyOption> options, AdministratorAction adminAction,
127            DefaultBehaviorProvider<Integer> defaultBehavior) {
128            return new IntegerPropertyDefinition(d, propertyName, options, adminAction, defaultBehavior, lowerLimit,
129                upperLimit, allowUnlimited);
130        }
131
132    }
133
134    /**
135     * Create an integer property definition builder.
136     *
137     * @param d
138     *            The managed object definition associated with this property
139     *            definition.
140     * @param propertyName
141     *            The property name.
142     * @return Returns the new integer property definition builder.
143     */
144    public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
145        return new Builder(d, propertyName);
146    }
147
148    /** Private constructor. */
149    private IntegerPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d, String propertyName,
150        EnumSet<PropertyOption> options, AdministratorAction adminAction,
151        DefaultBehaviorProvider<Integer> defaultBehavior, int lowerLimit, Integer upperLimit, boolean allowUnlimited) {
152        super(d, Integer.class, propertyName, options, adminAction, defaultBehavior);
153        this.lowerLimit = lowerLimit;
154        this.upperLimit = upperLimit;
155        this.allowUnlimited = allowUnlimited;
156    }
157
158    /**
159     * Get the lower limit.
160     *
161     * @return Returns the lower limit.
162     */
163    public int getLowerLimit() {
164        return lowerLimit;
165    }
166
167    /**
168     * Get the upper limit.
169     *
170     * @return Returns the upper limit or <code>null</code> if there is no upper
171     *         limit.
172     */
173    public Integer getUpperLimit() {
174        return upperLimit;
175    }
176
177    /**
178     * Gets the optional unit synopsis of this integer property definition in
179     * the default locale.
180     *
181     * @return Returns the unit synopsis of this integer property definition in
182     *         the default locale, or <code>null</code> if there is no unit
183     *         synopsis.
184     */
185    public LocalizableMessage getUnitSynopsis() {
186        return getUnitSynopsis(Locale.getDefault());
187    }
188
189    /**
190     * Gets the optional unit synopsis of this integer property definition in
191     * the specified locale.
192     *
193     * @param locale
194     *            The locale.
195     * @return Returns the unit synopsis of this integer property definition in
196     *         the specified locale, or <code>null</code> if there is no unit
197     *         synopsis.
198     */
199    public LocalizableMessage getUnitSynopsis(Locale locale) {
200        ManagedObjectDefinitionI18NResource resource = ManagedObjectDefinitionI18NResource.getInstance();
201        String property = "property." + getName() + ".syntax.integer.unit-synopsis";
202        try {
203            return resource.getMessage(getManagedObjectDefinition(), property, locale);
204        } catch (MissingResourceException e) {
205            return null;
206        }
207    }
208
209    /**
210     * Determine whether this property allows unlimited values.
211     *
212     * @return Returns <code>true</code> if this this property allows unlimited
213     *         values.
214     */
215    public boolean isAllowUnlimited() {
216        return allowUnlimited;
217    }
218
219    /** {@inheritDoc} */
220    @Override
221    public void validateValue(Integer value) {
222        Reject.ifNull(value);
223
224        if (!allowUnlimited && value < lowerLimit) {
225            throw PropertyException.illegalPropertyValueException(this, value);
226
227            // unlimited allowed
228        } else if (value >= 0 && value < lowerLimit) {
229            throw PropertyException.illegalPropertyValueException(this, value);
230        }
231
232        if (upperLimit != null && value > upperLimit) {
233            throw PropertyException.illegalPropertyValueException(this, value);
234        }
235    }
236
237    /** {@inheritDoc} */
238    @Override
239    public String encodeValue(Integer value) {
240        Reject.ifNull(value);
241
242        // Make sure that we correctly encode negative values as "unlimited".
243        if (allowUnlimited && value < 0) {
244            return UNLIMITED;
245        }
246
247        return value.toString();
248    }
249
250    /** {@inheritDoc} */
251    @Override
252    public Integer decodeValue(String value) {
253        Reject.ifNull(value);
254
255        if (allowUnlimited && UNLIMITED.equalsIgnoreCase(value.trim())) {
256            return -1;
257        }
258
259        Integer i;
260        try {
261            i = Integer.valueOf(value);
262        } catch (NumberFormatException e) {
263            throw PropertyException.illegalPropertyValueException(this, value);
264        }
265
266        try {
267            validateValue(i);
268        } catch (PropertyException e) {
269            throw PropertyException.illegalPropertyValueException(this, value);
270        }
271
272        return i;
273    }
274
275    /** {@inheritDoc} */
276    @Override
277    public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
278        return v.visitInteger(this, p);
279    }
280
281    /** {@inheritDoc} */
282    @Override
283    public <R, P> R accept(PropertyValueVisitor<R, P> v, Integer value, P p) {
284        return v.visitInteger(this, value, p);
285    }
286
287    /** {@inheritDoc} */
288    @Override
289    public void toString(StringBuilder builder) {
290        super.toString(builder);
291
292        builder.append(" lowerLimit=");
293        builder.append(lowerLimit);
294
295        if (upperLimit != null) {
296            builder.append(" upperLimit=");
297            builder.append(upperLimit);
298        }
299
300        builder.append(" allowUnlimited=");
301        builder.append(allowUnlimited);
302    }
303
304    /** {@inheritDoc} */
305    @Override
306    public int compare(Integer o1, Integer o2) {
307        return o1.compareTo(o2);
308    }
309
310}