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