/**
   * Initial processing pass. Collections events and sections with backings as it goes. Events are
   * processed in the next pass.
   *
   * @param section
   * @param parameterMap
   */
  private void recurseProcessSectionBackings(
      TemplatedSection section, Map<Object, Object> parameterMap) {

    Map<Object, Object> vars = new HashMap<Object, Object>();
    if (this.getTemplateVariables() != null) {
      vars.putAll(this.getTemplateVariables());
    }
    if (section.getTemplateVariables() != null) {
      vars.putAll(section.getTemplateVariables());
    }
    section.setTemplateVariables(vars);

    if (section.getSectionBacking() != null) {

      ProcessingContext context = new ProcessingContextImpl();
      TemplatedSectionBacking backing = section.getSectionBacking();

      // as we process sections, we accumulate the sections that have backings
      // so that they may target each other in the events processing pass
      // the map is Map<Section,List<TemplatedSection>>
      List<String> targets = backing.getProcessingTargetIds();
      if (targets != null)
        for (String target : targets) {

          if (sectionsWithBackings.get(target) == null)
            sectionsWithBackings.put(target, new HashSet<TemplatedSection>());
          sectionsWithBackings.get(target).add(section);
        }

      Collection<ProcessingEvent> events = backing.process(context, vars, parameterMap);

      // put the events in their appropriate target location
      if (events != null && events.size() > 0)
        for (ProcessingEvent event : events) {
          for (String targetName : event.getTargets()) {
            if (processingEvents.get(targetName) == null)
              processingEvents.put(targetName, new ArrayList<ProcessingEvent>());
            processingEvents.get(targetName).add(event);
          }
        }
    }

    // recurse up the template tree toward the leaf levels
    if (section.getChildren() != null)
      for (Object thisSection : section.getChildren().values().toArray())
        recurseProcessSectionBackings((TemplatedSection) thisSection, parameterMap);
  }
  public void handleProcessAndRender(Map<Object, Object> parameterMap, Writer writer) {

    sectionsWithBackings = new HashMap<String, Set<TemplatedSection>>();
    processingEvents = new HashMap<String, List<ProcessingEvent>>();
    int lastHashCode = 0;

    // pass one may generate events for other backings to process
    recurseProcessSectionBackings(templateTree, parameterMap);

    // iteratively process events till none are thrown...
    while (processingEvents.size() > 0 && processingEvents.hashCode() != lastHashCode) {

      Map<String, List<ProcessingEvent>> originalEvents = processingEvents;
      lastHashCode = processingEvents.hashCode();
      processingEvents = new HashMap<String, List<ProcessingEvent>>();

      for (Map.Entry<String, List<ProcessingEvent>> entry : originalEvents.entrySet()) {
        if (sectionsWithBackings.get(entry.getKey()) != null) {
          Set<TemplatedSection> sections = sectionsWithBackings.get(entry.getKey());
          for (TemplatedSection section : sections) {

            Map<Object, Object> vars = new HashMap<Object, Object>();
            if (this.getTemplateVariables() != null) {
              vars.putAll(this.getTemplateVariables());
            }
            if (section.getTemplateVariables() != null) {
              vars.putAll(section.getTemplateVariables());
            }
            section.setTemplateVariables(vars);

            ProcessingContext context = new ProcessingContextImpl();
            Collection<ProcessingEvent> newEvents =
                section
                    .getSectionBacking()
                    .processEvents(context, entry.getValue(), vars, parameterMap);

            // put the events in their appropriate target location
            if (newEvents != null)
              for (ProcessingEvent event : newEvents) {
                for (String targetName : event.getTargets()) {
                  if (processingEvents.get(targetName) == null)
                    processingEvents.put(targetName, new ArrayList<ProcessingEvent>());
                  processingEvents.get(targetName).add(event);
                }
              }
          }
        }
      }
    }

    try {
      templateTree.render(writer);
    } catch (TemplateException te) {
      throw new RuntimeException(te);
    } catch (IOException ioe) {
      throw new RuntimeException(ioe);
    }
  }