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 org.apache.commons.lang.StringUtils;
019import org.apache.log4j.Logger;
020import org.kuali.rice.core.api.config.ConfigurationException;
021import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator;
022import org.kuali.rice.coreservice.api.component.Component;
023import org.kuali.rice.coreservice.api.component.ComponentService;
024import org.kuali.rice.coreservice.framework.parameter.ParameterConstants;
025import org.kuali.rice.krad.bo.BusinessObject;
026import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
027import org.kuali.rice.krad.datadictionary.DocumentEntry;
028import org.kuali.rice.krad.datadictionary.TransactionalDocumentEntry;
029import org.kuali.rice.krad.document.TransactionalDocument;
030import org.kuali.rice.krad.service.DataDictionaryComponentPublisherService;
031import org.kuali.rice.krad.service.DataDictionaryService;
032import org.kuali.rice.krad.service.KualiModuleService;
033import org.kuali.rice.krad.util.KRADUtils;
034
035import java.util.ArrayList;
036import java.util.HashMap;
037import java.util.List;
038import java.util.Map;
039
040/**
041 * Reference implementation of the {@code DataDictionaryComponentPublisherService}.
042 *
043 * This implementation derives components from the DataDictionary for all BusinessObjects and Documents.
044 *
045 * @author Kuali Rice Team (rice.collab@kuali.org)
046 */
047public class DataDictionaryComponentPublisherServiceImpl implements DataDictionaryComponentPublisherService {
048
049    private static final Logger LOG = Logger.getLogger(DataDictionaryComponentPublisherServiceImpl.class);
050
051    private static final String DEFAULT_COMPONENT_SET_ID_PREFIX = "DD:";
052
053    private DataDictionaryService dataDictionaryService;
054    private KualiModuleService kualiModuleService;
055    private ComponentService componentService;
056    private String applicationId;
057
058    @Override
059    public void publishAllComponents() {
060        List<Component> componentsToPublish = getComponentsToPublish();
061        getComponentService().publishDerivedComponents(generateComponentSetId(), componentsToPublish);
062    }
063
064    protected String generateComponentSetId() {
065        if (StringUtils.isBlank(getApplicationId())) {
066            throw new ConfigurationException("A valid non-null, non-blank application id was not injected into " + getClass().getName());
067        }
068        return DEFAULT_COMPONENT_SET_ID_PREFIX + getApplicationId();
069    }
070
071    protected List<Component> getComponentsToPublish() {
072        List<Component> components = new ArrayList<Component>();
073
074        Map<String, Component> uniqueComponentMap = new HashMap<String, Component>();
075        for (BusinessObjectEntry businessObjectEntry : getDataDictionaryService().getDataDictionary().getBusinessObjectEntries().values()) {
076            try {
077                Component component = deriveComponentFromBusinessObjectEntry(businessObjectEntry);
078                uniqueComponentMap.put(component.getCode(), component);
079            }
080            catch (Exception e) {
081                LOG.error("An exception was encountered when attempting to publish all components for business object class: " + businessObjectEntry.getBusinessObjectClass(), e);
082            }
083        }
084        for (DocumentEntry documentEntry : getDataDictionaryService().getDataDictionary().getDocumentEntries().values()) {
085            if (documentEntry instanceof TransactionalDocumentEntry) {
086                try {
087                    Component component = deriveComponentFromDocumentEntry(documentEntry);
088                    uniqueComponentMap.put(component.getCode(), component);
089                }
090                catch (Exception e) {
091                    LOG.error("An exception was encountered when attempting to publish all components for transactional document class: " + documentEntry.getDocumentClass(), e);
092                }
093            }
094        }
095        components.addAll(uniqueComponentMap.values());
096        return components;
097    }
098
099        protected Component deriveComponentFromClass(Class<?> componentSourceClass) {
100        String componentCode = getKualiModuleService().getComponentCode(componentSourceClass);
101        String componentName = deriveComponentName(componentSourceClass);
102        String namespace = getKualiModuleService().getNamespaceCode(componentSourceClass);
103        if (StringUtils.isBlank(componentName)) {
104            componentName = componentCode;
105        }
106        Component.Builder detailType = Component.Builder.create(namespace, componentCode, componentName);
107        return detailType.build();
108    }
109
110    protected Component deriveComponentFromBusinessObjectEntry(BusinessObjectEntry businessObjectEntry) {
111        Class<?> businessObjectClass = businessObjectEntry.getBaseBusinessObjectClass();
112        if (businessObjectClass == null) {
113            businessObjectClass = businessObjectEntry.getBusinessObjectClass();
114        }
115        return deriveComponentFromClass(businessObjectClass);
116    }
117
118    protected Component deriveComponentFromDocumentEntry(DocumentEntry documentEntry) {
119        Class<?> documentClass = documentEntry.getBaseDocumentClass();
120        if (documentClass == null) {
121            documentClass = documentEntry.getDocumentClass();
122        }
123        return deriveComponentFromClass(documentClass);
124    }
125
126        protected String deriveComponentName(Class<?> componentSourceClass) {
127        if (componentSourceClass == null) {
128            throw new IllegalArgumentException("The deriveComponentName method requires non-null componentSourceClass");
129        }
130        
131        /*
132         * Some business objects have a Component annotation that sets the value
133         * of the classes annotaion.  This if block will test to see if it is there, try to get the
134         * component value from the Data Dictionary if the BusinessObjectEntry exists, if it doesn't
135         * exist, it will fall back to the annotation's value.
136         */
137        if (componentSourceClass.isAnnotationPresent(ParameterConstants.COMPONENT.class)) {
138            BusinessObjectEntry boe = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(componentSourceClass.getName());
139            if (boe != null) {
140                return boe.getObjectLabel();
141            }
142            else {
143                return ((ParameterConstants.COMPONENT) componentSourceClass.getAnnotation(ParameterConstants.COMPONENT.class)).component();
144            }
145        }
146
147        /*
148         * If block that determines if the class is either a BusinessObject or a TransactionalDocument
149         * return calls try to either get the BusinessObjectEntry's ObjectLable, or grabbing the
150         * data dictionary's BusinessTitleForClass if it is a BusinessObject, or the DocumentLabel if it is a
151         * TransactionalDocument
152         */
153        if (TransactionalDocument.class.isAssignableFrom(componentSourceClass)) {
154            return getDataDictionaryService().getDocumentLabelByClass(componentSourceClass);
155        }
156        else if (BusinessObject.class.isAssignableFrom(componentSourceClass) ) {
157            BusinessObjectEntry boe = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(componentSourceClass.getName());
158            if (boe != null) {
159                return boe.getObjectLabel();
160            }
161            else {
162                return KRADUtils.getBusinessTitleForClass(componentSourceClass);
163            }
164        }
165        throw new IllegalArgumentException("The deriveComponentName method of requires TransactionalDocument or BusinessObject class. Was: " + componentSourceClass.getName() );
166    }
167
168    public DataDictionaryService getDataDictionaryService() {
169        return dataDictionaryService;
170    }
171
172    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
173        this.dataDictionaryService = dataDictionaryService;
174    }
175
176    public KualiModuleService getKualiModuleService() {
177        return kualiModuleService;
178    }
179
180    public void setKualiModuleService(KualiModuleService kualiModuleService) {
181        this.kualiModuleService = kualiModuleService;
182    }
183
184    public ComponentService getComponentService() {
185        if (componentService == null) {
186            componentService = CoreServiceApiServiceLocator.getComponentService();
187        }
188        return componentService;
189    }
190
191    public void setComponentService(ComponentService componentService) {
192        this.componentService = componentService;
193    }
194
195    public String getApplicationId() {
196        return applicationId;
197    }
198
199    public void setApplicationId(String applicationId) {
200        this.applicationId = applicationId;
201    }
202    
203}