001    /**
002     * Copyright 2005-2014 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.rice.kew.rule.web;
017    
018    import org.apache.commons.beanutils.PropertyUtils;
019    import org.apache.struts.action.ActionErrors;
020    import org.apache.struts.action.ActionMessage;
021    import org.kuali.rice.core.api.util.RiceConstants;
022    import org.kuali.rice.core.framework.persistence.jdbc.sql.SQLUtils;
023    import org.kuali.rice.kew.api.KewApiConstants;
024    import org.kuali.rice.kew.api.rule.RoleName;
025    import org.kuali.rice.kew.api.util.CodeTranslator;
026    import org.kuali.rice.kew.exception.WorkflowServiceError;
027    import org.kuali.rice.kew.rule.KeyValueId;
028    import org.kuali.rice.kew.rule.RuleBaseValues;
029    import org.kuali.rice.kew.rule.RuleDelegationBo;
030    import org.kuali.rice.kew.rule.RuleExtensionValue;
031    import org.kuali.rice.kew.rule.RuleResponsibilityBo;
032    import org.kuali.rice.kew.rule.WorkflowRuleAttributeRows;
033    import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo;
034    import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
035    import org.kuali.rice.kew.rule.service.RuleDelegationService;
036    import org.kuali.rice.kew.rule.service.RuleTemplateService;
037    import org.kuali.rice.kew.service.KEWServiceLocator;
038    import org.kuali.rice.kns.web.ui.Field;
039    import org.kuali.rice.kns.web.ui.Row;
040    
041    import java.sql.Timestamp;
042    import java.text.ParseException;
043    import java.text.SimpleDateFormat;
044    import java.util.ArrayList;
045    import java.util.Calendar;
046    import java.util.Collection;
047    import java.util.Collections;
048    import java.util.Date;
049    import java.util.HashMap;
050    import java.util.Iterator;
051    import java.util.List;
052    import 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     */
062    public 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    }