001package org.kuali.rice.krad.service.impl; 002 003import java.beans.PropertyDescriptor; 004import java.lang.reflect.InvocationTargetException; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.Collections; 008import java.util.Date; 009import java.util.HashMap; 010import java.util.Iterator; 011import java.util.List; 012import java.util.Map; 013import java.util.TreeMap; 014 015import org.apache.commons.beanutils.PropertyUtils; 016import org.apache.commons.lang.StringUtils; 017import org.kuali.rice.core.api.config.property.ConfigurationService; 018import org.kuali.rice.core.api.criteria.OrderByField; 019import org.kuali.rice.core.api.criteria.OrderDirection; 020import org.kuali.rice.core.api.criteria.QueryByCriteria; 021import org.kuali.rice.core.api.criteria.QueryResults; 022import org.kuali.rice.core.api.mo.common.Versioned; 023import org.kuali.rice.core.api.search.SearchOperator; 024import org.kuali.rice.core.api.uif.RemotableQuickFinder; 025import org.kuali.rice.core.api.util.RiceKeyConstants; 026import org.kuali.rice.core.framework.persistence.ojb.conversion.OjbCharBooleanConversion; 027import org.kuali.rice.krad.bo.BusinessObject; 028import org.kuali.rice.krad.bo.InactivatableFromTo; 029import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension; 030import org.kuali.rice.krad.data.CompoundKey; 031import org.kuali.rice.krad.data.DataObjectService; 032import org.kuali.rice.krad.data.DataObjectWrapper; 033import org.kuali.rice.krad.data.KradDataServiceLocator; 034import org.kuali.rice.krad.data.PersistenceOption; 035import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship; 036import org.kuali.rice.krad.data.metadata.DataObjectCollection; 037import org.kuali.rice.krad.data.metadata.DataObjectMetadata; 038import org.kuali.rice.krad.data.metadata.DataObjectRelationship; 039import org.kuali.rice.krad.data.provider.annotation.ExtensionFor; 040import org.kuali.rice.krad.datadictionary.DataDictionaryEntry; 041import org.kuali.rice.krad.datadictionary.DataObjectEntry; 042import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition; 043import org.kuali.rice.krad.datadictionary.RelationshipDefinition; 044import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition; 045import org.kuali.rice.krad.document.Document; 046import org.kuali.rice.krad.exception.ValidationException; 047import org.kuali.rice.krad.lookup.LookupUtils; 048import org.kuali.rice.krad.service.DataDictionaryService; 049import org.kuali.rice.krad.service.DocumentAdHocService; 050import org.kuali.rice.krad.service.KRADServiceLocator; 051import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 052import org.kuali.rice.krad.service.KualiModuleService; 053import org.kuali.rice.krad.service.LegacyDataAdapter; 054import org.kuali.rice.krad.service.ModuleService; 055import org.kuali.rice.krad.uif.service.ViewDictionaryService; 056import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 057import org.kuali.rice.krad.util.ForeignKeyFieldsPopulationState; 058import org.kuali.rice.krad.util.GlobalVariables; 059import org.kuali.rice.krad.util.KRADConstants; 060import org.kuali.rice.krad.util.KRADPropertyConstants; 061import org.kuali.rice.krad.util.KRADUtils; 062import org.kuali.rice.krad.util.LegacyUtils; 063import org.springframework.beans.PropertyAccessorUtils; 064import org.springframework.beans.factory.annotation.Required; 065import org.springframework.dao.IncorrectResultSizeDataAccessException; 066 067/** 068 * Created by sheiksalahudeenm on 10/10/14. 069 * 070 * Overridden for fixing the issue in KRAD Transaction document/JPA which is having composite primary key. 071 * Modified method name: findByDocumentHeaderId. 072 * Changes description : Instead of passing single 'id' passing CompoundKey as a parameter. 073 */ 074public class KRADLegacyDataAdapterImpl implements LegacyDataAdapter { 075 private DataObjectService dataObjectService; 076 private LookupCriteriaGenerator lookupCriteriaGenerator; 077 078 private ConfigurationService kualiConfigurationService; 079 private KualiModuleService kualiModuleService; 080 private DataDictionaryService dataDictionaryService; 081 private ViewDictionaryService viewDictionaryService; 082 083 @Override 084 public <T> T save(T dataObject) { 085 if (dataObject instanceof Collection) { 086 Collection<Object> newList = new ArrayList<Object>(((Collection) dataObject).size()); 087 for (Object obj : (Collection<?>) dataObject) { 088 newList.add(save(obj)); 089 } 090 return (T) newList; 091 } else { 092 return dataObjectService.save(dataObject); 093 } 094 } 095 096 @Override 097 public <T> T linkAndSave(T dataObject) { 098 // This method is only used from MaintainableImpl 099 return dataObjectService.save(dataObject, PersistenceOption.LINK_KEYS); 100 } 101 102 @Override 103 public <T> T saveDocument(T document) { 104 return dataObjectService.save(document, PersistenceOption.LINK_KEYS, PersistenceOption.FLUSH); 105 } 106 107 @Override 108 public <T> T findByPrimaryKey(Class<T> clazz, Map<String, ?> primaryKeys) { 109 return dataObjectService.find(clazz, new CompoundKey(primaryKeys)); 110 } 111 112 @Override 113 public <T> T findBySinglePrimaryKey(Class<T> clazz, Object primaryKey) { 114 return dataObjectService.find(clazz, primaryKey); 115 } 116 117 @Override 118 public void delete(Object dataObject) { 119 if (dataObject instanceof Collection) { 120 for (Object dobj : (Collection) dataObject) { 121 delete(dobj); 122 } 123 } else { 124 dataObjectService.delete(dataObject); 125 } 126 } 127 128 @Override 129 public void deleteMatching(Class<?> type, Map<String, ?> fieldValues) { 130 dataObjectService.deleteMatching(type, QueryByCriteria.Builder.andAttributes(fieldValues).build()); 131 } 132 133 @Override 134 public <T> T retrieve(T dataObject) { 135 Object id = null; 136 Map<String, Object> primaryKeyValues = dataObjectService.wrap(dataObject).getPrimaryKeyValues(); 137 if (primaryKeyValues.isEmpty()) { 138 throw new IllegalArgumentException("Given data object has no primary key!"); 139 } 140 if (primaryKeyValues.size() == 1) { 141 id = primaryKeyValues.values().iterator().next(); 142 } else { 143 id = new CompoundKey(primaryKeyValues); 144 } 145 return dataObjectService.find((Class<T>) dataObject.getClass(), id); 146 } 147 148 @Override 149 public <T> Collection<T> findAll(Class<T> clazz) { 150 // just find all objects of given type without any attribute criteria 151 return findMatching(clazz, Collections.<String, Object>emptyMap()); 152 } 153 154 @Override 155 public <T> Collection<T> findMatching(Class<T> clazz, Map<String, ?> fieldValues) { 156 QueryResults<T> result = dataObjectService.findMatching(clazz, QueryByCriteria.Builder.andAttributes( 157 fieldValues).build()); 158 return result.getResults(); 159 } 160 161 @Override 162 public <T> Collection<T> findMatchingOrderBy(Class<T> clazz, Map<String, ?> fieldValues, String sortField, 163 boolean sortAscending) { 164 OrderDirection direction = sortAscending ? OrderDirection.ASCENDING : OrderDirection.DESCENDING; 165 OrderByField orderBy = OrderByField.Builder.create(sortField, direction).build(); 166 QueryResults<T> result = dataObjectService.findMatching(clazz, QueryByCriteria.Builder.andAttributes( 167 fieldValues).setOrderByFields(orderBy).build()); 168 return result.getResults(); 169 } 170 171 @Override 172 public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject) { 173 return dataObjectService.wrap(dataObject).getPrimaryKeyValues(); 174 } 175 176 @Override 177 public void retrieveNonKeyFields(Object persistableObject) { 178 List<DataObjectRelationship> relationships = dataObjectService.getMetadataRepository().getMetadata( 179 persistableObject.getClass()).getRelationships(); 180 for (DataObjectRelationship relationship : relationships) { 181 retrieveReferenceObject(persistableObject, relationship.getName()); 182 } 183 } 184 185 @Override 186 public void retrieveReferenceObject(Object persistableObject, String referenceObjectName) { 187 dataObjectService.wrap(persistableObject).fetchRelationship(referenceObjectName); 188 } 189 190 @Override 191 public void refreshAllNonUpdatingReferences(Object persistableObject) { 192 List<DataObjectRelationship> nonUpdateableRelationships = findNonUpdateableRelationships(persistableObject); 193 for (DataObjectRelationship relationship : nonUpdateableRelationships) { 194 retrieveReferenceObject(persistableObject, relationship.getName()); 195 } 196 } 197 198 protected List<DataObjectRelationship> findNonUpdateableRelationships(Object persistableObject) { 199 List<DataObjectRelationship> nonUpdateableRelationships = new ArrayList<DataObjectRelationship>(); 200 DataObjectMetadata dataObjectMetadata = dataObjectService.getMetadataRepository(). 201 getMetadata(persistableObject.getClass()); 202 if (dataObjectMetadata != null) { 203 List<DataObjectRelationship> relationships = dataObjectMetadata.getRelationships(); 204 for (DataObjectRelationship relationship : relationships) { 205 if (!relationship.isSavedWithParent()) { 206 nonUpdateableRelationships.add(relationship); 207 } 208 } 209 } 210 return nonUpdateableRelationships; 211 } 212 213 @Override 214 public boolean isProxied(Object object) { 215 // KRAD data adapter does nothing 216 return false; 217 } 218 219 @Override 220 public Object resolveProxy(Object o) { 221 // KRAD data adapter does nothing 222 return o; 223 } 224 225 // Lookup methods 226 227 @Override 228 public <T> Collection<T> findCollectionBySearchHelper(Class<T> dataObjectClass, Map<String, String> formProperties, 229 boolean unbounded, boolean allPrimaryKeyValuesPresentAndNotWildcard, Integer searchResultsLimit) { 230 return performDataObjectServiceLookup(dataObjectClass, formProperties, unbounded, 231 allPrimaryKeyValuesPresentAndNotWildcard, searchResultsLimit); 232 } 233 234 @Override 235 public <T> Collection<T> findCollectionBySearchHelper(Class<T> dataObjectClass, Map<String, String> formProperties, 236 List<String> wildcardAsLiteralPropertyNames, boolean unbounded, 237 boolean allPrimaryKeyValuesPresentAndNotWildcard, Integer searchResultsLimit) { 238 return performDataObjectServiceLookup(dataObjectClass, formProperties, wildcardAsLiteralPropertyNames, 239 unbounded, allPrimaryKeyValuesPresentAndNotWildcard, searchResultsLimit); 240 } 241 242 /** 243 * Our new DataObjectService-based lookup implementation 244 * 245 * @param dataObjectClass the dataobject class 246 * @param formProperties the incoming lookup form properties 247 * @param unbounded whether the search is unbounded 248 * @param searchResultsLimit the searchResultsLimit; null implies use of default KNS value if set for the class 249 * @param <T> the data object type 250 * @return collection of lookup results 251 */ 252 protected <T> Collection<T> performDataObjectServiceLookup(Class<T> dataObjectClass, 253 Map<String, String> formProperties, boolean unbounded, boolean allPrimaryKeyValuesPresentAndNotWildcard, 254 Integer searchResultsLimit) { 255 if (!unbounded && searchResultsLimit == null) { 256 // use KRAD LookupUtils.getSearchResultsLimit instead of KNS version. we have no LookupForm, so pass null, only the class will be used 257 //searchResultsLimit = LookupUtils.getSearchResultsLimit(example, null); 258 searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils.getSearchResultsLimit(dataObjectClass); 259 } 260 QueryByCriteria.Builder query = lookupCriteriaGenerator.generateCriteria(dataObjectClass, formProperties, 261 allPrimaryKeyValuesPresentAndNotWildcard); 262 if (!unbounded && searchResultsLimit != null) { 263 query.setMaxResults(searchResultsLimit); 264 } 265 266 Collection<T> results = dataObjectService.findMatching(dataObjectClass, query.build()).getResults(); 267 return filterCurrentDataObjects(dataObjectClass, results, formProperties); 268 } 269 270 /** 271 * Our newer DataObjectService-based lookup implementation 272 * 273 * @param dataObjectClass the dataobject class 274 * @param formProperties the incoming lookup form properties 275 * @param wildcardAsLiteralPropertyNames list of the lookup properties with wildcard characters disabled 276 * @param unbounded whether the search is unbounded 277 * @param searchResultsLimit the searchResultsLimit; null implies use of default KNS value if set for the class 278 * @param <T> the data object type 279 * @return collection of lookup results 280 */ 281 protected <T> Collection<T> performDataObjectServiceLookup(Class<T> dataObjectClass, 282 Map<String, String> formProperties, List<String> wildcardAsLiteralPropertyNames, boolean unbounded, 283 boolean allPrimaryKeyValuesPresentAndNotWildcard, Integer searchResultsLimit) { 284 if (!unbounded && searchResultsLimit == null) { 285 // use KRAD LookupUtils.getSearchResultsLimit instead of KNS version. we have no LookupForm, so pass null, only the class will be used 286 //searchResultsLimit = LookupUtils.getSearchResultsLimit(example, null); 287 searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils.getSearchResultsLimit(dataObjectClass); 288 } 289 290 QueryByCriteria.Builder query = lookupCriteriaGenerator.generateCriteria(dataObjectClass, formProperties, 291 wildcardAsLiteralPropertyNames, allPrimaryKeyValuesPresentAndNotWildcard); 292 if (!unbounded && searchResultsLimit != null) { 293 query.setMaxResults(searchResultsLimit); 294 } 295 296 Collection<T> results = dataObjectService.findMatching(dataObjectClass, query.build()).getResults(); 297 return filterCurrentDataObjects(dataObjectClass, results, formProperties); 298 } 299 300 protected <T> Collection<T> filterCurrentDataObjects(Class<T> dataObjectClass, Collection<T> unfiltered, 301 Map<String, String> formProps) { 302 if (InactivatableFromTo.class.isAssignableFrom(dataObjectClass)) { 303 Boolean currentSpecifier = lookupCriteriaCurrentSpecifier(formProps); 304 if (currentSpecifier != null) { 305 List<InactivatableFromTo> onlyCurrent = 306 KRADServiceLocator.getInactivateableFromToService().filterOutNonCurrent(new ArrayList( 307 unfiltered), new Date(LookupUtils.getActiveDateTimestampForCriteria(formProps) 308 .getTime())); 309 if (currentSpecifier) { 310 return (Collection<T>) onlyCurrent; 311 } else { 312 unfiltered.removeAll(onlyCurrent); 313 return unfiltered; 314 } 315 } 316 } 317 return unfiltered; 318 } 319 320 protected Boolean lookupCriteriaCurrentSpecifier(Map<String, String> formProps) { 321 String value = formProps.get(KRADPropertyConstants.CURRENT); 322 if (StringUtils.isNotBlank(value)) { 323 // FIXME: use something more portable than this direct OJB converter 324 String currentBooleanStr = (String) new OjbCharBooleanConversion().javaToSql(value); 325 if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(currentBooleanStr)) { 326 return Boolean.TRUE; 327 } else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals( 328 currentBooleanStr)) { 329 return Boolean.FALSE; 330 } 331 } 332 return null; 333 } 334 335 @Override 336 public <T> T findObjectBySearch(Class<T> type, Map<String, String> formProps) { 337 // This is the strictly Lookup-compatible way of constructing the criteria 338 // from a map of properties, which performs some minor logic such as only including 339 // non-blank and "writable" properties, as well as minor type coercions of string values 340 QueryByCriteria.Builder queryByCriteria = lookupCriteriaGenerator.createObjectCriteriaFromMap(type, formProps); 341 List<T> results = dataObjectService.findMatching(type, queryByCriteria.build()).getResults(); 342 if (results.isEmpty()) { 343 return null; 344 } 345 if (results.size() != 1) { 346 // this behavior is different from the legacy OJB behavior in that it throws an exception if more than 347 // one result from such a single object query 348 throw new IncorrectResultSizeDataAccessException("Incorrect number of results returned when finding object", 349 1, results.size()); 350 } 351 return results.get(0); 352 } 353 354 /** 355 * Returns whether all primary key values are specified in the lookup map and do not contain any wildcards 356 * 357 * @param boClass the bo class to lookup 358 * @param formProps the incoming form/lookup properties 359 * @return whether all primary key values are specified in the lookup map and do not contain any wildcards 360 */ 361 @Override 362 public boolean allPrimaryKeyValuesPresentAndNotWildcard(Class<?> boClass, Map<String, String> formProps) { 363 List<String> pkFields = listPrimaryKeyFieldNames(boClass); 364 Iterator<String> pkIter = pkFields.iterator(); 365 boolean returnVal = true; 366 while (returnVal && pkIter.hasNext()) { 367 String pkName = pkIter.next(); 368 String pkValue = formProps.get(pkName); 369 370 if (StringUtils.isBlank(pkValue)) { 371 returnVal = false; 372 } else { 373 for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) { 374 if (pkValue.contains(op.op())) { 375 returnVal = false; 376 break; 377 } 378 } 379 } 380 } 381 return returnVal; 382 } 383 384 // @Override 385 // public Attachment getAttachmentByNoteId(Long noteId) { 386 // // noteIdentifier is the PK of Attachment, so just look up by PK 387 // return dataObjectService.find(Attachment.class, noteId); 388 // } 389 390 @Override 391 public List<String> listPrimaryKeyFieldNames(Class<?> type) { 392 List<String> keys = Collections.emptyList(); 393 if (dataObjectService.getMetadataRepository().contains(type)) { 394 keys = dataObjectService.getMetadataRepository().getMetadata(type).getPrimaryKeyAttributeNames(); 395 } else { 396 // check the Data Dictionary for PK's of non-persisted objects 397 DataObjectEntry dataObjectEntry = dataDictionaryService.getDataDictionary().getDataObjectEntry( 398 type.getName()); 399 if (dataObjectEntry != null) { 400 List<String> pks = dataObjectEntry.getPrimaryKeys(); 401 if (pks != null) { 402 keys = pks; 403 } 404 } else { 405 ModuleService responsibleModuleService = kualiModuleService.getResponsibleModuleService(type); 406 if (responsibleModuleService != null && responsibleModuleService.isExternalizable(type)) { 407 keys = responsibleModuleService.listPrimaryKeyFieldNames(type); 408 } 409 } 410 } 411 return keys; 412 } 413 414 /** 415 * LookupServiceImpl calls BusinessObjectMetaDataService to listPrimaryKeyFieldNames. 416 * The BusinessObjectMetaDataService goes beyond the PersistenceStructureService to consult 417 * the associated ModuleService in determining the primary key field names. 418 * TODO: Do we need both listPrimaryKeyFieldNames/persistenceStructureService and 419 * listPrimaryKeyFieldNamesConsultingAllServices/businesObjectMetaDataService or 420 * can the latter superset be used for the former? 421 * 422 * @param type the data object class 423 * @return list of primary key field names, consulting persistence structure service, module service and 424 * datadictionary 425 */ 426 protected List<String> listPrimaryKeyFieldNamesConsultingAllServices(Class<?> type) { 427 List<String> keys = new ArrayList<String>(); 428 if (dataObjectService.getMetadataRepository().contains(type)) { 429 keys = dataObjectService.getMetadataRepository().getMetadata(type).getPrimaryKeyAttributeNames(); 430 } 431 return keys; 432 } 433 434 @Override 435 public Class<?> determineCollectionObjectType(Class<?> containingType, String collectionPropertyName) { 436 final Class<?> collectionObjectType; 437 if (dataObjectService.getMetadataRepository().contains(containingType)) { 438 DataObjectMetadata metadata = dataObjectService.getMetadataRepository().getMetadata(containingType); 439 DataObjectCollection collection = metadata.getCollection(collectionPropertyName); 440 if (collection == null) { 441 throw new IllegalArgumentException( 442 "Failed to locate a collection property with the given name: " + collectionPropertyName); 443 } 444 collectionObjectType = collection.getRelatedType(); 445 } else { 446 throw new IllegalArgumentException( 447 "Given containing class is not a valid data object, no metadata could be located for " 448 + containingType.getName()); 449 } 450 return collectionObjectType; 451 452 } 453 454 @Override 455 public boolean hasReference(Class<?> boClass, String referenceName) { 456 throw new UnsupportedOperationException("hasReference not valid for KRAD data operation"); 457 } 458 459 @Override 460 public boolean hasCollection(Class<?> boClass, String collectionName) { 461 throw new UnsupportedOperationException("hasCollection not valid for KRAD data operation"); 462 } 463 464 @Override 465 public boolean isExtensionAttribute(Class<?> boClass, String attributePropertyName, Class<?> propertyType) { 466 DataObjectMetadata metadata = dataObjectService.getMetadataRepository().getMetadata(boClass); 467 if (metadata != null) { 468 DataObjectRelationship relationship = metadata.getRelationship(attributePropertyName); 469 if (relationship != null) { 470 Class<?> relatedType = relationship.getRelatedType(); 471 // right now, the only way to tell if an attribute is an extension is to check this annotation, the 472 // metadata repository does not currently store any such info that we can glom onto 473 ExtensionFor annotation = relatedType.getAnnotation(ExtensionFor.class); 474 if (annotation != null) { 475 return annotation.value().equals(boClass); 476 } 477 } 478 } 479 return false; 480 } 481 482 @Override 483 public Class<?> getExtensionAttributeClass(Class<?> boClass, String attributePropertyName) { 484 DataObjectMetadata metadata = dataObjectService.getMetadataRepository().getMetadata(boClass); 485 if (metadata != null) { 486 DataObjectRelationship relationship = metadata.getRelationship(attributePropertyName); 487 if (relationship != null) { 488 return relationship.getRelatedType(); 489 } 490 } 491 return null; 492 } 493 494 @Override 495 public Map<String, ?> getPrimaryKeyFieldValuesDOMDS(Object dataObject) { 496 return dataObjectService.wrap(dataObject).getPrimaryKeyValues(); 497 } 498 499 @Override 500 public boolean equalsByPrimaryKeys(Object do1, Object do2) { 501 return dataObjectService.wrap(do1).equalsByPrimaryKey(do2); 502 } 503 504// @Override 505// public PersistableBusinessObject toPersistableBusinessObject(Object object) { 506// throw new UnsupportedOperationException("toPersistableBusinessObject not valid for KRAD data operation"); 507// } 508 509 @Override 510 public void materializeAllSubObjects(Object object) { 511 DataObjectWrapper<?> wrappedObject = dataObjectService.wrap(object); 512 513 // Using 3 as that is what the KNS version of this method did 514 wrappedObject.materializeReferencedObjectsToDepth(3); 515 } 516 517 @Override 518 /** 519 * Recursively calls getPropertyTypeChild if nested property to allow it to properly look it up 520 */ 521 public Class<?> getPropertyType(Object object, String propertyName) { 522 DataObjectWrapper wrappedObject = dataObjectService.wrap(object); 523 if (PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)) { 524 return wrappedObject.getPropertyTypeNullSafe(wrappedObject.getWrappedClass(), propertyName); 525 } 526 return wrappedObject.getPropertyType(propertyName); 527 } 528 529 @Override 530 public Object getExtension( 531 Class<?> businessObjectClass) throws InstantiationException, IllegalAccessException { 532 DataObjectMetadata metadata = dataObjectService.getMetadataRepository().getMetadata(businessObjectClass); 533 DataObjectRelationship extensionRelationship = metadata.getRelationship("extension"); 534 if (extensionRelationship != null) { 535 Class<?> extensionType = extensionRelationship.getRelatedType(); 536 return extensionType.newInstance(); 537 } 538 return null; 539 } 540 541 @Override 542 public void refreshReferenceObject(Object businessObject, String referenceObjectName) { 543 dataObjectService.wrap(businessObject).fetchRelationship(referenceObjectName); 544 } 545 546 @Override 547 public boolean isLockable(Object object) { 548 return isPersistable(object.getClass()); 549 } 550 551 @Override 552 public void verifyVersionNumber(Object dataObject) { 553 DataObjectMetadata metadata = dataObjectService.getMetadataRepository().getMetadata(dataObject.getClass()); 554 if (metadata == null) { 555 return; 556 } 557 558 if (metadata.isSupportsOptimisticLocking()) { 559 if (dataObject instanceof Versioned) { 560 Map<String, ?> keyPropertyValues = dataObjectService.wrap(dataObject).getPrimaryKeyValues(); 561 CompoundKey key = new CompoundKey(keyPropertyValues); 562 Object persistableDataObject = null; 563 if (!key.hasNullKeyValues()) { 564 persistableDataObject = dataObjectService.find(dataObject.getClass(), key); 565 } 566 // if it's null that means that this is an insert, not an update 567 if (persistableDataObject != null) { 568 Long databaseVersionNumber = ((Versioned) persistableDataObject).getVersionNumber(); 569 Long documentVersionNumber = ((Versioned) dataObject).getVersionNumber(); 570 if (databaseVersionNumber != null && !(databaseVersionNumber.equals(documentVersionNumber))) { 571 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, 572 RiceKeyConstants.ERROR_VERSION_MISMATCH); 573 throw new ValidationException( 574 "Version mismatch between the local business object and the database business object"); 575 } 576 } 577 } 578 } 579 } 580 581 @Override 582 public RemotableQuickFinder.Builder createQuickFinder(Class<?> containingClass, String attributeName) { 583 return createQuickFinderNew(containingClass, attributeName); 584 } 585 586 /** 587 * New implementation of createQuickFinder which uses the new dataObjectService.getMetadataRepository(). 588 */ 589 protected RemotableQuickFinder.Builder createQuickFinderNew(Class<?> containingClass, String attributeName) { 590 if (dataObjectService.getMetadataRepository().contains(containingClass)) { 591 592 String lookupClassName = null; 593 Map<String, String> fieldConversions = new HashMap<String, String>(); 594 Map<String, String> lookupParameters = new HashMap<String, String>(); 595 596 DataObjectMetadata metadata = dataObjectService.getMetadataRepository().getMetadata(containingClass); 597 DataObjectRelationship relationship = metadata.getRelationshipByLastAttributeInRelationship(attributeName); 598 if (relationship != null) { 599 DataObjectMetadata lookupClassMetadata = dataObjectService.getMetadataRepository().getMetadata( 600 relationship.getRelatedType()); 601 lookupClassName = lookupClassMetadata.getClass().getName(); 602 for (DataObjectAttributeRelationship attributeRelationship : relationship.getAttributeRelationships()) { 603 604 // for field conversions, we map from the child attribute name to the parent attribute name because 605 // whenever the value is returned from the object being looked up (child in this case) we want to 606 // map the result back to the corresponding attributes on the "parent" object 607 fieldConversions.put(attributeRelationship.getChildAttributeName(), 608 attributeRelationship.getParentAttributeName()); 609 610 // for lookup parameters, we need to map the other direction since we are passing parameters *from* our parent 611 // object *to* the child object 612 lookupParameters.put(attributeRelationship.getParentAttributeName(), 613 attributeRelationship.getChildAttributeName()); 614 } 615 // in the legacy implementation of this, if there was a "userVisibleIdentifierKey" defined on 616 // the relationship, it would only add the lookup parameter for that key 617 // 618 // In krad-data, we recognize that related objects have business keys and we use that information 619 // to alter the lookup parameters (only) to pass the business key field(s) to the lookup 620 if (lookupClassMetadata.hasDistinctBusinessKey()) { 621 lookupParameters.clear(); 622 for (String businessKeyAttributeName : lookupClassMetadata.getBusinessKeyAttributeNames()) { 623 lookupParameters.put(relationship.getName() + "." + businessKeyAttributeName, 624 businessKeyAttributeName); 625 } 626 } 627 } else { 628 // check for primary display attribute attribute and if match build lookup to target class using primary key fields 629 String primaryDisplayAttributeName = metadata.getPrimaryDisplayAttributeName(); 630 if (StringUtils.equals(primaryDisplayAttributeName, attributeName)) { 631 lookupClassName = containingClass.getName(); 632 List<String> primaryKeyAttributes = metadata.getPrimaryKeyAttributeNames(); 633 for (String primaryKeyAttribute : primaryKeyAttributes) { 634 fieldConversions.put(primaryKeyAttribute, primaryKeyAttribute); 635 if (!StringUtils.equals(primaryKeyAttribute, attributeName)) { 636 lookupParameters.put(primaryKeyAttribute, primaryKeyAttribute); 637 } 638 } 639 } 640 } 641 642 if (StringUtils.isNotBlank(lookupClassName)) { 643 String baseUrl = kualiConfigurationService.getPropertyValueAsString(KRADConstants.KRAD_LOOKUP_URL_KEY); 644 RemotableQuickFinder.Builder builder = RemotableQuickFinder.Builder.create(baseUrl, lookupClassName); 645 builder.setLookupParameters(lookupParameters); 646 builder.setFieldConversions(fieldConversions); 647 return builder; 648 } 649 650 } 651 return null; 652 } 653 654 @Override 655 public boolean isReferenceUpdatable(Class<?> type, String referenceName) { 656 if (dataObjectService.getMetadataRepository().contains(type)) { 657 DataObjectRelationship relationship = dataObjectService.getMetadataRepository().getMetadata(type) 658 .getRelationship(referenceName); 659 if (relationship != null) { 660 return relationship.isSavedWithParent(); 661 } 662 } 663 return false; 664 } 665 666 @SuppressWarnings("rawtypes") 667 @Override 668 public Map<String, Class> listReferenceObjectFields(Class<?> type) { 669 Map<String, Class> referenceNameToTypeMap = new HashMap<String, Class>(); 670 if (dataObjectService.getMetadataRepository().contains(type)) { 671 List<DataObjectRelationship> relationships = dataObjectService.getMetadataRepository().getMetadata(type) 672 .getRelationships(); 673 for (DataObjectRelationship rel : relationships) { 674 referenceNameToTypeMap.put(rel.getName(), rel.getRelatedType()); 675 } 676 } 677 return referenceNameToTypeMap; 678 } 679 680 @Override 681 public boolean isCollectionUpdatable(Class<?> type, String collectionName) { 682 if (dataObjectService.getMetadataRepository().contains(type)) { 683 DataObjectCollection collection = dataObjectService.getMetadataRepository().getMetadata(type).getCollection( 684 collectionName); 685 if (collection != null) { 686 return collection.isSavedWithParent(); 687 } 688 } 689 return false; 690 } 691 692 @Override 693 public Map<String, Class> listCollectionObjectTypes(Class<?> type) { 694 Map<String, Class> collectionNameToTypeMap = new HashMap<String, Class>(); 695 if (dataObjectService.getMetadataRepository().contains(type)) { 696 List<DataObjectCollection> collections = dataObjectService.getMetadataRepository().getMetadata(type) 697 .getCollections(); 698 for (DataObjectCollection coll : collections) { 699 collectionNameToTypeMap.put(coll.getName(), coll.getRelatedType()); 700 } 701 } 702 return collectionNameToTypeMap; 703 } 704 705 @Override 706 public Object getReferenceIfExists(Object bo, String referenceName) { 707 // fetches relationship if key is set and return populated value or null 708 DataObjectWrapper<Object> dataObjectWrapper = dataObjectService.wrap(bo); 709 dataObjectWrapper.fetchRelationship(referenceName); 710 return dataObjectWrapper.getPropertyValueNullSafe(referenceName); 711 } 712 713 @Override 714 public boolean allForeignKeyValuesPopulatedForReference(Object bo, String referenceName) { 715 Map<String, String> fkReferences = getForeignKeysForReference(bo.getClass(), referenceName); 716 if (fkReferences.size() > 0) { 717 DataObjectWrapper<Object> dataObjectWrapper = dataObjectService.wrap(bo); 718 719 for (String fkFieldName : fkReferences.keySet()) { 720 Object fkFieldValue = dataObjectWrapper.getForeignKeyAttributeValue(fkFieldName); 721 if (fkFieldValue == null) { 722 return false; 723 } else if (fkFieldValue instanceof CompoundKey) { 724 return !((CompoundKey) fkFieldValue).hasNullKeyValues(); 725 } else if (String.class.isAssignableFrom(fkFieldValue.getClass())) { 726 if (StringUtils.isBlank((String) fkFieldValue)) { 727 return false; 728 } 729 } 730 } 731 } 732 733 return true; 734 } 735 736 /** 737 * gets the relationship that the attribute represents on the class 738 * 739 * @param c - the class to which the attribute belongs 740 * @param attributeName - property name for the attribute 741 * @return a relationship definition for the attribute 742 */ 743 @Override 744 public RelationshipDefinition getDictionaryRelationship(Class<?> c, String attributeName) { 745 DataDictionaryEntry entryBase = 746 KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry( 747 c.getName()); 748 if (entryBase == null) { 749 return null; 750 } 751 752 RelationshipDefinition relationship = null; 753 754 List<RelationshipDefinition> ddRelationships = entryBase.getRelationships(); 755 756 int minKeys = Integer.MAX_VALUE; 757 for (RelationshipDefinition def : ddRelationships) { 758 // favor key sizes of 1 first 759 if (def.getPrimitiveAttributes().size() == 1) { 760 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) { 761 if (primitive.getSourceName().equals(attributeName) || def.getObjectAttributeName().equals( 762 attributeName)) { 763 relationship = def; 764 minKeys = 1; 765 break; 766 } 767 } 768 } else if (def.getPrimitiveAttributes().size() < minKeys) { 769 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) { 770 if (primitive.getSourceName().equals(attributeName) || def.getObjectAttributeName().equals( 771 attributeName)) { 772 relationship = def; 773 minKeys = def.getPrimitiveAttributes().size(); 774 break; 775 } 776 } 777 } 778 } 779 780 // check the support attributes 781 if (relationship == null) { 782 for (RelationshipDefinition def : ddRelationships) { 783 if (def.hasIdentifier()) { 784 if (def.getIdentifier().getSourceName().equals(attributeName)) { 785 relationship = def; 786 } 787 } 788 } 789 } 790 791 return relationship; 792 } 793 794 /** 795 * @see org.kuali.rice.krad.service.LegacyDataAdapter 796 */ 797 @Override 798 public String getTitleAttribute(Class<?> dataObjectClass) { 799 String titleAttribute = null; 800 DataObjectEntry entry = getDataObjectEntry(dataObjectClass); 801 if (entry != null) { 802 titleAttribute = entry.getTitleAttribute(); 803 } 804 return titleAttribute; 805 } 806 807 /** 808 * @param dataObjectClass 809 * @return DataObjectEntry for the given dataObjectClass, or null if 810 * there is none 811 * @throws IllegalArgumentException if the given Class is null 812 */ 813 protected DataObjectEntry getDataObjectEntry(Class<?> dataObjectClass) { 814 if (dataObjectClass == null) { 815 throw new IllegalArgumentException("invalid (null) dataObjectClass"); 816 } 817 818 DataObjectEntry entry = dataDictionaryService.getDataDictionary().getDataObjectEntry(dataObjectClass.getName()); 819 820 return entry; 821 } 822 823 /** 824 * @see org.kuali.rice.krad.service.LegacyDataAdapter#areNotesSupported(Class) 825 */ 826 @Override 827 public boolean areNotesSupported(Class<?> dataObjectClass) { 828 boolean hasNotesSupport = false; 829 830 DataObjectEntry entry = getDataObjectEntry(dataObjectClass); 831 if (entry != null) { 832 hasNotesSupport = entry.isBoNotesEnabled(); 833 } 834 835 return hasNotesSupport; 836 } 837 838 /** 839 * Grabs primary key fields and sorts them if sort field names is true 840 * 841 * @param dataObject 842 * @param sortFieldNames 843 * @return Map of sorted primary key field values 844 */ 845 public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject, boolean sortFieldNames) { 846 Map<String, Object> keyFieldValues = (Map<String, Object>) getPrimaryKeyFieldValues(dataObject); 847 if (sortFieldNames) { 848 Map<String, Object> sortedKeyFieldValues = new TreeMap<String, Object>(); 849 sortedKeyFieldValues.putAll(keyFieldValues); 850 return sortedKeyFieldValues; 851 } 852 return keyFieldValues; 853 } 854 855 /** 856 * @see org.kuali.rice.krad.service.DataObjectMetaDataService#getDataObjectIdentifierString 857 */ 858 @Override 859 public String getDataObjectIdentifierString(Object dataObject) { 860 String identifierString = ""; 861 862 if (dataObject == null) { 863 identifierString = "Null"; 864 return identifierString; 865 } 866 867 Class<?> dataObjectClass = dataObject.getClass(); 868 // build identifier string from primary key values 869 Map<String, ?> primaryKeyFieldValues = getPrimaryKeyFieldValues(dataObject, true); 870 for (Map.Entry<String, ?> primaryKeyValue : primaryKeyFieldValues.entrySet()) { 871 if (primaryKeyValue.getValue() == null) { 872 identifierString += "Null"; 873 } else { 874 identifierString += primaryKeyValue.getValue(); 875 } 876 identifierString += ":"; 877 } 878 return StringUtils.removeEnd(identifierString, ":"); 879 } 880 881 @Override 882 public Class<?> getInquiryObjectClassIfNotTitle(Object dataObject, String propertyName) { 883 DataObjectMetadata objectMetadata = 884 KRADServiceLocator.getDataObjectService().getMetadataRepository().getMetadata(dataObject.getClass()); 885 if (objectMetadata != null) { 886 DataObjectRelationship dataObjectRelationship = 887 objectMetadata.getRelationship(propertyName); 888 if (dataObjectRelationship != null) { 889 return dataObjectRelationship.getRelatedType(); 890 } 891 } 892 return null; 893 } 894 895 @Override 896 public Map<String, String> getInquiryParameters(Object dataObject, List<String> keys, String propertyName) { 897 Map<String, String> inquiryParameters = new HashMap<String, String>(); 898 org.kuali.rice.krad.data.metadata.DataObjectRelationship dataObjectRelationship = null; 899 900 DataObjectMetadata objectMetadata = 901 KRADServiceLocator.getDataObjectService().getMetadataRepository().getMetadata(dataObject.getClass()); 902 903 if (objectMetadata != null) { 904 dataObjectRelationship = objectMetadata.getRelationshipByLastAttributeInRelationship(propertyName); 905 } 906 907 for (String keyName : keys) { 908 String keyConversion = keyName; 909 if (dataObjectRelationship != null) { 910 keyConversion = dataObjectRelationship.getParentAttributeNameRelatedToChildAttributeName(keyName); 911 } else if (PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)) { 912 String nestedAttributePrefix = KRADUtils.getNestedAttributePrefix(propertyName); 913 keyConversion = nestedAttributePrefix + "." + keyName; 914 } 915 inquiryParameters.put(keyConversion, keyName); 916 } 917 return inquiryParameters; 918 } 919 920 @Override 921 public boolean hasLocalLookup(Class<?> dataObjectClass) { 922 return viewDictionaryService.isLookupable(dataObjectClass); 923 } 924 925 @Override 926 public boolean hasLocalInquiry(Class<?> dataObjectClass) { 927 return viewDictionaryService.isInquirable(dataObjectClass); 928 } 929 930 @Override 931 public org.kuali.rice.krad.bo.DataObjectRelationship getDataObjectRelationship(Object dataObject, 932 Class<?> dataObjectClass, String attributeName, String attributePrefix, boolean keysOnly, 933 boolean supportsLookup, boolean supportsInquiry) { 934 RelationshipDefinition ddReference = getDictionaryRelationship(dataObjectClass, attributeName); 935 936 org.kuali.rice.krad.bo.DataObjectRelationship relationship = null; 937 DataObjectAttributeRelationship rel = null; 938 if (PropertyAccessorUtils.isNestedOrIndexedProperty(attributeName)) { 939 if (ddReference != null) { 940 if (classHasSupportedFeatures(ddReference.getTargetClass(), supportsLookup, supportsInquiry)) { 941 relationship = populateRelationshipFromDictionaryReference(dataObjectClass, ddReference, 942 attributePrefix, keysOnly); 943 944 return relationship; 945 } 946 } 947 948 if (dataObject == null) { 949 try { 950 dataObject = KRADUtils.createNewObjectFromClass(dataObjectClass); 951 } catch (RuntimeException e) { 952 // found interface or abstract class, just swallow exception and return a null relationship 953 return null; 954 } 955 } 956 957 // recurse down to the next object to find the relationship 958 int nextObjectIndex = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(attributeName); 959 if (nextObjectIndex == StringUtils.INDEX_NOT_FOUND) { 960 nextObjectIndex = attributeName.length(); 961 } 962 String localPrefix = StringUtils.substring(attributeName, 0, nextObjectIndex); 963 String localAttributeName = StringUtils.substring(attributeName, nextObjectIndex + 1); 964 Object nestedObject = ObjectPropertyUtils.getPropertyValue(dataObject, localPrefix); 965 Class<?> nestedClass = null; 966 if (nestedObject == null) { 967 nestedClass = ObjectPropertyUtils.getPropertyType(dataObject, localPrefix); 968 } else { 969 nestedClass = nestedObject.getClass(); 970 } 971 972 String fullPrefix = localPrefix; 973 if (StringUtils.isNotBlank(attributePrefix)) { 974 fullPrefix = attributePrefix + "." + localPrefix; 975 } 976 977 relationship = getDataObjectRelationship(nestedObject, nestedClass, localAttributeName, fullPrefix, 978 keysOnly, supportsLookup, supportsInquiry); 979 980 return relationship; 981 } 982 983 // non-nested reference, get persistence relationships first 984 int maxSize = Integer.MAX_VALUE; 985 986 if (isPersistable(dataObjectClass)) { 987 DataObjectMetadata metadata = dataObjectService.getMetadataRepository().getMetadata(dataObjectClass); 988 DataObjectRelationship dataObjectRelationship = metadata.getRelationship(attributeName); 989 990 if (dataObjectRelationship != null) { 991 List<DataObjectAttributeRelationship> attributeRelationships = 992 dataObjectRelationship.getAttributeRelationships(); 993 for (DataObjectAttributeRelationship dataObjectAttributeRelationship : attributeRelationships) { 994 if (classHasSupportedFeatures(dataObjectRelationship.getRelatedType(), supportsLookup, 995 supportsInquiry)) { 996 maxSize = attributeRelationships.size(); 997 relationship = transformToDeprecatedDataObjectRelationship(dataObjectClass, attributeName, 998 attributePrefix, dataObjectRelationship.getRelatedType(), 999 dataObjectAttributeRelationship); 1000 1001 break; 1002 } 1003 } 1004 } 1005 1006 } else { 1007 ModuleService moduleService = kualiModuleService.getResponsibleModuleService(dataObjectClass); 1008 if (moduleService != null && moduleService.isExternalizable(dataObjectClass)) { 1009 relationship = getRelationshipMetadata(dataObjectClass, attributeName, attributePrefix); 1010 if ((relationship != null) && classHasSupportedFeatures(relationship.getRelatedClass(), supportsLookup, 1011 supportsInquiry)) { 1012 return relationship; 1013 } else { 1014 return null; 1015 } 1016 } 1017 } 1018 1019 if (ddReference != null && ddReference.getPrimitiveAttributes().size() < maxSize) { 1020 if (classHasSupportedFeatures(ddReference.getTargetClass(), supportsLookup, supportsInquiry)) { 1021 relationship = populateRelationshipFromDictionaryReference(dataObjectClass, ddReference, null, 1022 keysOnly); 1023 } 1024 } 1025 return relationship; 1026 } 1027 1028 protected org.kuali.rice.krad.bo.DataObjectRelationship transformToDeprecatedDataObjectRelationship( 1029 Class<?> dataObjectClass, String attributeName, String attributePrefix, Class<?> relatedObjectClass, 1030 DataObjectAttributeRelationship relationship) { 1031 org.kuali.rice.krad.bo.DataObjectRelationship rel = new org.kuali.rice.krad.bo.DataObjectRelationship( 1032 dataObjectClass, attributeName, relatedObjectClass); 1033 if (StringUtils.isBlank(attributePrefix)) { 1034 rel.getParentToChildReferences().put(relationship.getParentAttributeName(), 1035 relationship.getChildAttributeName()); 1036 } else { 1037 rel.getParentToChildReferences().put(attributePrefix + "." + relationship.getParentAttributeName(), 1038 relationship.getChildAttributeName()); 1039 } 1040 1041 return rel; 1042 } 1043 1044 protected org.kuali.rice.krad.bo.DataObjectRelationship populateRelationshipFromDictionaryReference( 1045 Class<?> dataObjectClass, RelationshipDefinition ddReference, String attributePrefix, boolean keysOnly) { 1046 org.kuali.rice.krad.bo.DataObjectRelationship relationship = new org.kuali.rice.krad.bo.DataObjectRelationship( 1047 dataObjectClass, ddReference.getObjectAttributeName(), ddReference.getTargetClass()); 1048 1049 for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) { 1050 if (StringUtils.isNotBlank(attributePrefix)) { 1051 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(), 1052 def.getTargetName()); 1053 } else { 1054 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 1055 } 1056 } 1057 1058 if (!keysOnly) { 1059 for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) { 1060 if (StringUtils.isNotBlank(attributePrefix)) { 1061 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(), 1062 def.getTargetName()); 1063 if (def.isIdentifier()) { 1064 relationship.setUserVisibleIdentifierKey(attributePrefix + "." + def.getSourceName()); 1065 } 1066 } else { 1067 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 1068 if (def.isIdentifier()) { 1069 relationship.setUserVisibleIdentifierKey(def.getSourceName()); 1070 } 1071 } 1072 } 1073 } 1074 1075 return relationship; 1076 } 1077 1078 @Override 1079 public boolean isPersistable(Class<?> dataObjectClass) { 1080 return dataObjectService.getMetadataRepository().contains(dataObjectClass); 1081 } 1082 1083 protected org.kuali.rice.krad.bo.DataObjectRelationship getRelationshipMetadata(Class<?> dataObjectClass, 1084 String attributeName, String attributePrefix) { 1085 1086 RelationshipDefinition relationshipDefinition = getDictionaryRelationship(dataObjectClass, attributeName); 1087 if (relationshipDefinition == null) { 1088 return null; 1089 } 1090 1091 org.kuali.rice.krad.bo.DataObjectRelationship dataObjectRelationship = 1092 new org.kuali.rice.krad.bo.DataObjectRelationship(relationshipDefinition.getSourceClass(), 1093 relationshipDefinition.getObjectAttributeName(), relationshipDefinition.getTargetClass()); 1094 1095 if (!StringUtils.isEmpty(attributePrefix)) { 1096 attributePrefix += "."; 1097 } 1098 1099 List<PrimitiveAttributeDefinition> primitives = relationshipDefinition.getPrimitiveAttributes(); 1100 for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitives) { 1101 dataObjectRelationship.getParentToChildReferences().put( 1102 attributePrefix + primitiveAttributeDefinition.getSourceName(), 1103 primitiveAttributeDefinition.getTargetName()); 1104 } 1105 1106 return dataObjectRelationship; 1107 } 1108 1109 protected boolean classHasSupportedFeatures(Class relationshipClass, boolean supportsLookup, 1110 boolean supportsInquiry) { 1111 boolean hasSupportedFeatures = true; 1112 if (supportsLookup && !getViewDictionaryService().isLookupable(relationshipClass)) { 1113 hasSupportedFeatures = false; 1114 } 1115 if (supportsInquiry && !getViewDictionaryService().isInquirable(relationshipClass)) { 1116 hasSupportedFeatures = false; 1117 } 1118 1119 return hasSupportedFeatures; 1120 } 1121 1122 @Override 1123 public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(Object dataObject, String referenceName) { 1124 DataObjectWrapper<Object> dataObjectWrapper = dataObjectService.wrap(dataObject); 1125 return new ForeignKeyFieldsPopulationState(dataObjectWrapper.areAllPrimaryKeyAttributesPopulated(), 1126 dataObjectWrapper.areAnyPrimaryKeyAttributesPopulated(), 1127 dataObjectWrapper.getUnpopulatedPrimaryKeyAttributeNames()); 1128 } 1129 1130 @Override 1131 public Map<String, String> getForeignKeysForReference(Class<?> clazz, String attributeName) { 1132 if (dataObjectService.getMetadataRepository().contains(clazz)) { 1133 DataObjectRelationship relationship = dataObjectService.getMetadataRepository().getMetadata(clazz) 1134 .getRelationship(attributeName); 1135 List<DataObjectAttributeRelationship> attributeRelationships = relationship.getAttributeRelationships(); 1136 Map<String, String> parentChildKeyRelationships = new HashMap<String, String>( 1137 attributeRelationships.size()); 1138 for (DataObjectAttributeRelationship doar : attributeRelationships) { 1139 parentChildKeyRelationships.put(doar.getParentAttributeName(), doar.getChildAttributeName()); 1140 } 1141 return parentChildKeyRelationships; 1142 } 1143 return Collections.emptyMap(); 1144 } 1145 1146 @Override 1147 public void setObjectPropertyDeep(Object bo, String propertyName, Class type, 1148 Object propertyValue) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 1149 DataObjectWrapper<Object> dataObjectWrapper = dataObjectService.wrap(bo); 1150 // Base return cases to avoid null pointers & infinite loops 1151 if (KRADUtils.isNull(bo) || !PropertyUtils.isReadable(bo, propertyName) || (propertyValue != null 1152 && propertyValue.equals(dataObjectWrapper.getPropertyValueNullSafe(propertyName))) || (type != null 1153 && !type.equals(KRADUtils.easyGetPropertyType(bo, propertyName)))) { 1154 return; 1155 } 1156 // Set the property in the BO 1157 KRADUtils.setObjectProperty(bo, propertyName, type, propertyValue); 1158 1159 // Now drill down and check nested BOs and BO lists 1160 PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass()); 1161 for (int i = 0; i < propertyDescriptors.length; i++) { 1162 1163 PropertyDescriptor propertyDescriptor = propertyDescriptors[i]; 1164 1165 // Business Objects 1166 if (propertyDescriptor.getPropertyType() != null && (BusinessObject.class).isAssignableFrom( 1167 propertyDescriptor.getPropertyType()) && PropertyUtils.isReadable(bo, 1168 propertyDescriptor.getName())) { 1169 Object nestedBo = dataObjectWrapper.getPropertyValueNullSafe(propertyDescriptor.getName()); 1170 if (nestedBo instanceof BusinessObject) { 1171 setObjectPropertyDeep(nestedBo, propertyName, type, propertyValue); 1172 } 1173 } 1174 1175 // Lists 1176 else if (propertyDescriptor.getPropertyType() != null && (List.class).isAssignableFrom( 1177 propertyDescriptor.getPropertyType()) && dataObjectWrapper.getPropertyValueNullSafe( 1178 propertyDescriptor.getName()) != null) { 1179 1180 List propertyList = (List) dataObjectWrapper.getPropertyValueNullSafe(propertyDescriptor.getName()); 1181 for (Object listedBo : propertyList) { 1182 if (listedBo != null && listedBo instanceof BusinessObject) { 1183 setObjectPropertyDeep(listedBo, propertyName, type, propertyValue); 1184 } 1185 } // end for 1186 } 1187 } // end for 1188 } 1189 1190 @Override 1191 public boolean hasPrimaryKeyFieldValues(Object dataObject) { 1192 DataObjectWrapper<Object> dataObjectWrapper = dataObjectService.wrap(dataObject); 1193 return dataObjectWrapper.areAllPrimaryKeyAttributesPopulated(); 1194 } 1195 1196 @Override 1197 public Class materializeClassForProxiedObject(Object object) { 1198 if (object == null) { 1199 return null; 1200 } 1201 if (LegacyUtils.isKradDataManaged(object.getClass())) { 1202 Object o = resolveProxy(object); 1203 if (o != null) { 1204 return o.getClass(); 1205 } 1206 } 1207 return object.getClass(); 1208 } 1209 1210 @Override 1211 public Object getNestedValue(Object bo, String fieldName) { 1212 return KradDataServiceLocator.getDataObjectService().wrap(bo).getPropertyValueNullSafe(fieldName); 1213 } 1214 1215 @Override 1216 public Object createNewObjectFromClass(Class clazz) { 1217 if (clazz == null) { 1218 throw new IllegalArgumentException("Class was passed in as null"); 1219 } 1220 1221 Object object = null; 1222 1223 try { 1224 object = clazz.newInstance(); 1225 } catch (InstantiationException e) { 1226 throw new RuntimeException(e); 1227 } catch (IllegalAccessException e) { 1228 throw new RuntimeException(e); 1229 } 1230 1231 return object; 1232 } 1233 1234 @Override 1235 public boolean isNull(Object object) { 1236 return object == null; 1237 } 1238 1239 @Override 1240 public void setObjectProperty(Object bo, String propertyName, Class propertyType, 1241 Object propertyValue) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 1242 PropertyUtils.setNestedProperty(bo, propertyName, propertyValue); 1243 } 1244 1245 @Override 1246 public <T extends Document> T findByDocumentHeaderId(Class<T> documentClass, String id) { 1247 /* Modified by 'Sheik Salahudeen' for fixing issue in KRAD Transaction Document/JPA which is having composite primary key 1248 * Passing CompoundKey instead of single id*/ 1249 Map<String,Object> parameterMap = new HashMap<>(); 1250 parameterMap.put("documentNumber",id); 1251 T document = KRADServiceLocator.getDataObjectService().find(documentClass, new CompoundKey(parameterMap)); 1252 // original KNS code always did this addAdHocs nonsense, so we'll do the same to preserve behavior 1253 ((DocumentAdHocService) KRADServiceLocatorWeb.getService("documentAdHocService")).addAdHocs(document); 1254 return document; 1255 } 1256 1257 @Override 1258 public <T extends Document> List<T> findByDocumentHeaderIds(Class<T> documentClass, List<String> ids) { 1259 List<T> documents = new ArrayList<T>(); 1260 for (String id : ids) { 1261 documents.add(findByDocumentHeaderId(documentClass, id)); 1262 } 1263 return documents; 1264 } 1265 1266 @Required 1267 public void setDataObjectService(DataObjectService dataObjectService) { 1268 this.dataObjectService = dataObjectService; 1269 } 1270 1271 @Required 1272 public void setLookupCriteriaGenerator(LookupCriteriaGenerator lookupCriteriaGenerator) { 1273 this.lookupCriteriaGenerator = lookupCriteriaGenerator; 1274 } 1275 1276 @Required 1277 public void setKualiConfigurationService(ConfigurationService kualiConfigurationService) { 1278 this.kualiConfigurationService = kualiConfigurationService; 1279 } 1280 1281 @Required 1282 public void setKualiModuleService(KualiModuleService kualiModuleService) { 1283 this.kualiModuleService = kualiModuleService; 1284 } 1285 1286 @Required 1287 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 1288 this.dataDictionaryService = dataDictionaryService; 1289 } 1290 1291 public ViewDictionaryService getViewDictionaryService() { 1292 return viewDictionaryService; 1293 } 1294 1295 public void setViewDictionaryService(ViewDictionaryService viewDictionaryService) { 1296 this.viewDictionaryService = viewDictionaryService; 1297 } 1298 1299}