/*
   * This could be made a whole lot faster using a recursive query or some other techniques, but due to the caching and,
   * generally speaking, the relatively flat nature of the state machine this shouldn't be expensive.
   */
  public State[] getConcreteDescendentStates(int[] stateIds) throws ApplicationException {
    if (stateIds == null) {
      return null;
    }

    if (stateIds.length == 0) {
      return new State[0];
    }

    ArrayList results = new ArrayList();
    for (int i = 0; i < stateIds.length; i++) {
      ArrayList childrenOfState = (ArrayList) cache.get("descendents/" + stateIds[i]);
      if (childrenOfState == null) {
        childrenOfState = new ArrayList();
        State state = this.getStateById(stateIds[i]);
        if (!state.isAbstract()) {
          childrenOfState.add(state);
        }

        ArrayList children = StateManager.getStatesByParentId(state.getId());
        int[] childrenIds = new int[children.size()];
        for (int j = 0; j < children.size(); j++) {
          childrenIds[j] = ((State) children.get(j)).getId();
        }
        childrenOfState.addAll(Arrays.asList(getConcreteDescendentStates(childrenIds)));
        if (!childrenOfState.isEmpty()) {
          cache.put("descendents/" + stateIds[i], childrenOfState);
        }
      }

      results.addAll(childrenOfState);
    }

    return (State[]) ArrayUtils.toTypedArray(results, State.class);
  }
 public State getStateById(int id) throws ApplicationException {
   State state = (State) cache.get(id);
   if (state == null) {
     state = StateManager.getStateById(id);
     if (state != null) {
       cache.put(id, state);
       cache.put("<workflow>=" + state.getWorkflowId() + "/<name>=" + state.getName(), state);
     }
   }
   return state;
 }
 public State[] getStatesByWorkflowId(int worflowId) throws ApplicationException {
   ArrayList list = StateManager.getStatesByWorkflowId(worflowId);
   State[] result = new State[list.size()];
   for (int i = 0; i < list.size(); i++) {
     State current = (State) list.get(i);
     if (current != null) {
       cache.put(current.getId(), current);
     }
     result[i] = current;
   }
   return result;
 }
 public State getStateByNameAndWorkflowId(String name, int workflowId)
     throws ApplicationException {
   String key = "<workflow>=" + workflowId + "/<name>=" + name;
   State state = (State) cache.get(key);
   if (state == null) {
     state = StateManager.getStateByNameAndWorkflowId(name, workflowId);
     if (state != null) {
       cache.put(key, state);
       cache.put(state.getId(), state);
     }
   }
   return state;
 }
 public State getStateByCurrentBreadcrumbId(int breadcrumbId) throws ApplicationException {
   return StateManager.getStateByCurrentBreadcrumbId(breadcrumbId);
 }