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 }