001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.data.metadata.impl; 017 018import java.beans.PropertyEditor; 019import java.util.Collections; 020import java.util.Set; 021 022import org.apache.commons.lang.StringUtils; 023import org.kuali.rice.core.api.data.DataType; 024import org.kuali.rice.krad.data.metadata.DataObjectAttribute; 025import org.kuali.rice.krad.data.provider.annotation.UifDisplayHint; 026import org.kuali.rice.krad.keyvalues.KeyValuesFinder; 027 028import com.google.common.annotations.Beta; 029 030/** 031 * Base implementation class for attribute metadata for data object classes. 032 * 033 * <p> 034 * This implementation supports "chaining" for most attributes. That is, if the value for a property is defined locally, 035 * it will me used. If unset (null) it will, if there is an {@link #embeddedAttribute}, request it from that 036 * DataObjectAttribute. (This could be a recursive operation if multiple metadata providers are chained.) 037 * </p> 038 * <p> 039 * If the value is unset and there is no embedded attribute, most methods will return a non-null default value. 040 * </p> 041 * 042 * @author Kuali Rice Team (rice.collab@kuali.org) 043 */ 044public class DataObjectAttributeImpl extends MetadataCommonBase implements DataObjectAttributeInternal { 045 private static final long serialVersionUID = -5241499559388935579L; 046 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataObjectAttributeImpl.class); 047 048 protected DataObjectAttribute embeddedAttribute; 049 050 protected Class<?> owningType; 051 052 // These are temporary placeholders for the source property from which a property was inherited when 053 // it "lives" on a related object. E.g., accountType.codeAndDescription 054 // After all metadata has been imported, this information will be used to "embed" the parent 055 // DataObjectAttribute so that properties (E.g., label) are inherited from there 056 protected Class<?> inheritedFromType; 057 protected String inheritedFromAttributeName; 058 protected String inheritedFromParentAttributeName; 059 protected String displayAttributeName; 060 protected Boolean caseInsensitive; 061 protected Boolean forceUppercase; 062 protected Boolean required; 063 protected Boolean persisted; 064 protected Boolean sensitive; 065 protected Long maxLength; 066 protected Long minLength; 067 protected String validCharactersConstraintBeanName; 068 069 protected PropertyEditor propertyEditor; 070 protected KeyValuesFinder validValues; 071 protected DataType dataType; 072 protected Class<?> type; 073 074 protected Set<UifDisplayHint> displayHints; 075 076 /** 077 * {@inheritDoc} 078 */ 079 @Override 080 public String getDisplayAttributeName() { 081 if (displayAttributeName != null) { 082 return displayAttributeName; 083 } 084 085 if (embeddedAttribute != null) { 086 return embeddedAttribute.getDisplayAttributeName(); 087 } 088 089 return getName(); 090 } 091 092 /** 093 * Sets the attribute display name. 094 * 095 * @param displayAttributeName the attribute name. 096 */ 097 public void setDisplayAttributeName(String displayAttributeName) { 098 if (StringUtils.isBlank(displayAttributeName)) { 099 displayAttributeName = null; 100 } 101 102 this.displayAttributeName = displayAttributeName; 103 } 104 105 /** 106 * {@inheritDoc} 107 */ 108 @Override 109 public boolean isCaseInsensitive() { 110 if (caseInsensitive != null) { 111 return caseInsensitive; 112 } 113 114 if (embeddedAttribute != null) { 115 return embeddedAttribute.isCaseInsensitive(); 116 } 117 118 return false; 119 } 120 121 /** 122 * Sets value that determines whether attribute is case insensitive. 123 * 124 * @param caseInsensitive whether attribute is case insensitive. 125 */ 126 public void setCaseInsensitive(boolean caseInsensitive) { 127 this.caseInsensitive = caseInsensitive; 128 } 129 130 /** 131 * {@inheritDoc} 132 */ 133 @Override 134 public boolean isForceUppercase() { 135 if (forceUppercase != null) { 136 return forceUppercase; 137 } 138 139 if (embeddedAttribute != null) { 140 return embeddedAttribute.isForceUppercase(); 141 } 142 143 return false; 144 } 145 146 147 /** 148 * Determines if attribute should be forced to upper case. 149 * 150 * @param forceUppercase whether attribute should be forced to upper. 151 */ 152 public void setForceUppercase(boolean forceUppercase) { 153 this.forceUppercase = forceUppercase; 154 } 155 156 /** 157 * {@inheritDoc} 158 */ 159 @Override 160 public PropertyEditor getPropertyEditor() { 161 if (propertyEditor != null) { 162 return propertyEditor; 163 } 164 165 if (embeddedAttribute != null) { 166 return embeddedAttribute.getPropertyEditor(); 167 } 168 169 return null; 170 } 171 172 /** 173 * Sets the property editor used when loading data. 174 * 175 * @param propertyEditor determines formats when loading data. 176 */ 177 public void setPropertyEditor(PropertyEditor propertyEditor) { 178 this.propertyEditor = propertyEditor; 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public KeyValuesFinder getValidValues() { 186 if (validValues != null) { 187 return validValues; 188 } 189 190 if (embeddedAttribute != null) { 191 return embeddedAttribute.getValidValues(); 192 } 193 194 return null; 195 } 196 197 /** 198 * Sets keyValueFinder used for dropdown. 199 * 200 * @param validValues dropdown keyValueFinder. 201 */ 202 public void setValidValues(KeyValuesFinder validValues) { 203 this.validValues = validValues; 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 public DataType getDataType() { 211 if (dataType != null) { 212 return dataType; 213 } 214 215 if (embeddedAttribute != null) { 216 return embeddedAttribute.getDataType(); 217 } 218 219 return DataType.STRING; 220 } 221 222 /** 223 * Sets KRAD data type. 224 * 225 * @param dataType KRAD derived data type. 226 */ 227 public void setDataType(DataType dataType) { 228 this.dataType = dataType; 229 } 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override 235 public String toString() { 236 StringBuilder builder = new StringBuilder(); 237 builder.append("DataObjectAttribute ["); 238 builder.append("name=").append(name); 239 if (label != null) { 240 builder.append(", ").append("label=").append(label); 241 } 242 if (backingObjectName != null) { 243 builder.append(", ").append("backingObjectName=").append(backingObjectName); 244 } 245 if (dataType != null) { 246 builder.append(", ").append("dataType=").append(dataType); 247 } 248 if (type != null) { 249 builder.append(", ").append("type=").append(type.getName()); 250 } 251 if (caseInsensitive != null) { 252 builder.append(", ").append("caseInsensitive=").append(caseInsensitive); 253 } 254 if (propertyEditor != null) { 255 builder.append(", ").append("propertyEditor=").append(propertyEditor); 256 } 257 if (sensitive != null && sensitive) { 258 builder.append(", ").append("sensitive=").append(sensitive); 259 } 260 if (validValues != null) { 261 builder.append(", ").append("validValues=").append(validValues); 262 } 263 if (inheritedFromType != null) { 264 builder.append(", ").append("inheritedFromType=").append(inheritedFromType); 265 } 266 if (inheritedFromAttributeName != null) { 267 builder.append(", ").append("inheritedFromAttributeName=").append(inheritedFromAttributeName); 268 } 269 builder.append(", ").append("mergeAction=").append(mergeAction); 270 builder.append("]"); 271 return builder.toString(); 272 } 273 274 /** 275 * {@inheritDoc} 276 */ 277 @Override 278 public Long getMaxLength() { 279 if (maxLength != null) { 280 return maxLength; 281 } 282 283 if (embeddedAttribute != null) { 284 return embeddedAttribute.getMaxLength(); 285 } 286 287 return null; 288 } 289 290 /** 291 * Sets max length of attribute. 292 * 293 * @param maxLength attribute max length. 294 */ 295 public void setMaxLength(Long maxLength) { 296 this.maxLength = maxLength; 297 } 298 299 /** 300 * {@inheritDoc} 301 */ 302 @Override 303 public DataObjectAttribute getEmbeddedAttribute() { 304 return embeddedAttribute; 305 } 306 307 /** 308 * {@inheritDoc} 309 */ 310 @Override 311 public void setEmbeddedAttribute(DataObjectAttribute embeddedAttribute) { 312 // protect against embedding itself 313 if (embeddedAttribute == this) { 314 LOG.warn( 315 "ERROR!!!! Attempt to embed a DataObjectAttribute into itself. You must really want a stack overflow! Trace: ", 316 new Throwable("Throw-away Throwable for tracing purposes.")); 317 return; 318 } 319 320 this.embeddedAttribute = embeddedAttribute; 321 setEmbeddedCommonMetadata(embeddedAttribute); 322 } 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override 328 public boolean isRequired() { 329 if (required != null) { 330 return required; 331 } 332 333 if (embeddedAttribute != null) { 334 return embeddedAttribute.isRequired(); 335 } 336 337 return false; 338 } 339 340 /** 341 * Set whether attribute is required. 342 * 343 * @param required attribute required flag. 344 */ 345 public void setRequired(boolean required) { 346 this.required = required; 347 } 348 349 /** 350 * {@inheritDoc} 351 */ 352 @Override 353 @Beta 354 public String getValidCharactersConstraintBeanName() { 355 if (validCharactersConstraintBeanName != null) { 356 return validCharactersConstraintBeanName; 357 } 358 359 if (embeddedAttribute != null) { 360 return embeddedAttribute.getValidCharactersConstraintBeanName(); 361 } 362 363 return validCharactersConstraintBeanName; 364 } 365 366 /** 367 * BETA: Sets valid character constraint bean name. 368 * 369 * @param validCharactersConstraintBeanName character constraint bean name. 370 */ 371 @Beta 372 public void setValidCharactersConstraintBeanName(String validCharactersConstraintBeanName) { 373 this.validCharactersConstraintBeanName = validCharactersConstraintBeanName; 374 } 375 376 /** 377 * {@inheritDoc} 378 */ 379 @Override 380 public Class<?> getOwningType() { 381 if (owningType != null) { 382 return owningType; 383 } 384 385 if (embeddedAttribute != null) { 386 return embeddedAttribute.getOwningType(); 387 } 388 389 return null; 390 } 391 392 /** 393 * Sets the data object type to which this attribute belongs. 394 * 395 * @param owningType data object type to which this attribute belongs. 396 */ 397 public void setOwningType(Class<?> owningType) { 398 this.owningType = owningType; 399 } 400 401 /** 402 * {@inheritDoc} 403 */ 404 @Override 405 public boolean isPersisted() { 406 if (persisted != null) { 407 return persisted; 408 } 409 410 if (embeddedAttribute != null) { 411 return embeddedAttribute.isPersisted(); 412 } 413 414 return true; 415 } 416 417 /** 418 * Sets flag whether object is persisted. 419 * 420 * @param persisted flag whether object is persisted. 421 */ 422 public void setPersisted(boolean persisted) { 423 this.persisted = persisted; 424 } 425 426 /** 427 * Determines type of class. 428 * 429 */ 430 public Class<?> getType() { 431 if (type != null) { 432 return type; 433 } 434 435 return String.class; 436 } 437 438 /** 439 * Sets unknown class in order to determine type. 440 * 441 * @param javaType unknown class. 442 */ 443 public void setType(Class<?> javaType) { 444 this.type = javaType; 445 } 446 447 /** 448 * {@inheritDoc} 449 */ 450 @Override 451 public Class<?> getInheritedFromType() { 452 if (inheritedFromType != null) { 453 return inheritedFromType; 454 } 455 456 if (embeddedAttribute != null) { 457 return embeddedAttribute.getInheritedFromType(); 458 } 459 460 return null; 461 } 462 463 /** 464 * Sets unknown class to determine if inherited. 465 * 466 * @param inheritedFromType unknown class. 467 */ 468 public void setInheritedFromType(Class<?> inheritedFromType) { 469 this.inheritedFromType = inheritedFromType; 470 } 471 472 /** 473 * {@inheritDoc} 474 */ 475 @Override 476 public String getInheritedFromAttributeName() { 477 if (inheritedFromAttributeName != null) { 478 return inheritedFromAttributeName; 479 } 480 481 if (embeddedAttribute != null) { 482 return embeddedAttribute.getInheritedFromAttributeName(); 483 } 484 485 return null; 486 } 487 488 /** 489 * Sets data object name to determine if inherited. 490 * 491 * @param inheritedFromAttributeName name of attribute. 492 */ 493 public void setInheritedFromAttributeName(String inheritedFromAttributeName) { 494 this.inheritedFromAttributeName = inheritedFromAttributeName; 495 } 496 497 /** 498 * {@inheritDoc} 499 */ 500 @Override 501 public String getInheritedFromParentAttributeName() { 502 if (inheritedFromParentAttributeName != null) { 503 return inheritedFromParentAttributeName; 504 } 505 506 if (embeddedAttribute != null) { 507 return embeddedAttribute.getInheritedFromParentAttributeName(); 508 } 509 510 return null; 511 } 512 513 /** 514 * Sets parent data object name to determine if inherited. 515 * 516 * @param inheritedFromParentAttributeName name of attribute. 517 */ 518 public void setInheritedFromParentAttributeName(String inheritedFromParentAttributeName) { 519 this.inheritedFromParentAttributeName = inheritedFromParentAttributeName; 520 } 521 522 /** 523 * {@inheritDoc} 524 */ 525 @Override 526 public boolean isInherited() { 527 return getInheritedFromAttributeName() != null; 528 } 529 530 /** 531 * {@inheritDoc} 532 */ 533 @Override 534 public DataObjectAttribute getOriginalDataObjectAttribute() { 535 if (embeddedAttribute == null) { 536 return this; 537 } 538 return embeddedAttribute.getOriginalDataObjectAttribute(); 539 } 540 541 /** 542 * {@inheritDoc} 543 */ 544 @Override 545 public Long getMinLength() { 546 if (minLength != null) { 547 return minLength; 548 } 549 if (embeddedAttribute != null) { 550 return embeddedAttribute.getMinLength(); 551 } 552 return null; 553 } 554 555 /** 556 * Sets minimum length of attribute. 557 * 558 * @param minLength minimum length value. 559 */ 560 public void setMinLength(Long minLength) { 561 this.minLength = minLength; 562 } 563 564 /** 565 * {@inheritDoc} 566 */ 567 @Override 568 public boolean isSensitive() { 569 if (sensitive != null) { 570 return sensitive; 571 } 572 573 if (embeddedAttribute != null) { 574 return embeddedAttribute.isSensitive(); 575 } 576 577 return false; 578 } 579 580 /** 581 * Sets whether sensitive. 582 * 583 * @param sensitive whether attribute is sensitive. 584 */ 585 public void setSensitive(boolean sensitive) { 586 this.sensitive = sensitive; 587 } 588 589 /** 590 * {@inheritDoc} 591 */ 592 @Override 593 @Beta 594 public Set<UifDisplayHint> getDisplayHints() { 595 if (displayHints != null) { 596 return displayHints; 597 } 598 599 if (embeddedAttribute != null) { 600 return embeddedAttribute.getDisplayHints(); 601 } 602 603 return Collections.emptySet(); 604 } 605 606 /** 607 * BETA: Sets UIF display hints. 608 * 609 * @param displayHints UIF display hints. 610 */ 611 @Beta 612 public void setDisplayHints(Set<UifDisplayHint> displayHints) { 613 this.displayHints = displayHints; 614 } 615}