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.*;
033    
034    import java.util.EnumSet;
035    
036    
037    
038    /**
039     * Duration property definition.
040     * <p>
041     * A duration property definition comprises of:
042     * <ul>
043     * <li>a <i>base unit</i> - specifies the minimum granularity which
044     * can be used to specify duration property values. For example, if
045     * the base unit is in seconds then values represented in milliseconds
046     * will not be permitted. The default base unit is seconds
047     * <li>an optional <i>maximum unit</i> - specifies the biggest
048     * duration unit which can be used to specify duration property
049     * values. Values presented in units greater than this unit will not
050     * be permitted. There is no default maximum unit
051     * <li><i>lower limit</i> - specifies the smallest duration
052     * permitted by the property. The default lower limit is 0 and can
053     * never be less than 0
054     * <li>an optional <i>upper limit</i> - specifies the biggest
055     * duration permitted by the property. By default, there is no upper
056     * limit
057     * <li>support for <i>unlimited</i> durations - when permitted users
058     * can specify "unlimited" durations. These are represented using the
059     * decoded value, -1, or the encoded string value "unlimited". By
060     * default, unlimited durations are not permitted. In addition, it is
061     * not possible to define an upper limit and support unlimited values.
062     * </ul>
063     * Decoded values are represented using <code>long</code> values in
064     * the base unit defined for the duration property definition.
065     */
066    public final class DurationPropertyDefinition extends PropertyDefinition<Long> {
067    
068      // String used to represent unlimited durations.
069      private static final String UNLIMITED = "unlimited";
070    
071      // The base unit for this property definition.
072      private final DurationUnit baseUnit;
073    
074      // The optional maximum unit for this property definition.
075      private final DurationUnit maximumUnit;
076    
077      // The lower limit of the property value in milli-seconds.
078      private final long lowerLimit;
079    
080      // The optional upper limit of the property value in milli-seconds.
081      private final Long upperLimit;
082    
083      // Indicates whether this property allows the use of the "unlimited"
084      // duration value (represented using a -1L or the string
085      // "unlimited").
086      private final boolean allowUnlimited;
087    
088    
089    
090      /**
091       * An interface for incrementally constructing duration property
092       * definitions.
093       */
094      public static class Builder extends
095          AbstractBuilder<Long, DurationPropertyDefinition> {
096    
097        // The base unit for this property definition.
098        private DurationUnit baseUnit = DurationUnit.SECONDS;
099    
100        // The optional maximum unit for this property definition.
101        private DurationUnit maximumUnit = null;
102    
103        // The lower limit of the property value in milli-seconds.
104        private long lowerLimit = 0L;
105    
106        // The optional upper limit of the property value in
107        // milli-seconds.
108        private Long upperLimit = null;
109    
110        // Indicates whether this property allows the use of the
111        // "unlimited" duration value (represented using a -1L or the
112        // string "unlimited").
113        private boolean allowUnlimited = false;
114    
115    
116    
117        // Private constructor
118        private Builder(AbstractManagedObjectDefinition<?, ?> d,
119            String propertyName) {
120          super(d, propertyName);
121        }
122    
123    
124    
125        /**
126         * Set the base unit for this property definition (values
127         * including limits are specified in this unit). By default a
128         * duration property definition uses seconds.
129         *
130         * @param unit
131         *          The string representation of the base unit (must not
132         *          be <code>null</code>).
133         * @throws IllegalArgumentException
134         *           If the provided unit name did not correspond to a
135         *           known duration unit, or if the base unit is bigger
136         *           than the maximum unit.
137         */
138        public final void setBaseUnit(String unit) throws IllegalArgumentException {
139          ensureNotNull(unit);
140    
141          setBaseUnit(DurationUnit.getUnit(unit));
142        }
143    
144    
145    
146        /**
147         * Set the base unit for this property definition (values
148         * including limits are specified in this unit). By default a
149         * duration property definition uses seconds.
150         *
151         * @param unit
152         *          The base unit (must not be <code>null</code>).
153         * @throws IllegalArgumentException
154         *           If the provided base unit is bigger than the maximum
155         *           unit.
156         */
157        public final void setBaseUnit(DurationUnit unit)
158            throws IllegalArgumentException {
159          ensureNotNull(unit);
160    
161          // Make sure that the base unit is not bigger than the maximum
162          // unit.
163          if (maximumUnit != null) {
164            if (unit.getDuration() > maximumUnit.getDuration()) {
165              throw new IllegalArgumentException(
166                  "Base unit greater than maximum unit");
167            }
168          }
169    
170          this.baseUnit = unit;
171        }
172    
173    
174    
175        /**
176         * Set the maximum unit for this property definition. By default
177         * there is no maximum unit.
178         *
179         * @param unit
180         *          The string representation of the maximum unit, or
181         *          <code>null</code> if there should not be a maximum
182         *          unit.
183         * @throws IllegalArgumentException
184         *           If the provided unit name did not correspond to a
185         *           known duration unit, or if the maximum unit is
186         *           smaller than the base unit.
187         */
188        public final void setMaximumUnit(String unit)
189            throws IllegalArgumentException {
190          if (unit == null) {
191            setMaximumUnit((DurationUnit) null);
192          } else {
193            setMaximumUnit(DurationUnit.getUnit(unit));
194          }
195        }
196    
197    
198    
199        /**
200         * Set the maximum unit for this property definition. By default
201         * there is no maximum unit.
202         *
203         * @param unit
204         *          The maximum unit, or <code>null</code> if there
205         *          should not be a maximum unit.
206         * @throws IllegalArgumentException
207         *           If the provided maximum unit is smaller than the base
208         *           unit.
209         */
210        public final void setMaximumUnit(DurationUnit unit)
211            throws IllegalArgumentException {
212          if (unit != null) {
213            // Make sure that the maximum unit is not smaller than the
214            // base unit.
215            if (unit.getDuration() < baseUnit.getDuration()) {
216              throw new IllegalArgumentException(
217                  "Maximum unit smaller than base unit");
218            }
219          }
220    
221          this.maximumUnit = unit;
222        }
223    
224    
225    
226        /**
227         * Set the lower limit in milli-seconds.
228         *
229         * @param lowerLimit
230         *          The new lower limit (must be >= 0) in milli-seconds.
231         * @throws IllegalArgumentException
232         *           If a negative lower limit was specified, or the lower
233         *           limit is greater than the upper limit.
234         */
235        public final void setLowerLimit(long lowerLimit)
236            throws IllegalArgumentException {
237          if (lowerLimit < 0) {
238            throw new IllegalArgumentException("Negative lower limit");
239          }
240    
241          if (upperLimit != null && lowerLimit > upperLimit) {
242            throw new IllegalArgumentException(
243                "Lower limit greater than upper limit");
244          }
245    
246          this.lowerLimit = lowerLimit;
247        }
248    
249    
250    
251        /**
252         * Set the lower limit using a string representation of the limit.
253         * If the string does not specify a unit, the current base unit
254         * will be used.
255         *
256         * @param lowerLimit
257         *          The string representation of the new lower limit.
258         * @throws IllegalArgumentException
259         *           If the lower limit could not be parsed, or if a
260         *           negative lower limit was specified, or the lower
261         *           limit is greater than the upper limit.
262         */
263        public final void setLowerLimit(String lowerLimit)
264            throws IllegalArgumentException {
265          setLowerLimit(DurationUnit.parseValue(lowerLimit, baseUnit));
266        }
267    
268    
269    
270        /**
271         * Set the upper limit in milli-seconds.
272         *
273         * @param upperLimit
274         *          The new upper limit in milli-seconds, or
275         *          <code>null</code> if there is no upper limit.
276         * @throws IllegalArgumentException
277         *           If a negative upper limit was specified, or the lower
278         *           limit is greater than the upper limit or unlimited
279         *           durations are permitted.
280         */
281        public final void setUpperLimit(Long upperLimit)
282            throws IllegalArgumentException {
283          if (upperLimit != null) {
284            if (upperLimit < 0) {
285              throw new IllegalArgumentException("Negative upper limit");
286            }
287    
288            if (lowerLimit > upperLimit) {
289              throw new IllegalArgumentException(
290                  "Lower limit greater than upper limit");
291            }
292    
293            if (allowUnlimited) {
294              throw new IllegalArgumentException(
295                  "Upper limit specified when unlimited durations are permitted");
296            }
297          }
298    
299          this.upperLimit = upperLimit;
300        }
301    
302    
303    
304        /**
305         * Set the upper limit using a string representation of the limit.
306         * If the string does not specify a unit, the current base unit
307         * will be used.
308         *
309         * @param upperLimit
310         *          The string representation of the new upper limit, or
311         *          <code>null</code> if there is no upper limit.
312         * @throws IllegalArgumentException
313         *           If the upper limit could not be parsed, or if the
314         *           lower limit is greater than the upper limit.
315         */
316        public final void setUpperLimit(String upperLimit)
317            throws IllegalArgumentException {
318          if (upperLimit == null) {
319            setUpperLimit((Long) null);
320          } else {
321            setUpperLimit(DurationUnit.parseValue(upperLimit, baseUnit));
322          }
323        }
324    
325    
326    
327        /**
328         * Specify whether or not this property definition will allow
329         * unlimited values (default is false).
330         *
331         * @param allowUnlimited
332         *          <code>true</code> if the property will allow
333         *          unlimited values, or <code>false</code> otherwise.
334         * @throws IllegalArgumentException
335         *           If unlimited values are to be permitted but there is
336         *           an upper limit specified.
337         */
338        public final void setAllowUnlimited(boolean allowUnlimited)
339            throws IllegalArgumentException {
340          if (allowUnlimited && upperLimit != null) {
341            throw new IllegalArgumentException(
342                "Upper limit specified when unlimited durations are permitted");
343          }
344    
345          this.allowUnlimited = allowUnlimited;
346        }
347    
348    
349    
350        /**
351         * {@inheritDoc}
352         */
353        @Override
354        protected DurationPropertyDefinition buildInstance(
355            AbstractManagedObjectDefinition<?, ?> d, String propertyName,
356            EnumSet<PropertyOption> options,
357            AdministratorAction adminAction,
358            DefaultBehaviorProvider<Long> defaultBehavior) {
359          return new DurationPropertyDefinition(d, propertyName, options,
360              adminAction, defaultBehavior, baseUnit, maximumUnit, lowerLimit,
361              upperLimit, allowUnlimited);
362        }
363      }
364    
365    
366    
367      /**
368       * Create a duration property definition builder.
369       *
370       * @param d
371       *          The managed object definition associated with this
372       *          property definition.
373       * @param propertyName
374       *          The property name.
375       * @return Returns the new integer property definition builder.
376       */
377      public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d,
378          String propertyName) {
379        return new Builder(d, propertyName);
380      }
381    
382    
383    
384      // Private constructor.
385      private DurationPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
386          String propertyName, EnumSet<PropertyOption> options,
387          AdministratorAction adminAction,
388          DefaultBehaviorProvider<Long> defaultBehavior, DurationUnit baseUnit,
389          DurationUnit maximumUnit, Long lowerLimit, Long upperLimit,
390          boolean allowUnlimited) {
391        super(d, Long.class, propertyName, options, adminAction, defaultBehavior);
392        this.baseUnit = baseUnit;
393        this.maximumUnit = maximumUnit;
394        this.lowerLimit = lowerLimit;
395        this.upperLimit = upperLimit;
396        this.allowUnlimited = allowUnlimited;
397      }
398    
399    
400    
401      /**
402       * Get the base unit for this property definition (values including
403       * limits are specified in this unit).
404       *
405       * @return Returns the base unit for this property definition
406       *         (values including limits are specified in this unit).
407       */
408      public DurationUnit getBaseUnit() {
409        return baseUnit;
410      }
411    
412    
413    
414      /**
415       * Get the maximum unit for this property definition if specified.
416       *
417       * @return Returns the maximum unit for this property definition, or
418       *         <code>null</code> if there is no maximum unit.
419       */
420      public DurationUnit getMaximumUnit() {
421        return maximumUnit;
422      }
423    
424    
425    
426      /**
427       * Get the lower limit in milli-seconds.
428       *
429       * @return Returns the lower limit in milli-seconds.
430       */
431      public long getLowerLimit() {
432        return lowerLimit;
433      }
434    
435    
436    
437      /**
438       * Get the upper limit in milli-seconds.
439       *
440       * @return Returns the upper limit in milli-seconds, or
441       *         <code>null</code> if there is no upper limit.
442       */
443      public Long getUpperLimit() {
444        return upperLimit;
445      }
446    
447    
448    
449      /**
450       * Determine whether this property allows unlimited durations.
451       *
452       * @return Returns <code>true</code> if this this property allows
453       *         unlimited durations.
454       */
455      public boolean isAllowUnlimited() {
456        return allowUnlimited;
457      }
458    
459    
460    
461      /**
462       * {@inheritDoc}
463       */
464      @Override
465      public void validateValue(Long value) throws IllegalPropertyValueException {
466        ensureNotNull(value);
467    
468        long nvalue = baseUnit.toMilliSeconds(value);
469        if (!allowUnlimited && nvalue < lowerLimit) {
470          throw new IllegalPropertyValueException(this, value);
471    
472          // unlimited allowed
473        } else if (nvalue >= 0 && nvalue < lowerLimit) {
474          throw new IllegalPropertyValueException(this, value);
475        }
476    
477        if ((upperLimit != null) && (nvalue > upperLimit)) {
478          throw new IllegalPropertyValueException(this, value);
479        }
480      }
481    
482    
483    
484      /**
485       * {@inheritDoc}
486       */
487      @Override
488      public String encodeValue(Long value) throws IllegalPropertyValueException {
489        ensureNotNull(value);
490    
491        // Make sure that we correctly encode negative values as
492        // "unlimited".
493        if (allowUnlimited) {
494          if (value < 0) {
495            return UNLIMITED;
496          }
497        }
498    
499        // Encode the size value using the base unit.
500        StringBuilder builder = new StringBuilder();
501        builder.append(value);
502        builder.append(' ');
503        builder.append(baseUnit.toString());
504        return builder.toString();
505      }
506    
507    
508    
509      /**
510       * {@inheritDoc}
511       */
512      @Override
513      public Long decodeValue(String value)
514          throws IllegalPropertyValueStringException {
515        ensureNotNull(value);
516    
517        // First check for the special "unlimited" value when necessary.
518        if (allowUnlimited) {
519          if (value.trim().equalsIgnoreCase(UNLIMITED)) {
520            return -1L;
521          }
522        }
523    
524        // Parse the string representation.
525        long ms;
526        try {
527          ms = DurationUnit.parseValue(value);
528        } catch (NumberFormatException e) {
529          throw new IllegalPropertyValueStringException(this, value);
530        }
531    
532        // Check the unit is in range - values must not be more granular
533        // than the base unit.
534        if ((ms % baseUnit.getDuration()) != 0) {
535          throw new IllegalPropertyValueStringException(this, value);
536        }
537    
538        // Convert the value a long in the property's required unit.
539        Long i = (long) baseUnit.fromMilliSeconds(ms);
540        try {
541          validateValue(i);
542        } catch (IllegalPropertyValueException e) {
543          throw new IllegalPropertyValueStringException(this, value);
544        }
545        return i;
546      }
547    
548    
549    
550      /**
551       * {@inheritDoc}
552       */
553      @Override
554      public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
555        return v.visitDuration(this, p);
556      }
557    
558    
559    
560      /**
561       * {@inheritDoc}
562       */
563      @Override
564      public <R, P> R accept(PropertyValueVisitor<R, P> v, Long value, P p) {
565        return v.visitDuration(this, value, p);
566      }
567    
568    
569    
570      /**
571       * {@inheritDoc}
572       */
573      @Override
574      public void toString(StringBuilder builder) {
575        super.toString(builder);
576    
577        builder.append(" baseUnit=");
578        builder.append(baseUnit);
579    
580        if (maximumUnit != null) {
581          builder.append(" maximumUnit=");
582          builder.append(maximumUnit);
583        }
584    
585        builder.append(" lowerLimit=");
586        builder.append(lowerLimit);
587        builder.append("ms");
588    
589        if (upperLimit != null) {
590          builder.append(" upperLimit=");
591          builder.append(upperLimit);
592          builder.append("ms");
593        }
594    
595        builder.append(" allowUnlimited=");
596        builder.append(allowUnlimited);
597      }
598    
599    
600    
601      /**
602       * {@inheritDoc}
603       */
604      @Override
605      public int compare(Long o1, Long o2) {
606        return o1.compareTo(o2);
607      }
608    
609    }