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