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.kew.rule.web;
017
018import org.apache.commons.beanutils.PropertyUtils;
019import org.apache.struts.action.ActionErrors;
020import org.apache.struts.action.ActionMessage;
021import org.kuali.rice.core.api.util.RiceConstants;
022import org.kuali.rice.core.framework.persistence.jdbc.sql.SQLUtils;
023import org.kuali.rice.kew.api.KewApiConstants;
024import org.kuali.rice.kew.api.rule.RoleName;
025import org.kuali.rice.kew.api.util.CodeTranslator;
026import org.kuali.rice.kew.exception.WorkflowServiceError;
027import org.kuali.rice.kew.rule.KeyValueId;
028import org.kuali.rice.kew.rule.RuleBaseValues;
029import org.kuali.rice.kew.rule.RuleDelegationBo;
030import org.kuali.rice.kew.rule.RuleExtensionValue;
031import org.kuali.rice.kew.rule.RuleResponsibilityBo;
032import org.kuali.rice.kew.rule.WorkflowRuleAttributeRows;
033import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo;
034import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
035import org.kuali.rice.kew.rule.service.RuleDelegationService;
036import org.kuali.rice.kew.rule.service.RuleTemplateService;
037import org.kuali.rice.kew.service.KEWServiceLocator;
038import org.kuali.rice.kns.web.ui.Field;
039import org.kuali.rice.kns.web.ui.Row;
040
041import java.sql.Timestamp;
042import java.text.ParseException;
043import java.text.SimpleDateFormat;
044import java.util.ArrayList;
045import java.util.Calendar;
046import java.util.Collection;
047import java.util.Collections;
048import java.util.Date;
049import java.util.HashMap;
050import java.util.Iterator;
051import java.util.List;
052import java.util.Map;
053
054
055/**
056 * A decorator around a {@link RuleBaseValues} object which provides some
057 * convienance functions for interacting with the bean from the web-tier.
058 * This helps to alleviate some of the weaknesses of JSTL.
059 *
060 * @author Kuali Rice Team (rice.collab@kuali.org)
061 */
062public class WebRuleBaseValues extends RuleBaseValues {
063
064        private static final long serialVersionUID = 5938997470219200474L;
065    private static final int TO_DATE_UPPER_LIMIT = 2100;
066        private List rows = new ArrayList();
067        private List fields = new ArrayList();
068        private List roles = new ArrayList();
069        private String fromDateString;
070        private String toDateString;
071        private String ruleTemplateName;
072        private boolean hasExtensionValueErrors = false;
073
074        public WebRuleBaseValues() {
075        }
076
077        public WebRuleBaseValues(RuleBaseValues rule) throws Exception {
078                edit(rule);
079        }
080
081    private void loadFields() {
082                fields.clear();
083                if (getRuleTemplateId() != null) {
084                        RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId());
085                        if (ruleTemplate != null) {
086                                List ruleTemplateAttributes = ruleTemplate.getActiveRuleTemplateAttributes();
087                                Collections.sort(ruleTemplateAttributes);
088                                for (Iterator iter = ruleTemplateAttributes.iterator(); iter.hasNext();) {
089                                        RuleTemplateAttributeBo ruleTemplateAttribute = (RuleTemplateAttributeBo) iter.next();
090                                        if (!ruleTemplateAttribute.isWorkflowAttribute()) {
091                                                continue;
092                                        }
093                    WorkflowRuleAttributeRows workflowRuleAttributeRows =
094                            KEWServiceLocator.getWorkflowRuleAttributeMediator().getRuleRows(null, ruleTemplateAttribute);
095                                        for (Row row : workflowRuleAttributeRows.getRows()) {
096                                                for (Field field : row.getFields()) {
097                               String fieldValue = "";
098                               RuleExtensionValue extensionValue = getRuleExtensionValue(ruleTemplateAttribute.getId(), field.getPropertyName());
099                               fieldValue = (extensionValue != null) ? extensionValue.getValue() : field.getPropertyValue();
100                               fields.add(new KeyValueId(field.getPropertyName(), fieldValue, ruleTemplateAttribute.getId()));
101                           }
102                                        }
103                                }
104                        }
105                }
106        }
107
108        private void loadWebValues() {
109                loadRows();
110                loadDates();
111                loadRuleTemplateName();
112        }
113
114    private void loadRows() {
115                getRoles().clear();
116                if (getRuleTemplateId() != null) {
117                        RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId());
118                        if (ruleTemplate != null) {
119                                setRuleTemplateName(ruleTemplate.getName());
120                                List<RuleTemplateAttributeBo> ruleTemplateAttributes = ruleTemplate.getActiveRuleTemplateAttributes();
121                                Collections.sort(ruleTemplateAttributes);
122                                List<Row> rows = new ArrayList<Row>();
123                                for (RuleTemplateAttributeBo ruleTemplateAttribute : ruleTemplateAttributes) {
124                                        if (!ruleTemplateAttribute.isWorkflowAttribute()) {
125                                                continue;
126                                        }
127                    WorkflowRuleAttributeRows workflowRuleAttributeRows =
128                            KEWServiceLocator.getWorkflowRuleAttributeMediator().getRuleRows(getFieldMap(ruleTemplateAttribute.getId()), ruleTemplateAttribute);
129                    rows.addAll(workflowRuleAttributeRows.getRows());
130                    getRoles().addAll(KEWServiceLocator.getWorkflowRuleAttributeMediator().getRoleNames(ruleTemplateAttribute));
131                                }
132                                setRows(rows);
133                        }
134                }
135        }
136
137        private void loadDates() {
138                if (getFromDateString() != null) {
139                        setFromDateString(RiceConstants.getDefaultDateFormat().format(getFromDateValue()));
140                }
141                if (getToDateString() != null) {
142                        setToDateString(RiceConstants.getDefaultDateFormat().format(getToDateValue()));
143                }
144        }
145
146        private void loadRuleTemplateName() {
147                if (org.apache.commons.lang.StringUtils.isEmpty(getRuleTemplateName()) && getRuleTemplateId() != null) {
148                        RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId());
149                        if (ruleTemplate != null) {
150                                setRuleTemplateName(ruleTemplate.getName());
151                        }
152                }
153        }
154
155        public List getFields() {
156                return fields;
157        }
158
159        public void setFields(List fields) {
160                this.fields = fields;
161        }
162
163        public KeyValueId getField(int index) {
164                while (getFields().size() <= index) {
165                        KeyValueId field = new KeyValueId();
166                        getFields().add(field);
167                }
168                return (KeyValueId) getFields().get(index);
169        }
170
171        public String getFromDateString() {
172                return fromDateString;
173        }
174
175        public void setFromDateString(String fromDateString) {
176                this.fromDateString = fromDateString;
177        }
178
179        public List<RoleName> getRoles() {
180                return roles;
181        }
182
183        public void setRoles(List<RoleName> roles) {
184                this.roles = roles;
185        }
186
187        public List<RoleName> getRows() {
188                return rows;
189        }
190
191        public void setRows(List ruleTemplateAttributes) {
192                this.rows = ruleTemplateAttributes;
193        }
194
195    @Override
196        public String getToDateString() {
197                return this.toDateString;
198        }
199
200        public void setToDateString(String toDateString) {
201                this.toDateString = toDateString;
202        }
203
204        @Override
205        public String getRuleTemplateName() {
206                return ruleTemplateName;
207        }
208
209        public void setRuleTemplateName(String ruleTemplateName) {
210                this.ruleTemplateName = ruleTemplateName;
211        }
212
213        public boolean isHasExtensionValueErrors() {
214                return hasExtensionValueErrors;
215        }
216
217        public void setHasExtensionValueErrors(boolean hasRuleExtensionValueErrors) {
218                this.hasExtensionValueErrors = hasRuleExtensionValueErrors;
219        }
220
221        /** Web Logic * */
222
223        /**
224         * Populates this WebRuleBaseValues object for editing the given rule.
225         */
226        public void edit(RuleBaseValues rule) throws Exception {
227                load(rule);
228                initialize();
229        }
230
231        /**
232         * Loads the given rule into this WebRuleBaseValues.
233         */
234        public void load(RuleBaseValues rule) throws Exception {
235                PropertyUtils.copyProperties(this, rule);
236                injectWebMembers();
237        }
238
239        public void initialize() throws Exception {
240                loadFields();
241                // setPreviousRuleId(getId());
242                for (Object element : getRuleResponsibilities()) {
243                        WebRuleResponsibility responsibility = (WebRuleResponsibility) element;
244                        responsibility.initialize();
245                }
246                establishRequiredState();
247        }
248
249        private void injectWebMembers() throws Exception {
250                List currentResponsibilities = getRuleResponsibilities();
251                setRuleResponsibilities(new ArrayList());
252                for (Iterator iterator = currentResponsibilities.iterator(); iterator.hasNext();) {
253                        RuleResponsibilityBo responsibility = (RuleResponsibilityBo) iterator.next();
254                        WebRuleResponsibility webResponsibility = createNewRuleResponsibility();
255                        webResponsibility.load(responsibility);
256                }
257        }
258
259        /**
260         * Establishes any missing and required state in the WebRuleBaseValues.
261         */
262        public void establishRequiredState() throws Exception {
263                loadWebValues();
264                if (getRuleResponsibilities().isEmpty()) {
265                        createNewRuleResponsibility();
266                }
267                for (Object element : getRuleResponsibilities()) {
268                        WebRuleResponsibility responsibility = (WebRuleResponsibility) element;
269                        responsibility.establishRequiredState();
270                }
271        }
272
273        @Override
274        public RuleResponsibilityBo getResponsibility(int index) {
275                while (getRuleResponsibilities().size() <= index) {
276                        createNewRuleResponsibility();
277                }
278                return getRuleResponsibilities().get(index);
279        }
280
281        public int getResponsibilitiesSize() {
282                return getRuleResponsibilities().size();
283        }
284
285        public WebRuleResponsibility createNewRuleResponsibility() {
286                WebRuleResponsibility responsibility = new WebRuleResponsibility();
287                responsibility.setRuleBaseValues(this);
288                addRuleResponsibility(responsibility);
289                return responsibility;
290        }
291
292        public Map getFieldMap(String ruleTemplateAttributeId) {
293                Map fieldMap = new HashMap();
294                for (Iterator iterator = getFields().iterator(); iterator.hasNext();) {
295                        KeyValueId field = (KeyValueId) iterator.next();
296                        if (ruleTemplateAttributeId.equals(field.getId())) {
297                                fieldMap.put(field.getKey(), field.getValue());
298                        }
299                }
300                return fieldMap;
301        }
302
303        public void populatePreviousRuleIds() {
304                if (getPreviousRuleId() == null) {
305                        setPreviousRuleId(getId());
306                }
307                for (Object element : getRuleResponsibilities()) {
308                        WebRuleResponsibility responsibility = (WebRuleResponsibility) element;
309                        responsibility.populatePreviousRuleIds();
310                }
311        }
312
313        /**
314         * This method is used to "materialize" the web rule before it gets saved, if we don't do this then certain fields will be saved as NULL. For example, ruleTemplate.
315         */
316        public void materialize() {
317                if (getRuleTemplate() == null && getRuleTemplateId() != null) {
318                        setRuleTemplate(getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId()));
319                }
320        }
321
322    /**
323     * This will ensure that the toDate is never larger than 2100, currently
324     * doesn't do any throttling on the from date
325     */
326    private void throttleDates() {
327        if (getToDateValue() != null) {
328            Calendar calendar = Calendar.getInstance();
329            calendar.setTime(getToDateValue());
330            if (calendar.get(Calendar.YEAR) > TO_DATE_UPPER_LIMIT) {
331                calendar.set(Calendar.YEAR, TO_DATE_UPPER_LIMIT);
332                setToDateValue(new Timestamp(calendar.getTimeInMillis()));
333                setToDateString(new SimpleDateFormat("MM/dd/yyyy").format(getToDateValue()));
334            }
335        }
336    }
337
338        private void saveServiceErrors(String errorKey, Collection srvErrors, ActionErrors errors) {
339                for (Iterator iterator = srvErrors.iterator(); iterator.hasNext();) {
340                        WorkflowServiceError error = (WorkflowServiceError) iterator.next();
341                        if (error.getArg1() == null && error.getArg2() == null) {
342                                errors.add(errorKey, new ActionMessage(error.getKey()));
343                        } else if (error.getArg1() != null && error.getArg2() == null) {
344                                errors.add(errorKey, new ActionMessage(error.getKey(), error.getArg1()));
345                        } else {
346                                errors.add(errorKey, new ActionMessage(error.getKey(), error.getArg1(), error.getArg2()));
347                        }
348                }
349        }
350
351        private Timestamp decodeTimestamp(String dateValue) throws ParseException {
352                if (org.apache.commons.lang.StringUtils.isEmpty(dateValue)) {
353                        return null;
354                }
355                /*  Not the best solution below but does allow our forcing of the 4 digit year
356                 *  until KEW and use the KNS for it's date entry/validation
357                 */
358                String convertedDate = SQLUtils.getEntryFormattedDate(dateValue);
359                if (convertedDate == null) {
360                    throw new ParseException("Date entered as '" + dateValue + "' is in invalid format", 0);
361                }
362                Date date = RiceConstants.getDefaultDateFormat().parse(convertedDate);
363                return new Timestamp(date.getTime());
364        }
365
366        private RuleTemplateService getRuleTemplateService() {
367                return (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE);
368        }
369
370        /**
371         * @return Returns the actionRequestCodes.
372         */
373        public Map getActionRequestCodes() {
374                Map actionRequestCodes = new HashMap();
375                actionRequestCodes.putAll(CodeTranslator.arLabels);
376                if (getRuleTemplateId() != null) {
377                        RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(getRuleTemplateId());
378                        if (ruleTemplate != null) {
379                                if (ruleTemplate.getAcknowledge() != null && "false".equals(ruleTemplate.getAcknowledge().getValue())) {
380                                        actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ);
381                                }
382                                if (ruleTemplate.getComplete() != null && "false".equals(ruleTemplate.getComplete().getValue())) {
383                                        actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ);
384                                }
385                                if (ruleTemplate.getApprove() != null && "false".equals(ruleTemplate.getApprove().getValue())) {
386                                        actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_APPROVE_REQ);
387                                }
388                                if (ruleTemplate.getFyi() != null && "false".equals(ruleTemplate.getFyi().getValue())) {
389                                        actionRequestCodes.remove(KewApiConstants.ACTION_REQUEST_FYI_REQ);
390                                }
391                        }
392                }
393                return actionRequestCodes;
394        }
395
396        public RuleDelegationBo getRuleDelegation() {
397                if (getDelegateRule().booleanValue()) {
398                        List ruleDelegations = getRuleDelegationService().findByDelegateRuleId(getId());
399                        RuleDelegationBo currentRuleDelegation = (RuleDelegationBo) ruleDelegations.get(0);
400                        RuleBaseValues mostRecentRule = currentRuleDelegation.getRuleResponsibility().getRuleBaseValues();
401
402                        for (Iterator iter = ruleDelegations.iterator(); iter.hasNext();) {
403                                RuleDelegationBo ruleDelegation = (RuleDelegationBo) iter.next();
404                                RuleBaseValues parentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues();
405
406                                if (parentRule.getActivationDate().after(mostRecentRule.getActivationDate())) {
407                                        mostRecentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues();
408                                        currentRuleDelegation = ruleDelegation;
409                                }
410                        }
411                        return currentRuleDelegation;
412                }
413                return null;
414        }
415
416        public String getParentRuleId() {
417                if (getDelegateRule().booleanValue()) {
418                        List ruleDelegations = getRuleDelegationService().findByDelegateRuleId(getId());
419                        RuleDelegationBo currentRuleDelegation = (RuleDelegationBo) ruleDelegations.get(0);
420                        RuleBaseValues mostRecentRule = currentRuleDelegation.getRuleResponsibility().getRuleBaseValues();
421
422                        for (Iterator iter = ruleDelegations.iterator(); iter.hasNext();) {
423                                RuleDelegationBo ruleDelegation = (RuleDelegationBo) iter.next();
424                                RuleBaseValues parentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues();
425
426                                if (parentRule.getActivationDate().after(mostRecentRule.getActivationDate())) {
427                                        mostRecentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues();
428                                }
429                        }
430                        return mostRecentRule.getId();
431                }
432                return null;
433        }
434
435        private RuleDelegationService getRuleDelegationService() {
436                return (RuleDelegationService) KEWServiceLocator.getService(KEWServiceLocator.RULE_DELEGATION_SERVICE);
437        }
438}