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-2016 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 @Override 275 public int compare(T o1, T o2) { 276 Reject.ifNull(o1); 277 Reject.ifNull(o2); 278 279 String s1 = normalizeValue(o1); 280 String s2 = normalizeValue(o2); 281 282 return s1.compareTo(s2); 283 } 284 285 /** 286 * Compares this property definition with the specified property definition 287 * for order. Returns a negative integer, zero, or a positive integer if 288 * this property definition is less than, equal to, or greater than the 289 * specified property definition. 290 * <p> 291 * The ordering must be determined first from the property name and then 292 * base on the underlying value type. 293 * 294 * @param o 295 * The reference property definition with which to compare. 296 * @return Returns a negative integer, zero, or a positive integer if this 297 * property definition is less than, equal to, or greater than the 298 * specified property definition. 299 */ 300 @Override 301 public final int compareTo(PropertyDefinition<?> o) { 302 int rc = propertyName.compareTo(o.propertyName); 303 if (rc == 0) { 304 rc = theClass.getName().compareTo(o.theClass.getName()); 305 } 306 return rc; 307 } 308 309 /** 310 * Parse and validate a string representation of a property value. 311 * 312 * @param value 313 * The property string value (must not be <code>null</code>). 314 * @return Returns the decoded property value. 315 * @throws PropertyException 316 * If the property value string is invalid. 317 */ 318 public abstract T decodeValue(String value); 319 320 /** 321 * Encode the provided property value into its string representation. 322 * <p> 323 * This default implementation simply returns invokes the 324 * {@link Object#toString()} method on the provided value. 325 * 326 * @param value 327 * The property value (must not be <code>null</code>). 328 * @return Returns the encoded property string value. 329 * @throws PropertyException 330 * If the property value is invalid. 331 */ 332 public String encodeValue(T value) { 333 Reject.ifNull(value); 334 335 return value.toString(); 336 } 337 338 /** 339 * Indicates whether some other object is "equal to" this property 340 * definition. This method must obey the general contract of 341 * <tt>Object.equals(Object)</tt>. Additionally, this method can return 342 * <tt>true</tt> <i>only</i> if the specified Object is also a property 343 * definition and it has the same name, as returned by {@link #getName()}, 344 * and also is deemed to be "compatible" with this property 345 * definition. Compatibility means that the two property definitions share 346 * the same underlying value type and provide similar comparator 347 * implementations. 348 * 349 * @param o 350 * The reference object with which to compare. 351 * @return Returns <code>true</code> only if the specified object is also a 352 * property definition and it has the same name and is compatible 353 * with this property definition. 354 * @see java.lang.Object#equals(java.lang.Object) 355 * @see java.lang.Object#hashCode() 356 */ 357 @Override 358 public final boolean equals(Object o) { 359 if (this == o) { 360 return true; 361 } else if (o instanceof PropertyDefinition) { 362 PropertyDefinition<?> other = (PropertyDefinition<?>) o; 363 return propertyName.equals(other.propertyName) 364 && theClass.equals(other.theClass); 365 } else { 366 return false; 367 } 368 } 369 370 /** 371 * Get the administrator action associated with this property definition. 372 * The administrator action describes any action which the administrator 373 * must perform in order for changes to this property to take effect. 374 * 375 * @return Returns the administrator action associated with this property 376 * definition. 377 */ 378 public final AdministratorAction getAdministratorAction() { 379 return adminAction; 380 } 381 382 /** 383 * Get the default behavior provider associated with this property 384 * definition. 385 * 386 * @return Returns the default behavior provider associated with this 387 * property definition. 388 */ 389 public final DefaultBehaviorProvider<T> getDefaultBehaviorProvider() { 390 return defaultBehavior; 391 } 392 393 /** 394 * Gets the optional description of this property definition in the default 395 * locale. 396 * 397 * @return Returns the description of this property definition in the 398 * default locale, or <code>null</code> if there is no description. 399 */ 400 public final LocalizableMessage getDescription() { 401 return getDescription(Locale.getDefault()); 402 } 403 404 /** 405 * Gets the optional description of this property definition in the 406 * specified locale. 407 * 408 * @param locale 409 * The locale. 410 * @return Returns the description of this property definition in the 411 * specified locale, or <code>null</code> if there is no 412 * description. 413 */ 414 public final LocalizableMessage getDescription(Locale locale) { 415 ManagedObjectDefinitionI18NResource resource = ManagedObjectDefinitionI18NResource.getInstance(); 416 String property = "property." + propertyName + ".description"; 417 try { 418 return resource.getMessage(definition, property, locale); 419 } catch (MissingResourceException e) { 420 return null; 421 } 422 } 423 424 /** 425 * Gets the managed object definition associated with this property 426 * definition. 427 * 428 * @return Returns the managed object definition associated with this 429 * property definition. 430 */ 431 public final AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition() { 432 return definition; 433 } 434 435 /** 436 * Get the name of the property. 437 * 438 * @return Returns the name of the property. 439 */ 440 public final String getName() { 441 return propertyName; 442 } 443 444 /** 445 * Gets the synopsis of this property definition in the default locale. 446 * 447 * @return Returns the synopsis of this property definition in the default 448 * locale. 449 */ 450 public final LocalizableMessage getSynopsis() { 451 return getSynopsis(Locale.getDefault()); 452 } 453 454 /** 455 * Gets the synopsis of this property definition in the specified locale. 456 * 457 * @param locale 458 * The locale. 459 * @return Returns the synopsis of this property definition in the specified 460 * locale. 461 */ 462 public final LocalizableMessage getSynopsis(Locale locale) { 463 ManagedObjectDefinitionI18NResource resource = ManagedObjectDefinitionI18NResource.getInstance(); 464 String property = "property." + propertyName + ".synopsis"; 465 return resource.getMessage(definition, property, locale); 466 } 467 468 /** 469 * Returns a hash code value for this property definition. The hash code 470 * should be derived from the property name and the type of values handled 471 * by this property definition. 472 * 473 * @return Returns the hash code value for this property definition. 474 */ 475 @Override 476 public final int hashCode() { 477 int rc = 17 + propertyName.hashCode(); 478 return 37 * rc + theClass.hashCode(); 479 } 480 481 /** 482 * Check if the specified option is set for this property definition. 483 * 484 * @param option 485 * The option to test. 486 * @return Returns <code>true</code> if the option is set, or 487 * <code>false</code> otherwise. 488 */ 489 public final boolean hasOption(PropertyOption option) { 490 return options.contains(option); 491 } 492 493 /** 494 * Get a normalized string representation of a property value. This can then 495 * be used for comparisons and for generating hash-codes. 496 * <p> 497 * This method may throw an exception if the provided value is invalid. 498 * However, applications should not assume that implementations of this 499 * method will always validate a value. This task is the responsibility of 500 * {@link #validateValue(Object)}. 501 * <p> 502 * This default implementation simply returns the string representation of 503 * the provided value. Sub-classes might want to override this method if 504 * this behavior is insufficient (for example, a string property definition 505 * might strip white-space and convert characters to lower-case). 506 * 507 * @param value 508 * The property value to be normalized. 509 * @return Returns the normalized property value. 510 * @throws PropertyException 511 * If the property value is invalid. 512 */ 513 public String normalizeValue(T value) { 514 Reject.ifNull(value); 515 516 return encodeValue(value); 517 } 518 519 /** 520 * Returns a string representation of this property definition. 521 * 522 * @return Returns a string representation of this property definition. 523 * @see Object#toString() 524 */ 525 @Override 526 public final String toString() { 527 StringBuilder builder = new StringBuilder(); 528 toString(builder); 529 return builder.toString(); 530 } 531 532 /** 533 * Append a string representation of the property definition to the provided 534 * string builder. 535 * <p> 536 * This simple implementation just outputs the propertyName of the property 537 * definition. Sub-classes should override this method to provide more 538 * complete string representations. 539 * 540 * @param builder 541 * The string builder where the string representation should be 542 * appended. 543 */ 544 public void toString(StringBuilder builder) { 545 builder.append(propertyName); 546 } 547 548 /** 549 * Determine if the provided property value is valid according to this 550 * property definition. 551 * 552 * @param value 553 * The property value (must not be <code>null</code>). 554 * @throws PropertyException 555 * If the property value is invalid. 556 */ 557 public abstract void validateValue(T value); 558 559 /** 560 * Performs any run-time initialization required by this property 561 * definition. This may include resolving managed object paths and property 562 * names. 563 * 564 * @throws Exception 565 * If this property definition could not be initialized. 566 */ 567 protected void initialize() throws Exception { 568 // No implementation required. 569 } 570}