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