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