001    /*
002     * Copyright 2011 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     */
016    package org.kuali.kfs.sys.service.impl;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.HashSet;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Set;
024    import java.util.regex.Pattern;
025    import java.util.regex.PatternSyntaxException;
026    
027    import org.apache.commons.beanutils.PropertyUtils;
028    import org.apache.commons.lang.StringUtils;
029    import org.apache.log4j.Logger;
030    import org.apache.ojb.broker.metadata.ClassDescriptor;
031    import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
032    import org.kuali.kfs.sys.KFSPropertyConstants;
033    import org.kuali.kfs.sys.businessobject.BusinessObjectComponent;
034    import org.kuali.kfs.sys.businessobject.BusinessObjectProperty;
035    import org.kuali.kfs.sys.businessobject.DataMappingFieldDefinition;
036    import org.kuali.kfs.sys.businessobject.FunctionalFieldDescription;
037    import org.kuali.kfs.sys.dataaccess.BusinessObjectMetaDataDao;
038    import org.kuali.kfs.sys.service.KfsBusinessObjectMetaDataService;
039    import org.kuali.kfs.sys.service.NonTransactional;
040    import org.kuali.rice.kns.bo.BusinessObject;
041    import org.kuali.rice.kns.bo.BusinessObjectRelationship;
042    import org.kuali.rice.kns.datadictionary.AttributeDefinition;
043    import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
044    import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
045    import org.kuali.rice.kns.service.BusinessObjectService;
046    import org.kuali.rice.kns.service.DataDictionaryService;
047    import org.kuali.rice.kns.service.LookupService;
048    import org.kuali.rice.kns.service.ParameterService;
049    
050    @NonTransactional
051    public class KfsBusinessObjectMetaDataServiceImpl implements KfsBusinessObjectMetaDataService {
052        private Logger LOG = Logger.getLogger(KfsBusinessObjectMetaDataServiceImpl.class);
053        private DataDictionaryService dataDictionaryService;
054        private ParameterService parameterService;
055        private BusinessObjectService businessObjectService;
056        private BusinessObjectMetaDataService businessObjectMetaDataService;
057        private BusinessObjectMetaDataDao businessObjectMetaDataDao;
058        private LookupService lookupService;
059    
060        public void setParameterService(ParameterService parameterService) {
061            this.parameterService = parameterService;
062        }
063    
064        public BusinessObjectComponent getBusinessObjectComponent(String componentClass) {
065            try {
066                return new BusinessObjectComponent(parameterService.getNamespace(Class.forName(componentClass)), dataDictionaryService.getDataDictionary().getBusinessObjectEntry(componentClass));
067            }
068            catch (ClassNotFoundException e) {
069                throw new RuntimeException("KfsBusinessObjectMetaDataServiceImpl unable to translate the provided componentClass String into a Class: " + componentClass, e);
070            }
071        }
072    
073        public BusinessObjectProperty getBusinessObjectProperty(String componentClass, String propertyName) {
074            return new BusinessObjectProperty(getBusinessObjectComponent(componentClass), dataDictionaryService.getDataDictionary().getBusinessObjectEntry(componentClass).getAttributeDefinition(propertyName));
075        }
076    
077        public DataMappingFieldDefinition getDataMappingFieldDefinition(String componentClass, String propertyName) {
078            Map<String, String> primaryKeys = new HashMap<String, String>();
079            primaryKeys.put(KFSPropertyConstants.COMPONENT_CLASS, componentClass);
080            primaryKeys.put(KFSPropertyConstants.PROPERTY_NAME, propertyName);
081            FunctionalFieldDescription functionalFieldDescription = (FunctionalFieldDescription) businessObjectService.findByPrimaryKey(FunctionalFieldDescription.class, primaryKeys);
082            if (functionalFieldDescription == null) {
083                functionalFieldDescription = new FunctionalFieldDescription(componentClass, propertyName);
084            }
085            functionalFieldDescription.refreshNonUpdateableReferences();
086            return getDataMappingFieldDefinition(functionalFieldDescription);
087        }
088    
089        public DataMappingFieldDefinition getDataMappingFieldDefinition(FunctionalFieldDescription functionalFieldDescription) {
090            BusinessObjectEntry businessObjectEntry = dataDictionaryService.getDataDictionary().getBusinessObjectEntry(functionalFieldDescription.getComponentClass());
091            String propertyType = "";
092            try {
093                propertyType = PropertyUtils.getPropertyType(businessObjectEntry.getBusinessObjectClass().newInstance(), functionalFieldDescription.getPropertyName()).getSimpleName();
094            }
095            catch (Exception e) {
096                if (LOG.isDebugEnabled()) {
097                    LOG.debug("KfsBusinessObjectMetaDataServiceImpl unable to get type of property: " + functionalFieldDescription.getPropertyName(), e);
098                }
099            }
100            return new DataMappingFieldDefinition(functionalFieldDescription, businessObjectEntry, businessObjectEntry.getAttributeDefinition(functionalFieldDescription.getPropertyName()), businessObjectMetaDataDao.getFieldMetaData(businessObjectEntry.getBusinessObjectClass(), functionalFieldDescription.getPropertyName()), propertyType, getReferenceComponentLabel(businessObjectEntry.getBusinessObjectClass(), functionalFieldDescription.getPropertyName()));
101        }
102    
103        public List<BusinessObjectComponent> findBusinessObjectComponents(String namespaceCode, String componentLabel) {
104            Map<Class, BusinessObjectComponent> matchingBusinessObjectComponents = new HashMap<Class, BusinessObjectComponent>();
105            Pattern componentLabelRegex = null;
106            if (StringUtils.isNotBlank(componentLabel)) {
107                String patternStr = componentLabel.replace("*", ".*").toUpperCase();
108                try {
109                    componentLabelRegex = Pattern.compile(patternStr);
110                }
111                catch (PatternSyntaxException ex) {
112                    LOG.error("KfsBusinessObjectMetaDataServiceImpl unable to parse componentLabel pattern, ignoring.", ex);
113                }
114            }
115            for (BusinessObjectEntry businessObjectEntry : dataDictionaryService.getDataDictionary().getBusinessObjectEntries().values()) {
116                if ((StringUtils.isBlank(namespaceCode) || namespaceCode.equals(parameterService.getNamespace(businessObjectEntry.getBusinessObjectClass()))) 
117                        && ((componentLabelRegex == null) || (StringUtils.isNotBlank(businessObjectEntry.getObjectLabel()) && componentLabelRegex.matcher(businessObjectEntry.getObjectLabel().toUpperCase()).matches()))) {
118                    matchingBusinessObjectComponents.put(businessObjectEntry.getBusinessObjectClass(), new BusinessObjectComponent(parameterService.getNamespace(businessObjectEntry.getBusinessObjectClass()), businessObjectEntry));
119                }
120            }
121            return new ArrayList<BusinessObjectComponent>(matchingBusinessObjectComponents.values());
122        }
123    
124        public List<BusinessObjectProperty> findBusinessObjectProperties(String namespaceCode, String componentLabel, String propertyLabel) {
125            List<BusinessObjectComponent> businessObjectComponents = findBusinessObjectComponents(namespaceCode, componentLabel);
126    
127            Pattern propertyLabelRegex = null;
128            if (StringUtils.isNotBlank(propertyLabel)) {
129                String patternStr = propertyLabel.replace("*", ".*").toUpperCase();
130                try {
131                    propertyLabelRegex = Pattern.compile(patternStr);
132                }
133                catch (PatternSyntaxException ex) {
134                    LOG.error("KfsBusinessObjectMetaDataServiceImpl unable to parse propertyLabel pattern, ignoring.", ex);
135                }
136            }
137    
138            List<BusinessObjectProperty> matchingBusinessObjectProperties = new ArrayList<BusinessObjectProperty>();
139            for (BusinessObjectComponent businessObjectComponent : businessObjectComponents) {
140                for (AttributeDefinition attributeDefinition : dataDictionaryService.getDataDictionary().getBusinessObjectEntry(businessObjectComponent.getComponentClass().toString()).getAttributes()) {
141                    if (!attributeDefinition.getName().endsWith(KFSPropertyConstants.VERSION_NUMBER) && !attributeDefinition.getName().endsWith(KFSPropertyConstants.OBJECT_ID) && ((propertyLabelRegex == null) || propertyLabelRegex.matcher(attributeDefinition.getLabel().toUpperCase()).matches())) {
142                        matchingBusinessObjectProperties.add(new BusinessObjectProperty(businessObjectComponent, attributeDefinition));
143                    }
144                }
145            }
146    
147            return matchingBusinessObjectProperties;
148        }
149    
150        public List<FunctionalFieldDescription> findFunctionalFieldDescriptions(String namespaceCode, String componentLabel, String propertyLabel, String description, String active) {
151            Set<String> componentClasses = new HashSet<String>();
152            Set<String> propertyNames = new HashSet<String>();
153            for (BusinessObjectProperty businessObjectProperty : findBusinessObjectProperties(namespaceCode, componentLabel, propertyLabel)) {
154                componentClasses.add(businessObjectProperty.getComponentClass());
155                propertyNames.add(businessObjectProperty.getPropertyName());
156            }
157            Map<String, String> fieldValues = new HashMap<String, String>();
158            fieldValues.put(KFSPropertyConstants.NAMESPACE_CODE, namespaceCode);
159            fieldValues.put(KFSPropertyConstants.COMPONENT_CLASS, buildOrCriteria(componentClasses));
160            fieldValues.put(KFSPropertyConstants.PROPERTY_NAME, buildOrCriteria(propertyNames));
161            fieldValues.put(KFSPropertyConstants.DESCRIPTION, description);
162            fieldValues.put(KFSPropertyConstants.ACTIVE, active);
163            List<FunctionalFieldDescription> searchResults = (List<FunctionalFieldDescription>) lookupService.findCollectionBySearchHelper(FunctionalFieldDescription.class, fieldValues, false);
164            for (FunctionalFieldDescription functionalFieldDescription : searchResults) {
165                functionalFieldDescription.refreshNonUpdateableReferences();
166            }
167            return searchResults;
168        }
169    
170        protected String buildOrCriteria(Set<String> values) {
171            StringBuffer orCriteria = new StringBuffer();
172            List<String> valueList = new ArrayList<String>(values);
173            for (int i = 0; i < valueList.size(); i++) {
174                orCriteria.append(valueList.get(i));
175                if (i < (valueList.size() - 1)) {
176                    orCriteria.append("|");
177                }
178            }
179            return orCriteria.toString();
180        }
181    
182        public boolean isMatch(String componentClass, String propertyName, String tableNameSearchCriterion, String fieldNameSearchCriterion) {
183            ClassDescriptor classDescriptor = null;
184            try {
185                classDescriptor = org.apache.ojb.broker.metadata.MetadataManager.getInstance().getGlobalRepository().getDescriptorFor(componentClass);
186                Pattern tableNameRegex = null;
187                if (StringUtils.isNotBlank(tableNameSearchCriterion)) {
188                    String patternStr = tableNameSearchCriterion.replace("*", ".*").toUpperCase();
189                    try {
190                        tableNameRegex = Pattern.compile(patternStr);
191                    }
192                    catch (PatternSyntaxException ex) {
193                        LOG.error("DataMappingFieldDefinitionLookupableHelperServiceImpl unable to parse tableName pattern, ignoring.", ex);
194                    }
195                }
196                Pattern fieldNameRegex = null;
197                if (StringUtils.isNotBlank(fieldNameSearchCriterion)) {
198                    String patternStr = fieldNameSearchCriterion.replace("*", ".*").toUpperCase();
199                    try {
200                        fieldNameRegex = Pattern.compile(patternStr);
201                    }
202                    catch (PatternSyntaxException ex) {
203                        LOG.error("DataMappingFieldDefinitionLookupableHelperServiceImpl unable to parse fieldName pattern, ignoring.", ex);
204                    }
205                }
206                return ((tableNameRegex == null) || tableNameRegex.matcher(classDescriptor.getFullTableName().toUpperCase()).matches()) && ((fieldNameRegex == null) || ((classDescriptor.getFieldDescriptorByName(propertyName) != null) && fieldNameRegex.matcher(classDescriptor.getFieldDescriptorByName(propertyName).getColumnName().toUpperCase()).matches()));
207            }
208            catch (ClassNotPersistenceCapableException e) {
209                return StringUtils.isBlank(tableNameSearchCriterion) && StringUtils.isBlank(fieldNameSearchCriterion);
210            }
211        }
212    
213        public String getReferenceComponentLabel(Class componentClass, String propertyName) {
214            BusinessObjectRelationship relationship = null;
215            try {
216                relationship = businessObjectMetaDataService.getBusinessObjectRelationship((BusinessObject) componentClass.newInstance(), propertyName);
217            }
218            catch (Exception e) {
219                LOG.debug("KfsBusinessObjectMetadataServiceImpl unable to instantiate componentClass: " + componentClass, e);
220            }
221            if (relationship != null) {
222                return dataDictionaryService.getDataDictionary().getBusinessObjectEntry(relationship.getRelatedClass().getName()).getObjectLabel();
223            }
224            return "";
225        }
226    
227        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
228            this.dataDictionaryService = dataDictionaryService;
229        }
230    
231        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
232            this.businessObjectService = businessObjectService;
233        }
234    
235        public void setBusinessObjectMetaDataService(BusinessObjectMetaDataService businessObjectMetaDataService) {
236            this.businessObjectMetaDataService = businessObjectMetaDataService;
237        }
238    
239        public void setBusinessObjectMetaDataDao(BusinessObjectMetaDataDao businessObjectMetaDataDao) {
240            this.businessObjectMetaDataDao = businessObjectMetaDataDao;
241        }
242    
243        public void setLookupService(LookupService lookupService) {
244            this.lookupService = lookupService;
245        }
246    }