001    /**
002     * Copyright 2004-2013 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.hr.time.base.web;
017    
018    import java.security.GeneralSecurityException;
019    import java.sql.Date;
020    import java.text.SimpleDateFormat;
021    import java.util.ArrayList;
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Properties;
028    
029    import org.apache.commons.collections.BidiMap;
030    import org.apache.commons.collections.bidimap.DualHashBidiMap;
031    import org.apache.commons.lang.StringUtils;
032    import org.apache.log4j.Logger;
033    import org.kuali.rice.core.web.format.Formatter;
034    import org.kuali.rice.kns.inquiry.KualiInquirableImpl;
035    import org.kuali.rice.kns.lookup.HtmlData;
036    import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData;
037    import org.kuali.rice.kns.lookup.LookupUtils;
038    import org.kuali.rice.kns.service.KNSServiceLocator;
039    import org.kuali.rice.krad.bo.BusinessObject;
040    import org.kuali.rice.krad.bo.DataObjectRelationship;
041    import org.kuali.rice.krad.bo.DocumentHeader;
042    import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
043    import org.kuali.rice.krad.datadictionary.AttributeSecurity;
044    import org.kuali.rice.krad.util.ExternalizableBusinessObjectUtils;
045    import org.kuali.rice.krad.util.KRADConstants;
046    import org.kuali.rice.krad.util.ObjectUtils;
047    import org.kuali.rice.krad.util.UrlFactory;
048    
049    public class TkInquirableImpl extends KualiInquirableImpl {
050            
051            private static final Logger LOG = Logger.getLogger(TkInquirableImpl.class);
052            
053            @Override
054            // copied the getInquiryUrl() from KualiInquirableImpl and added effectiveDate to parameters
055        public HtmlData getInquiryUrl(BusinessObject businessObject, String attributeName, boolean forceInquiry) {
056            Properties parameters = new Properties();
057            AnchorHtmlData hRef = new AnchorHtmlData(KRADConstants.EMPTY_STRING, KRADConstants.EMPTY_STRING);
058            parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start");
059    
060            Class inquiryBusinessObjectClass = null;
061            String attributeRefName = "";
062            boolean isPkReference = false;
063    
064            boolean doesNestedReferenceHaveOwnPrimitiveReference = false;
065            BusinessObject nestedBusinessObject = null;
066    
067            if (attributeName.equals(getBusinessObjectDictionaryService().getTitleAttribute(businessObject.getClass()))) {
068                inquiryBusinessObjectClass = businessObject.getClass();
069                isPkReference = true;
070            }
071            else {
072                if (ObjectUtils.isNestedAttribute(attributeName)) {
073                    // if we have a reference object, we should determine if we should either provide an inquiry link to
074                    // the reference object itself, or some other nested primitive.
075    
076                    // for example, if the attribute is "referenceObject.someAttribute", and there is no primitive reference for
077                    // "someAttribute", then an inquiry link is provided to the "referenceObject".  If it does have a primitive reference, then
078                    // the inquiry link is directed towards it instead
079                    String nestedReferenceName = ObjectUtils.getNestedAttributePrefix(attributeName);
080                    Object nestedReferenceObject = ObjectUtils.getNestedValue(businessObject, nestedReferenceName);
081    
082                    if (ObjectUtils.isNotNull(nestedReferenceObject) && nestedReferenceObject instanceof BusinessObject) {
083                        nestedBusinessObject = (BusinessObject) nestedReferenceObject;
084                        String nestedAttributePrimitive = ObjectUtils.getNestedAttributePrimitive(attributeName);
085    
086                        if (nestedAttributePrimitive.equals(getBusinessObjectDictionaryService().getTitleAttribute(nestedBusinessObject.getClass()))) {
087                            // we are going to inquiry the record that contains the attribute we're rendering an inquiry URL for
088                            inquiryBusinessObjectClass = nestedBusinessObject.getClass();
089                            // I know it's already set to false, just to show how this variable is set
090                            doesNestedReferenceHaveOwnPrimitiveReference = false;
091                        }
092                        else {
093                                Map primitiveReference = LookupUtils.getPrimitiveReference(nestedBusinessObject, nestedAttributePrimitive);
094                                if (primitiveReference != null && !primitiveReference.isEmpty()) {
095                                    attributeRefName = (String) primitiveReference.keySet().iterator().next();
096                                    inquiryBusinessObjectClass = (Class) primitiveReference.get(attributeRefName);
097                                    doesNestedReferenceHaveOwnPrimitiveReference = true;
098                                }
099                                else {
100                                    // we are going to inquiry the record that contains the attribute we're rendering an inquiry URL for
101                                            inquiryBusinessObjectClass = nestedBusinessObject.getClass();
102                                    // I know it's already set to false, just to show how this variable is set
103                                    doesNestedReferenceHaveOwnPrimitiveReference = false;
104                                }
105                        }
106                    }
107                }
108                else {
109                    Map primitiveReference = LookupUtils.getPrimitiveReference(businessObject, attributeName);
110                    if (primitiveReference != null && !primitiveReference.isEmpty()) {
111                        attributeRefName = (String) primitiveReference.keySet().iterator().next();
112                        inquiryBusinessObjectClass = (Class) primitiveReference.get(attributeRefName);
113                    }
114                }
115            }
116    
117            if (inquiryBusinessObjectClass != null && DocumentHeader.class.isAssignableFrom(inquiryBusinessObjectClass)) {
118                String documentNumber = (String) ObjectUtils.getPropertyValue(businessObject, attributeName);
119                if (!StringUtils.isBlank(documentNumber)) {
120                    // if NullPointerException on the following line, maybe the Spring bean wasn't injected w/ KualiConfigurationException, or if
121                    // instances of a sub-class of this class are not Spring created, then override getKualiConfigurationService() in the subclass
122                    // to return the configuration service from a Spring service locator (or set it).
123                    hRef.setHref(getKualiConfigurationService().getPropertyValueAsString(KRADConstants.WORKFLOW_URL_KEY) + KRADConstants.DOCHANDLER_DO_URL + documentNumber + KRADConstants.DOCHANDLER_URL_CHUNK);
124                }
125                return hRef;
126            }
127    
128            if (inquiryBusinessObjectClass == null || getBusinessObjectDictionaryService().isInquirable(inquiryBusinessObjectClass) == null || !getBusinessObjectDictionaryService().isInquirable(inquiryBusinessObjectClass).booleanValue()) {
129                return hRef;
130            }
131    
132            synchronized (SUPER_CLASS_TRANSLATOR_LIST) {
133                for (Class clazz : SUPER_CLASS_TRANSLATOR_LIST) {
134                    if (clazz.isAssignableFrom(inquiryBusinessObjectClass)) {
135                        inquiryBusinessObjectClass = clazz;
136                        break;
137                    }
138                }
139            }
140    
141            if (!inquiryBusinessObjectClass.isInterface() && ExternalizableBusinessObject.class.isAssignableFrom(inquiryBusinessObjectClass)) {
142                    inquiryBusinessObjectClass = ExternalizableBusinessObjectUtils.determineExternalizableBusinessObjectSubInterface(inquiryBusinessObjectClass);
143            }
144    
145            parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, inquiryBusinessObjectClass.getName());
146    
147    
148            // listPrimaryKeyFieldNames returns an unmodifiable list.  So a copy is necessary.
149            List<String> keys = new ArrayList<String>(getBusinessObjectMetaDataService().listPrimaryKeyFieldNames(inquiryBusinessObjectClass));
150    
151            if (keys == null) {
152                    keys = Collections.emptyList();
153            }
154    
155            DataObjectRelationship businessObjectRelationship = null;
156    
157            if(attributeRefName != null && !"".equals(attributeRefName)){
158                    businessObjectRelationship =
159                            getBusinessObjectMetaDataService().getBusinessObjectRelationship(
160                                            businessObject, attributeRefName);
161    
162                    if (businessObjectRelationship != null && businessObjectRelationship.getParentToChildReferences() != null) {
163                            for (String targetNamePrimaryKey : businessObjectRelationship.getParentToChildReferences().values()) {
164                                    keys.add(targetNamePrimaryKey);
165                            }
166                    }
167            }
168            // build key value url parameters used to retrieve the business object
169            String keyName = null;
170            String keyConversion = null;
171            Map<String, String> fieldList = new HashMap<String,String>();
172            for (Iterator iter = keys.iterator(); iter.hasNext();) {
173                keyName = (String) iter.next();
174                keyConversion = keyName;
175                if (ObjectUtils.isNestedAttribute(attributeName)) {
176                    if (doesNestedReferenceHaveOwnPrimitiveReference) {
177                        String nestedAttributePrefix = ObjectUtils.getNestedAttributePrefix(attributeName);
178                        //String foreignKeyFieldName = getBusinessObjectMetaDataService().getForeignKeyFieldName(
179                        //        inquiryBusinessObjectClass.getClass(), attributeRefName, keyName);
180    
181                        String foreignKeyFieldName = getBusinessObjectMetaDataService().getForeignKeyFieldName(
182                                                                                    nestedBusinessObject.getClass(), attributeRefName, keyName);
183                        keyConversion = nestedAttributePrefix + "." + foreignKeyFieldName;
184                    }
185                    else {
186                        keyConversion = ObjectUtils.getNestedAttributePrefix(attributeName) + "." + keyName;
187                    }
188                }
189                else {
190                    if (isPkReference) {
191                        keyConversion = keyName;
192                    }
193                    else if (businessObjectRelationship != null) {
194                            //Using BusinessObjectMetaDataService instead of PersistenceStructureService
195                            //since otherwise, relationship information from datadictionary is not used at all
196                            //Also, BOMDS.getBusinessObjectRelationship uses PersistenceStructureService,
197                            //so both datadictionary and the persistance layer get covered
198                            /*
199                            BusinessObjectRelationship businessObjectRelationship =
200                                    getBusinessObjectMetaDataService().getBusinessObjectRelationship(
201                                                    businessObject, attributeRefName);
202                                                    */
203                            BidiMap bidiMap = new DualHashBidiMap(businessObjectRelationship.getParentToChildReferences());
204                            keyConversion = (String)bidiMap.getKey(keyName);
205                        //keyConversion = getPersistenceStructureService().getForeignKeyFieldName(businessObject.getClass(), attributeRefName, keyName);
206                    }
207                }
208                Object keyValue = null;
209                if (keyConversion != null) {
210                    keyValue = ObjectUtils.getPropertyValue(businessObject, keyConversion);
211                }
212    
213                if (keyValue == null) {
214                    keyValue = "";
215                } else if (keyValue instanceof java.sql.Date) { //format the date for passing in url
216                    if (Formatter.findFormatter(keyValue.getClass()) != null) {
217                        Formatter formatter = Formatter.getFormatter(keyValue.getClass());
218                        keyValue = (String) formatter.format(keyValue);
219                    }
220                } else {
221                    keyValue = keyValue.toString();
222                }
223    
224                // Encrypt value if it is a field that has restriction that prevents a value from being shown to user,
225                // because we don't want the browser history to store the restricted attribute's value in the URL
226                AttributeSecurity attributeSecurity = KNSServiceLocator.getDataDictionaryService().getAttributeSecurity(businessObject.getClass().getName(), keyName);
227                if(attributeSecurity != null && attributeSecurity.hasRestrictionThatRemovesValueFromUI()){
228                    try {
229                        keyValue = getEncryptionService().encrypt(keyValue);
230                    }
231                    catch (GeneralSecurityException e) {
232                        LOG.error("Exception while trying to encrypted value for inquiry framework.", e);
233                        throw new RuntimeException(e);
234                    }
235                }
236    
237                parameters.put(keyName, keyValue);
238                fieldList.put(keyName, keyValue.toString());
239            }
240            
241            // pass in effective date of the businessObject
242            Date aDate = (Date) ObjectUtils.getPropertyValue(businessObject, "effectiveDate");
243            if(aDate != null) {
244                    parameters.put("effectiveDate", new SimpleDateFormat("MM/dd/yyyy").format(aDate));
245            }
246                            
247            return getHyperLink(inquiryBusinessObjectClass, fieldList, UrlFactory.parameterizeUrl(KRADConstants.INQUIRY_ACTION, parameters));
248        }
249    }