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