1 16 package org.springframework.webflow.engine; 17 18 import java.util.Iterator ; 19 import java.util.Set ; 20 21 import org.apache.commons.logging.Log; 22 import org.apache.commons.logging.LogFactory; 23 import org.springframework.binding.mapping.AttributeMapper; 24 import org.springframework.core.CollectionFactory; 25 import org.springframework.core.style.StylerUtils; 26 import org.springframework.core.style.ToStringCreator; 27 import org.springframework.util.Assert; 28 import org.springframework.util.StringUtils; 29 import org.springframework.webflow.core.collection.MutableAttributeMap; 30 import org.springframework.webflow.definition.FlowDefinition; 31 import org.springframework.webflow.definition.StateDefinition; 32 import org.springframework.webflow.execution.FlowExecutionException; 33 import org.springframework.webflow.execution.RequestContext; 34 import org.springframework.webflow.execution.ViewSelection; 35 36 116 public class Flow extends AnnotatedObject implements FlowDefinition { 117 118 121 protected final Log logger = LogFactory.getLog(getClass()); 122 123 127 private String id; 128 129 132 private Set states = CollectionFactory.createLinkedSetIfPossible(9); 133 134 137 private State startState; 138 139 142 private Set variables = CollectionFactory.createLinkedSetIfPossible(3); 143 144 147 private AttributeMapper inputMapper; 148 149 156 private ActionList startActionList = new ActionList(); 157 158 161 private TransitionSet globalTransitionSet = new TransitionSet(); 162 163 166 private ActionList endActionList = new ActionList(); 167 168 171 private AttributeMapper outputMapper; 172 173 176 private FlowExecutionExceptionHandlerSet exceptionHandlerSet = new FlowExecutionExceptionHandlerSet(); 177 178 181 private Set inlineFlows = CollectionFactory.createLinkedSetIfPossible(3); 182 183 188 public Flow(String id) { 189 setId(id); 190 } 191 192 194 public String getId() { 195 return id; 196 } 197 198 public StateDefinition getStartState() { 199 if (startState == null) { 200 throw new IllegalStateException ("No start state has been set for this flow ('" + getId() 201 + "') -- flow builder configuration error?"); 202 } 203 return startState; 204 } 205 206 public StateDefinition getState(String stateId) { 207 return getStateInstance(stateId); 208 } 209 210 213 protected void setId(String id) { 214 Assert.hasText(id, "This flow must have a unique, non-blank identifier"); 215 this.id = id; 216 } 217 218 227 protected void add(State state) throws IllegalArgumentException { 228 if (this != state.getFlow() && state.getFlow() != null) { 229 throw new IllegalArgumentException ("State " + state + " cannot be added to this flow '" + getId() 230 + "' -- it already belongs to a different flow: '" + state.getFlow().getId() + "'"); 231 } 232 if (this.states.contains(state) || this.containsState(state.getId())) { 233 throw new IllegalArgumentException ("This flow '" + getId() + "' already contains a state with id '" 234 + state.getId() + "' -- state ids must be locally unique to the flow definition; " 235 + "existing state-ids of this flow include: " + StylerUtils.style(getStateIds())); 236 } 237 boolean firstAdd = states.isEmpty(); 238 states.add(state); 239 if (firstAdd) { 240 setStartState(state); 241 } 242 } 243 244 248 public int getStateCount() { 249 return states.size(); 250 } 251 252 257 public boolean containsState(String stateId) { 258 Iterator it = states.iterator(); 259 while (it.hasNext()) { 260 State state = (State)it.next(); 261 if (state.getId().equals(stateId)) { 262 return true; 263 } 264 } 265 return false; 266 } 267 268 276 public void setStartState(String stateId) throws IllegalArgumentException { 277 setStartState(getStateInstance(stateId)); 278 } 279 280 287 public void setStartState(State state) throws IllegalArgumentException { 288 if (!states.contains(state)) { 289 throw new IllegalArgumentException ("State '" + state + "' is not a state of flow '" + getId() + "'"); 290 } 291 startState = state; 292 } 293 294 302 public TransitionableState getTransitionableState(String stateId) 303 throws IllegalArgumentException , ClassCastException { 304 State state = getStateInstance(stateId); 305 if (state != null && !(state instanceof TransitionableState)) { 306 throw new ClassCastException ("The state '" + stateId + "' of flow '" + getId() + "' must be transitionable"); 307 } 308 return (TransitionableState)state; 309 } 310 311 317 public State getStateInstance(String stateId) throws IllegalArgumentException { 318 if (!StringUtils.hasText(stateId)) { 319 throw new IllegalArgumentException ("The specified stateId is invalid: state identifiers must be non-blank"); 320 } 321 Iterator it = states.iterator(); 322 while (it.hasNext()) { 323 State state = (State)it.next(); 324 if (state.getId().equals(stateId)) { 325 return state; 326 } 327 } 328 throw new IllegalArgumentException ("Cannot find state with id '" + stateId + "' in flow '" + getId() + "' -- " 329 + "Known state ids are '" + StylerUtils.style(getStateIds()) + "'"); 330 } 331 332 338 public String [] getStateIds() { 339 String [] stateIds = new String [getStateCount()]; 340 int i = 0; 341 Iterator it = states.iterator(); 342 while (it.hasNext()) { 343 stateIds[i++] = ((State)it.next()).getId(); 344 } 345 return stateIds; 346 } 347 348 352 public void addVariable(FlowVariable variable) { 353 variables.add(variable); 354 } 355 356 360 public void addVariables(FlowVariable[] variables) { 361 if (variables == null) { 362 return; 363 } 364 for (int i = 0; i < variables.length; i++) { 365 addVariable(variables[i]); 366 } 367 } 368 369 372 public FlowVariable[] getVariables() { 373 return (FlowVariable[])variables.toArray(new FlowVariable[variables.size()]); 374 } 375 376 380 public AttributeMapper getInputMapper() { 381 return inputMapper; 382 } 383 384 388 public void setInputMapper(AttributeMapper inputMapper) { 389 this.inputMapper = inputMapper; 390 } 391 392 397 public ActionList getStartActionList() { 398 return startActionList; 399 } 400 401 406 public ActionList getEndActionList() { 407 return endActionList; 408 } 409 410 414 public AttributeMapper getOutputMapper() { 415 return outputMapper; 416 } 417 418 422 public void setOutputMapper(AttributeMapper outputMapper) { 423 this.outputMapper = outputMapper; 424 } 425 426 436 public FlowExecutionExceptionHandlerSet getExceptionHandlerSet() { 437 return exceptionHandlerSet; 438 } 439 440 444 public void addInlineFlow(Flow flow) { 445 inlineFlows.add(flow); 446 } 447 448 452 public String [] getInlineFlowIds() { 453 String [] flowIds = new String [getInlineFlowCount()]; 454 int i = 0; 455 Iterator it = inlineFlows.iterator(); 456 while (it.hasNext()) { 457 flowIds[i++] = ((Flow)it.next()).getId(); 458 } 459 return flowIds; 460 } 461 462 466 public Flow[] getInlineFlows() { 467 return (Flow[])inlineFlows.toArray(new Flow[inlineFlows.size()]); 468 } 469 470 474 public int getInlineFlowCount() { 475 return inlineFlows.size(); 476 } 477 478 484 public boolean containsInlineFlow(String id) { 485 return getInlineFlow(id) != null; 486 } 487 488 495 public Flow getInlineFlow(String id) throws IllegalArgumentException { 496 if (!StringUtils.hasText(id)) { 497 throw new IllegalArgumentException ( 498 "The specified inline flowId is invalid: flow identifiers must be non-blank"); 499 } 500 Iterator it = inlineFlows.iterator(); 501 while (it.hasNext()) { 502 Flow flow = (Flow)it.next(); 503 if (flow.getId().equals(id)) { 504 return flow; 505 } 506 } 507 return null; 508 } 509 510 515 public TransitionSet getGlobalTransitionSet() { 516 return globalTransitionSet; 517 } 518 519 521 public boolean equals(Object o) { 522 if (!(o instanceof Flow)) { 523 return false; 524 } 525 Flow other = (Flow)o; 526 return id.equals(other.id); 527 } 528 529 public int hashCode() { 530 return id.hashCode(); 531 } 532 533 535 551 public ViewSelection start(RequestControlContext context, MutableAttributeMap input) throws FlowExecutionException { 552 createVariables(context); 553 if (inputMapper != null) { 554 inputMapper.map(input, context, null); 555 } 556 startActionList.execute(context); 557 return startState.enter(context); 558 } 559 560 569 public ViewSelection onEvent(RequestControlContext context) throws FlowExecutionException { 570 TransitionableState currentState = getCurrentTransitionableState(context); 571 try { 572 return currentState.onEvent(context); 573 } 574 catch (NoMatchingTransitionException e) { 575 Transition transition = globalTransitionSet.getTransition(context); 577 if (transition != null) { 578 return transition.execute(currentState, context); 579 } 580 else { 581 throw e; 584 } 585 } 586 } 587 588 602 public void end(RequestControlContext context, MutableAttributeMap output) throws FlowExecutionException { 603 endActionList.execute(context); 604 if (outputMapper != null) { 605 outputMapper.map(context, output, null); 606 } 607 } 608 609 616 public ViewSelection handleException(FlowExecutionException exception, RequestControlContext context) 617 throws FlowExecutionException { 618 return getExceptionHandlerSet().handleException(exception, context); 619 } 620 621 623 626 private void createVariables(RequestContext context) { 627 Iterator it = variables.iterator(); 628 while (it.hasNext()) { 629 FlowVariable variable = (FlowVariable)it.next(); 630 if (logger.isDebugEnabled()) { 631 logger.debug("Creating " + variable); 632 } 633 variable.create(context); 634 } 635 } 636 637 640 private TransitionableState getCurrentTransitionableState(RequestControlContext context) { 641 State currentState = (State)context.getCurrentState(); 642 if (!(currentState instanceof TransitionableState)) { 643 throw new IllegalStateException ("You can only signal events in transitionable states, and state " 644 + context.getCurrentState() + " is not transitionable - programmer error"); 645 } 646 return (TransitionableState)currentState; 647 } 648 649 public String toString() { 650 return new ToStringCreator(this).append("id", id).append("states", states).append("startState", startState) 651 .append("variables", variables).append("inputMapper", inputMapper).append("startActionList", 652 startActionList).append("exceptionHandlerSet", exceptionHandlerSet).append( 653 "globalTransitionSet", globalTransitionSet).append("endActionList", endActionList).append( 654 "outputMapper", outputMapper).append("inlineFlows", inlineFlows).toString(); 655 } 656 } | Popular Tags |