001/** 002 * Copyright 2005-2017 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.kns.service.impl; 017 018import java.lang.reflect.InvocationTargetException; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.commons.lang.StringUtils; 027import org.kuali.rice.kns.datadictionary.FieldDefinition; 028import org.kuali.rice.kns.datadictionary.InquirySectionDefinition; 029import org.kuali.rice.kns.service.BusinessObjectDictionaryService; 030import org.kuali.rice.kns.service.BusinessObjectMetaDataService; 031import org.kuali.rice.kns.service.KNSServiceLocator; 032import org.kuali.rice.krad.bo.BusinessObject; 033import org.kuali.rice.krad.bo.DataObjectRelationship; 034import org.kuali.rice.krad.bo.PersistableBusinessObject; 035import org.kuali.rice.krad.datadictionary.DataDictionaryEntry; 036import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition; 037import org.kuali.rice.krad.datadictionary.RelationshipDefinition; 038import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition; 039import org.kuali.rice.krad.service.DataDictionaryService; 040import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 041import org.kuali.rice.krad.service.ModuleService; 042import org.kuali.rice.krad.service.PersistenceStructureService; 043import org.kuali.rice.krad.service.impl.DataObjectMetaDataServiceImpl; 044import org.kuali.rice.krad.util.LegacyDataFramework; 045import org.kuali.rice.krad.util.ObjectUtils; 046import org.kuali.rice.krad.valuefinder.ValueFinder; 047 048/** 049 * 050 * Implementation of the <code>BusinessObjectMetaDataService</code> which uses 051 * the following services to gather its meta data: 052 * 053 * @see BusinessObjectDictionaryService 054 * @see DataDictionaryService 055 * @see PersistenceStructureService 056 * 057 * @deprecated Only use {@link DataObjectMetaDataServiceImpl} if still using legacy data framework, otherwise use new 058 * KRAD Data framework. 059 */ 060@Deprecated // Replaced by new metadata provider 061@LegacyDataFramework 062public class BusinessObjectMetaDataServiceImpl extends DataObjectMetaDataServiceImpl implements BusinessObjectMetaDataService { 063 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger 064 .getLogger(BusinessObjectMetaDataServiceImpl.class); 065 066 private BusinessObjectDictionaryService businessObjectDictionaryService; 067 068 @Override 069 public Collection<String> getCollectionNames(Object bo) { 070 return getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(bo.getClass().getName()) 071 .getCollectionNames(); 072 } 073 074 @Override 075 public Collection<String> getInquirableFieldNames(Class boClass, String sectionTitle) { 076 return businessObjectDictionaryService.getInquiryFieldNames(boClass, sectionTitle); 077 } 078 079 @Override 080 public List<String> getLookupableFieldNames(Class boClass) { 081 return businessObjectDictionaryService.getLookupFieldNames(boClass); 082 } 083 084 @Override 085 public String getLookupFieldDefaultValue(Class businessObjectClass, String attributeName) { 086 return businessObjectDictionaryService.getLookupFieldDefaultValue(businessObjectClass, attributeName); 087 } 088 089 @Override 090 public Class getLookupFieldDefaultValueFinderClass(Class businessObjectClass, String attributeName) { 091 return businessObjectDictionaryService 092 .getLookupFieldDefaultValueFinderClass(businessObjectClass, attributeName); 093 } 094 095 /** {@inheritDoc} */ 096 @Override 097 public String getLookupFieldQuickfinderParameterString(Class businessObjectClass, String attributeName) { 098 return businessObjectDictionaryService.getLookupFieldQuickfinderParameterString(businessObjectClass, 099 attributeName); 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 public Class<? extends ValueFinder> getLookupFieldQuickfinderParameterStringBuilderClass(Class businessObjectClass, 105 String attributeName) { 106 return businessObjectDictionaryService.getLookupFieldQuickfinderParameterStringBuilderClass( 107 businessObjectClass, attributeName); 108 } 109 110 @Override 111 public boolean isAttributeInquirable(Class boClass, String attributeName, String sectionTitle) { 112 Collection sections = businessObjectDictionaryService.getInquirySections(boClass); 113 boolean isInquirable = true; 114 115 Iterator iter = sections.iterator(); 116 117 while (iter.hasNext()) { 118 InquirySectionDefinition def = (InquirySectionDefinition) iter.next(); 119 for (FieldDefinition field : def.getInquiryFields()) { 120 if (field.getAttributeName().equalsIgnoreCase(attributeName)) { 121 isInquirable = !field.isNoInquiry(); 122 } 123 } 124 } 125 if (isInquirable) { 126 Object obj = null; 127 if (boClass != null && BusinessObject.class.isAssignableFrom(boClass)) { 128 obj = ObjectUtils.createNewObjectFromClass(boClass); 129 } 130 131 if (obj != null) { 132 BusinessObject bo = (BusinessObject) obj; 133 Class clazz = getNestedBOClass(bo, attributeName); 134 if (clazz != null && BusinessObject.class.isAssignableFrom(clazz)) { 135 return businessObjectDictionaryService.isInquirable(clazz); 136 } 137 else { 138 return false; 139 } 140 } 141 else { 142 return false; 143 } 144 145 } 146 147 return isInquirable; 148 } 149 150 @Override 151 public boolean isInquirable(Class boClass) { 152 boolean inquirable = false; 153 ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(boClass); 154 if (moduleService != null && moduleService.isExternalizable(boClass)) { 155 inquirable = moduleService.isExternalizableBusinessObjectInquirable(boClass); 156 } 157 else { 158 Boolean isLookupable = businessObjectDictionaryService.isInquirable(boClass); 159 if (isLookupable != null) { 160 inquirable = isLookupable.booleanValue(); 161 } 162 } 163 return inquirable; 164 } 165 166 @Override 167 public boolean isAttributeLookupable(Class boClass, String attributeName) { 168 Object obj = null; 169 if (boClass != null && BusinessObject.class.isAssignableFrom(boClass)) { 170 obj = ObjectUtils.createNewObjectFromClass(boClass); 171 } 172 if (obj != null) { 173 BusinessObject bo = (BusinessObject) obj; 174 DataObjectRelationship relationship = getBusinessObjectRelationship(bo, attributeName); 175 176 if (relationship != null && relationship.getRelatedClass() != null 177 && BusinessObject.class.isAssignableFrom(relationship.getRelatedClass())) { 178 return isLookupable(relationship.getRelatedClass()); 179 } 180 else { 181 return false; 182 } 183 } 184 else { 185 return false; 186 } 187 } 188 189 @Override 190 public boolean isLookupable(Class boClass) { 191 boolean lookupable = false; 192 ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(boClass); 193 if (moduleService != null && moduleService.isExternalizable(boClass)) { 194 lookupable = moduleService.isExternalizableBusinessObjectLookupable(boClass); 195 } 196 else { 197 Boolean isLookupable = businessObjectDictionaryService.isLookupable(boClass); 198 if (isLookupable != null) { 199 lookupable = isLookupable.booleanValue(); 200 } 201 } 202 return lookupable; 203 } 204 205 @Override 206 public DataObjectRelationship getBusinessObjectRelationship(Object bo, String attributeName) { 207 return getBusinessObjectRelationship(bo, bo.getClass(), attributeName, "", true); 208 } 209 210 // TODO: four different exit points?! 211 @Override 212 public DataObjectRelationship getBusinessObjectRelationship(RelationshipDefinition ddReference, 213 Object bo, Class boClass, String attributeName, String attributePrefix, boolean keysOnly) { 214 215 DataObjectRelationship relationship = null; 216 217 // if it is nested then replace the bo and attributeName with the 218 // sub-refs 219 if (ObjectUtils.isNestedAttribute(attributeName)) { 220 if (ddReference != null) { 221 relationship = new DataObjectRelationship(boClass, ddReference.getObjectAttributeName(), 222 ddReference.getTargetClass()); 223 for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) { 224 if (StringUtils.isNotBlank(attributePrefix)) { 225 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(), 226 def.getTargetName()); 227 } 228 else { 229 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 230 } 231 } 232 if (!keysOnly) { 233 for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) { 234 if (StringUtils.isNotBlank(attributePrefix)) { 235 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(), 236 def.getTargetName()); 237 if (def.isIdentifier()) { 238 relationship.setUserVisibleIdentifierKey(attributePrefix + "." + def.getSourceName()); 239 } 240 } 241 else { 242 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 243 if (def.isIdentifier()) { 244 relationship.setUserVisibleIdentifierKey(def.getSourceName()); 245 } 246 } 247 } 248 } 249 return relationship; 250 } 251 // recurse down to the next object to find the relationship 252 253 String localPrefix = StringUtils.substringBefore(attributeName, "."); 254 String localAttributeName = StringUtils.substringAfter(attributeName, "."); 255 if (bo == null) { 256 bo = (BusinessObject) ObjectUtils.createNewObjectFromClass(boClass); 257 } 258 Class nestedClass = ObjectUtils.getPropertyType(bo, localPrefix, getPersistenceStructureService()); 259 String fullPrefix = localPrefix; 260 if (StringUtils.isNotBlank(attributePrefix)) { 261 fullPrefix = attributePrefix + "." + localPrefix; 262 } 263 if (BusinessObject.class.isAssignableFrom(nestedClass)) { 264 relationship = getBusinessObjectRelationship(null, nestedClass, localAttributeName, fullPrefix, 265 keysOnly); 266 267 // Since it was a nested property, we need to set the "parent" object 268 // back to the parent BO - the code above returns the next level down 269 if (relationship != null) { 270 relationship.setParentClass(boClass); 271 } 272 } 273 return relationship; 274 } 275 int maxSize = Integer.MAX_VALUE; 276 // try persistable reference first 277 if (PersistableBusinessObject.class.isAssignableFrom(boClass) 278 && getPersistenceStructureService().isPersistable(boClass)) { 279 Map<String, DataObjectRelationship> rels = getPersistenceStructureService().getRelationshipMetadata(boClass, 280 attributeName, attributePrefix); 281 if (rels.size() > 0) { 282 for (DataObjectRelationship rel : rels.values()) { 283 if (rel.getParentToChildReferences().size() < maxSize && isLookupable(rel.getRelatedClass())) { 284 maxSize = rel.getParentToChildReferences().size(); 285 relationship = rel; 286 } 287 } 288 } 289 } 290 else { 291 ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService() 292 .getResponsibleModuleService(boClass); 293 if (moduleService != null && moduleService.isExternalizable(boClass)) { 294 relationship = getRelationshipMetadata(boClass, attributeName, attributePrefix); 295 // relationship = 296 // moduleService.getBusinessObjectRelationship(boClass, 297 // attributeName, attributePrefix); 298 if (relationship != null) { 299 return relationship; 300 } 301 } 302 } 303 304 // then check the DD for relationships defined there 305 // TODO move out to a separate method 306 // so that the logic for finding the relationships is similar to 307 // primitiveReference 308 if (ddReference != null && BusinessObject.class.isAssignableFrom(ddReference.getTargetClass()) && isLookupable(ddReference.getTargetClass()) && bo != null 309 && ddReference.getPrimitiveAttributes().size() < maxSize) { 310 relationship = new DataObjectRelationship(boClass, ddReference.getObjectAttributeName(), 311 ddReference.getTargetClass()); 312 for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) { 313 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 314 } 315 if (!keysOnly) { 316 for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) { 317 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 318 } 319 } 320 } 321 322 return relationship; 323 324 } 325 326 327 328 @Override 329 public RelationshipDefinition getBusinessObjectRelationshipDefinition(Class c, String attributeName) { 330 return getDictionaryRelationship(c, attributeName); 331 } 332 333 @Override 334 public RelationshipDefinition getBusinessObjectRelationshipDefinition(Object bo, String attributeName) { 335 return getBusinessObjectRelationshipDefinition(bo.getClass(), attributeName); 336 } 337 338 @Override 339 public DataObjectRelationship getBusinessObjectRelationship(Object bo, Class boClass, 340 String attributeName, String attributePrefix, boolean keysOnly) { 341 RelationshipDefinition ddReference = getBusinessObjectRelationshipDefinition(boClass, attributeName); 342 return getBusinessObjectRelationship(ddReference, bo, boClass, attributeName, attributePrefix, keysOnly); 343 } 344 345 /** 346 * 347 * This method retrieves the business object class for a specific attribute 348 * 349 * @param bo 350 * @param attributeName 351 * @return a business object class for a specific attribute 352 */ 353 private Class getNestedBOClass(BusinessObject bo, String attributeName) { 354 355 String[] nestedAttributes = StringUtils.split(attributeName, "."); 356 String attributeRefName = ""; 357 Class clazz = null; 358 if (nestedAttributes.length > 1) { 359 String attributeStringSoFar = ""; 360 for (int i = 0; i < nestedAttributes.length - 1; i++) { 361 try { 362 // we need to build a string of the attribute names 363 // depending on which iteration we're in. 364 // so if the original attributeName string we're using is 365 // "a.b.c.d.e", then first iteration would use 366 // "a", 2nd "a.b", 3rd "a.b.c", etc. 367 if (i != 0) { 368 attributeStringSoFar = attributeStringSoFar + "."; 369 } 370 attributeStringSoFar = attributeStringSoFar + nestedAttributes[i]; 371 clazz = ObjectUtils.easyGetPropertyType(bo, attributeStringSoFar); 372 } 373 catch (InvocationTargetException ite) { 374 LOG.info(ite); 375 return null; 376 } 377 catch (NoSuchMethodException nsme) { 378 LOG.info(nsme); 379 return null; 380 } 381 catch (IllegalAccessException iae) { 382 LOG.info(iae); 383 return null; 384 } 385 } 386 } 387 return clazz; 388 } 389 390 @Override 391 public List<DataObjectRelationship> getBusinessObjectRelationships(Object bo) { 392 if (bo == null) { 393 return null; 394 } 395 return getBusinessObjectRelationships(bo.getClass()); 396 } 397 398 @Override 399 @SuppressWarnings("unchecked") 400 public List<DataObjectRelationship> getBusinessObjectRelationships(Class<? extends Object> boClass) { 401 if (boClass == null) { 402 return null; 403 } 404 405 Map<String, Class> referenceClasses = null; 406 if (PersistableBusinessObject.class.isAssignableFrom(boClass) 407 && getPersistenceStructureService().isPersistable(boClass)) { 408 referenceClasses = getPersistenceStructureService().listReferenceObjectFields(boClass); 409 } 410 DataDictionaryEntry ddEntry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry( 411 boClass.getName()); 412 List<RelationshipDefinition> ddRelationships = (ddEntry == null ? new ArrayList<RelationshipDefinition>() 413 : ddEntry.getRelationships()); 414 List<DataObjectRelationship> relationships = new ArrayList<DataObjectRelationship>(); 415 416 // loop over all relationships 417 if (referenceClasses != null) { 418 for (Map.Entry<String, Class> entry : referenceClasses.entrySet()) { 419 if (isLookupable(entry.getValue())) { 420 Map<String, String> fkToPkRefs = getPersistenceStructureService().getForeignKeysForReference(boClass, 421 entry.getKey()); 422 DataObjectRelationship rel = new DataObjectRelationship(boClass, entry.getKey(), 423 entry.getValue()); 424 for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) { 425 rel.getParentToChildReferences().put(ref.getKey(), ref.getValue()); 426 } 427 relationships.add(rel); 428 } 429 } 430 } 431 432 for (RelationshipDefinition rd : ddRelationships) { 433 if (isLookupable(rd.getTargetClass())) { 434 DataObjectRelationship rel = new DataObjectRelationship(boClass, rd.getObjectAttributeName(), 435 rd.getTargetClass()); 436 for (PrimitiveAttributeDefinition def : rd.getPrimitiveAttributes()) { 437 rel.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 438 } 439 relationships.add(rel); 440 } 441 } 442 443 return relationships; 444 } 445 446 /*************************************************************************** 447 * @see org.kuali.rice.kns.service.BusinessObjectMetaDataService#getReferencesForForeignKey(java.lang.Class, 448 * java.lang.String) 449 */ 450 @Override 451 public Map<String, Class> getReferencesForForeignKey(Object bo, String attributeName) { 452 List<DataObjectRelationship> dataObjectRelationships = getBusinessObjectRelationships(bo); 453 Map<String, Class> referencesForForeignKey = new HashMap<String, Class>(); 454 for (DataObjectRelationship dataObjectRelationship : dataObjectRelationships) { 455 if (dataObjectRelationship != null && dataObjectRelationship.getParentToChildReferences() != null 456 && dataObjectRelationship.getParentToChildReferences().containsKey(attributeName)) { 457 referencesForForeignKey.put(dataObjectRelationship.getParentAttributeName(), 458 dataObjectRelationship.getRelatedClass()); 459 } 460 } 461 return referencesForForeignKey; 462 } 463 464 @Override 465 public String getForeignKeyFieldName(Class businessObjectClass, String attributeName, String targetName) { 466 467 String fkName = ""; 468 469 // first try DD-based relationships 470 RelationshipDefinition relationshipDefinition = getDictionaryRelationship(businessObjectClass, attributeName); 471 472 if (relationshipDefinition != null) { 473 List<PrimitiveAttributeDefinition> primitives = relationshipDefinition.getPrimitiveAttributes(); 474 for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitives) { 475 if (primitiveAttributeDefinition.getTargetName().equals(targetName)) { 476 fkName = primitiveAttributeDefinition.getSourceName(); 477 break; 478 } 479 } 480 } 481 482 // if we can't find anything in the DD, then try the persistence service 483 if (StringUtils.isBlank(fkName) && PersistableBusinessObject.class.isAssignableFrom(businessObjectClass) 484 && getPersistenceStructureService().isPersistable(businessObjectClass)) { 485 fkName = getPersistenceStructureService().getForeignKeyFieldName(businessObjectClass, attributeName, 486 targetName); 487 } 488 return fkName; 489 } 490 491 /** 492 * @see org.kuali.rice.krad.service.DataObjectMetaDataService#hasLocalLookup 493 */ 494 @Override 495 public boolean hasLocalLookup(Class<?> dataObjectClass) { 496 boolean hasLookup = super.hasLocalLookup(dataObjectClass); 497 498 // if no krad lookup check for kns 499 if (!hasLookup) { 500 Boolean isLookupable = getBusinessObjectDictionaryService().isLookupable(dataObjectClass); 501 if (isLookupable != null) { 502 hasLookup = isLookupable.booleanValue(); 503 } 504 } 505 506 return hasLookup; 507 } 508 509 /** 510 * @see org.kuali.rice.krad.service.DataObjectMetaDataService#hasLocalInquiry 511 */ 512 @Override 513 public boolean hasLocalInquiry(Class<?> dataObjectClass) { 514 boolean hasInquiry = super.hasLocalInquiry(dataObjectClass); 515 516 // if no krad inquiry check for kns 517 if (!hasInquiry) { 518 Boolean isInquirable = getBusinessObjectDictionaryService().isInquirable(dataObjectClass); 519 if (isInquirable != null) { 520 hasInquiry = isInquirable.booleanValue(); 521 } 522 } 523 524 return hasInquiry; 525 } 526 527 /** 528 * Gets the businessObjectDictionaryService attribute. 529 * 530 * @return Returns the businessObjectDictionaryService. 531 */ 532 protected BusinessObjectDictionaryService getBusinessObjectDictionaryService() { 533 if (businessObjectDictionaryService == null) { 534 businessObjectDictionaryService = KNSServiceLocator.getBusinessObjectDictionaryService(); 535 } 536 537 return businessObjectDictionaryService; 538 } 539 540 /** 541 * Sets the businessObjectDictionaryService attribute value. 542 * 543 * @param businessObjectDictionaryService 544 * The BusinessObjectDictionaryService to set. 545 */ 546 public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) { 547 this.businessObjectDictionaryService = businessObjectDictionaryService; 548 } 549}