All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.webflow.engine.builder.xml.XmlFlowBuilder Maven / Gradle / Ivy

There is a newer version: 1.0.6
Show newest version
/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.webflow.engine.builder.xml;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.expression.SettableExpression;
import org.springframework.binding.expression.support.CollectionAddingExpression;
import org.springframework.binding.mapping.AttributeMapper;
import org.springframework.binding.mapping.DefaultAttributeMapper;
import org.springframework.binding.mapping.Mapping;
import org.springframework.binding.mapping.RequiredMapping;
import org.springframework.binding.method.MethodSignature;
import org.springframework.binding.method.Parameter;
import org.springframework.binding.method.Parameters;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.webflow.action.ActionResultExposer;
import org.springframework.webflow.action.EvaluateAction;
import org.springframework.webflow.action.SetAction;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.engine.AnnotatedAction;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.FlowAttributeMapper;
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
import org.springframework.webflow.engine.FlowVariable;
import org.springframework.webflow.engine.TargetStateResolver;
import org.springframework.webflow.engine.Transition;
import org.springframework.webflow.engine.TransitionCriteria;
import org.springframework.webflow.engine.ViewSelector;
import org.springframework.webflow.engine.builder.BaseFlowBuilder;
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
import org.springframework.webflow.engine.support.AttributeExpression;
import org.springframework.webflow.engine.support.BeanFactoryFlowVariable;
import org.springframework.webflow.engine.support.BooleanExpressionTransitionCriteria;
import org.springframework.webflow.engine.support.SimpleFlowVariable;
import org.springframework.webflow.engine.support.TransitionCriteriaChain;
import org.springframework.webflow.engine.support.TransitionExecutingStateExceptionHandler;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.ScopeType;
import org.springframework.webflow.util.ResourceHolder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Flow builder that builds flows as defined in an XML document. The XML document
 * should adhere to the following format:
 * 
 * 
 *     <?xml version="1.0" encoding="UTF-8"?>
 *     <flow xmlns="http://www.springframework.org/schema/webflow"
 *           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 *           xsi:schemaLocation="http://www.springframework.org/schema/webflow
 *                               http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">
 *                                        
 *         <!-- Define your states here -->
 *                  
 *     </flow>
 * 
* *

* Consult the webflow * XML schema for more information on the XML-based flow definition format. *

