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