View Javadoc

1   /**
2    * Copyright 2005-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.rule.web;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.struts.action.ActionForm;
20  import org.apache.struts.action.ActionForward;
21  import org.apache.struts.action.ActionMapping;
22  import org.apache.struts.action.ActionMessages;
23  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
24  import org.kuali.rice.kew.api.KewApiConstants;
25  import org.kuali.rice.kew.api.action.ActionRequestStatus;
26  import org.kuali.rice.kew.doctype.bo.DocumentType;
27  import org.kuali.rice.kew.doctype.service.DocumentTypeService;
28  import org.kuali.rice.kew.engine.ActivationContext;
29  import org.kuali.rice.kew.engine.RouteContext;
30  import org.kuali.rice.kew.engine.node.RouteNode;
31  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
32  import org.kuali.rice.kew.exception.WorkflowServiceError;
33  import org.kuali.rice.kew.routeheader.AttributeDocumentContent;
34  import org.kuali.rice.kew.routeheader.DocumentContent;
35  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
36  import org.kuali.rice.kew.routelog.web.RouteLogAction;
37  import org.kuali.rice.kew.routelog.web.RouteLogForm;
38  import org.kuali.rice.kew.rule.FlexRM;
39  import org.kuali.rice.kew.rule.WorkflowRuleAttribute;
40  import org.kuali.rice.kew.rule.bo.RuleAttribute;
41  import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo;
42  import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
43  import org.kuali.rice.kew.rule.service.RuleTemplateService;
44  import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute;
45  import org.kuali.rice.kew.service.KEWServiceLocator;
46  import org.kuali.rice.kew.api.KewApiConstants;
47  import org.kuali.rice.kew.web.KewKualiAction;
48  import org.kuali.rice.kns.web.ui.Field;
49  import org.kuali.rice.kns.web.ui.Row;
50  import org.kuali.rice.krad.UserSession;
51  import org.kuali.rice.krad.exception.ValidationException;
52  import org.kuali.rice.krad.util.GlobalVariables;
53  
54  import javax.servlet.http.HttpServletRequest;
55  import javax.servlet.http.HttpServletResponse;
56  import java.sql.Timestamp;
57  import java.text.SimpleDateFormat;
58  import java.util.ArrayList;
59  import java.util.Calendar;
60  import java.util.Collections;
61  import java.util.Date;
62  import java.util.HashMap;
63  import java.util.HashSet;
64  import java.util.Iterator;
65  import java.util.List;
66  import java.util.Map;
67  import java.util.Set;
68  
69  
70  /**
71   * A Struts Action for executing routing reports and retrieving the results.
72   *
73   * @author Kuali Rice Team (rice.collab@kuali.org)
74   */
75  public class RoutingReportAction extends KewKualiAction {
76  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RoutingReportAction.class);
77  
78  	public static final String DOC_TYPE_REPORTING = "documentType";
79  	public static final String TEMPLATE_REPORTING = "template";
80  
81      @Override
82      public ActionForward execute(ActionMapping mapping, ActionForm form,
83              HttpServletRequest request, HttpServletResponse response)
84              throws Exception {
85          this.initiateForm(request, form);
86          RoutingReportForm routingForm = (RoutingReportForm)form;
87          if (org.apache.commons.lang.StringUtils.isEmpty(routingForm.getDateRef())) {
88              SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
89              routingForm.setEffectiveHour("5");
90              routingForm.setEffectiveMinute("0");
91              routingForm.setAmPm("1");
92              routingForm.setDateRef(sdf.format(new Date()));
93              routingForm.setReportType(TEMPLATE_REPORTING);
94          }
95          if (DOC_TYPE_REPORTING.equals(routingForm.getReportType())) {
96              if (org.apache.commons.lang.StringUtils.isEmpty(routingForm.getDocumentTypeParam())) {
97                  throw new RuntimeException("No document type was given");
98              }
99              if (org.apache.commons.lang.StringUtils.isEmpty(routingForm.getInitiatorPrincipalId())) {
100                 throw new RuntimeException("No initiator principal id was given");
101             }
102             if (org.apache.commons.lang.StringUtils.isEmpty(routingForm.getDocumentContent())) {
103                 throw new RuntimeException("No document content was given");
104             }
105         } else if (!(TEMPLATE_REPORTING.equals(routingForm.getReportType()))) {
106             // report type is not Document Type or Template Type... error out
107             throw new RuntimeException("The Routing Report type is not set");
108         }
109         return super.execute(mapping, form, request, response);
110     }
111 
112 	public ActionForward calculateRoute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
113 		RoutingReportForm routingForm = (RoutingReportForm) form;
114 
115         // this is never actually used??
116 		List<WorkflowServiceError> errors = new ArrayList<WorkflowServiceError>();
117 
118 		if (getDocumentTypeService().findByName(routingForm.getDocumentType()) == null) {
119 		    GlobalVariables.getMessageMap().putError("Document type is required.", "doctype.documenttypeservice.doctypename.required");
120 		}
121 		Timestamp date = null;
122 		if (!org.apache.commons.lang.StringUtils.isEmpty(routingForm.getDateRef())) {
123 			SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
124 			try {
125 				Calendar calendar = Calendar.getInstance();
126 				calendar.setTime(sdf.parse(routingForm.getDateRef()));
127 				calendar.set(Calendar.HOUR, Integer.parseInt(routingForm.getEffectiveHour()));
128 				calendar.set(Calendar.MINUTE, Integer.parseInt(routingForm.getEffectiveMinute()));
129 				calendar.set(Calendar.AM_PM, Integer.parseInt(routingForm.getAmPm()));
130 				date = new Timestamp(calendar.getTimeInMillis());
131 			} catch (Exception e) {
132 				LOG.error("error parsing date", e);
133 				GlobalVariables.getMessageMap().putError("Invalid date.", "routereport.effectiveDate.invalid");
134 			}
135 		}
136 
137 		if (!GlobalVariables.getMessageMap().hasNoErrors()) {
138             throw new ValidationException("Errors populating rule attributes.");
139         }
140 
141 		DocumentTypeService documentTypeService = (DocumentTypeService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE);
142 		DocumentType docType = documentTypeService.findByName(routingForm.getDocumentType());
143 
144 		DocumentRouteHeaderValue routeHeader = new DocumentRouteHeaderValue();
145 		routeHeader.setDocumentId("");
146 		routeHeader.setDocumentTypeId(docType.getDocumentTypeId());
147 		routeHeader.setDocRouteLevel(new Integer(0));
148         routeHeader.setDocVersion(new Integer(KewApiConstants.DocumentContentVersions.CURRENT));
149 
150         List<RouteReportRuleTemplateContainer> ruleTemplateContainers = new ArrayList<RouteReportRuleTemplateContainer>();
151 		if (routingForm.getReportType().equals(DOC_TYPE_REPORTING)) {
152 
153           List routeNodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(docType, true);
154 			for (Iterator iter = routeNodes.iterator(); iter.hasNext();) {
155                 RouteNode routeNode = (RouteNode) iter.next();
156 				if (routeNode.isFlexRM()) {
157 					RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateName(routeNode.getRouteMethodName());
158 					if (ruleTemplate != null) {
159 					    ruleTemplateContainers.add(new RouteReportRuleTemplateContainer(ruleTemplate, routeNode));
160 						if (ruleTemplate.getDelegationTemplate() != null) {
161 						    ruleTemplateContainers.add(new RouteReportRuleTemplateContainer(ruleTemplate.getDelegationTemplate(), routeNode));
162 						}
163 					}
164 				}
165 			}
166 		} else {
167 			RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(routingForm.getRuleTemplateId());
168 			RouteNode routeNode = new RouteNode();
169 			routeNode.setRouteNodeName(ruleTemplate.getName());
170 			ruleTemplateContainers.add(new RouteReportRuleTemplateContainer(ruleTemplate, routeNode));
171 			if (ruleTemplate.getDelegationTemplate() != null) {
172 			    ruleTemplateContainers.add(new RouteReportRuleTemplateContainer(ruleTemplate.getDelegationTemplate(), routeNode));
173 			}
174 		}
175 
176         String xmlDocumentContent = routingForm.getDocumentContent();
177         if (routingForm.getReportType().equals(TEMPLATE_REPORTING)) {
178             List<WorkflowRuleAttribute> attributes = new ArrayList<WorkflowRuleAttribute>();
179             for (RouteReportRuleTemplateContainer ruleTemplateContainer : ruleTemplateContainers) {
180                 RuleTemplateBo ruleTemplate = ruleTemplateContainer.ruleTemplate;
181                 for (RuleTemplateAttributeBo ruleTemplateAttribute : ruleTemplate.getActiveRuleTemplateAttributes()) {
182                     if (!ruleTemplateAttribute.isWorkflowAttribute()) {
183                         continue;
184                     }
185                     WorkflowRuleAttribute workflowAttribute = ruleTemplateAttribute.getWorkflowAttribute();
186 
187                     RuleAttribute ruleAttribute = ruleTemplateAttribute.getRuleAttribute();
188                     if (ruleAttribute.getType().equals(KewApiConstants.RULE_XML_ATTRIBUTE_TYPE)) {
189                         ((GenericXMLRuleAttribute) workflowAttribute).setExtensionDefinition(RuleAttribute.to(ruleAttribute));
190                     }
191                     List attValidationErrors = workflowAttribute.validateRoutingData(routingForm.getFields());
192                     if (attValidationErrors != null && !attValidationErrors.isEmpty()) {
193                         errors.addAll(attValidationErrors);
194                     }
195                     attributes.add(workflowAttribute);
196                 }
197             }
198 
199             if (!GlobalVariables.getMessageMap().hasNoErrors()) {
200                 throw new ValidationException("errors in search criteria");
201             }
202 
203             DocumentContent docContent = new AttributeDocumentContent(attributes);
204             xmlDocumentContent = docContent.getDocContent();
205         }
206 
207 		routeHeader.setDocContent(xmlDocumentContent);
208 		routeHeader.setInitiatorWorkflowId(getUserSession(request).getPrincipalId());
209 		routeHeader.setDocRouteStatus(KewApiConstants.ROUTE_HEADER_INITIATED_CD);
210 		routeHeader.setDocTitle("Routing Report");
211 		routeHeader.setRoutingReport(true);
212 		long magicCounter = 0;
213 
214 		FlexRM flexRM = new FlexRM(date);
215 
216 		int numberOfRules = 0;
217 		int numberOfActionRequests = 0;
218 		Set<String> alreadyProcessedRuleTemplateNames = new HashSet<String>();
219 		for (Object element : ruleTemplateContainers) {
220 			// initialize the RouteContext
221 		    RouteContext context = RouteContext.createNewRouteContext();
222 		context.setActivationContext(new ActivationContext(ActivationContext.CONTEXT_IS_SIMULATION));
223 			try {
224 			    RouteReportRuleTemplateContainer ruleTemplateContainer = (RouteReportRuleTemplateContainer) element;
225 				RuleTemplateBo ruleTemplate = ruleTemplateContainer.ruleTemplate;
226 				RouteNode routeLevel = ruleTemplateContainer.routeNode;
227 
228 				if (!alreadyProcessedRuleTemplateNames.contains(ruleTemplate.getName())) {
229 				    alreadyProcessedRuleTemplateNames.add(ruleTemplate.getName());
230     				List<ActionRequestValue> actionRequests = flexRM.getActionRequests(routeHeader, routeLevel, null, ruleTemplate.getName());
231 
232     				numberOfActionRequests += actionRequests.size();
233     				numberOfRules += flexRM.getNumberOfMatchingRules();
234 
235     				magicCounter = populateActionRequestsWithRouteLevelInformationAndIterateMagicCounter(routeLevel, actionRequests, magicCounter);
236     				//routeHeader.getActionRequests().addAll(actionRequests);
237     				routeHeader.getSimulatedActionRequests().addAll(actionRequests);
238 				}
239 			} finally {
240 				RouteContext.clearCurrentRouteContext();
241 			}
242 		}
243 
244 		if (numberOfActionRequests == 0) {
245 			if (numberOfRules == 0) {
246 			    GlobalVariables.getMessageMap().putError("*", "routereport.noRules");
247 			} else {
248 			    GlobalVariables.getMessageMap().putError("*", "routereport.noMatchingRules");
249 			}
250 			if (GlobalVariables.getMessageMap().hasErrors()) {
251 	            throw new ValidationException("errors in search criteria");
252 	        }
253 		}
254 
255 
256 		// PROBLEM HERE!!!!
257 		RouteLogForm routeLogForm = new RouteLogForm();
258 		routeLogForm.setShowFuture(true);
259         if (StringUtils.isNotBlank(routingForm.getBackUrl())) {
260             routeLogForm.setReturnUrlLocation(routingForm.getBackUrl());
261         }
262         LOG.debug("Value of getDisplayCloseButton " + routingForm.getShowCloseButton());
263         LOG.debug("Value of isDisplayCloseButton " + routingForm.isDisplayCloseButton());
264         routeLogForm.setShowCloseButton(routingForm.isDisplayCloseButton());
265 		request.setAttribute("routeHeader", routeHeader);
266 		new RouteLogAction().populateRouteLogFormActionRequests(routeLogForm, routeHeader);
267 		request.setAttribute("KualiForm", routeLogForm);
268 		//END PROBLEM AREA
269 
270 		//return mapping.findForward("basic");
271 		return mapping.findForward("routeLog");
272 	}
273 
274 	private class RouteReportRuleTemplateContainer {
275 	    public RuleTemplateBo ruleTemplate = null;
276 	    public RouteNode routeNode = null;
277 	    public RouteReportRuleTemplateContainer(RuleTemplateBo template, RouteNode node) {
278 	        this.ruleTemplate = template;
279 	        this.routeNode = node;
280 	    }
281 	}
282 
283 	public long populateActionRequestsWithRouteLevelInformationAndIterateMagicCounter(RouteNode routeLevel, List<ActionRequestValue> actionRequests, long magicCounter) {
284 
285 		for (ActionRequestValue actionRequest : actionRequests) {
286 			populateActionRequestsWithRouteLevelInformationAndIterateMagicCounter(routeLevel, actionRequest.getChildrenRequests(), magicCounter);
287 			actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode());
288 //			actionRequest.setRouteMethodName(routeLevel.getRouteMethodName());
289 			RouteNodeInstance routeNode = new RouteNodeInstance();
290 			routeNode.setRouteNode(routeLevel);
291 			actionRequest.setNodeInstance(routeNode);
292 			actionRequest.setRouteLevel(new Integer(0));
293 			magicCounter++;
294 			actionRequest.setActionRequestId(String.valueOf(magicCounter));
295 		}
296 		return magicCounter;
297 	}
298 
299 	@Override
300 	public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
301 	    return mapping.findForward("basic");
302 	}
303 
304 	private ActionMessages initiateForm(HttpServletRequest request, ActionForm form) throws Exception {
305         RoutingReportForm routingReportForm = (RoutingReportForm) form;
306         if (routingReportForm.getReportType() == null) {
307             // no report type means we must check for potential setup
308             if ( (!org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getDocumentTypeParam())) ||
309                  (!org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getInitiatorPrincipalId())) ||
310                  (!org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getDocumentContent())) ) {
311                 // at least one parameter was passed... attempt to use Doc Type Report
312                 routingReportForm.setReportType(DOC_TYPE_REPORTING);
313             } else {
314                 // no parameters passed... default to Template Type Rreport
315                 routingReportForm.setReportType(TEMPLATE_REPORTING);
316             }
317         }
318 
319         if (routingReportForm.getReportType().equals(DOC_TYPE_REPORTING)) {
320             if (org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getDocumentTypeParam())) {
321                 throw new RuntimeException("Document Type was not given");
322             } else {
323                 DocumentType docType = getDocumentTypeService().findByName(routingReportForm.getDocumentTypeParam());
324                 if (docType == null) {
325                     throw new RuntimeException("Document Type is invalid");
326                 }
327             }
328             if (org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getInitiatorPrincipalId())) {
329                 throw new RuntimeException("Initiator Principal ID was not given");
330             } else {
331                 KEWServiceLocator.getIdentityHelperService().getPrincipal(routingReportForm.getInitiatorPrincipalId());
332             }
333             if (org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getDocumentContent())) {
334                 throw new RuntimeException("Document Content was not given");
335             }
336 
337             if (!org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getDocumentType())) {
338                 DocumentType docType = getDocumentTypeService().findByName(routingReportForm.getDocumentType());
339                 if (docType == null) {
340                     throw new RuntimeException("Document Type is missing or invalid");
341                 }
342                 routingReportForm.getRuleTemplateAttributes().clear();
343                 List<RouteNode> routeNodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(docType, true);
344                 for (RouteNode routeNode : routeNodes) {
345                     if (routeNode.isFlexRM()) {
346                         RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateName(routeNode.getRouteMethodName());
347                         if (ruleTemplate != null) {
348                             loadRuleTemplateOnForm(ruleTemplate, routingReportForm, request, false);
349                             if (ruleTemplate.getDelegationTemplate() != null) {
350                                 loadRuleTemplateOnForm(ruleTemplate.getDelegationTemplate(), routingReportForm, request, true);
351                             }
352                         }
353                     }
354                 }
355             }
356 //          routingReportForm.setShowFields(true);
357         } else if (routingReportForm.getReportType().equals(TEMPLATE_REPORTING)) {
358             routingReportForm.setRuleTemplates(getRuleTemplateService().findAll());
359             if (routingReportForm.getRuleTemplateId() != null) {
360                 RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateId(routingReportForm.getRuleTemplateId());
361                 routingReportForm.getRuleTemplateAttributes().clear();
362                 loadRuleTemplateOnForm(ruleTemplate, routingReportForm, request, false);
363                 if (ruleTemplate.getDelegationTemplate() != null) {
364                     loadRuleTemplateOnForm(ruleTemplate.getDelegationTemplate(), routingReportForm, request, true);
365                 }
366             }
367         }
368         return null;
369 	}
370 
371 	private void loadRuleTemplateOnForm(RuleTemplateBo ruleTemplate, RoutingReportForm routingReportForm, HttpServletRequest request, boolean isDelegate) {
372 
373 		Map<String, String> fieldValues = new HashMap<String, String>();
374 
375 		List<RuleTemplateAttributeBo> ruleTemplateAttributes = ruleTemplate.getActiveRuleTemplateAttributes();
376 		Collections.sort(ruleTemplateAttributes);
377 
378 		List<Row> rows = new ArrayList<Row>();
379 		for (RuleTemplateAttributeBo ruleTemplateAttribute : ruleTemplateAttributes) {
380 			if (!ruleTemplateAttribute.isWorkflowAttribute()) {
381 				continue;
382 			}
383 			WorkflowRuleAttribute workflowAttribute = ruleTemplateAttribute.getWorkflowAttribute();
384 
385 			RuleAttribute ruleAttribute = ruleTemplateAttribute.getRuleAttribute();
386 			if (ruleAttribute.getType().equals(KewApiConstants.RULE_XML_ATTRIBUTE_TYPE)) {
387 				((GenericXMLRuleAttribute) workflowAttribute).setExtensionDefinition(RuleAttribute.to(ruleAttribute));
388 			}
389 			for (Row row : workflowAttribute.getRoutingDataRows()) {
390 
391 				List<Field> fields = new ArrayList<Field>();
392 				for (Object element2 : row.getFields()) {
393 					Field field = (Field) element2;
394 					if (request.getParameter(field.getPropertyName()) != null) {
395 						field.setPropertyValue(request.getParameter(field.getPropertyName()));
396 					} else if (routingReportForm.getFields() != null && !routingReportForm.getFields().isEmpty()) {
397 						field.setPropertyValue((String) routingReportForm.getFields().get(field.getPropertyName()));
398 					}
399 					fields.add(field);
400 					fieldValues.put(field.getPropertyName(), field.getPropertyValue());
401 				}
402 			}
403 
404 			workflowAttribute.validateRuleData(fieldValues);// populate attribute
405 			List<Row> rdRows = workflowAttribute.getRoutingDataRows();
406 			for (Row row : rdRows)
407 			{
408 				List<Field> fields = new ArrayList<Field>();
409 				List<Field> rowFields = row.getFields();
410 				for (Field field : rowFields )
411 				{
412 					if (request.getParameter(field.getPropertyName()) != null) {
413 						field.setPropertyValue(request.getParameter(field.getPropertyName()));
414 					} else if (routingReportForm.getFields() != null && !routingReportForm.getFields().isEmpty()) {
415 						field.setPropertyValue((String) routingReportForm.getFields().get(field.getPropertyName()));
416 					}
417 					fields.add(field);
418 					fieldValues.put(field.getPropertyName(), field.getPropertyValue());
419 				}
420 				row.setFields(fields);
421 				rows.add(row);
422 
423 			}
424 		}
425 
426 		routingReportForm.getFields().putAll(fieldValues);
427 		routingReportForm.getRuleTemplateAttributes().addAll(rows);
428 		routingReportForm.setShowFields(true);
429 		routingReportForm.setShowViewResults(true);
430 	}
431 
432 	public ActionForward loadTemplate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
433 		RoutingReportForm routingReportForm = (RoutingReportForm) form;
434 		if (org.apache.commons.lang.StringUtils.isEmpty(routingReportForm.getDateRef())) {
435 			SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
436 			routingReportForm.setEffectiveHour("5");
437 			routingReportForm.setEffectiveMinute("0");
438 			routingReportForm.setAmPm("1");
439 			routingReportForm.setDateRef(sdf.format(new Date()));
440 		}
441 		return mapping.findForward("basic");
442 	}
443 
444 	private RuleTemplateService getRuleTemplateService() {
445 		return (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE);
446 	}
447 
448 	private DocumentTypeService getDocumentTypeService() {
449 		return (DocumentTypeService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE);
450 	}
451 
452 	private UserSession getUserSession(HttpServletRequest request) {
453 	    return GlobalVariables.getUserSession();
454 	}
455 
456 
457 
458 
459 }