/*
###############################################################################
# #
# Copyright (C) 2011-2016 OpenMEAP, Inc. #
# Credits to Jonathan Schang & Rob Thacher #
# #
# Released under the LGPLv3 #
# #
# OpenMEAP is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Lesser General Public License as published #
# by the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# OpenMEAP is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU Lesser General Public License for more details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with OpenMEAP. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
*/
package com.openmeap.web;
import java.io.IOException;
import java.io.Writer;
import java.util.*;
import com.openmeap.event.ProcessingEvent;
import freemarker.template.TemplateException;
public class GenericDocumentProcessor implements DocumentProcessor {
private Map<Object,Object> templateVariables = null;
private TemplatedSection templateTree = null;
private Boolean processesFormData = Boolean.TRUE;
private Map<String,Set<TemplatedSection>> sectionsWithBackings;
private Map<String,List<ProcessingEvent>> processingEvents;
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);
}
}
/**
* 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 setTemplateVariables(Map<Object, Object> variables) {
templateVariables = variables;
}
public Map<Object, Object> getTemplateVariables() {
return templateVariables;
}
public void setTemplateTree(TemplatedSection templates) {
templateTree = templates;
}
public TemplatedSection getTemplateTree() {
return templateTree;
}
/**
* @param processesFormData a hint to the servlet on how to prepare for rendering.
*/
public void setProcessesFormData(Boolean processesFormData) {
this.processesFormData = processesFormData;
}
public Boolean getProcessesFormData() {
return processesFormData;
}
}