1 16 package org.springframework.webflow.engine.builder.xml; 17 18 import java.io.IOException ; 19 import java.util.Arrays ; 20 import java.util.Collections ; 21 import java.util.Iterator ; 22 import java.util.LinkedList ; 23 import java.util.List ; 24 25 import javax.xml.parsers.ParserConfigurationException ; 26 27 import org.springframework.beans.factory.BeanFactory; 28 import org.springframework.binding.convert.ConversionExecutor; 29 import org.springframework.binding.convert.ConversionService; 30 import org.springframework.binding.expression.Expression; 31 import org.springframework.binding.expression.ExpressionParser; 32 import org.springframework.binding.expression.SettableExpression; 33 import org.springframework.binding.expression.support.CollectionAddingExpression; 34 import org.springframework.binding.mapping.AttributeMapper; 35 import org.springframework.binding.mapping.DefaultAttributeMapper; 36 import org.springframework.binding.mapping.Mapping; 37 import org.springframework.binding.mapping.RequiredMapping; 38 import org.springframework.binding.method.MethodSignature; 39 import org.springframework.binding.method.Parameter; 40 import org.springframework.binding.method.Parameters; 41 import org.springframework.core.io.Resource; 42 import org.springframework.util.Assert; 43 import org.springframework.util.StringUtils; 44 import org.springframework.util.xml.DomUtils; 45 import org.springframework.webflow.action.ActionResultExposer; 46 import org.springframework.webflow.action.EvaluateAction; 47 import org.springframework.webflow.action.SetAction; 48 import org.springframework.webflow.core.collection.AttributeMap; 49 import org.springframework.webflow.core.collection.LocalAttributeMap; 50 import org.springframework.webflow.core.collection.MutableAttributeMap; 51 import org.springframework.webflow.engine.AnnotatedAction; 52 import org.springframework.webflow.engine.Flow; 53 import org.springframework.webflow.engine.FlowAttributeMapper; 54 import org.springframework.webflow.engine.FlowExecutionExceptionHandler; 55 import org.springframework.webflow.engine.FlowVariable; 56 import org.springframework.webflow.engine.TargetStateResolver; 57 import org.springframework.webflow.engine.Transition; 58 import org.springframework.webflow.engine.TransitionCriteria; 59 import org.springframework.webflow.engine.ViewSelector; 60 import org.springframework.webflow.engine.builder.BaseFlowBuilder; 61 import org.springframework.webflow.engine.builder.FlowArtifactFactory; 62 import org.springframework.webflow.engine.builder.FlowBuilderException; 63 import org.springframework.webflow.engine.builder.FlowServiceLocator; 64 import org.springframework.webflow.engine.support.AttributeExpression; 65 import org.springframework.webflow.engine.support.BeanFactoryFlowVariable; 66 import org.springframework.webflow.engine.support.BooleanExpressionTransitionCriteria; 67 import org.springframework.webflow.engine.support.SimpleFlowVariable; 68 import org.springframework.webflow.engine.support.TransitionCriteriaChain; 69 import org.springframework.webflow.engine.support.TransitionExecutingStateExceptionHandler; 70 import org.springframework.webflow.execution.Action; 71 import org.springframework.webflow.execution.ScopeType; 72 import org.springframework.webflow.util.ResourceHolder; 73 import org.w3c.dom.Document ; 74 import org.w3c.dom.Element ; 75 import org.w3c.dom.Node ; 76 import org.w3c.dom.NodeList ; 77 import org.xml.sax.SAXException ; 78 79 111 public class XmlFlowBuilder extends BaseFlowBuilder implements ResourceHolder { 112 113 115 private static final String ID_ATTRIBUTE = "id"; 116 117 private static final String IDREF_ATTRIBUTE = "idref"; 118 119 private static final String BEAN_ATTRIBUTE = "bean"; 120 121 private static final String FLOW_ELEMENT = "flow"; 122 123 private static final String START_STATE_ELEMENT = "start-state"; 124 125 private static final String ACTION_STATE_ELEMENT = "action-state"; 126 127 private static final String ACTION_ELEMENT = "action"; 128 129 private static final String NAME_ATTRIBUTE = "name"; 130 131 private static final String METHOD_ATTRIBUTE = "method"; 132 133 private static final String BEAN_ACTION_ELEMENT = "bean-action"; 134 135 private static final String METHOD_ARGUMENTS_ELEMENT = "method-arguments"; 136 137 private static final String ARGUMENT_ELEMENT = "argument"; 138 139 private static final String EXPRESSION_ATTRIBUTE = "expression"; 140 141 private static final String PARAMETER_TYPE_ATTRIBUTE = "parameter-type"; 142 143 private static final String METHOD_RESULT_ELEMENT = "method-result"; 144 145 private static final String EVALUATE_ACTION_ELEMENT = "evaluate-action"; 146 147 private static final String SET_ELEMENT = "set"; 148 149 private static final String ATTRIBUTE_ATTRIBUTE = "attribute"; 150 151 private static final String EVALUATION_RESULT_ELEMENT = "evaluation-result"; 152 153 private static final String DEFAULT_VALUE = "default"; 154 155 private static final String VIEW_STATE_ELEMENT = "view-state"; 156 157 private static final String VIEW_ATTRIBUTE = "view"; 158 159 private static final String DECISION_STATE_ELEMENT = "decision-state"; 160 161 private static final String IF_ELEMENT = "if"; 162 163 private static final String TEST_ATTRIBUTE = "test"; 164 165 private static final String THEN_ATTRIBUTE = "then"; 166 167 private static final String ELSE_ATTRIBUTE = "else"; 168 169 private static final String SUBFLOW_STATE_ELEMENT = "subflow-state"; 170 171 private static final String FLOW_ATTRIBUTE = "flow"; 172 173 private static final String ATTRIBUTE_MAPPER_ELEMENT = "attribute-mapper"; 174 175 private static final String OUTPUT_MAPPER_ELEMENT = "output-mapper"; 176 177 private static final String OUTPUT_ATTRIBUTE_ELEMENT = "output-attribute"; 178 179 private static final String INPUT_MAPPER_ELEMENT = "input-mapper"; 180 181 private static final String INPUT_ATTRIBUTE_ELEMENT = "input-attribute"; 182 183 private static final String MAPPING_ELEMENT = "mapping"; 184 185 private static final String SOURCE_ATTRIBUTE = "source"; 186 187 private static final String TARGET_ATTRIBUTE = "target"; 188 189 private static final String FROM_ATTRIBUTE = "from"; 190 191 private static final String TO_ATTRIBUTE = "to"; 192 193 private static final String REQUIRED_ATTRIBUTE = "required"; 194 195 private static final String TARGET_COLLECTION_ATTRIBUTE = "target-collection"; 196 197 private static final String END_STATE_ELEMENT = "end-state"; 198 199 private static final String TRANSITION_ELEMENT = "transition"; 200 201 private static final String GLOBAL_TRANSITIONS_ELEMENT = "global-transitions"; 202 203 private static final String ON_ATTRIBUTE = "on"; 204 205 private static final String ON_EXCEPTION_ATTRIBUTE = "on-exception"; 206 207 private static final String ATTRIBUTE_ELEMENT = "attribute"; 208 209 private static final String TYPE_ATTRIBUTE = "type"; 210 211 private static final String VALUE_ELEMENT = "value"; 212 213 private static final String VALUE_ATTRIBUTE = "value"; 214 215 private static final String VAR_ELEMENT = "var"; 216 217 private static final String SCOPE_ATTRIBUTE = "scope"; 218 219 private static final String CLASS_ATTRIBUTE = "class"; 220 221 private static final String START_ACTIONS_ELEMENT = "start-actions"; 222 223 private static final String END_ACTIONS_ELEMENT = "end-actions"; 224 225 private static final String ENTRY_ACTIONS_ELEMENT = "entry-actions"; 226 227 private static final String RENDER_ACTIONS_ELEMENT = "render-actions"; 228 229 private static final String EXIT_ACTIONS_ELEMENT = "exit-actions"; 230 231 private static final String EXCEPTION_HANDLER_ELEMENT = "exception-handler"; 232 233 private static final String INLINE_FLOW_ELEMENT = "inline-flow"; 234 235 private static final String IMPORT_ELEMENT = "import"; 236 237 private static final String RESOURCE_ATTRIBUTE = "resource"; 238 239 243 protected Resource location; 244 245 250 private LocalFlowServiceLocator localFlowServiceLocator; 251 252 255 private DocumentLoader documentLoader = new DefaultDocumentLoader(); 256 257 261 private Document document; 262 263 268 public XmlFlowBuilder(Resource location) { 269 setLocation(location); 270 } 271 272 280 public XmlFlowBuilder(Resource location, FlowServiceLocator flowServiceLocator) { 281 super(flowServiceLocator); 282 setLocation(location); 283 } 284 285 289 public Resource getLocation() { 290 return location; 291 } 292 293 297 public void setLocation(Resource location) { 298 Assert.notNull(location, "The resource location of the XML-based flow definition is required"); 299 this.location = location; 300 } 301 302 307 public void setDocumentLoader(DocumentLoader documentLoader) { 308 Assert.notNull(documentLoader, "The XML document loader is required"); 309 this.documentLoader = documentLoader; 310 } 311 312 314 public void init(String id, AttributeMap attributes) throws FlowBuilderException { 315 localFlowServiceLocator = new LocalFlowServiceLocator(getFlowServiceLocator()); 316 try { 317 document = documentLoader.loadDocument(location); 318 } 319 catch (IOException e) { 320 throw new FlowBuilderException("Could not access the XML flow definition resource at " + location, e); 321 } 322 catch (ParserConfigurationException e) { 323 throw new FlowBuilderException("Could not configure the parser to parse the XML flow definition at " 324 + location, e); 325 } 326 catch (SAXException e) { 327 throw new FlowBuilderException("Could not parse the XML flow definition document at " + location, e); 328 } 329 setFlow(parseFlow(id, attributes, getDocumentElement())); 330 } 331 332 public void buildVariables() throws FlowBuilderException { 333 parseAndAddFlowVariables(getDocumentElement(), getFlow()); 334 } 335 336 public void buildInputMapper() throws FlowBuilderException { 337 getFlow().setInputMapper(parseInputMapper(getDocumentElement())); 338 } 339 340 public void buildStartActions() throws FlowBuilderException { 341 parseAndAddStartActions(getDocumentElement(), getFlow()); 342 } 343 344 public void buildInlineFlows() throws FlowBuilderException { 345 parseAndAddInlineFlowDefinitions(getDocumentElement(), getFlow()); 346 } 347 348 public void buildStates() throws FlowBuilderException { 349 parseAndAddStateDefinitions(getDocumentElement(), getFlow()); 350 } 351 352 public void buildGlobalTransitions() throws FlowBuilderException { 353 parseAndAddGlobalTransitions(getDocumentElement(), getFlow()); 354 } 355 356 public void buildEndActions() throws FlowBuilderException { 357 parseAndAddEndActions(getDocumentElement(), getFlow()); 358 } 359 360 public void buildOutputMapper() throws FlowBuilderException { 361 getFlow().setOutputMapper(parseOutputMapper(getDocumentElement())); 362 } 363 364 public void buildExceptionHandlers() throws FlowBuilderException { 365 getFlow().getExceptionHandlerSet().addAll(parseExceptionHandlers(getDocumentElement())); 366 } 367 368 public void dispose() { 369 super.dispose(); 370 localFlowServiceLocator.diposeOfAnyRegistries(); 371 document = null; 372 } 373 374 376 public Resource getResource() { 377 return location; 378 } 379 380 382 385 protected Document getDocument() { 386 return document; 387 } 388 389 392 protected Element getDocumentElement() { 393 return document.getDocumentElement(); 394 } 395 396 399 protected FlowServiceLocator getLocalFlowServiceLocator() { 400 return localFlowServiceLocator; 401 } 402 403 407 protected FlowArtifactFactory getFlowArtifactFactory() { 408 return getLocalFlowServiceLocator().getFlowArtifactFactory(); 409 } 410 411 413 421 protected Element getChildElementByTagName(Element ele, String childEleName) { 422 NodeList nl = ele.getChildNodes(); 423 for (int i = 0; i < nl.getLength(); i++) { 424 Node node = nl.item(i); 425 if (node instanceof Element && nodeNameEquals(node, childEleName)) { 426 return (Element )node; 427 } 428 } 429 return null; 430 } 431 432 437 protected boolean nodeNameEquals(Node node, String desiredName) { 438 return desiredName.equals(node.getNodeName()) || desiredName.equals(node.getLocalName()); 439 } 440 441 443 private Flow parseFlow(String id, AttributeMap attributes, Element flowElement) { 444 if (!isFlowElement(flowElement)) { 445 throw new IllegalStateException ("This is not the '" + FLOW_ELEMENT + "' element"); 446 } 447 Flow flow = getFlowArtifactFactory().createFlow(id, parseAttributes(flowElement).union(attributes)); 448 initLocalServiceRegistry(flowElement, flow); 449 return flow; 450 } 451 452 private boolean isFlowElement(Element flowElement) { 453 return FLOW_ELEMENT.equals(flowElement.getTagName()); 454 } 455 456 private void initLocalServiceRegistry(Element flowElement, Flow flow) { 457 List importElements = DomUtils.getChildElementsByTagName(flowElement, IMPORT_ELEMENT); 458 Resource[] resources = new Resource[importElements.size()]; 459 for (int i = 0; i < importElements.size(); i++) { 460 Element importElement = (Element )importElements.get(i); 461 try { 462 resources[i] = getLocation().createRelative(importElement.getAttribute(RESOURCE_ATTRIBUTE)); 463 } 464 catch (IOException e) { 465 throw new FlowBuilderException("Could not access flow-relative artifact resource '" 466 + importElement.getAttribute(RESOURCE_ATTRIBUTE) + "'", e); 467 } 468 } 469 localFlowServiceLocator.push(new LocalFlowServiceRegistry(flow, resources)); 470 } 471 472 private void destroyLocalServiceRegistry(Flow flow) { 473 localFlowServiceLocator.pop(); 474 } 475 476 private void parseAndAddFlowVariables(Element flowElement, Flow flow) { 477 List varElements = DomUtils.getChildElementsByTagName(flowElement, VAR_ELEMENT); 478 for (Iterator it = varElements.iterator(); it.hasNext();) { 479 flow.addVariable(parseVariable((Element )it.next())); 480 } 481 } 482 483 private FlowVariable parseVariable(Element element) { 484 ScopeType scope = parseScope(element, ScopeType.FLOW); 485 if (StringUtils.hasText(element.getAttribute(BEAN_ATTRIBUTE))) { 486 BeanFactory beanFactory = getLocalFlowServiceLocator().getBeanFactory(); 487 return new BeanFactoryFlowVariable(element.getAttribute(NAME_ATTRIBUTE), 488 element.getAttribute(BEAN_ATTRIBUTE), beanFactory, scope); 489 } 490 else { 491 if (StringUtils.hasText(element.getAttribute(CLASS_ATTRIBUTE))) { 492 Class variableClass = (Class )fromStringTo(Class .class).execute(element.getAttribute(CLASS_ATTRIBUTE)); 493 return new SimpleFlowVariable(element.getAttribute(NAME_ATTRIBUTE), variableClass, scope); 494 } 495 else { 496 BeanFactory beanFactory = getLocalFlowServiceLocator().getBeanFactory(); 497 return new BeanFactoryFlowVariable(element.getAttribute(NAME_ATTRIBUTE), null, beanFactory, scope); 498 } 499 } 500 } 501 502 private void parseAndAddStartActions(Element element, Flow flow) { 503 Element startElement = getChildElementByTagName(element, START_ACTIONS_ELEMENT); 504 if (startElement != null) { 505 flow.getStartActionList().addAll(parseAnnotatedActions(startElement)); 506 } 507 } 508 509 private void parseAndAddEndActions(Element element, Flow flow) { 510 Element endElement = getChildElementByTagName(element, END_ACTIONS_ELEMENT); 511 if (endElement != null) { 512 flow.getEndActionList().addAll(parseAnnotatedActions(endElement)); 513 } 514 } 515 516 private void parseAndAddGlobalTransitions(Element element, Flow flow) { 517 Element globalTransitionsElement = getChildElementByTagName(element, GLOBAL_TRANSITIONS_ELEMENT); 518 if (globalTransitionsElement != null) { 519 flow.getGlobalTransitionSet().addAll(parseTransitions(globalTransitionsElement)); 520 } 521 } 522 523 private void parseAndAddInlineFlowDefinitions(Element parentFlowElement, Flow flow) { 524 List inlineFlowElements = DomUtils.getChildElementsByTagName(parentFlowElement, INLINE_FLOW_ELEMENT); 525 for (Iterator it = inlineFlowElements.iterator(); it.hasNext();) { 526 Element inlineFlowElement = (Element )it.next(); 527 String inlineFlowId = inlineFlowElement.getAttribute(ID_ATTRIBUTE); 528 Element flowElement = getChildElementByTagName(inlineFlowElement, FLOW_ATTRIBUTE); 529 Flow inlineFlow = parseFlow(inlineFlowId, null, flowElement); 530 buildInlineFlow(flowElement, inlineFlow); 531 flow.addInlineFlow(inlineFlow); 532 } 533 } 534 535 private void buildInlineFlow(Element flowElement, Flow inlineFlow) { 536 parseAndAddFlowVariables(flowElement, inlineFlow); 537 inlineFlow.setInputMapper(parseInputMapper(flowElement)); 538 parseAndAddStartActions(flowElement, inlineFlow); 539 parseAndAddInlineFlowDefinitions(flowElement, inlineFlow); 540 parseAndAddStateDefinitions(flowElement, inlineFlow); 541 parseAndAddGlobalTransitions(flowElement, inlineFlow); 542 parseAndAddEndActions(flowElement, inlineFlow); 543 inlineFlow.setOutputMapper(parseOutputMapper(flowElement)); 544 inlineFlow.getExceptionHandlerSet().addAll(parseExceptionHandlers(flowElement)); 545 546 destroyLocalServiceRegistry(inlineFlow); 547 } 548 549 private void parseAndAddStateDefinitions(Element flowElement, Flow flow) { 550 NodeList childNodeList = flowElement.getChildNodes(); 551 for (int i = 0; i < childNodeList.getLength(); i++) { 552 Node childNode = childNodeList.item(i); 553 if (childNode instanceof Element ) { 554 Element stateElement = (Element )childNode; 555 if (nodeNameEquals(stateElement, ACTION_STATE_ELEMENT)) { 556 parseAndAddActionState(stateElement, flow); 557 } 558 else if (nodeNameEquals(stateElement, VIEW_STATE_ELEMENT)) { 559 parseAndAddViewState(stateElement, flow); 560 } 561 else if (nodeNameEquals(stateElement, DECISION_STATE_ELEMENT)) { 562 parseAndAddDecisionState(stateElement, flow); 563 } 564 else if (nodeNameEquals(stateElement, SUBFLOW_STATE_ELEMENT)) { 565 parseAndAddSubflowState(stateElement, flow); 566 } 567 else if (nodeNameEquals(stateElement, END_STATE_ELEMENT)) { 568 parseAndAddEndState(stateElement, flow); 569 } 570 } 571 } 572 parseAndSetStartState(flowElement, flow); 573 } 574 575 private void parseAndSetStartState(Element element, Flow flow) { 576 String startStateId = getStartStateId(element); 577 flow.setStartState(startStateId); 578 } 579 580 private String getStartStateId(Element element) { 581 Element startStateElement = getChildElementByTagName(element, START_STATE_ELEMENT); 582 return startStateElement.getAttribute(IDREF_ATTRIBUTE); 583 } 584 585 private void parseAndAddActionState(Element element, Flow flow) { 586 getFlowArtifactFactory().createActionState(parseId(element), flow, parseEntryActions(element), 587 parseAnnotatedActions(element), parseTransitions(element), parseExceptionHandlers(element), 588 parseExitActions(element), parseAttributes(element)); 589 } 590 591 private void parseAndAddViewState(Element element, Flow flow) { 592 getFlowArtifactFactory().createViewState(parseId(element), flow, parseEntryActions(element), 593 parseViewSelector(element), parseRenderActions(element), parseTransitions(element), 594 parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element)); 595 } 596 597 private void parseAndAddDecisionState(Element element, Flow flow) { 598 getFlowArtifactFactory().createDecisionState( 599 parseId(element), flow, parseEntryActions(element), parseIfs(element), 600 parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element)); 601 } 602 603 private void parseAndAddSubflowState(Element element, Flow flow) { 604 getFlowArtifactFactory().createSubflowState(parseId(element), flow, parseEntryActions(element), 605 parseSubflow(element), parseFlowAttributeMapper(element), parseTransitions(element), 606 parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element)); 607 } 608 609 private void parseAndAddEndState(Element element, Flow flow) { 610 getFlowArtifactFactory().createEndState(parseId(element), flow, parseEntryActions(element), 611 parseViewSelector(element), parseOutputMapper(element), parseExceptionHandlers(element), 612 parseAttributes(element)); 613 } 614 615 private String parseId(Element element) { 616 return element.getAttribute(ID_ATTRIBUTE); 617 } 618 619 private Action[] parseEntryActions(Element element) { 620 Element entryActionsElement = getChildElementByTagName(element, ENTRY_ACTIONS_ELEMENT); 621 if (entryActionsElement != null) { 622 return parseAnnotatedActions(entryActionsElement); 623 } 624 else { 625 return null; 626 } 627 } 628 629 private Action[] parseRenderActions(Element element) { 630 Element renderActionsElement = getChildElementByTagName(element, RENDER_ACTIONS_ELEMENT); 631 if (renderActionsElement != null) { 632 return parseAnnotatedActions(renderActionsElement); 633 } 634 else { 635 return null; 636 } 637 } 638 639 private Action[] parseExitActions(Element element) { 640 Element exitActionsElement = getChildElementByTagName(element, EXIT_ACTIONS_ELEMENT); 641 if (exitActionsElement != null) { 642 return parseAnnotatedActions(exitActionsElement); 643 } 644 else { 645 return null; 646 } 647 } 648 649 private Transition[] parseTransitions(Element element) { 650 List transitions = new LinkedList (); 651 List transitionElements = DomUtils.getChildElementsByTagName(element, TRANSITION_ELEMENT); 652 for (Iterator it = transitionElements.iterator(); it.hasNext();) { 653 Element transitionElement = (Element )it.next(); 654 if (!StringUtils.hasText(transitionElement.getAttribute(ON_EXCEPTION_ATTRIBUTE))) { 655 transitions.add(parseTransition(transitionElement)); 656 } 657 } 658 return (Transition[])transitions.toArray(new Transition[transitions.size()]); 659 } 660 661 private Transition parseTransition(Element element) { 662 TransitionCriteria matchingCriteria = (TransitionCriteria)fromStringTo(TransitionCriteria.class).execute( 663 element.getAttribute(ON_ATTRIBUTE)); 664 TargetStateResolver targetStateResolver = (TargetStateResolver)fromStringTo(TargetStateResolver.class).execute( 665 element.getAttribute(TO_ATTRIBUTE)); 666 TransitionCriteria executionCriteria = TransitionCriteriaChain.criteriaChainFor(parseAnnotatedActions(element)); 667 return getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria, executionCriteria, 668 parseAttributes(element)); 669 } 670 671 private ViewSelector parseViewSelector(Element element) { 672 String viewName = element.getAttribute(VIEW_ATTRIBUTE); 673 return (ViewSelector)fromStringTo(ViewSelector.class).execute(viewName); 674 } 675 676 private Flow parseSubflow(Element element) { 677 return getLocalFlowServiceLocator().getSubflow(element.getAttribute(FLOW_ATTRIBUTE)); 678 } 679 680 private AnnotatedAction[] parseAnnotatedActions(Element element) { 681 List actions = new LinkedList (); 682 NodeList childNodeList = element.getChildNodes(); 683 for (int i=0; i < childNodeList.getLength(); i++) { 684 Node childNode = childNodeList.item(i); 685 if (!(childNode instanceof Element )) { 686 continue; 687 } 688 689 if (nodeNameEquals(childNode, ACTION_ELEMENT)) { 690 actions.add(parseAnnotatedAction((Element )childNode)); 692 } 693 else if (nodeNameEquals(childNode, BEAN_ACTION_ELEMENT)) { 694 actions.add(parseAnnotatedBeanInvokingAction((Element )childNode)); 696 } 697 else if (nodeNameEquals(childNode, EVALUATE_ACTION_ELEMENT)) { 698 actions.add(parseAnnotatedEvaluateAction((Element )childNode)); 700 } 701 else if (nodeNameEquals(childNode, SET_ELEMENT)) { 702 actions.add(parseAnnotatedSetAction((Element )childNode)); 704 } 705 } 706 return (AnnotatedAction[])actions.toArray(new AnnotatedAction[actions.size()]); 707 } 708 709 private AnnotatedAction parseAnnotatedAction(Element element) { 710 AnnotatedAction annotated = new AnnotatedAction(parseAction(element)); 711 parseCommonProperties(element, annotated); 712 if (element.hasAttribute(METHOD_ATTRIBUTE)) { 713 annotated.setMethod(element.getAttribute(METHOD_ATTRIBUTE)); 714 } 715 return annotated; 716 } 717 718 private Action parseAction(Element element) { 719 String actionId = element.getAttribute(BEAN_ATTRIBUTE); 720 return getLocalFlowServiceLocator().getAction(actionId); 721 } 722 723 private AnnotatedAction parseCommonProperties(Element element, AnnotatedAction annotated) { 724 if (element.hasAttribute(NAME_ATTRIBUTE)) { 725 annotated.setName(element.getAttribute(NAME_ATTRIBUTE)); 726 } 727 annotated.getAttributeMap().putAll(parseAttributes(element)); 728 return annotated; 729 } 730 731 private AnnotatedAction parseAnnotatedBeanInvokingAction(Element element) { 732 AnnotatedAction annotated = new AnnotatedAction(parseBeanInvokingAction(element)); 733 return parseCommonProperties(element, annotated); 734 } 735 736 private Action parseBeanInvokingAction(Element element) { 737 String beanId = element.getAttribute(BEAN_ATTRIBUTE); 738 String methodName = element.getAttribute(METHOD_ATTRIBUTE); 739 Parameters parameters = parseMethodParameters(element); 740 MethodSignature methodSignature = new MethodSignature(methodName, parameters); 741 ActionResultExposer resultExposer = parseMethodResultExposer(element); 742 return getLocalFlowServiceLocator().getBeanInvokingActionFactory().createBeanInvokingAction(beanId, 743 getLocalFlowServiceLocator().getBeanFactory(), methodSignature, resultExposer, 744 getLocalFlowServiceLocator().getConversionService(), null); 745 } 746 747 private Parameters parseMethodParameters(Element element) { 748 Element methodArgumentsElement = getChildElementByTagName(element, METHOD_ARGUMENTS_ELEMENT); 749 if (methodArgumentsElement == null) { 750 return Parameters.NONE; 751 } 752 Parameters parameters = new Parameters(); 753 Iterator it = DomUtils.getChildElementsByTagName(methodArgumentsElement, ARGUMENT_ELEMENT).iterator(); 754 while (it.hasNext()) { 755 Element argumentElement = (Element )it.next(); 756 Expression name = getLocalFlowServiceLocator().getExpressionParser() 757 .parseExpression(argumentElement.getAttribute(EXPRESSION_ATTRIBUTE)); 758 Class type = null; 759 if (argumentElement.hasAttribute(PARAMETER_TYPE_ATTRIBUTE)) { 760 type = (Class )fromStringTo(Class .class).execute(argumentElement.getAttribute(PARAMETER_TYPE_ATTRIBUTE)); 761 } 762 parameters.add(new Parameter(type, name)); 763 } 764 return parameters; 765 } 766 767 private ActionResultExposer parseMethodResultExposer(Element element) { 768 Element resultElement = getChildElementByTagName(element, METHOD_RESULT_ELEMENT); 769 if (resultElement != null) { 770 return parseActionResultExposer(resultElement); 771 } 772 else { 773 return null; 774 } 775 } 776 777 private ActionResultExposer parseActionResultExposer(Element element) { 778 String resultName = element.getAttribute(NAME_ATTRIBUTE); 779 return new ActionResultExposer(resultName, parseScope(element, ScopeType.REQUEST)); 780 } 781 782 private AnnotatedAction parseAnnotatedEvaluateAction(Element element) { 783 AnnotatedAction annotated = new AnnotatedAction(parseEvaluateAction(element)); 784 return parseCommonProperties(element, annotated); 785 } 786 787 private Action parseEvaluateAction(Element element) { 788 String expressionString = element.getAttribute(EXPRESSION_ATTRIBUTE); 789 Expression expression = getLocalFlowServiceLocator().getExpressionParser() 790 .parseExpression(expressionString); 791 return new EvaluateAction(expression, parseEvaluationResultExposer(element)); 792 } 793 794 private ActionResultExposer parseEvaluationResultExposer(Element element) { 795 Element resultElement = getChildElementByTagName(element, EVALUATION_RESULT_ELEMENT); 796 if (resultElement != null) { 797 return parseActionResultExposer(resultElement); 798 } 799 else { 800 return null; 801 } 802 } 803 804 private AnnotatedAction parseAnnotatedSetAction(Element element) { 805 AnnotatedAction annotated = new AnnotatedAction(parseSetAction(element)); 806 return parseCommonProperties(element, annotated); 807 } 808 809 private Action parseSetAction(Element element) { 810 String attributeExpressionString = element.getAttribute(ATTRIBUTE_ATTRIBUTE); 811 SettableExpression attributeExpression = getLocalFlowServiceLocator().getExpressionParser() 812 .parseSettableExpression(attributeExpressionString); 813 Expression valueExpression = getLocalFlowServiceLocator().getExpressionParser() 814 .parseExpression(element.getAttribute(VALUE_ATTRIBUTE)); 815 return new SetAction(attributeExpression, parseScope(element, ScopeType.REQUEST), valueExpression); 816 } 817 818 private ScopeType parseScope(Element element, ScopeType defaultValue) { 819 if (element.hasAttribute(SCOPE_ATTRIBUTE) && !element.getAttribute(SCOPE_ATTRIBUTE).equals(DEFAULT_VALUE)) { 820 return (ScopeType)fromStringTo(ScopeType.class).execute(element.getAttribute(SCOPE_ATTRIBUTE)); 821 } 822 else { 823 return defaultValue; 824 } 825 } 826 827 private AttributeMap parseAttributes(Element element) { 828 LocalAttributeMap attributes = new LocalAttributeMap(); 829 List propertyElements = DomUtils.getChildElementsByTagName(element, ATTRIBUTE_ELEMENT); 830 for (int i = 0; i < propertyElements.size(); i++) { 831 parseAndSetAttribute((Element )propertyElements.get(i), attributes); 832 } 833 return attributes; 834 } 835 836 private void parseAndSetAttribute(Element element, MutableAttributeMap attributes) { 837 String name = element.getAttribute(NAME_ATTRIBUTE); 838 String value = null; 839 if (element.hasAttribute(VALUE_ATTRIBUTE)) { 840 value = element.getAttribute(VALUE_ATTRIBUTE); 841 } 842 else { 843 List valueElements = DomUtils.getChildElementsByTagName(element, VALUE_ELEMENT); 844 Assert.state(valueElements.size() == 1, "A property value should be specified for property '" + name + "'"); 845 value = DomUtils.getTextValue((Element )valueElements.get(0)); 846 } 847 attributes.put(name, convertPropertyValue(element, value)); 848 } 849 850 private Object convertPropertyValue(Element element, String stringValue) { 851 if (element.hasAttribute(TYPE_ATTRIBUTE)) { 852 ConversionExecutor executor = fromStringTo(element.getAttribute(TYPE_ATTRIBUTE)); 853 if (executor != null) { 854 return executor.execute(stringValue); 856 } 857 else { 858 Class targetClass = (Class )fromStringTo(Class .class).execute(element.getAttribute(TYPE_ATTRIBUTE)); 859 return fromStringTo(targetClass).execute(stringValue); 861 } 862 } 863 else { 864 return stringValue; 865 } 866 } 867 868 private Transition[] parseIfs(Element element) { 869 List transitions = new LinkedList (); 870 List transitionElements = DomUtils.getChildElementsByTagName(element, IF_ELEMENT); 871 for (Iterator it = transitionElements.iterator(); it.hasNext();) { 872 transitions.addAll(Arrays.asList(parseIf((Element )it.next()))); 873 } 874 return (Transition[])transitions.toArray(new Transition[transitions.size()]); 875 } 876 877 private Transition[] parseIf(Element element) { 878 Transition thenTransition = parseThen(element); 879 if (StringUtils.hasText(element.getAttribute(ELSE_ATTRIBUTE))) { 880 Transition elseTransition = parseElse(element); 881 return new Transition[] { thenTransition, elseTransition }; 882 } 883 else { 884 return new Transition[] { thenTransition }; 885 } 886 } 887 888 private Transition parseThen(Element element) { 889 Expression expression = getLocalFlowServiceLocator().getExpressionParser() 890 .parseExpression(element.getAttribute(TEST_ATTRIBUTE)); 891 TransitionCriteria matchingCriteria = new BooleanExpressionTransitionCriteria(expression); 892 TargetStateResolver targetStateResolver = (TargetStateResolver)fromStringTo(TargetStateResolver.class).execute( 893 element.getAttribute(THEN_ATTRIBUTE)); 894 return getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria, null, null); 895 } 896 897 private Transition parseElse(Element element) { 898 TargetStateResolver targetStateResolver = (TargetStateResolver)fromStringTo(TargetStateResolver.class).execute( 899 element.getAttribute(ELSE_ATTRIBUTE)); 900 return getFlowArtifactFactory().createTransition(targetStateResolver, null, null, null); 901 } 902 903 private FlowAttributeMapper parseFlowAttributeMapper(Element element) { 904 Element mapperElement = getChildElementByTagName(element, ATTRIBUTE_MAPPER_ELEMENT); 905 if (mapperElement == null) { 906 return null; 907 } 908 if (StringUtils.hasText(mapperElement.getAttribute(BEAN_ATTRIBUTE))) { 909 return getLocalFlowServiceLocator().getAttributeMapper(mapperElement.getAttribute(BEAN_ATTRIBUTE)); 910 } 911 else { 912 return new ImmutableFlowAttributeMapper(parseInputMapper(mapperElement), parseOutputMapper(mapperElement)); 913 } 914 } 915 916 private AttributeMapper parseInputMapper(Element element) { 917 Element mapperElement = getChildElementByTagName(element, INPUT_MAPPER_ELEMENT); 918 if (mapperElement != null) { 919 DefaultAttributeMapper mapper = new DefaultAttributeMapper(); 920 parseSimpleAttributeMappings(mapper, 921 DomUtils.getChildElementsByTagName(mapperElement, INPUT_ATTRIBUTE_ELEMENT)); 922 parseMappings(mapper, mapperElement); 923 return mapper; 924 } 925 else { 926 return null; 927 } 928 } 929 930 private AttributeMapper parseOutputMapper(Element element) { 931 Element mapperElement = getChildElementByTagName(element, OUTPUT_MAPPER_ELEMENT); 932 if (mapperElement != null) { 933 DefaultAttributeMapper mapper = new DefaultAttributeMapper(); 934 parseSimpleAttributeMappings(mapper, 935 DomUtils.getChildElementsByTagName(mapperElement, OUTPUT_ATTRIBUTE_ELEMENT)); 936 parseMappings(mapper, mapperElement); 937 return mapper; 938 } 939 else { 940 return null; 941 } 942 } 943 944 private void parseMappings(DefaultAttributeMapper mapper, Element element) { 945 ExpressionParser parser = getLocalFlowServiceLocator().getExpressionParser(); 946 List mappingElements = DomUtils.getChildElementsByTagName(element, MAPPING_ELEMENT); 947 for (Iterator it = mappingElements.iterator(); it.hasNext();) { 948 Element mappingElement = (Element )it.next(); 949 Expression source = parser.parseExpression(mappingElement.getAttribute(SOURCE_ATTRIBUTE)); 950 SettableExpression target = null; 951 if (StringUtils.hasText(mappingElement.getAttribute(TARGET_ATTRIBUTE))) { 952 target = parser.parseSettableExpression(mappingElement.getAttribute(TARGET_ATTRIBUTE)); 953 } 954 else if (StringUtils.hasText(mappingElement.getAttribute(TARGET_COLLECTION_ATTRIBUTE))) { 955 target = new CollectionAddingExpression( 956 parser.parseSettableExpression(mappingElement.getAttribute(TARGET_COLLECTION_ATTRIBUTE))); 957 } 958 if (getRequired(mappingElement, false)) { 959 mapper.addMapping(new RequiredMapping(source, target, parseTypeConverter(mappingElement))); 960 } 961 else { 962 mapper.addMapping(new Mapping(source, target, parseTypeConverter(mappingElement))); 963 } 964 } 965 } 966 967 private void parseSimpleAttributeMappings(DefaultAttributeMapper mapper, List elements) { 968 ExpressionParser parser = getLocalFlowServiceLocator().getExpressionParser(); 969 for (Iterator it = elements.iterator(); it.hasNext();) { 970 Element element = (Element )it.next(); 971 SettableExpression attribute = parser.parseSettableExpression(element.getAttribute(NAME_ATTRIBUTE)); 972 SettableExpression expression = new AttributeExpression(attribute, parseScope(element, ScopeType.FLOW)); 973 if (getRequired(element, false)) { 974 mapper.addMapping(new RequiredMapping(expression, expression, null)); 975 } 976 else { 977 mapper.addMapping(new Mapping(expression, expression, null)); 978 } 979 } 980 } 981 982 private boolean getRequired(Element element, boolean defaultValue) { 983 if (StringUtils.hasText(element.getAttribute(REQUIRED_ATTRIBUTE))) { 984 return ((Boolean )fromStringTo(Boolean .class).execute(element.getAttribute(REQUIRED_ATTRIBUTE))) 985 .booleanValue(); 986 } 987 else { 988 return defaultValue; 989 } 990 } 991 992 private ConversionExecutor parseTypeConverter(Element element) { 993 String from = element.getAttribute(FROM_ATTRIBUTE); 994 String to = element.getAttribute(TO_ATTRIBUTE); 995 if (StringUtils.hasText(from)) { 996 if (StringUtils.hasText(to)) { 997 ConversionService service = getLocalFlowServiceLocator().getConversionService(); 998 return service.getConversionExecutor(service.getClassByAlias(from), service.getClassByAlias(to)); 999 } 1000 else { 1001 throw new IllegalArgumentException ("Use of the 'from' attribute requires use of the 'to' attribute"); 1002 } 1003 } 1004 else { 1005 Assert.isTrue(!StringUtils.hasText(to), "Use of the 'to' attribute requires use of the 'from' attribute"); 1006 } 1007 return null; 1008 } 1009 1010 private FlowExecutionExceptionHandler[] parseExceptionHandlers(Element element) { 1011 FlowExecutionExceptionHandler[] transitionExecutingHandlers = parseTransitionExecutingExceptionHandlers(element); 1012 FlowExecutionExceptionHandler[] customHandlers = parseCustomExceptionHandlers(element); 1013 FlowExecutionExceptionHandler[] exceptionHandlers = 1014 new FlowExecutionExceptionHandler[transitionExecutingHandlers.length + customHandlers.length]; 1015 System.arraycopy(transitionExecutingHandlers, 0, exceptionHandlers, 0, transitionExecutingHandlers.length); 1016 System.arraycopy(customHandlers, 0, exceptionHandlers, transitionExecutingHandlers.length, 1017 customHandlers.length); 1018 return exceptionHandlers; 1019 } 1020 1021 private FlowExecutionExceptionHandler[] parseTransitionExecutingExceptionHandlers(Element element) { 1022 List transitionElements = Collections.EMPTY_LIST; 1023 if (isFlowElement(element)) { 1024 Element globalTransitionsElement = getChildElementByTagName(element, GLOBAL_TRANSITIONS_ELEMENT); 1025 if (globalTransitionsElement != null) { 1026 transitionElements = DomUtils.getChildElementsByTagName(globalTransitionsElement, TRANSITION_ELEMENT); 1027 } 1028 } 1029 else { 1030 transitionElements = DomUtils.getChildElementsByTagName(element, TRANSITION_ELEMENT); 1031 } 1032 List exceptionHandlers = new LinkedList (); 1033 for (Iterator it = transitionElements.iterator(); it.hasNext();) { 1034 Element transitionElement = (Element )it.next(); 1035 if (StringUtils.hasText(transitionElement.getAttribute(ON_EXCEPTION_ATTRIBUTE))) { 1036 exceptionHandlers.add(parseTransitionExecutingExceptionHandler(transitionElement)); 1037 } 1038 } 1039 return (FlowExecutionExceptionHandler[])exceptionHandlers 1040 .toArray(new FlowExecutionExceptionHandler[exceptionHandlers.size()]); 1041 } 1042 1043 private FlowExecutionExceptionHandler parseTransitionExecutingExceptionHandler(Element element) { 1044 TransitionExecutingStateExceptionHandler handler = new TransitionExecutingStateExceptionHandler(); 1045 Class exceptionClass = (Class )fromStringTo(Class .class).execute(element.getAttribute(ON_EXCEPTION_ATTRIBUTE)); 1046 handler.add(exceptionClass, element.getAttribute(TO_ATTRIBUTE)); 1047 handler.getActionList().addAll(parseAnnotatedActions(element)); 1048 return handler; 1049 } 1050 1051 private FlowExecutionExceptionHandler[] parseCustomExceptionHandlers(Element element) { 1052 List exceptionHandlers = new LinkedList (); 1053 List handlerElements = DomUtils.getChildElementsByTagName(element, EXCEPTION_HANDLER_ELEMENT); 1054 for (int i = 0; i < handlerElements.size(); i++) { 1055 Element handlerElement = (Element )handlerElements.get(i); 1056 exceptionHandlers.add(parseCustomExceptionHandler(handlerElement)); 1057 } 1058 return (FlowExecutionExceptionHandler[])exceptionHandlers 1059 .toArray(new FlowExecutionExceptionHandler[exceptionHandlers.size()]); 1060 } 1061 1062 private FlowExecutionExceptionHandler parseCustomExceptionHandler(Element element) { 1063 return getLocalFlowServiceLocator().getExceptionHandler(element.getAttribute(BEAN_ATTRIBUTE)); 1064 } 1065} | Popular Tags |