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