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