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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.server.admin;
029    
030    
031    
032    import static org.opends.server.util.Validator.ensureNotNull;
033    
034    import java.util.EnumSet;
035    
036    
037    
038    /**
039     * Memory size property definition.
040     * <p>
041     * All memory size property values are represented in bytes using longs.
042     * <p>
043     * All values must be zero or positive and within the lower/upper limit
044     * constraints. Support is provided for "unlimited" memory sizes. These are
045     * represented using a negative memory size value or using the string
046     * "unlimited".
047     */
048    public final class SizePropertyDefinition extends PropertyDefinition<Long> {
049    
050      // String used to represent unlimited memory sizes.
051      private static final String UNLIMITED = "unlimited";
052    
053      // The lower limit of the property value in bytes.
054      private final long lowerLimit;
055    
056      // The optional upper limit of the property value in bytes.
057      private final Long upperLimit;
058    
059      // Indicates whether this property allows the use of the "unlimited" memory
060      // size value (represented using a -1L or the string "unlimited").
061      private final boolean allowUnlimited;
062    
063    
064    
065      /**
066       * An interface for incrementally constructing memory size property
067       * definitions.
068       */
069      public static class Builder extends
070          AbstractBuilder<Long, SizePropertyDefinition> {
071    
072        // The lower limit of the property value in bytes.
073        private long lowerLimit = 0L;
074    
075        // The optional upper limit of the property value in bytes.
076        private Long upperLimit = null;
077    
078        // Indicates whether this property allows the use of the "unlimited" memory
079        // size value (represented using a -1L or the string "unlimited").
080        private boolean allowUnlimited = false;
081    
082    
083    
084        // Private constructor
085        private Builder(
086            AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
087          super(d, propertyName);
088        }
089    
090    
091    
092        /**
093         * Set the lower limit in bytes.
094         *
095         * @param lowerLimit
096         *          The new lower limit (must be >= 0) in bytes.
097         * @throws IllegalArgumentException
098         *           If a negative lower limit was specified, or if the lower limit
099         *           is greater than the upper limit.
100         */
101        public final void setLowerLimit(long lowerLimit)
102            throws IllegalArgumentException {
103          if (lowerLimit < 0) {
104            throw new IllegalArgumentException("Negative lower limit");
105          }
106          if (upperLimit != null && lowerLimit > upperLimit) {
107            throw new IllegalArgumentException(
108                "Lower limit greater than upper limit");
109          }
110          this.lowerLimit = lowerLimit;
111        }
112    
113    
114    
115        /**
116         * Set the lower limit using a string representation of the limit.
117         *
118         * @param lowerLimit
119         *          The string representation of the new lower limit.
120         * @throws IllegalArgumentException
121         *           If the lower limit could not be parsed, or if a negative lower
122         *           limit was specified, or the lower limit is greater than the
123         *           upper limit.
124         */
125        public final void setLowerLimit(String lowerLimit)
126            throws IllegalArgumentException {
127          setLowerLimit(SizeUnit.parseValue(lowerLimit, SizeUnit.BYTES));
128        }
129    
130    
131    
132        /**
133         * Set the upper limit in bytes.
134         *
135         * @param upperLimit
136         *          The new upper limit in bytes or <code>null</code> if there is
137         *          no upper limit.
138         * @throws IllegalArgumentException
139         *           If the lower limit is greater than the upper limit.
140         */
141        public final void setUpperLimit(Long upperLimit)
142            throws IllegalArgumentException {
143          if (upperLimit != null) {
144            if (upperLimit < 0) {
145              throw new IllegalArgumentException("Negative upper limit");
146            }
147            if (lowerLimit > upperLimit) {
148              throw new IllegalArgumentException(
149                  "Lower limit greater than upper limit");
150            }
151          }
152          this.upperLimit = upperLimit;
153        }
154    
155    
156    
157        /**
158         * Set the upper limit using a string representation of the limit.
159         *
160         * @param upperLimit
161         *          The string representation of the new upper limit, or
162         *          <code>null</code> if there is no upper limit.
163         * @throws IllegalArgumentException
164         *           If the upper limit could not be parsed, or if the lower limit
165         *           is greater than the upper limit.
166         */
167        public final void setUpperLimit(String upperLimit)
168            throws IllegalArgumentException {
169          if (upperLimit == null) {
170            setUpperLimit((Long) null);
171          } else {
172            setUpperLimit(SizeUnit.parseValue(upperLimit, SizeUnit.BYTES));
173          }
174        }
175    
176    
177    
178        /**
179         * Specify whether or not this property definition will allow unlimited
180         * values (default is false).
181         *
182         * @param allowUnlimited
183         *          <code>true</code> if the property will allow unlimited values,
184         *          or <code>false</code> otherwise.
185         */
186        public final void setAllowUnlimited(boolean allowUnlimited) {
187          this.allowUnlimited = allowUnlimited;
188        }
189    
190    
191    
192        /**
193         * {@inheritDoc}
194         */
195        @Override
196        protected SizePropertyDefinition buildInstance(
197            AbstractManagedObjectDefinition<?, ?> d, String propertyName,
198            EnumSet<PropertyOption> options,
199            AdministratorAction adminAction,
200            DefaultBehaviorProvider<Long> defaultBehavior) {
201          return new SizePropertyDefinition(d, propertyName, options, adminAction,
202              defaultBehavior, lowerLimit, upperLimit, allowUnlimited);
203        }
204    
205      }
206    
207    
208    
209      /**
210       * Create an memory size property definition builder.
211       *
212       * @param d
213       *          The managed object definition associated with this
214       *          property definition.
215       * @param propertyName
216       *          The property name.
217       * @return Returns the new integer property definition builder.
218       */
219      public static Builder createBuilder(
220          AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
221        return new Builder(d, propertyName);
222      }
223    
224    
225    
226      // Private constructor.
227      private SizePropertyDefinition(
228          AbstractManagedObjectDefinition<?, ?> d, String propertyName,
229          EnumSet<PropertyOption> options,
230          AdministratorAction adminAction,
231          DefaultBehaviorProvider<Long> defaultBehavior, Long lowerLimit,
232          Long upperLimit, boolean allowUnlimited) {
233        super(d, Long.class, propertyName, options, adminAction,
234            defaultBehavior);
235        this.lowerLimit = lowerLimit;
236        this.upperLimit = upperLimit;
237        this.allowUnlimited = allowUnlimited;
238      }
239    
240    
241    
242      /**
243       * Get the lower limit in bytes.
244       *
245       * @return Returns the lower limit in bytes.
246       */
247      public long getLowerLimit() {
248        return lowerLimit;
249      }
250    
251    
252    
253      /**
254       * Get the upper limit in bytes.
255       *
256       * @return Returns the upper limit in bytes or <code>null</code> if there is
257       *         no upper limit.
258       */
259      public Long getUpperLimit() {
260        return upperLimit;
261      }
262    
263    
264    
265      /**
266       * Determine whether this property allows unlimited memory sizes.
267       *
268       * @return Returns <code>true</code> if this this property allows unlimited
269       *         memory sizes.
270       */
271      public boolean isAllowUnlimited() {
272        return allowUnlimited;
273      }
274    
275    
276    
277      /**
278       * {@inheritDoc}
279       */
280      @Override
281      public void validateValue(Long value) throws IllegalPropertyValueException {
282        ensureNotNull(value);
283    
284        if (!allowUnlimited && value < lowerLimit) {
285          throw new IllegalPropertyValueException(this, value);
286    
287        // unlimited allowed
288        } else if (value >= 0 && value < lowerLimit) {
289          throw new IllegalPropertyValueException(this, value);
290        }
291    
292        if ((upperLimit != null) && (value > upperLimit)) {
293          throw new IllegalPropertyValueException(this, value);
294        }
295      }
296    
297    
298    
299      /**
300       * {@inheritDoc}
301       */
302      @Override
303      public String encodeValue(Long value) throws IllegalPropertyValueException {
304        ensureNotNull(value);
305    
306        // Make sure that we correctly encode negative values as "unlimited".
307        if (allowUnlimited) {
308          if (value < 0) {
309            return UNLIMITED;
310          }
311        }
312    
313        // Encode the size value using the best-fit unit.
314        StringBuilder builder = new StringBuilder();
315        SizeUnit unit = SizeUnit.getBestFitUnitExact(value);
316    
317        // Cast to a long to remove fractional part (which should not be there
318        // anyway as the best-fit unit should result in an exact conversion).
319        builder.append((long) unit.fromBytes(value));
320        builder.append(' ');
321        builder.append(unit.toString());
322        return builder.toString();
323      }
324    
325    
326    
327      /**
328       * {@inheritDoc}
329       */
330      @Override
331      public Long decodeValue(String value)
332          throws IllegalPropertyValueStringException {
333        ensureNotNull(value);
334    
335        // First check for the special "unlimited" value when necessary.
336        if (allowUnlimited) {
337          if (value.trim().equalsIgnoreCase(UNLIMITED)) {
338            return -1L;
339          }
340        }
341    
342        // Decode the value.
343        Long i;
344        try {
345          i = SizeUnit.parseValue(value, SizeUnit.BYTES);
346        } catch (NumberFormatException e) {
347          throw new IllegalPropertyValueStringException(this, value);
348        }
349    
350        try {
351          validateValue(i);
352        } catch (IllegalPropertyValueException e) {
353          throw new IllegalPropertyValueStringException(this, value);
354        }
355        return i;
356      }
357    
358    
359    
360      /**
361       * {@inheritDoc}
362       */
363      @Override
364      public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
365        return v.visitSize(this, p);
366      }
367    
368    
369    
370      /**
371       * {@inheritDoc}
372       */
373      @Override
374      public <R, P> R accept(PropertyValueVisitor<R, P> v, Long value, P p) {
375        return v.visitSize(this, value, p);
376      }
377    
378    
379    
380      /**
381       * {@inheritDoc}
382       */
383      @Override
384      public void toString(StringBuilder builder) {
385        super.toString(builder);
386    
387        builder.append(" lowerLimit=");
388        builder.append(lowerLimit);
389    
390        if (upperLimit != null) {
391          builder.append(" upperLimit=");
392          builder.append(upperLimit);
393        }
394    
395        builder.append(" allowUnlimited=");
396        builder.append(allowUnlimited);
397    
398      }
399    
400    
401    
402      /**
403       * {@inheritDoc}
404       */
405      @Override
406      public int compare(Long o1, Long o2) {
407        return o1.compareTo(o2);
408      }
409    
410    }