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