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.Comparator;
035 import java.util.EnumSet;
036 import java.util.Locale;
037 import java.util.MissingResourceException;
038 import java.util.Set;
039
040 import org.opends.messages.Message;
041
042
043
044 /**
045 * An interface for querying generic property definition features.
046 * <p>
047 * Property definitions are analogous to ConfigAttributes in the
048 * current model and will play a similar role. Eventually these will
049 * replace them.
050 * <p>
051 * Implementations <b>must</b> take care to implement the various
052 * comparison methods.
053 *
054 * @param <T>
055 * The data-type of values of the property.
056 */
057 public abstract class PropertyDefinition<T> implements Comparator<T>,
058 Comparable<PropertyDefinition<?>> {
059
060 /**
061 * An interface for incrementally constructing property definitions.
062 *
063 * @param <T>
064 * The data-type of values of the property.
065 * @param <D>
066 * The type of property definition constructed by this
067 * builder.
068 */
069 protected abstract static class AbstractBuilder
070 <T, D extends PropertyDefinition<T>> {
071
072 // The administrator action.
073 private AdministratorAction adminAction;
074
075 // The default behavior provider.
076 private DefaultBehaviorProvider<T> defaultBehavior;
077
078 // The abstract managed object
079 private final AbstractManagedObjectDefinition<?, ?> definition;
080
081 // The options applicable to this definition.
082 private final EnumSet<PropertyOption> options;
083
084 // The name of this property definition.
085 private final String propertyName;
086
087
088
089 /**
090 * Create a property definition builder.
091 *
092 * @param d
093 * The managed object definition associated with this
094 * property definition.
095 * @param propertyName
096 * The property name.
097 */
098 protected AbstractBuilder(AbstractManagedObjectDefinition<?, ?> d,
099 String propertyName) {
100 this.definition = d;
101 this.propertyName = propertyName;
102 this.options = EnumSet.noneOf(PropertyOption.class);
103 this.adminAction = new AdministratorAction(AdministratorAction.Type.NONE,
104 d, propertyName);
105 this.defaultBehavior = new UndefinedDefaultBehaviorProvider<T>();
106 }
107
108
109
110 /**
111 * Construct a property definition based on the properties of this
112 * builder.
113 *
114 * @return The new property definition.
115 */
116 public final D getInstance() {
117 return buildInstance(definition, propertyName, options, adminAction,
118 defaultBehavior);
119 }
120
121
122
123 /**
124 * Set the administrator action.
125 *
126 * @param adminAction
127 * The administrator action.
128 */
129 public final void setAdministratorAction(AdministratorAction adminAction) {
130 ensureNotNull(adminAction);
131 this.adminAction = adminAction;
132 }
133
134
135
136 /**
137 * Set the default behavior provider.
138 *
139 * @param defaultBehavior
140 * The default behavior provider.
141 */
142 public final void setDefaultBehaviorProvider(
143 DefaultBehaviorProvider<T> defaultBehavior) {
144 ensureNotNull(defaultBehavior);
145 this.defaultBehavior = defaultBehavior;
146 }
147
148
149
150 /**
151 * Add a property definition option.
152 *
153 * @param option
154 * The property option.
155 */
156 public final void setOption(PropertyOption option) {
157 ensureNotNull(option);
158 options.add(option);
159 }
160
161
162
163 /**
164 * Build a property definition based on the properties of this
165 * builder.
166 *
167 * @param d
168 * The managed object definition associated with this
169 * property definition.
170 * @param propertyName
171 * The property name.
172 * @param options
173 * Options applicable to this definition.
174 * @param adminAction
175 * The administrator action.
176 * @param defaultBehavior
177 * The default behavior provider.
178 * @return The new property definition.
179 */
180 protected abstract D buildInstance(AbstractManagedObjectDefinition<?, ?> d,
181 String propertyName, EnumSet<PropertyOption> options,
182 AdministratorAction adminAction,
183 DefaultBehaviorProvider<T> defaultBehavior);
184 }
185
186 // The administrator action.
187 private final AdministratorAction adminAction;
188
189 // The default behavior provider.
190 private final DefaultBehaviorProvider<T> defaultBehavior;
191
192 // The abstract managed object
193 private final AbstractManagedObjectDefinition<?, ?> definition;
194
195 // Options applicable to this definition.
196 private final Set<PropertyOption> options;
197
198 // The property name.
199 private final String propertyName;
200
201 // The property value class.
202 private final Class<T> theClass;
203
204
205
206 /**
207 * Create a property definition.
208 *
209 * @param d
210 * The managed object definition associated with this
211 * property definition.
212 * @param theClass
213 * The property value class.
214 * @param propertyName
215 * The property name.
216 * @param options
217 * Options applicable to this definition.
218 * @param adminAction
219 * The administrator action.
220 * @param defaultBehavior
221 * The default behavior provider.
222 */
223 protected PropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
224 Class<T> theClass, String propertyName, EnumSet<PropertyOption> options,
225 AdministratorAction adminAction,
226 DefaultBehaviorProvider<T> defaultBehavior) {
227 ensureNotNull(d, theClass, propertyName);
228 ensureNotNull(options, adminAction, defaultBehavior);
229
230 this.definition = d;
231 this.theClass = theClass;
232 this.propertyName = propertyName;
233 this.options = EnumSet.copyOf(options);
234 this.adminAction = adminAction;
235 this.defaultBehavior = defaultBehavior;
236 }
237
238
239
240 /**
241 * Apply a visitor to this property definition.
242 *
243 * @param <R>
244 * The return type of the visitor's methods.
245 * @param <P>
246 * The type of the additional parameters to the visitor's
247 * methods.
248 * @param v
249 * The property definition visitor.
250 * @param p
251 * Optional additional visitor parameter.
252 * @return Returns a result as specified by the visitor.
253 */
254 public abstract <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p);
255
256
257
258 /**
259 * Apply a visitor to a property value associated with this property
260 * definition.
261 *
262 * @param <R>
263 * The return type of the visitor's methods.
264 * @param <P>
265 * The type of the additional parameters to the visitor's
266 * methods.
267 * @param v
268 * The property value visitor.
269 * @param value
270 * The property value.
271 * @param p
272 * Optional additional visitor parameter.
273 * @return Returns a result as specified by the visitor.
274 */
275 public abstract <R, P> R accept(PropertyValueVisitor<R, P> v, T value, P p);
276
277
278
279 /**
280 * Cast the provided value to the type associated with this property
281 * definition.
282 * <p>
283 * This method only casts the object to the required type; it does
284 * not validate the value once it has been cast. Subsequent
285 * validation should be performed using the method
286 * {@link #validateValue(Object)}.
287 * <p>
288 * This method guarantees the following expression is always
289 * <code>true</code>:
290 *
291 * <pre>
292 * PropertyDefinition d;
293 * x == d.cast(x);
294 * </pre>
295 *
296 * @param object
297 * The property value to be cast (can be <code>null</code>).
298 * @return Returns the property value cast to the correct type.
299 * @throws ClassCastException
300 * If the provided property value did not have the correct
301 * type.
302 */
303 public final T castValue(Object object) throws ClassCastException {
304 return theClass.cast(object);
305 }
306
307
308
309 /**
310 * Compares two property values for order. Returns a negative
311 * integer, zero, or a positive integer as the first argument is
312 * less than, equal to, or greater than the second.
313 * <p>
314 * This default implementation normalizes both values using
315 * {@link #normalizeValue(Object)} and then performs a
316 * case-sensitive string comparison.
317 *
318 * @param o1
319 * the first object to be compared.
320 * @param o2
321 * the second object to be compared.
322 * @return a negative integer, zero, or a positive integer as the
323 * first argument is less than, equal to, or greater than
324 * the second.
325 */
326 public int compare(T o1, T o2) {
327 ensureNotNull(o1, o2);
328
329 String s1 = normalizeValue(o1);
330 String s2 = normalizeValue(o2);
331
332 return s1.compareTo(s2);
333 }
334
335
336
337 /**
338 * Compares this property definition with the specified property
339 * definition for order. Returns a negative integer, zero, or a
340 * positive integer if this property definition is less than, equal
341 * to, or greater than the specified property definition.
342 * <p>
343 * The ordering must be determined first from the property name and
344 * then base on the underlying value type.
345 *
346 * @param o
347 * The reference property definition with which to compare.
348 * @return Returns a negative integer, zero, or a positive integer
349 * if this property definition is less than, equal to, or
350 * greater than the specified property definition.
351 */
352 public final int compareTo(PropertyDefinition<?> o) {
353 int rc = propertyName.compareTo(o.propertyName);
354 if (rc == 0) {
355 rc = theClass.getName().compareTo(o.theClass.getName());
356 }
357 return rc;
358 }
359
360
361
362 /**
363 * Parse and validate a string representation of a property value.
364 *
365 * @param value
366 * The property string value (must not be <code>null</code>).
367 * @return Returns the decoded property value.
368 * @throws IllegalPropertyValueStringException
369 * If the property value string is invalid.
370 */
371 public abstract T decodeValue(String value)
372 throws IllegalPropertyValueStringException;
373
374
375
376 /**
377 * Encode the provided property value into its string
378 * representation.
379 * <p>
380 * This default implementation simply returns invokes the
381 * {@link Object#toString()} method on the provided value.
382 *
383 * @param value
384 * The property value (must not be <code>null</code>).
385 * @return Returns the encoded property string value.
386 * @throws IllegalPropertyValueException
387 * If the property value is invalid.
388 */
389 public String encodeValue(T value) throws IllegalPropertyValueException {
390 ensureNotNull(value);
391
392 return value.toString();
393 }
394
395
396
397 /**
398 * Indicates whether some other object is "equal to" this
399 * property definition. This method must obey the general contract
400 * of <tt>Object.equals(Object)</tt>. Additionally, this method
401 * can return <tt>true</tt> <i>only</i> if the specified Object
402 * is also a property definition and it has the same name, as
403 * returned by {@link #getName()}, and also is deemed to be
404 * "compatible" with this property definition.
405 * Compatibility means that the two property definitions share the
406 * same underlying value type and provide similar comparator
407 * implementations.
408 *
409 * @param o
410 * The reference object with which to compare.
411 * @return Returns <code>true</code> only if the specified object
412 * is also a property definition and it has the same name
413 * and is compatible with this property definition.
414 * @see java.lang.Object#equals(java.lang.Object)
415 * @see java.lang.Object#hashCode()
416 */
417 @Override
418 public final boolean equals(Object o) {
419 if (this == o) {
420 return true;
421 } else if (o instanceof PropertyDefinition) {
422 PropertyDefinition<?> other = (PropertyDefinition<?>) o;
423 if (propertyName.equals(other.propertyName)) {
424 if (theClass.equals(other.theClass)) {
425 return true;
426 }
427 }
428 return false;
429 } else {
430 return false;
431 }
432 }
433
434
435
436 /**
437 * Get the administrator action associated with this property
438 * definition. The administrator action describes any action which
439 * the administrator must perform in order for changes to this
440 * property to take effect.
441 *
442 * @return Returns the administrator action associated with this
443 * property definition.
444 */
445 public final AdministratorAction getAdministratorAction() {
446 return adminAction;
447 }
448
449
450
451 /**
452 * Get the default behavior provider associated with this property
453 * definition.
454 *
455 * @return Returns the default behavior provider associated with
456 * this property definition.
457 */
458 public final DefaultBehaviorProvider<T> getDefaultBehaviorProvider() {
459 return defaultBehavior;
460 }
461
462
463
464 /**
465 * Gets the optional description of this property definition in the
466 * default locale.
467 *
468 * @return Returns the description of this property definition in
469 * the default locale, or <code>null</code> if there is no
470 * description.
471 */
472 public final Message getDescription() {
473 return getDescription(Locale.getDefault());
474 }
475
476
477
478 /**
479 * Gets the optional description of this property definition in the
480 * specified locale.
481 *
482 * @param locale
483 * The locale.
484 * @return Returns the description of this property definition in
485 * the specified locale, or <code>null</code> if there is
486 * no description.
487 */
488 public final Message getDescription(Locale locale) {
489 ManagedObjectDefinitionI18NResource resource =
490 ManagedObjectDefinitionI18NResource.getInstance();
491 String property = "property." + propertyName + ".description";
492 try {
493 return resource.getMessage(definition, property, locale);
494 } catch (MissingResourceException e) {
495 return null;
496 }
497 }
498
499
500
501 /**
502 * Gets the managed object definition associated with this property
503 * definition.
504 *
505 * @return Returns the managed object definition associated with
506 * this property definition.
507 */
508 public final AbstractManagedObjectDefinition<?, ?>
509 getManagedObjectDefinition() {
510 return definition;
511 }
512
513
514
515 /**
516 * Get the name of the property.
517 *
518 * @return Returns the name of the property.
519 */
520 public final String getName() {
521 return propertyName;
522 }
523
524
525
526 /**
527 * Gets the synopsis of this property definition in the default
528 * locale.
529 *
530 * @return Returns the synopsis of this property definition in the
531 * default locale.
532 */
533 public final Message getSynopsis() {
534 return getSynopsis(Locale.getDefault());
535 }
536
537
538
539 /**
540 * Gets the synopsis of this property definition in the specified
541 * locale.
542 *
543 * @param locale
544 * The locale.
545 * @return Returns the synopsis of this property definition in the
546 * specified locale.
547 */
548 public final Message getSynopsis(Locale locale) {
549 ManagedObjectDefinitionI18NResource resource =
550 ManagedObjectDefinitionI18NResource.getInstance();
551 String property = "property." + propertyName + ".synopsis";
552 return resource.getMessage(definition, property, locale);
553 }
554
555
556
557 /**
558 * Returns a hash code value for this property definition. The hash
559 * code should be derived from the property name and the type of
560 * values handled by this property definition.
561 *
562 * @return Returns the hash code value for this property definition.
563 */
564 @Override
565 public final int hashCode() {
566 int rc = 17 + propertyName.hashCode();
567 return 37 * rc + theClass.hashCode();
568 }
569
570
571
572 /**
573 * Check if the specified option is set for this property
574 * definition.
575 *
576 * @param option
577 * The option to test.
578 * @return Returns <code>true</code> if the option is set, or
579 * <code>false</code> otherwise.
580 */
581 public final boolean hasOption(PropertyOption option) {
582 return options.contains(option);
583 }
584
585
586
587 /**
588 * Get a normalized string representation of a property value. This
589 * can then be used for comparisons and for generating hash-codes.
590 * <p>
591 * This method may throw an exception if the provided value is
592 * invalid. However, applications should not assume that
593 * implementations of this method will always validate a value. This
594 * task is the responsibility of {@link #validateValue(Object)}.
595 * <p>
596 * This default implementation simply returns the string
597 * representation of the provided value. Sub-classes might want to
598 * override this method if this behavior is insufficient (for
599 * example, a string property definition might strip white-space and
600 * convert characters to lower-case).
601 *
602 * @param value
603 * The property value to be normalized.
604 * @return Returns the normalized property value.
605 * @throws IllegalPropertyValueException
606 * If the property value is invalid.
607 */
608 public String normalizeValue(T value) throws IllegalPropertyValueException {
609 ensureNotNull(value);
610
611 return encodeValue(value);
612 }
613
614
615
616 /**
617 * Returns a string representation of this property definition.
618 *
619 * @return Returns a string representation of this property
620 * definition.
621 * @see Object#toString()
622 */
623 @Override
624 public final String toString() {
625 StringBuilder builder = new StringBuilder();
626 toString(builder);
627 return builder.toString();
628 }
629
630
631
632 /**
633 * Append a string representation of the property definition to the
634 * provided string builder.
635 * <p>
636 * This simple implementation just outputs the propertyName of the
637 * property definition. Sub-classes should override this method to
638 * provide more complete string representations.
639 *
640 * @param builder
641 * The string builder where the string representation
642 * should be appended.
643 */
644 public void toString(StringBuilder builder) {
645 builder.append(propertyName);
646 }
647
648
649
650 /**
651 * Determine if the provided property value is valid according to
652 * this property definition.
653 *
654 * @param value
655 * The property value (must not be <code>null</code>).
656 * @throws IllegalPropertyValueException
657 * If the property value is invalid.
658 */
659 public abstract void validateValue(T value)
660 throws IllegalPropertyValueException;
661
662
663
664 /**
665 * Performs any run-time initialization required by this property
666 * definition. This may include resolving managed object paths and
667 * property names.
668 *
669 * @throws Exception
670 * If this property definition could not be initialized.
671 */
672 protected void initialize() throws Exception {
673 // No implementation required.
674 }
675 }