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 "equal to" 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 * "compatible" 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}