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 &quot;equal to&quot; 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       * &quot;compatible&quot; 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    }