significant xsd polish

- ""-actions -> on-"event"
- simplified input/output mapping
- improved xsd doc
This commit is contained in:
Keith Donald
2008-03-04 23:02:08 +00:00
parent 9842753793
commit 452ba0ed0e
4 changed files with 467 additions and 619 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.webflow.engine.builder.xml;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
@@ -32,16 +33,15 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.convert.support.RuntimeBindingConversionExecutor;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.expression.ParserContext;
import org.springframework.binding.expression.support.CollectionAddingExpression;
import org.springframework.binding.expression.support.ParserContextImpl;
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.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
@@ -373,15 +373,86 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
}
private AttributeMapper parseInputMapper(Element element, Class sourceType, Class targetType) {
Collection inputs = DomUtils.getChildElementsByTagName(element, "input");
if (inputs.size() == 0) {
return null;
}
DefaultAttributeMapper inputMapper = new DefaultAttributeMapper();
for (Iterator it = inputs.iterator(); it.hasNext();) {
parseInputMapping((Element) it.next(), inputMapper, sourceType, targetType);
}
return inputMapper;
}
private AttributeMapper parseOutputMapper(Element element, Class sourceType, Class targetType) {
Collection inputs = DomUtils.getChildElementsByTagName(element, "output");
if (inputs.size() == 0) {
return null;
}
DefaultAttributeMapper outputMapper = new DefaultAttributeMapper();
for (Iterator it = inputs.iterator(); it.hasNext();) {
parseOutputMapping((Element) it.next(), outputMapper, sourceType, targetType);
}
return outputMapper;
}
private Mapping parseInputMapping(Element element, DefaultAttributeMapper mapper, Class sourceClass,
Class targetClass) {
ExpressionParser parser = getLocalContext().getExpressionParser();
String name = element.getAttribute("name");
String value = null;
if (element.hasAttribute("value")) {
value = element.getAttribute("value");
} else {
value = element.getAttribute(name);
}
Expression source = parser.parseExpression(name, new ParserContextImpl().eval(sourceClass));
Expression target = parser.parseExpression(value, new ParserContextImpl().eval(targetClass));
return new Mapping(source, target, parseMappingConversionExecutor(element), parseMappingRequired(element));
}
private Mapping parseOutputMapping(Element element, DefaultAttributeMapper mapper, Class sourceClass,
Class targetClass) {
ExpressionParser parser = getLocalContext().getExpressionParser();
String name = element.getAttribute("name");
String value = null;
if (element.hasAttribute("value")) {
value = element.getAttribute("value");
} else {
value = element.getAttribute(name);
}
Expression source = parser.parseExpression(value, new ParserContextImpl().eval(sourceClass));
Expression target = parser.parseExpression(name, new ParserContextImpl().eval(targetClass));
return new Mapping(source, target, parseMappingConversionExecutor(element), parseMappingRequired(element));
}
private ConversionExecutor parseMappingConversionExecutor(Element element) {
if (element.hasAttribute("type")) {
Class type = (Class) fromStringTo(Class.class).execute(element.getAttribute("type"));
return new RuntimeBindingConversionExecutor(type, getConversionService());
} else {
return null;
}
}
private boolean parseMappingRequired(Element element) {
if (element.hasAttribute("required")) {
return ((Boolean) fromStringTo(Boolean.class).execute(element.getAttribute("required"))).booleanValue();
} else {
return false;
}
}
private void parseAndAddStartActions(Element element, Flow flow) {
Element startElement = DomUtils.getChildElementByTagName(element, "start-actions");
Element startElement = DomUtils.getChildElementByTagName(element, "on-start");
if (startElement != null) {
flow.getStartActionList().addAll(parseActions(startElement));
}
}
private void parseAndAddEndActions(Element element, Flow flow) {
Element endElement = DomUtils.getChildElementByTagName(element, "end-actions");
Element endElement = DomUtils.getChildElementByTagName(element, "on-end");
if (endElement != null) {
flow.getEndActionList().addAll(parseActions(endElement));
}
@@ -479,27 +550,6 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
return new SubflowExpression(subflowId, getLocalContext().getFlowDefinitionLocator());
}
private static class SubflowExpression implements Expression {
private Expression subflowId;
private FlowDefinitionLocator flowDefinitionLocator;
public SubflowExpression(Expression subflowId, FlowDefinitionLocator flowDefinitionLocator) {
this.subflowId = subflowId;
this.flowDefinitionLocator = flowDefinitionLocator;
}
public Object getValue(Object context) throws EvaluationException {
String subflowId = (String) this.subflowId.getValue(context);
return flowDefinitionLocator.getFlowDefinition(subflowId);
}
public void setValue(Object context, Object value) throws EvaluationException {
throw new UnsupportedOperationException("Cannot set a subflow expression");
}
}
private void parseAndAddEndState(Element element, Flow flow) {
MutableAttributeMap attributes = parseMetaAttributes(element);
if (element.hasAttribute("commit")) {
@@ -533,7 +583,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
private Action[] parseEntryActions(Element element) {
Element entryActionsElement = DomUtils.getChildElementByTagName(element, "entry-actions");
Element entryActionsElement = DomUtils.getChildElementByTagName(element, "on-entry");
if (entryActionsElement != null) {
return parseActions(entryActionsElement);
} else {
@@ -580,7 +630,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
private Action[] parseRenderActions(Element element) {
Element renderActionsElement = DomUtils.getChildElementByTagName(element, "render-actions");
Element renderActionsElement = DomUtils.getChildElementByTagName(element, "on-render");
if (renderActionsElement != null) {
return parseActions(renderActionsElement);
} else {
@@ -589,7 +639,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
private Action[] parseExitActions(Element element) {
Element exitActionsElement = DomUtils.getChildElementByTagName(element, "exit-actions");
Element exitActionsElement = DomUtils.getChildElementByTagName(element, "on-exit");
if (exitActionsElement != null) {
return parseActions(exitActionsElement);
} else {
@@ -762,77 +812,6 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
}
private AttributeMapper parseInputMapper(Element element, Class sourceType, Class targetType) {
Element mapperElement = DomUtils.getChildElementByTagName(element, "input-mapper");
if (mapperElement != null) {
DefaultAttributeMapper mapper = new DefaultAttributeMapper();
parseMappings(mapper, mapperElement, sourceType, targetType);
return mapper;
} else {
return null;
}
}
private AttributeMapper parseOutputMapper(Element element, Class sourceType, Class targetType) {
Element mapperElement = DomUtils.getChildElementByTagName(element, "output-mapper");
if (mapperElement != null) {
DefaultAttributeMapper mapper = new DefaultAttributeMapper();
parseMappings(mapper, mapperElement, sourceType, targetType);
return mapper;
} else {
return null;
}
}
private void parseMappings(DefaultAttributeMapper mapper, Element element, Class sourceClass, Class targetClass) {
ExpressionParser parser = getLocalContext().getExpressionParser();
List mappingElements = DomUtils.getChildElementsByTagName(element, "mapping");
for (Iterator it = mappingElements.iterator(); it.hasNext();) {
Element mappingElement = (Element) it.next();
Expression source = parser.parseExpression(mappingElement.getAttribute("source"), new ParserContextImpl()
.eval(sourceClass));
Expression target = null;
if (StringUtils.hasText(mappingElement.getAttribute("target"))) {
target = parser.parseExpression(mappingElement.getAttribute("target"), new ParserContextImpl()
.eval(targetClass));
} else if (StringUtils.hasText(mappingElement.getAttribute("target-collection"))) {
target = new CollectionAddingExpression(parser.parseExpression(mappingElement
.getAttribute("target-collection"), new ParserContextImpl().eval(targetClass)));
}
if (getRequiredAttribute(mappingElement, false)) {
mapper.addMapping(new RequiredMapping(source, target, parseTypeConverter(mappingElement)));
} else {
mapper.addMapping(new Mapping(source, target, parseTypeConverter(mappingElement)));
}
}
}
private ConversionExecutor parseTypeConverter(Element element) {
String from = element.getAttribute("from");
String to = element.getAttribute("to");
if (StringUtils.hasText(from)) {
if (StringUtils.hasText(to)) {
ConversionService service = getLocalContext().getConversionService();
Class sourceClass = (Class) fromStringTo(Class.class).execute(from);
Class targetClass = (Class) fromStringTo(Class.class).execute(to);
return service.getConversionExecutor(sourceClass, targetClass);
} 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 boolean getRequiredAttribute(Element element, boolean defaultValue) {
if (StringUtils.hasText(element.getAttribute("required"))) {
return ((Boolean) fromStringTo(Boolean.class).execute(element.getAttribute("required"))).booleanValue();
} else {
return defaultValue;
}
}
private FlowExecutionExceptionHandler[] parseExceptionHandlers(Element element) {
FlowExecutionExceptionHandler[] transitionExecutingHandlers = parseTransitionExecutingExceptionHandlers(element);
FlowExecutionExceptionHandler[] customHandlers = parseCustomExceptionHandlers(element);
@@ -892,7 +871,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
private void parseAndSetSecuredAttribute(Element element, MutableAttributeMap attributes) {
Element secured = DomUtils.getChildElementByTagName(element, "secured");
Element secured = DomUtils.getChildElementByTagName(element, SecurityRule.SECURITY_AUTHORITY_ATTRIBUTE_NAME);
if (secured != null) {
SecurityRule rule = new SecurityRule();
rule.setRequiredAuthorities(SecurityRule.convertAuthoritiesFromCommaSeparatedString(secured
@@ -906,7 +885,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
// default to any
rule.setComparisonType(SecurityRule.COMPARISON_ANY);
}
attributes.put("secured", rule);
attributes.put(SecurityRule.SECURITY_AUTHORITY_ATTRIBUTE_NAME, rule);
}
}
@@ -942,6 +921,27 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
}
private static class SubflowExpression implements Expression {
private Expression subflowId;
private FlowDefinitionLocator flowDefinitionLocator;
public SubflowExpression(Expression subflowId, FlowDefinitionLocator flowDefinitionLocator) {
this.subflowId = subflowId;
this.flowDefinitionLocator = flowDefinitionLocator;
}
public Object getValue(Object context) throws EvaluationException {
String subflowId = (String) this.subflowId.getValue(context);
return flowDefinitionLocator.getFlowDefinition(subflowId);
}
public void setValue(Object context, Object value) throws EvaluationException {
throw new UnsupportedOperationException("Cannot set a subflow expression");
}
}
public String toString() {
return new ToStringCreator(this).append("location", resource).toString();
}

View File

@@ -24,9 +24,9 @@ import org.springframework.webflow.config.FlowDefinitionResource;
import org.springframework.webflow.config.FlowDefinitionResourceFactory;
import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser;
import org.springframework.webflow.engine.EndState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.expression.el.WebFlowELExpressionParser;
import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests;
/**

View File

@@ -2,31 +2,29 @@
<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-2.0.xsd">
<view-state id="enterCriteria" view="searchCriteria">
<render-actions>
<on-render>
<evaluate expression="#{formAction.setupForm}" />
</render-actions>
</on-render>
<transition on="search" to="displayResults">
<evaluate expression="#{formAction.bindAndValidate}" />
</transition>
</view-state>
<view-state id="displayResults" view="searchResults">
<render-actions>
<on-render>
<evaluate expression="#{phonebook.search(searchCriteria)}" result="#{flowScope.results}" />
</render-actions>
</on-render>
<transition on="newSearch" to="enterCriteria"/>
<transition on="select" to="browseDetails"/>
</view-state>
<subflow-state id="browseDetails" subflow="detail-flow">
<input-mapper>
<mapping source="#{requestParameters.id}" target="#{id}" from="string" to="long"/>
</input-mapper>
<subflow-state id="browseDetails" subflow="detail-flow">
<input name="id" value="#{requestParameters.id}" type="long" required="true"/>
<transition on="finish" to="displayResults"/>
</subflow-state>
<import resource="search-flow-beans.xml"/>
<bean-import resource="search-flow-beans.xml"/>
</flow>