* This builder will setup a flow-local bean factory for the flow being * constructed. That flow-local bean factory will be populated with XML bean * definitions contained in files referenced using the "import" element. The * flow-local bean factory will use the bean factory defing this flow builder as * a parent. As such, the flow can access artifacts in either its flow-local * bean factory or in the parent bean factory hierarchy, e.g. the bean factory * of the dispatcher. * * @author Erwin Vervaet * @author Keith Donald */ public class XmlFlowBuilder extends BaseFlowBuilder implements ResourceHolder { // recognized XML elements and attributes private static final String ID_ATTRIBUTE = "id"; private static final String IDREF_ATTRIBUTE = "idref"; private static final String BEAN_ATTRIBUTE = "bean"; private static final String FLOW_ELEMENT = "flow"; private static final String START_STATE_ELEMENT = "start-state"; private static final String ACTION_STATE_ELEMENT = "action-state"; private static final String ACTION_ELEMENT = "action"; private static final String NAME_ATTRIBUTE = "name"; private static final String METHOD_ATTRIBUTE = "method"; private static final String BEAN_ACTION_ELEMENT = "bean-action"; private static final String METHOD_ARGUMENTS_ELEMENT = "method-arguments"; private static final String ARGUMENT_ELEMENT = "argument"; private static final String EXPRESSION_ATTRIBUTE = "expression"; private static final String PARAMETER_TYPE_ATTRIBUTE = "parameter-type"; private static final String METHOD_RESULT_ELEMENT = "method-result"; private static final String EVALUATE_ACTION_ELEMENT = "evaluate-action"; private static final String SET_ELEMENT = "set"; private static final String ATTRIBUTE_ATTRIBUTE = "attribute"; private static final String EVALUATION_RESULT_ELEMENT = "evaluation-result"; private static final String DEFAULT_VALUE = "default"; private static final String VIEW_STATE_ELEMENT = "view-state"; private static final String VIEW_ATTRIBUTE = "view"; private static final String DECISION_STATE_ELEMENT = "decision-state"; private static final String IF_ELEMENT = "if"; private static final String TEST_ATTRIBUTE = "test"; private static final String THEN_ATTRIBUTE = "then"; private static final String ELSE_ATTRIBUTE = "else"; private static final String SUBFLOW_STATE_ELEMENT = "subflow-state"; private static final String FLOW_ATTRIBUTE = "flow"; private static final String ATTRIBUTE_MAPPER_ELEMENT = "attribute-mapper"; private static final String OUTPUT_MAPPER_ELEMENT = "output-mapper"; private static final String OUTPUT_ATTRIBUTE_ELEMENT = "output-attribute"; private static final String INPUT_MAPPER_ELEMENT = "input-mapper"; private static final String INPUT_ATTRIBUTE_ELEMENT = "input-attribute"; private static final String MAPPING_ELEMENT = "mapping"; private static final String SOURCE_ATTRIBUTE = "source"; private static final String TARGET_ATTRIBUTE = "target"; private static final String FROM_ATTRIBUTE = "from"; private static final String TO_ATTRIBUTE = "to"; private static final String REQUIRED_ATTRIBUTE = "required"; private static final String TARGET_COLLECTION_ATTRIBUTE = "target-collection"; private static final String END_STATE_ELEMENT = "end-state"; private static final String TRANSITION_ELEMENT = "transition"; private static final String GLOBAL_TRANSITIONS_ELEMENT = "global-transitions"; private static final String ON_ATTRIBUTE = "on"; private static final String ON_EXCEPTION_ATTRIBUTE = "on-exception"; private static final String ATTRIBUTE_ELEMENT = "attribute"; private static final String TYPE_ATTRIBUTE = "type"; private static final String VALUE_ELEMENT = "value"; private static final String VALUE_ATTRIBUTE = "value"; private static final String VAR_ELEMENT = "var"; private static final String SCOPE_ATTRIBUTE = "scope"; private static final String CLASS_ATTRIBUTE = "class"; private static final String START_ACTIONS_ELEMENT = "start-actions"; private static final String END_ACTIONS_ELEMENT = "end-actions"; private static final String ENTRY_ACTIONS_ELEMENT = "entry-actions"; private static final String RENDER_ACTIONS_ELEMENT = "render-actions"; private static final String EXIT_ACTIONS_ELEMENT = "exit-actions"; private static final String EXCEPTION_HANDLER_ELEMENT = "exception-handler"; private static final String INLINE_FLOW_ELEMENT = "inline-flow"; private static final String IMPORT_ELEMENT = "import"; private static final String RESOURCE_ATTRIBUTE = "resource"; /** * The resource from which the document element being parsed was read. Used * as a location for relative resource lookup. */ protected Resource location; /** * A flow service locator local to this builder that first looks in a * locally-managed Spring application context for services before searching * the externally managed {@link #getFlowServiceLocator()}. */ private LocalFlowServiceLocator localFlowServiceLocator; /** * The loader for loading the flow definition resource XML document. */ private DocumentLoader documentLoader = new DefaultDocumentLoader(); /** * The in-memory document object model (DOM) of the XML Document read from * the flow definition resource. */ private Document document; /** * Create a new XML flow builder parsing the document at the specified * location. * @param location the location of the XML-based flow definition resource */ public XmlFlowBuilder(Resource location) { setLocation(location); } /** * Create a new XML flow builder parsing the document at the specified * location, using the provided service locator to access externally managed * flow artifacts. * @param location the location of the XML-based flow definition resource * @param flowServiceLocator the locator for services needed by this builder * to build its Flow */ public XmlFlowBuilder(Resource location, FlowServiceLocator flowServiceLocator) { super(flowServiceLocator); setLocation(location); } /** * Returns the resource from which the document element was loaded. This is * used for location relative loading of other resources. */ public Resource getLocation() { return location; } /** * Sets the resource from which the document element was loaded. This is * used for location relative loading of other resources. */ public void setLocation(Resource location) { Assert.notNull(location, "The resource location of the XML-based flow definition is required"); this.location = location; } /** * Sets the loader that will load the XML-based flow definition document. * Optional, defaults to {@link DefaultDocumentLoader}. * @param documentLoader the document loader */ public void setDocumentLoader(DocumentLoader documentLoader) { Assert.notNull(documentLoader, "The XML document loader is required"); this.documentLoader = documentLoader; } // implementing FlowBuilder public void init(String id, AttributeMap attributes) throws FlowBuilderException { localFlowServiceLocator = new LocalFlowServiceLocator(getFlowServiceLocator()); try { document = documentLoader.loadDocument(location); } catch (IOException e) { throw new FlowBuilderException("Could not access the XML flow definition resource at " + location, e); } catch (ParserConfigurationException e) { throw new FlowBuilderException("Could not configure the parser to parse the XML flow definition at " + location, e); } catch (SAXException e) { throw new FlowBuilderException("Could not parse the XML flow definition document at " + location, e); } setFlow(parseFlow(id, attributes, getDocumentElement())); } public void buildVariables() throws FlowBuilderException { parseAndAddFlowVariables(getDocumentElement(), getFlow()); } public void buildInputMapper() throws FlowBuilderException { getFlow().setInputMapper(parseInputMapper(getDocumentElement())); } public void buildStartActions() throws FlowBuilderException { parseAndAddStartActions(getDocumentElement(), getFlow()); } public void buildInlineFlows() throws FlowBuilderException { parseAndAddInlineFlowDefinitions(getDocumentElement(), getFlow()); } public void buildStates() throws FlowBuilderException { parseAndAddStateDefinitions(getDocumentElement(), getFlow()); } public void buildGlobalTransitions() throws FlowBuilderException { parseAndAddGlobalTransitions(getDocumentElement(), getFlow()); } public void buildEndActions() throws FlowBuilderException { parseAndAddEndActions(getDocumentElement(), getFlow()); } public void buildOutputMapper() throws FlowBuilderException { getFlow().setOutputMapper(parseOutputMapper(getDocumentElement())); } public void buildExceptionHandlers() throws FlowBuilderException { getFlow().getExceptionHandlerSet().addAll(parseExceptionHandlers(getDocumentElement())); } public void dispose() { super.dispose(); localFlowServiceLocator.diposeOfAnyRegistries(); document = null; } // implementing ResourceHolder public Resource getResource() { return location; } // helpers /** * Returns the DOM document parsed from the XML file. */ protected Document getDocument() { return document; } /** * Returns the root document element. */ protected Element getDocumentElement() { return document.getDocumentElement(); } /** * Returns the flow service locator local to this builder. */ protected FlowServiceLocator getLocalFlowServiceLocator() { return localFlowServiceLocator; } /** * Returns the artifact factory of the flow service locator local * to this builder. */ protected FlowArtifactFactory getFlowArtifactFactory() { return getLocalFlowServiceLocator().getFlowArtifactFactory(); } // utility (from Spring 2.x DomUtils) /** * Utility method that returns the first child element identified by its * name. * @param ele the DOM element to analyze * @param childEleName the child element name to look for * @return the org.w3c.dom.Element instance, or * null if none found */ protected Element getChildElementByTagName(Element ele, String childEleName) { NodeList nl = ele.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element && nodeNameEquals(node, childEleName)) { return (Element)node; } } return null; } /** * Namespace-aware equals comparison. Returns true if either * {@link Node#getLocalName} or {@link Node#getNodeName} equals * desiredName, otherwise returns false. */ protected boolean nodeNameEquals(Node node, String desiredName) { return desiredName.equals(node.getNodeName()) || desiredName.equals(node.getLocalName()); } // internal parsing logic private Flow parseFlow(String id, AttributeMap attributes, Element flowElement) { if (!isFlowElement(flowElement)) { throw new IllegalStateException("This is not the '" + FLOW_ELEMENT + "' element"); } Flow flow = getFlowArtifactFactory().createFlow(id, parseAttributes(flowElement).union(attributes)); initLocalServiceRegistry(flowElement, flow); return flow; } private boolean isFlowElement(Element flowElement) { return FLOW_ELEMENT.equals(flowElement.getTagName()); } private void initLocalServiceRegistry(Element flowElement, Flow flow) { List importElements = DomUtils.getChildElementsByTagName(flowElement, IMPORT_ELEMENT); Resource[] resources = new Resource[importElements.size()]; for (int i = 0; i < importElements.size(); i++) { Element importElement = (Element)importElements.get(i); try { resources[i] = getLocation().createRelative(importElement.getAttribute(RESOURCE_ATTRIBUTE)); } catch (IOException e) { throw new FlowBuilderException("Could not access flow-relative artifact resource '" + importElement.getAttribute(RESOURCE_ATTRIBUTE) + "'", e); } } localFlowServiceLocator.push(new LocalFlowServiceRegistry(flow, resources)); } private void destroyLocalServiceRegistry(Flow flow) { localFlowServiceLocator.pop(); } private void parseAndAddFlowVariables(Element flowElement, Flow flow) { List varElements = DomUtils.getChildElementsByTagName(flowElement, VAR_ELEMENT); for (Iterator it = varElements.iterator(); it.hasNext();) { flow.addVariable(parseVariable((Element)it.next())); } } private FlowVariable parseVariable(Element element) { ScopeType scope = parseScope(element, ScopeType.FLOW); if (StringUtils.hasText(element.getAttribute(BEAN_ATTRIBUTE))) { BeanFactory beanFactory = getLocalFlowServiceLocator().getBeanFactory(); return new BeanFactoryFlowVariable(element.getAttribute(NAME_ATTRIBUTE), element.getAttribute(BEAN_ATTRIBUTE), beanFactory, scope); } else { if (StringUtils.hasText(element.getAttribute(CLASS_ATTRIBUTE))) { Class variableClass = (Class)fromStringTo(Class.class).execute(element.getAttribute(CLASS_ATTRIBUTE)); return new SimpleFlowVariable(element.getAttribute(NAME_ATTRIBUTE), variableClass, scope); } else { BeanFactory beanFactory = getLocalFlowServiceLocator().getBeanFactory(); return new BeanFactoryFlowVariable(element.getAttribute(NAME_ATTRIBUTE), null, beanFactory, scope); } } } private void parseAndAddStartActions(Element element, Flow flow) { Element startElement = getChildElementByTagName(element, START_ACTIONS_ELEMENT); if (startElement != null) { flow.getStartActionList().addAll(parseAnnotatedActions(startElement)); } } private void parseAndAddEndActions(Element element, Flow flow) { Element endElement = getChildElementByTagName(element, END_ACTIONS_ELEMENT); if (endElement != null) { flow.getEndActionList().addAll(parseAnnotatedActions(endElement)); } } private void parseAndAddGlobalTransitions(Element element, Flow flow) { Element globalTransitionsElement = getChildElementByTagName(element, GLOBAL_TRANSITIONS_ELEMENT); if (globalTransitionsElement != null) { flow.getGlobalTransitionSet().addAll(parseTransitions(globalTransitionsElement)); } } private void parseAndAddInlineFlowDefinitions(Element parentFlowElement, Flow flow) { List inlineFlowElements = DomUtils.getChildElementsByTagName(parentFlowElement, INLINE_FLOW_ELEMENT); for (Iterator it = inlineFlowElements.iterator(); it.hasNext();) { Element inlineFlowElement = (Element)it.next(); String inlineFlowId = inlineFlowElement.getAttribute(ID_ATTRIBUTE); Element flowElement = getChildElementByTagName(inlineFlowElement, FLOW_ATTRIBUTE); Flow inlineFlow = parseFlow(inlineFlowId, null, flowElement); buildInlineFlow(flowElement, inlineFlow); flow.addInlineFlow(inlineFlow); } } private void buildInlineFlow(Element flowElement, Flow inlineFlow) { parseAndAddFlowVariables(flowElement, inlineFlow); inlineFlow.setInputMapper(parseInputMapper(flowElement)); parseAndAddStartActions(flowElement, inlineFlow); parseAndAddInlineFlowDefinitions(flowElement, inlineFlow); parseAndAddStateDefinitions(flowElement, inlineFlow); parseAndAddGlobalTransitions(flowElement, inlineFlow); parseAndAddEndActions(flowElement, inlineFlow); inlineFlow.setOutputMapper(parseOutputMapper(flowElement)); inlineFlow.getExceptionHandlerSet().addAll(parseExceptionHandlers(flowElement)); destroyLocalServiceRegistry(inlineFlow); } private void parseAndAddStateDefinitions(Element flowElement, Flow flow) { NodeList childNodeList = flowElement.getChildNodes(); for (int i = 0; i < childNodeList.getLength(); i++) { Node childNode = childNodeList.item(i); if (childNode instanceof Element) { Element stateElement = (Element)childNode; if (nodeNameEquals(stateElement, ACTION_STATE_ELEMENT)) { parseAndAddActionState(stateElement, flow); } else if (nodeNameEquals(stateElement, VIEW_STATE_ELEMENT)) { parseAndAddViewState(stateElement, flow); } else if (nodeNameEquals(stateElement, DECISION_STATE_ELEMENT)) { parseAndAddDecisionState(stateElement, flow); } else if (nodeNameEquals(stateElement, SUBFLOW_STATE_ELEMENT)) { parseAndAddSubflowState(stateElement, flow); } else if (nodeNameEquals(stateElement, END_STATE_ELEMENT)) { parseAndAddEndState(stateElement, flow); } } } parseAndSetStartState(flowElement, flow); } private void parseAndSetStartState(Element element, Flow flow) { String startStateId = getStartStateId(element); flow.setStartState(startStateId); } private String getStartStateId(Element element) { Element startStateElement = getChildElementByTagName(element, START_STATE_ELEMENT); return startStateElement.getAttribute(IDREF_ATTRIBUTE); } private void parseAndAddActionState(Element element, Flow flow) { getFlowArtifactFactory().createActionState(parseId(element), flow, parseEntryActions(element), parseAnnotatedActions(element), parseTransitions(element), parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element)); } private void parseAndAddViewState(Element element, Flow flow) { getFlowArtifactFactory().createViewState(parseId(element), flow, parseEntryActions(element), parseViewSelector(element), parseRenderActions(element), parseTransitions(element), parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element)); } private void parseAndAddDecisionState(Element element, Flow flow) { getFlowArtifactFactory().createDecisionState( parseId(element), flow, parseEntryActions(element), parseIfs(element), parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element)); } private void parseAndAddSubflowState(Element element, Flow flow) { getFlowArtifactFactory().createSubflowState(parseId(element), flow, parseEntryActions(element), parseSubflow(element), parseFlowAttributeMapper(element), parseTransitions(element), parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element)); } private void parseAndAddEndState(Element element, Flow flow) { getFlowArtifactFactory().createEndState(parseId(element), flow, parseEntryActions(element), parseViewSelector(element), parseOutputMapper(element), parseExceptionHandlers(element), parseAttributes(element)); } private String parseId(Element element) { return element.getAttribute(ID_ATTRIBUTE); } private Action[] parseEntryActions(Element element) { Element entryActionsElement = getChildElementByTagName(element, ENTRY_ACTIONS_ELEMENT); if (entryActionsElement != null) { return parseAnnotatedActions(entryActionsElement); } else { return null; } } private Action[] parseRenderActions(Element element) { Element renderActionsElement = getChildElementByTagName(element, RENDER_ACTIONS_ELEMENT); if (renderActionsElement != null) { return parseAnnotatedActions(renderActionsElement); } else { return null; } } private Action[] parseExitActions(Element element) { Element exitActionsElement = getChildElementByTagName(element, EXIT_ACTIONS_ELEMENT); if (exitActionsElement != null) { return parseAnnotatedActions(exitActionsElement); } else { return null; } } private Transition[] parseTransitions(Element element) { List transitions = new LinkedList(); List transitionElements = DomUtils.getChildElementsByTagName(element, TRANSITION_ELEMENT); for (Iterator it = transitionElements.iterator(); it.hasNext();) { Element transitionElement = (Element)it.next(); if (!StringUtils.hasText(transitionElement.getAttribute(ON_EXCEPTION_ATTRIBUTE))) { transitions.add(parseTransition(transitionElement)); } } return (Transition[])transitions.toArray(new Transition[transitions.size()]); } private Transition parseTransition(Element element) { TransitionCriteria matchingCriteria = (TransitionCriteria)fromStringTo(TransitionCriteria.class).execute( element.getAttribute(ON_ATTRIBUTE)); TargetStateResolver targetStateResolver = (TargetStateResolver)fromStringTo(TargetStateResolver.class).execute( element.getAttribute(TO_ATTRIBUTE)); TransitionCriteria executionCriteria = TransitionCriteriaChain.criteriaChainFor(parseAnnotatedActions(element)); return getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria, executionCriteria, parseAttributes(element)); } private ViewSelector parseViewSelector(Element element) { String viewName = element.getAttribute(VIEW_ATTRIBUTE); return (ViewSelector)fromStringTo(ViewSelector.class).execute(viewName); } private Flow parseSubflow(Element element) { return getLocalFlowServiceLocator().getSubflow(element.getAttribute(FLOW_ATTRIBUTE)); } private AnnotatedAction[] parseAnnotatedActions(Element element) { List actions = new LinkedList(); NodeList childNodeList = element.getChildNodes(); for (int i=0; i < childNodeList.getLength(); i++) { Node childNode = childNodeList.item(i); if (!(childNode instanceof Element)) { continue; } if (nodeNameEquals(childNode, ACTION_ELEMENT)) { // parse standard action actions.add(parseAnnotatedAction((Element)childNode)); } else if (nodeNameEquals(childNode, BEAN_ACTION_ELEMENT)) { // parse bean invoking action actions.add(parseAnnotatedBeanInvokingAction((Element)childNode)); } else if (nodeNameEquals(childNode, EVALUATE_ACTION_ELEMENT)) { // parse evaluate action actions.add(parseAnnotatedEvaluateAction((Element)childNode)); } else if (nodeNameEquals(childNode, SET_ELEMENT)) { // parse set action actions.add(parseAnnotatedSetAction((Element)childNode)); } } return (AnnotatedAction[])actions.toArray(new AnnotatedAction[actions.size()]); } private AnnotatedAction parseAnnotatedAction(Element element) { AnnotatedAction annotated = new AnnotatedAction(parseAction(element)); parseCommonProperties(element, annotated); if (element.hasAttribute(METHOD_ATTRIBUTE)) { annotated.setMethod(element.getAttribute(METHOD_ATTRIBUTE)); } return annotated; } private Action parseAction(Element element) { String actionId = element.getAttribute(BEAN_ATTRIBUTE); return getLocalFlowServiceLocator().getAction(actionId); } private AnnotatedAction parseCommonProperties(Element element, AnnotatedAction annotated) { if (element.hasAttribute(NAME_ATTRIBUTE)) { annotated.setName(element.getAttribute(NAME_ATTRIBUTE)); } annotated.getAttributeMap().putAll(parseAttributes(element)); return annotated; } private AnnotatedAction parseAnnotatedBeanInvokingAction(Element element) { AnnotatedAction annotated = new AnnotatedAction(parseBeanInvokingAction(element)); return parseCommonProperties(element, annotated); } private Action parseBeanInvokingAction(Element element) { String beanId = element.getAttribute(BEAN_ATTRIBUTE); String methodName = element.getAttribute(METHOD_ATTRIBUTE); Parameters parameters = parseMethodParameters(element); MethodSignature methodSignature = new MethodSignature(methodName, parameters); ActionResultExposer resultExposer = parseMethodResultExposer(element); return getLocalFlowServiceLocator().getBeanInvokingActionFactory().createBeanInvokingAction(beanId, getLocalFlowServiceLocator().getBeanFactory(), methodSignature, resultExposer, getLocalFlowServiceLocator().getConversionService(), null); } private Parameters parseMethodParameters(Element element) { Element methodArgumentsElement = getChildElementByTagName(element, METHOD_ARGUMENTS_ELEMENT); if (methodArgumentsElement == null) { return Parameters.NONE; } Parameters parameters = new Parameters(); Iterator it = DomUtils.getChildElementsByTagName(methodArgumentsElement, ARGUMENT_ELEMENT).iterator(); while (it.hasNext()) { Element argumentElement = (Element)it.next(); Expression name = getLocalFlowServiceLocator().getExpressionParser() .parseExpression(argumentElement.getAttribute(EXPRESSION_ATTRIBUTE)); Class type = null; if (argumentElement.hasAttribute(PARAMETER_TYPE_ATTRIBUTE)) { type = (Class)fromStringTo(Class.class).execute(argumentElement.getAttribute(PARAMETER_TYPE_ATTRIBUTE)); } parameters.add(new Parameter(type, name)); } return parameters; } private ActionResultExposer parseMethodResultExposer(Element element) { Element resultElement = getChildElementByTagName(element, METHOD_RESULT_ELEMENT); if (resultElement != null) { return parseActionResultExposer(resultElement); } else { return null; } } private ActionResultExposer parseActionResultExposer(Element element) { String resultName = element.getAttribute(NAME_ATTRIBUTE); return new ActionResultExposer(resultName, parseScope(element, ScopeType.REQUEST)); } private AnnotatedAction parseAnnotatedEvaluateAction(Element element) { AnnotatedAction annotated = new AnnotatedAction(parseEvaluateAction(element)); return parseCommonProperties(element, annotated); } private Action parseEvaluateAction(Element element) { String expressionString = element.getAttribute(EXPRESSION_ATTRIBUTE); Expression expression = getLocalFlowServiceLocator().getExpressionParser() .parseExpression(expressionString); return new EvaluateAction(expression, parseEvaluationResultExposer(element)); } private ActionResultExposer parseEvaluationResultExposer(Element element) { Element resultElement = getChildElementByTagName(element, EVALUATION_RESULT_ELEMENT); if (resultElement != null) { return parseActionResultExposer(resultElement); } else { return null; } } private AnnotatedAction parseAnnotatedSetAction(Element element) { AnnotatedAction annotated = new AnnotatedAction(parseSetAction(element)); return parseCommonProperties(element, annotated); } private Action parseSetAction(Element element) { String attributeExpressionString = element.getAttribute(ATTRIBUTE_ATTRIBUTE); SettableExpression attributeExpression = getLocalFlowServiceLocator().getExpressionParser() .parseSettableExpression(attributeExpressionString); Expression valueExpression = getLocalFlowServiceLocator().getExpressionParser() .parseExpression(element.getAttribute(VALUE_ATTRIBUTE)); return new SetAction(attributeExpression, parseScope(element, ScopeType.REQUEST), valueExpression); } private ScopeType parseScope(Element element, ScopeType defaultValue) { if (element.hasAttribute(SCOPE_ATTRIBUTE) && !element.getAttribute(SCOPE_ATTRIBUTE).equals(DEFAULT_VALUE)) { return (ScopeType)fromStringTo(ScopeType.class).execute(element.getAttribute(SCOPE_ATTRIBUTE)); } else { return defaultValue; } } private AttributeMap parseAttributes(Element element) { LocalAttributeMap attributes = new LocalAttributeMap(); List propertyElements = DomUtils.getChildElementsByTagName(element, ATTRIBUTE_ELEMENT); for (int i = 0; i < propertyElements.size(); i++) { parseAndSetAttribute((Element)propertyElements.get(i), attributes); } return attributes; } private void parseAndSetAttribute(Element element, MutableAttributeMap attributes) { String name = element.getAttribute(NAME_ATTRIBUTE); String value = null; if (element.hasAttribute(VALUE_ATTRIBUTE)) { value = element.getAttribute(VALUE_ATTRIBUTE); } else { List valueElements = DomUtils.getChildElementsByTagName(element, VALUE_ELEMENT); Assert.state(valueElements.size() == 1, "A property value should be specified for property '" + name + "'"); value = DomUtils.getTextValue((Element)valueElements.get(0)); } attributes.put(name, convertPropertyValue(element, value)); } private Object convertPropertyValue(Element element, String stringValue) { if (element.hasAttribute(TYPE_ATTRIBUTE)) { ConversionExecutor executor = fromStringTo(element.getAttribute(TYPE_ATTRIBUTE)); if (executor != null) { // convert string value to instance of aliased type return executor.execute(stringValue); } else { Class targetClass = (Class)fromStringTo(Class.class).execute(element.getAttribute(TYPE_ATTRIBUTE)); // convert string value to instance of target class return fromStringTo(targetClass).execute(stringValue); } } else { return stringValue; } } private Transition[] parseIfs(Element element) { List transitions = new LinkedList(); List transitionElements = DomUtils.getChildElementsByTagName(element, IF_ELEMENT); for (Iterator it = transitionElements.iterator(); it.hasNext();) { transitions.addAll(Arrays.asList(parseIf((Element)it.next()))); } return (Transition[])transitions.toArray(new Transition[transitions.size()]); } private Transition[] parseIf(Element element) { Transition thenTransition = parseThen(element); if (StringUtils.hasText(element.getAttribute(ELSE_ATTRIBUTE))) { Transition elseTransition = parseElse(element); return new Transition[] { thenTransition, elseTransition }; } else { return new Transition[] { thenTransition }; } } private Transition parseThen(Element element) { Expression expression = getLocalFlowServiceLocator().getExpressionParser() .parseExpression(element.getAttribute(TEST_ATTRIBUTE)); TransitionCriteria matchingCriteria = new BooleanExpressionTransitionCriteria(expression); TargetStateResolver targetStateResolver = (TargetStateResolver)fromStringTo(TargetStateResolver.class).execute( element.getAttribute(THEN_ATTRIBUTE)); return getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria, null, null); } private Transition parseElse(Element element) { TargetStateResolver targetStateResolver = (TargetStateResolver)fromStringTo(TargetStateResolver.class).execute( element.getAttribute(ELSE_ATTRIBUTE)); return getFlowArtifactFactory().createTransition(targetStateResolver, null, null, null); } private FlowAttributeMapper parseFlowAttributeMapper(Element element) { Element mapperElement = getChildElementByTagName(element, ATTRIBUTE_MAPPER_ELEMENT); if (mapperElement == null) { return null; } if (StringUtils.hasText(mapperElement.getAttribute(BEAN_ATTRIBUTE))) { return getLocalFlowServiceLocator().getAttributeMapper(mapperElement.getAttribute(BEAN_ATTRIBUTE)); } else { return new ImmutableFlowAttributeMapper(parseInputMapper(mapperElement), parseOutputMapper(mapperElement)); } } private AttributeMapper parseInputMapper(Element element) { Element mapperElement = getChildElementByTagName(element, INPUT_MAPPER_ELEMENT); if (mapperElement != null) { DefaultAttributeMapper mapper = new DefaultAttributeMapper(); parseSimpleAttributeMappings(mapper, DomUtils.getChildElementsByTagName(mapperElement, INPUT_ATTRIBUTE_ELEMENT)); parseMappings(mapper, mapperElement); return mapper; } else { return null; } } private AttributeMapper parseOutputMapper(Element element) { Element mapperElement = getChildElementByTagName(element, OUTPUT_MAPPER_ELEMENT); if (mapperElement != null) { DefaultAttributeMapper mapper = new DefaultAttributeMapper(); parseSimpleAttributeMappings(mapper, DomUtils.getChildElementsByTagName(mapperElement, OUTPUT_ATTRIBUTE_ELEMENT)); parseMappings(mapper, mapperElement); return mapper; } else { return null; } } private void parseMappings(DefaultAttributeMapper mapper, Element element) { ExpressionParser parser = getLocalFlowServiceLocator().getExpressionParser(); List mappingElements = DomUtils.getChildElementsByTagName(element, MAPPING_ELEMENT); for (Iterator it = mappingElements.iterator(); it.hasNext();) { Element mappingElement = (Element)it.next(); Expression source = parser.parseExpression(mappingElement.getAttribute(SOURCE_ATTRIBUTE)); SettableExpression target = null; if (StringUtils.hasText(mappingElement.getAttribute(TARGET_ATTRIBUTE))) { target = parser.parseSettableExpression(mappingElement.getAttribute(TARGET_ATTRIBUTE)); } else if (StringUtils.hasText(mappingElement.getAttribute(TARGET_COLLECTION_ATTRIBUTE))) { target = new CollectionAddingExpression( parser.parseSettableExpression(mappingElement.getAttribute(TARGET_COLLECTION_ATTRIBUTE))); } if (getRequired(mappingElement, false)) { mapper.addMapping(new RequiredMapping(source, target, parseTypeConverter(mappingElement))); } else { mapper.addMapping(new Mapping(source, target, parseTypeConverter(mappingElement))); } } } private void parseSimpleAttributeMappings(DefaultAttributeMapper mapper, List elements) { ExpressionParser parser = getLocalFlowServiceLocator().getExpressionParser(); for (Iterator it = elements.iterator(); it.hasNext();) { Element element = (Element)it.next(); SettableExpression attribute = parser.parseSettableExpression(element.getAttribute(NAME_ATTRIBUTE)); SettableExpression expression = new AttributeExpression(attribute, parseScope(element, ScopeType.FLOW)); if (getRequired(element, false)) { mapper.addMapping(new RequiredMapping(expression, expression, null)); } else { mapper.addMapping(new Mapping(expression, expression, null)); } } } private boolean getRequired(Element element, boolean defaultValue) { if (StringUtils.hasText(element.getAttribute(REQUIRED_ATTRIBUTE))) { return ((Boolean)fromStringTo(Boolean.class).execute(element.getAttribute(REQUIRED_ATTRIBUTE))) .booleanValue(); } else { return defaultValue; } } private ConversionExecutor parseTypeConverter(Element element) { String from = element.getAttribute(FROM_ATTRIBUTE); String to = element.getAttribute(TO_ATTRIBUTE); if (StringUtils.hasText(from)) { if (StringUtils.hasText(to)) { ConversionService service = getLocalFlowServiceLocator().getConversionService(); return service.getConversionExecutor(service.getClassByAlias(from), service.getClassByAlias(to)); } else { throw new IllegalArgumentException("Use of the 'from' attribute requires use of the 'to' attribute"); } } else { Assert.isTrue(!StringUtils.hasText(to), "Use of the 'to' attribute requires use of the 'from' attribute"); } return null; } private FlowExecutionExceptionHandler[] parseExceptionHandlers(Element element) { FlowExecutionExceptionHandler[] transitionExecutingHandlers = parseTransitionExecutingExceptionHandlers(element); FlowExecutionExceptionHandler[] customHandlers = parseCustomExceptionHandlers(element); FlowExecutionExceptionHandler[] exceptionHandlers = new FlowExecutionExceptionHandler[transitionExecutingHandlers.length + customHandlers.length]; System.arraycopy(transitionExecutingHandlers, 0, exceptionHandlers, 0, transitionExecutingHandlers.length); System.arraycopy(customHandlers, 0, exceptionHandlers, transitionExecutingHandlers.length, customHandlers.length); return exceptionHandlers; } private FlowExecutionExceptionHandler[] parseTransitionExecutingExceptionHandlers(Element element) { List transitionElements = Collections.EMPTY_LIST; if (isFlowElement(element)) { Element globalTransitionsElement = getChildElementByTagName(element, GLOBAL_TRANSITIONS_ELEMENT); if (globalTransitionsElement != null) { transitionElements = DomUtils.getChildElementsByTagName(globalTransitionsElement, TRANSITION_ELEMENT); } } else { transitionElements = DomUtils.getChildElementsByTagName(element, TRANSITION_ELEMENT); } List exceptionHandlers = new LinkedList(); for (Iterator it = transitionElements.iterator(); it.hasNext();) { Element transitionElement = (Element)it.next(); if (StringUtils.hasText(transitionElement.getAttribute(ON_EXCEPTION_ATTRIBUTE))) { exceptionHandlers.add(parseTransitionExecutingExceptionHandler(transitionElement)); } } return (FlowExecutionExceptionHandler[])exceptionHandlers .toArray(new FlowExecutionExceptionHandler[exceptionHandlers.size()]); } private FlowExecutionExceptionHandler parseTransitionExecutingExceptionHandler(Element element) { TransitionExecutingStateExceptionHandler handler = new TransitionExecutingStateExceptionHandler(); Class exceptionClass = (Class)fromStringTo(Class.class).execute(element.getAttribute(ON_EXCEPTION_ATTRIBUTE)); handler.add(exceptionClass, element.getAttribute(TO_ATTRIBUTE)); handler.getActionList().addAll(parseAnnotatedActions(element)); return handler; } private FlowExecutionExceptionHandler[] parseCustomExceptionHandlers(Element element) { List exceptionHandlers = new LinkedList(); List handlerElements = DomUtils.getChildElementsByTagName(element, EXCEPTION_HANDLER_ELEMENT); for (int i = 0; i < handlerElements.size(); i++) { Element handlerElement = (Element)handlerElements.get(i); exceptionHandlers.add(parseCustomExceptionHandler(handlerElement)); } return (FlowExecutionExceptionHandler[])exceptionHandlers .toArray(new FlowExecutionExceptionHandler[exceptionHandlers.size()]); } private FlowExecutionExceptionHandler parseCustomExceptionHandler(Element element) { return getLocalFlowServiceLocator().getExceptionHandler(element.getAttribute(BEAN_ATTRIBUTE)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy