org.xchain.framework.jsl.TemplateSourceBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.xchain.framework.jsl.TemplateSourceBuilder.java

Source

/**
 *    Copyright 2011 meltmedia
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.xchain.framework.jsl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.regex.Matcher;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.xml.sax.SAXException;

/**
 * A utility class to build jsl template command classes.
 *
 * @author Christian Trimble
 * @author Jason Rose
 * @author Josh Kennedy
 */
public class TemplateSourceBuilder {
    public static Logger log = LoggerFactory.getLogger(TemplateSourceBuilder.class);

    public static final String TEMPLATE_PACKAGE = "org.xchain.namespaces.jsl";
    public static final String BASE_TEMPLATE_NAME = "TemplateCommand";

    public static final String FIXED_PART_REGEX = "((?:[^{]+|\\{\\{)+)";
    public static final String DYNAMIC_PART_REGEX = "(?:\\{((?:[^\\}\'\"]*|\"[^\"]*\"|\'[^\']*\')*)\\})";
    public static final String ENCODING_REGEX = "(?:([\u0000-\u007f]+)|([^\u0000-\u007f]+))";

    public static Pattern ATTRIBUTE_VALUE_TEMPLATE_PATTERN = null;
    public static Pattern ENCODING_PATTERN = null;

    static {
        try {
            ATTRIBUTE_VALUE_TEMPLATE_PATTERN = Pattern
                    .compile("\\G" + FIXED_PART_REGEX + "|" + DYNAMIC_PART_REGEX + "|\\Z");
        } catch (PatternSyntaxException pse) {
            log.error("Could not compile attribute value template pattern.", pse);
        }
        try {
            ENCODING_PATTERN = Pattern.compile(ENCODING_REGEX);
        } catch (PatternSyntaxException pse) {
            log.error("Could not compile encoding pattern.", pse);
        }
    }

    /** A stack of the contexts for the source files we are working on. */
    private LinkedList<Context> contextStack = new LinkedList<Context>();

    private int commandId = 0;

    public void pushContext(Context context) {
        contextStack.addFirst(context);
    }

    public Context popContext() {
        return contextStack.removeFirst();
    }

    public int nextCommandId() {
        return commandId++;
    }

    public void startSource(Map<String, String> transitionPrefixMapping,
            Set<String> transitionExcludeResultPrefixSet, boolean excludeResultPrefixBoundary) {
        Context context = new Context();
        context.setTransitionPrefixMapping(transitionPrefixMapping);
        context.setTransitionExcludeResultPrefixSet(transitionExcludeResultPrefixSet);
        context.setExcludeResultPrefixBoundary(excludeResultPrefixBoundary);

        pushContext(context);

        // add the source result to the context.
        context.setCommandIndex(nextCommandId());

        startVirtualChain();
    }

    public SourceResult endSource() {
        // clean up all of the virtual chains.
        Context context = contextStack.getFirst();
        while (!context.getVirtualChainContextStack().isEmpty()) {
            endVirtualChain();
        }

        // pop the current context off of the stack.
        context = popContext();

        StringBuilder sourceBuilder = new StringBuilder();

        sourceBuilder.append("package ").append(TEMPLATE_PACKAGE).append(";\n");
        sourceBuilder.append("\n");

        // add all of the imports.
        sourceBuilder.append("import org.apache.commons.jxpath.JXPathContext;\n");
        sourceBuilder.append("import org.xchain.framework.sax.CommandHandler;\n");
        sourceBuilder.append("import org.xml.sax.Attributes;\n");
        sourceBuilder.append("import org.xml.sax.ContentHandler;\n");
        sourceBuilder.append("import org.xml.sax.SAXException;\n");
        sourceBuilder.append("import org.xml.sax.helpers.AttributesImpl;\n");
        sourceBuilder.append("import org.apache.commons.jxpath.JXPathContext;\n");
        sourceBuilder.append("import javax.xml.namespace.QName;\n");
        sourceBuilder.append("\n");

        // add the class definition.
        sourceBuilder.append("public class ").append(BASE_TEMPLATE_NAME).append(context.getCommandIndex())
                .append("\n");
        sourceBuilder.append("  extends AbstractTemplateCommand\n");
        sourceBuilder.append("{\n");

        // add the execute template method.  This is the hook into the abstract template command.
        sourceBuilder.append("  public ").append(BASE_TEMPLATE_NAME).append(context.getCommandIndex())
                .append("()\n");
        sourceBuilder.append("  {\n");
        sourceBuilder.append("    super(").append(context.getElementCount()).append(");\n");
        sourceBuilder.append("  }\n");
        sourceBuilder.append("  public boolean executeTemplate(JXPathContext context)\n");
        sourceBuilder.append("    throws Exception\n");
        sourceBuilder.append("  {\n");
        sourceBuilder.append("    boolean result = false;\n");
        sourceBuilder.append("    Exception exception = null;\n");
        sourceBuilder.append("    CommandHandler handler = getContentHandler();\n");

        if (context.getExcludeResultPrefixBoundary()) {
            sourceBuilder.append("    handler.startExcludeResultPrefixContext();\n");
        }

        // we need to initialize the context of the output document with all of the namespace
        // prefixes that occur above this commands first element. 
        for (Map.Entry<String, String> mapping : context.getTransitionPrefixMapping().entrySet()) {
            sourceBuilder.append("  handler.startPrefixMapping(" + stringConstant(mapping.getKey()) + ", "
                    + stringConstant(mapping.getValue()) + ");\n");
        }

        for (String excludeResultPrefix : context.getTransitionExcludeResultPrefixSet()) {
            sourceBuilder.append("  handler.startExcludeResultPrefix(").append(stringConstant(excludeResultPrefix))
                    .append(");\n");
        }

        // code to call the first virtual chain.
        sourceBuilder.append("    try {\n");
        sourceBuilder.append("      result = virtualChain0(context);\n");
        sourceBuilder.append("    }\n");
        sourceBuilder.append("    catch( Exception e ) {\n");
        sourceBuilder.append("      exception = e;\n");
        sourceBuilder.append("    }\n");

        // TODO: skip end prefix mappings if we are handling an error.

        for (String excludeResultPrefix : context.getTransitionExcludeResultPrefixSet()) {
            sourceBuilder.append("  handler.endExcludeResultPrefix(").append(stringConstant(excludeResultPrefix))
                    .append(");\n");
        }

        // close all of the namespace prefixes that were set above this element.
        for (Map.Entry<String, String> mapping : context.getTransitionPrefixMapping().entrySet()) {
            sourceBuilder.append("  handler.endPrefixMapping(" + stringConstant(mapping.getKey()) + ");\n");
        }

        if (context.getExcludeResultPrefixBoundary()) {
            sourceBuilder.append("    handler.endExcludeResultPrefixContext();\n");
        }

        sourceBuilder.append("    if( exception != null ) {\n");
        sourceBuilder.append("      throw exception;\n");
        sourceBuilder.append("    }\n");

        // close the execute template method.
        sourceBuilder.append("    return result;\n");
        sourceBuilder.append("  }\n");

        // if there has not been a virtual chain created, then we need to add one.

        // append the source builder.
        sourceBuilder.append(context.getMethodBuilder());

        sourceBuilder.append("}\n");

        SourceResult sourceResult = new SourceResult();
        sourceResult.setSourceResourceName(
                "org/xchain/namespaces/jsl/" + BASE_TEMPLATE_NAME + context.getCommandIndex() + ".java");
        sourceResult.setClassResourceName(
                "org/xchain/namespaces/jsl/" + BASE_TEMPLATE_NAME + context.getCommandIndex() + ".class");
        sourceResult.setClassName("org.xchain.namespaces.jsl." + BASE_TEMPLATE_NAME + context.getCommandIndex());
        sourceResult.setSource(sourceBuilder.toString());

        return sourceResult;
    }

    /**
     * Signals the start of a new virtual command.  This method adds a call to the current virtual
     * method context and then creates a new virtual method context.
     */
    public void startVirtualChain() {
        // get the context.
        Context context = contextStack.getFirst();

        // get the virtual chain context index.
        int index = context.nextVirtualChainIndex();
        String virtualChainName = "virtualChain" + index;

        // if there is currently a virtual chain on the stack, then it needs to call this virual chain.
        if (!contextStack.getFirst().getVirtualChainContextStack().isEmpty()) {

            // change the mode to virtual chain.
            changeBodyMode(BodyMode.VIRTUAL_CHAIN);

            // get the current virtual chain builder.
            StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                    .getBodyBuilder();

            // append the call to the new virtual chain.
            indent(bodyBuilder).append("      result = ").append(virtualChainName).append("(context);\n");
        }

        // push the virtual chain context onto the stack.
        contextStack.getFirst().getVirtualChainContextStack().addFirst(new VirtualChainContext(virtualChainName));
    }

    /**
     * Signals the end of a virtual chain.  The method removes the current virtual chain context from the stack and 
     * uses it to build the virtual method.
     */
    public void endVirtualChain() {
        // change the mode to top level.
        changeBodyMode(BodyMode.TOP_LEVEL);

        // remove the top virtual chain builder from the stack.
        VirtualChainContext virtualChainContext = contextStack.getFirst().getVirtualChainContextStack()
                .removeFirst();

        StringBuilder methodBuilder = contextStack.getFirst().getMethodBuilder();

        methodBuilder.append("private boolean ").append(virtualChainContext.getName())
                .append("(JXPathContext context)\n");
        methodBuilder.append("  throws Exception\n");
        methodBuilder.append("{\n");
        methodBuilder.append("  // the result and exception for this virtual chain\n");
        methodBuilder.append("  boolean result = false;\n");
        methodBuilder.append("  Exception exception = null;\n");

        methodBuilder.append("  // the target for content handler events.\n");
        methodBuilder.append("  CommandHandler handler = getContentHandler();\n");

        methodBuilder.append("  // variables for building attribute objects.\n");
        methodBuilder.append("  AttributesImpl attributes = new AttributesImpl();\n");
        methodBuilder.append("  StringBuilder attributeValueBuilder = new StringBuilder();\n");

        methodBuilder.append("  // variables for processing value-of elements.\n");
        methodBuilder.append("  Object valueOfObject = null;\n");
        methodBuilder.append("  String attributeValueString = null;\n");

        // TODO: This should be a stack and the super classes stack should be removed.
        methodBuilder.append("  // variables for parsing dynamic qNames.\n");
        methodBuilder.append("  QName qName = null;\n");

        methodBuilder.append("  // variables for processing comment elements.\n");

        // add the indexes of the children chains.
        methodBuilder.append("  int[] commandChildrenIndecies = {");
        Iterator<Integer> childrenIndexIterator = virtualChainContext.getCommandIndexList().iterator();
        while (childrenIndexIterator.hasNext()) {
            methodBuilder.append(childrenIndexIterator.next());
            if (childrenIndexIterator.hasNext()) {
                methodBuilder.append(", ");
            }
        }
        methodBuilder.append("};\n");

        // add the indices of the children elements.
        methodBuilder.append("  int[] elementChildrenIndecies = {");
        Iterator<Integer> elementIndexIterator = virtualChainContext.getElementIndexList().iterator();
        while (elementIndexIterator.hasNext()) {
            methodBuilder.append(elementIndexIterator.next());
            if (elementIndexIterator.hasNext()) {
                methodBuilder.append(", ");
            }
        }
        methodBuilder.append("};\n");

        // add the body portion of the method, this contains all of the sax output code and command calls.
        methodBuilder.append(virtualChainContext.getBodyBuilder());

        // add the post process code.
        methodBuilder
                .append("  return virtualPostProcess( context, exception, result, commandChildrenIndecies);\n");

        // close the virtual chain.
        methodBuilder.append("}\n");
    }

    public void appendCommandCall() {
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        // get the index for the next command child and increment the command count in the context.
        Integer commandIndex = context.getCommandCount();
        context.setCommandCount(commandIndex + 1);

        // if the virtual chain context has a depth greater than 1, then we need a virtual chain here.
        if (virtualChainContext.getElementDepth() > 0) {
            startVirtualChain();
            virtualChainContext = context.getVirtualChainContextStack().getFirst();
        }

        // add the command index to the current virtual context.
        virtualChainContext.getCommandIndexList().add(commandIndex);

        // start the chain body mode if needed.
        changeBodyMode(BodyMode.CHAIN);

        virtualChainContext.getToExecuteIndexList().add(commandIndex);
    }

    public void startStartElement() {
        // push virtual chain is needed.
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        // get the index for the next element child and increment the element count in the context.
        Integer elementIndex = context.getElementCount();
        context.setElementCount(elementIndex + 1);

        // track the current element index.
        context.getElementIndexStack().addFirst(elementIndex);

        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        // track the element indices that are in this virtual chain context.
        virtualChainContext.getElementIndexList().add(elementIndex);
        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() + 1);
    }

    public void endStartElement() {

    }

    public void startEndElement() {
        // start the test for this element.
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        // close virtual chain contexts, until we find one with an element depth of at least one.
        while (virtualChainContext.getElementDepth() == 0) {
            endVirtualChain();
            virtualChainContext = context.getVirtualChainContextStack().getFirst();
        }

        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);

        // get the index for the next element child and increment the element count in the context.
        Integer elementIndex = context.getElementIndexStack().getFirst();

        // get the string builder.
        StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

        // TODO: track that the end element has been output.
        indent(bodyBuilder).append("if( isElementStarted(").append(elementIndex).append(") ) {\n");
    }

    public void endEndElement() {
        // end the test for this element.
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        // get the index for the next element child and increment the element count in the context.
        context.getElementIndexStack().removeFirst();

        // get the string builder.
        StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

        // TODO: track that the end element has been output.
        indent(bodyBuilder).append("}\n");

        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() - 1);
    }

    public void appendStartPrefixMapping(String prefix, String uri) {
        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);
        String escapedPrefix = stringConstant(prefix);
        String escapedUri = stringConstant(uri);
        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("handler.startPrefixMapping(").append(escapedPrefix).append(", ")
                .append(escapedUri).append(");\n");
        // TODO: We need to track the new namespace.
        indent(bodyBuilder).append("context.registerNamespace(").append(escapedPrefix).append(", ")
                .append(escapedUri).append(");\n");
    }

    public void appendEndPrefixMapping(String prefix) {
        String escapedPrefix = stringConstant(prefix);
        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);
        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        // TODO: We need to replace the namespace that was specified for this prefix.
        indent(bodyBuilder).append("context.registerNamespace(").append(escapedPrefix).append(", ")
                .append(escapedPrefix).append(");\n");
        indent(bodyBuilder).append("handler.endPrefixMapping(").append(escapedPrefix).append(");\n");
    }

    public void appendStartExcludeResultPrefix(String prefix) {
        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);
        String escapedPrefix = stringConstant(prefix);
        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("handler.startExcludeResultPrefix(").append(escapedPrefix).append(");\n");
    }

    public void appendEndExcludeResultPrefix(String prefix) {
        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);
        String escapedPrefix = stringConstant(prefix);
        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("handler.endExcludeResultPrefix(").append(escapedPrefix).append(");\n");
    }

    public void appendContextStartPrefixMapping(String prefix, String uri) {
        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);
        String escapedPrefix = stringConstant(prefix);
        String escapedUri = stringConstant(uri);
        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("context.registerNamespace(").append(escapedPrefix).append(", ")
                .append(escapedUri).append(");\n");
    }

    public void appendContextEndPrefixMapping(String prefix) {
        String escapedPrefix = stringConstant(prefix);
        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);
        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("context.registerNamespace(").append(escapedPrefix).append(", ")
                .append(escapedPrefix).append(");\n");
    }

    public void appendAttributeValueTemplate(String uri, String localName, String qName,
            String attributeValueTemplate) throws SAXException {
        // escape the values that will be passed on.
        String escapedUri = stringConstant(uri);
        String escapedLocalName = stringConstant(localName);
        String escapedQName = stringConstant(qName);

        // change the mode of the virtual chain to BodyMode.START_ELEMENT_EVENTS if needed.
        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        // get the string builder.
        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();

        // parse the attribute value template and build code to create it.
        Iterator<String> attributeValueIterator = parseAttributeValueTemplate(attributeValueTemplate).iterator();
        while (attributeValueIterator.hasNext()) {
            // add code for the fixed part.
            indent(bodyBuilder).append("attributeValueBuilder.append(")
                    .append(stringConstant(attributeValueIterator.next())).append(");\n");

            // if there is a dynamic part, then add it.
            if (attributeValueIterator.hasNext()) {
                indent(bodyBuilder).append("attributeValueString = (String)context.getValue(")
                        .append(stringConstant(attributeValueIterator.next())).append(", String.class);\n");
                indent(bodyBuilder).append(
                        "attributeValueBuilder.append(attributeValueString != null ? attributeValueString : \"\");\n");
            }
        }

        // add the code to add the attribute.
        indent(bodyBuilder).append("attributes.addAttribute(").append(escapedUri).append(", ")
                .append(escapedLocalName).append(", ").append(escapedQName)
                .append(", \"CDATA\", attributeValueBuilder.toString());\n");
        indent(bodyBuilder).append("attributeValueBuilder = new StringBuilder();\n");
    }

    /**
     * Appends code to send a start element event to the handler.
     */
    public void appendStartElement(String uri, String localName, String qName) {
        Context context = contextStack.getFirst();

        Integer elementIndex = context.getElementIndexStack().getFirst();

        // escape the values that will be passed on.
        String escapedUri = stringConstant(uri);
        String escapedLocalName = stringConstant(localName);
        String escapedQName = stringConstant(qName);

        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        // get the string builder.
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();
        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() + 1);
        StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

        // TODO: we need to track the element that the we output to the handler.
        indent(bodyBuilder).append("trackStartElement(").append(elementIndex).append(");\n");
        indent(bodyBuilder).append("handler.startElement(").append(escapedUri).append(", ").append(escapedLocalName)
                .append(", ").append(escapedQName).append(", attributes);\n");
        indent(bodyBuilder).append("attributes.clear();\n");
    }

    /**
     * Appends code to send an end element event to the handler.
     */
    public void appendEndElement(String uri, String localName, String qName) {
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();
        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() - 1);

        // get the index for the next element child and increment the element count in the context.
        Integer elementIndex = context.getElementIndexStack().getFirst();

        // if this element has any children that are filters, then they need to be backed out, before we can move on.
        // TODO: execute the children that are filters.

        // escape the values that will be passed on.
        String escapedUri = stringConstant(uri);
        String escapedLocalName = stringConstant(localName);
        String escapedQName = stringConstant(qName);

        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);

        // get the string builder.
        StringBuilder bodyBuilder = context.getVirtualChainContextStack().getFirst().getBodyBuilder();

        // TODO: track that the end element has been output.
        indent(bodyBuilder).append("trackEndElement(").append(elementIndex).append(");\n");
        indent(bodyBuilder).append("handler.endElement(").append(escapedUri).append(", ").append(escapedLocalName)
                .append(", ").append(escapedQName).append(");\n");
    }

    /**
     * Appends code to output a static string to the handler as a characters(char[], int, int) event to the handler.
     */
    public void appendCharacters(String characters) {
        String escapedCharacters = stringConstant(characters);

        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("handler.characters(").append(escapedCharacters).append(".toCharArray(), 0, ")
                .append(characters.length()).append(");\n");
    }

    public void appendIgnorableWhitespace(String ignorableWhitespace) {
        String escapedWhitespace = stringConstant(ignorableWhitespace);

        // TODO: should we change the body mode to START_ELEMENT_EVENTS here?

        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("handler.ignorableWhitespace(").append(escapedWhitespace)
                .append(".toCharArray(), 0, ").append(ignorableWhitespace.length()).append(");\n");
    }

    /**
     * Appends code to output an xpath to the handler as a characters(char[], int, int) event to the handler.
     * This should support disable-output-escaping="yes|no" and output the javax.xml.transform.disable-output-escaping processing instruction
     * if needed.
     */
    public void appendValueOf(String jxpath) {
        String escapedJXPath = stringConstant(jxpath);

        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        StringBuilder bodyBuilder = contextStack.getFirst().getVirtualChainContextStack().getFirst()
                .getBodyBuilder();
        indent(bodyBuilder).append("valueOfObject = context.getValue(").append(escapedJXPath).append(");\n");
        indent(bodyBuilder).append("if( valueOfObject != null ) {\n");
        indent(bodyBuilder).append("  char[] valueOfChars = valueOfObject.toString().toCharArray();\n");
        indent(bodyBuilder).append("  handler.characters(valueOfChars, 0, valueOfChars.length);\n");
        indent(bodyBuilder).append("}\n");
    }

    public void appendStartComment() {
        Context context = contextStack.getFirst();

        Integer elementIndex = context.getElementCount();
        context.setElementCount(elementIndex + 1);
        context.getElementIndexStack().addFirst(elementIndex);
        context.getVirtualChainContextStack().getFirst().getElementIndexList().add(elementIndex);

        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        // get the string builder.
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();
        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() + 1);
        StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

        indent(bodyBuilder).append("trackStartElement(").append(elementIndex).append(");\n");
        indent(bodyBuilder).append("handler.startComment();\n");
    }

    public void appendEndComment() {
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        // get the index for the next element child and increment the element count in the context.
        Integer elementIndex = context.getElementIndexStack().getFirst();

        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);

        // get the string builder.
        StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

        // TODO: track that the end element has been output.
        indent(bodyBuilder).append("if( isElementStarted(").append(elementIndex).append(") ) {\n");
        indent(bodyBuilder).append("  trackEndElement(").append(elementIndex).append(");\n");
        indent(bodyBuilder).append("  handler.endComment();\n");
        indent(bodyBuilder).append("}\n");

        // get the index for the next element child and increment the element count in the context.
        context.getElementIndexStack().removeFirst();
        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() - 1);
    }

    /**
     * Appends code for the start of a <jsl:element/> tag.
     *
     * @param name the required name attribute.
     * @param namespace the optional namespace attribute.
     */
    public void appendStartDynamicElement(String name, String namespace) {
        Context context = contextStack.getFirst();

        Integer elementIndex = context.getElementIndexStack().getFirst();

        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        // get the string builder.
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        // this is counted in the start start element.
        //virtualChainContext.setElementDepth(virtualChainContext.getElementDepth()+1);

        StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

        // TODO: we need to track the element that the we output to the handler.
        indent(bodyBuilder).append("trackStartElement(").append(elementIndex).append(");\n");

        // add the code to process the name and namespace attributes.
        indent(bodyBuilder).append("qName = dynamicQName(context, ").append(stringConstant(name)).append(", ")
                .append(stringConstant(namespace)).append(", true);\n");
        indent(bodyBuilder).append("getDynamicElementStack().addFirst(qName);\n");
        indent(bodyBuilder).append(
                "handler.startElement(qName.getNamespaceURI(), qName.getLocalPart(), toPrefixedQName(qName), attributes);\n");
        indent(bodyBuilder).append("attributes.clear();\n");
    }

    /**
     * Appends code for the end of a <jsl:element/> tag.
     */
    public void appendEndDynamicElement() {
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        // get the index for the next element child and increment the element count in the context.
        Integer elementIndex = context.getElementIndexStack().getFirst();

        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);

        // get the string builder.
        StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

        // TODO: track that the end element has been output.
        indent(bodyBuilder).append("trackEndElement(").append(elementIndex).append(");\n");
        indent(bodyBuilder).append("qName = getDynamicElementStack().removeFirst();\n");
        indent(bodyBuilder).append(
                "handler.endElement(qName.getNamespaceURI(), qName.getLocalPart(), toPrefixedQName(qName));\n");

        // this is counted in the endEndElement.
        // virtualChainContext.setElementDepth(virtualChainContext.getElementDepth()-1);
    }

    /**
     * Appends code for the start of a <jsl:attribute/> tag.
     *
     * @param name the required name attribute.
     * @param namespace the required namespace attribute.
     */
    public void appendStartDynamicAttribute(String name, String namespace) {
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();
        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() + 1);

        changeBodyMode(BodyMode.START_ELEMENT_EVENTS);

        // get the string builder.
        StringBuilder bodyBuilder = context.getVirtualChainContextStack().getFirst().getBodyBuilder();

        // add the code to process the name and namespace attributes.
        indent(bodyBuilder).append("qName = dynamicQName(context, ").append(stringConstant(name)).append(", ")
                .append(stringConstant(namespace)).append(", false);\n");
        indent(bodyBuilder).append("getDynamicElementStack().addFirst(qName);\n");
        indent(bodyBuilder).append(
                "handler.startAttribute(qName.getNamespaceURI(), qName.getLocalPart(), toPrefixedQName(qName) );\n");
        indent(bodyBuilder).append("attributes.clear();\n");
    }

    /**
     * Appends code for the end of a <jsl:attribute/> tag.
     */
    public void appendEndDynamicAttribute() {
        Context context = contextStack.getFirst();
        VirtualChainContext virtualChainContext = context.getVirtualChainContextStack().getFirst();

        changeBodyMode(BodyMode.END_ELEMENT_EVENTS);

        // get the string builder.
        StringBuilder bodyBuilder = context.getVirtualChainContextStack().getFirst().getBodyBuilder();

        // TODO: track that the end element has been output.
        indent(bodyBuilder).append("qName = getDynamicElementStack().removeFirst();\n");
        indent(bodyBuilder).append(
                "handler.endAttribute(qName.getNamespaceURI(), qName.getLocalPart(), toPrefixedQName(qName) );\n");

        virtualChainContext.setElementDepth(virtualChainContext.getElementDepth() - 1);
    }

    public void changeBodyMode(BodyMode newMode) {
        VirtualChainContext virtualChainContext = contextStack.getFirst().getVirtualChainContextStack().getFirst();

        // if the body mode is changing, then we need to add code to the close the current mode
        // and start the new mode.
        if (newMode != virtualChainContext.getBodyMode()) {
            StringBuilder bodyBuilder = virtualChainContext.getBodyBuilder();

            // stop the old mode.
            switch (virtualChainContext.getBodyMode()) {
            case CHAIN:
                indent(bodyBuilder).append("  result = executeChildren(context, new int[]{");
                Iterator<Integer> toExecuteIndexIterator = virtualChainContext.getToExecuteIndexList().iterator();
                while (toExecuteIndexIterator.hasNext()) {
                    Integer toExecuteIndex = toExecuteIndexIterator.next();
                    bodyBuilder.append(toExecuteIndex);
                    if (toExecuteIndexIterator.hasNext()) {
                        bodyBuilder.append(", ");
                    }
                }
                bodyBuilder.append("});\n");
                virtualChainContext.getToExecuteIndexList().clear();
            case VIRTUAL_CHAIN:
                decrementIndent();
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("  catch( Exception e ) {\n");
                indent(bodyBuilder).append("    exception = e;\n");
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("}\n");
                break;
            case START_ELEMENT_EVENTS:
                decrementIndent();
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("  catch( SAXException e ) {\n");
                indent(bodyBuilder).append("    registerSaxException(e);\n");
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("  catch( Exception e ) {\n");
                indent(bodyBuilder).append("    registerSaxException(new SAXException(e));\n");
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("  finally {\n");
                indent(bodyBuilder).append("    attributes.clear();\n");
                indent(bodyBuilder).append("    attributeValueBuilder = new StringBuilder();\n");
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("}\n");
                break;
            case END_ELEMENT_EVENTS:
                decrementIndent();
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("  catch( SAXException e ) {\n");
                indent(bodyBuilder).append("    registerSaxException(e);\n");
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("  catch( Exception e ) {\n");
                indent(bodyBuilder).append("    registerSaxException(new SAXException(e));\n");
                indent(bodyBuilder).append("  }\n");
                indent(bodyBuilder).append("}\n");
            case TOP_LEVEL:
                break;
            default:
                throw new IllegalStateException("Unknown virtual chain body mode encountered.");
            }

            // start the new mode.
            switch (newMode) {
            case CHAIN:
            case VIRTUAL_CHAIN:
            case START_ELEMENT_EVENTS:
                indent(bodyBuilder).append("if( exception == null && !result && !hasSaxExceptionFired() ) {\n");
                indent(bodyBuilder).append("  try {\n");
                incrementIndent();
                break;
            case END_ELEMENT_EVENTS:
                indent(bodyBuilder).append("if( !hasSaxExceptionFired() ) {\n");
                indent(bodyBuilder).append("  try {\n");
                incrementIndent();
                break;
            case TOP_LEVEL:
                break;
            default:
                throw new IllegalStateException("Unknown virtual chain body mode encountered.");
            }
        }

        virtualChainContext.setBodyMode(newMode);
    }

    public StringBuilder indent(StringBuilder builder) {
        int indent = contextStack.getFirst().getIndent();

        for (int i = 0; i < indent; i++) {
            builder.append("  ");
        }

        return builder;
    }

    public void incrementIndent() {
        contextStack.getFirst().setIndent(contextStack.getFirst().getIndent() + 1);
    }

    public void decrementIndent() {
        contextStack.getFirst().setIndent(contextStack.getFirst().getIndent() - 1);
    }

    /**
     * Parses an attribute value template into fixed and dynamic parts.  This list will always start with a fixed part and
     * then include alternating dynamic and fixed parts.
     */
    public static List<String> parseAttributeValueTemplate(String attributeValueTemplate) throws SAXException {
        // the result.
        ArrayList<String> result = new ArrayList<String>();

        // create the matcher.
        Matcher matcher = ATTRIBUTE_VALUE_TEMPLATE_PATTERN.matcher(attributeValueTemplate);

        while (matcher.find()) {
            String fixedPart = matcher.group(1);
            String dynamicPart = matcher.group(2);

            if (result.isEmpty() && fixedPart == null) {
                result.add("");
            }

            if (fixedPart != null) {
                result.add(fixedPart.replaceAll("\\{\\{", "{").replaceAll("\\}\\}", "}"));
            }
            if (dynamicPart != null) {
                result.add(dynamicPart);
            }
        }

        if (!matcher.hitEnd()) {
            throw new SAXException(
                    "The attribute value template '" + attributeValueTemplate + "' has an error between characters "
                            + matcher.regionStart() + " and " + matcher.regionEnd() + ".");
        }

        return result;
    }

    /**
     * The context for a sax template command.
     */
    public static class Context {
        private LinkedList<VirtualChainContext> virtualChainContextStack = new LinkedList<VirtualChainContext>();
        private StringBuilder methodBuilder = new StringBuilder();
        private int commandCount = 0;
        private int elementCount = 0;
        private StringBuilder headerBuilder = new StringBuilder();
        private StringBuilder footerBuilder = new StringBuilder();
        private int indent = 0;
        private int commandIndex = 0;
        private int nextVirtualChainIndex = 0;
        private Map<String, String> transitionPrefixMapping;
        private Set<String> transitionExcludeResultPrefixSet;
        private boolean excludeResultPrefixBoundary = false;
        private LinkedList<Integer> elementIndexStack = new LinkedList<Integer>();

        /** Returns the stack of virtual chain contexts. */
        public LinkedList<VirtualChainContext> getVirtualChainContextStack() {
            return virtualChainContextStack;
        }

        /** Returns the string builder used to store completed methods. */
        public StringBuilder getMethodBuilder() {
            return methodBuilder;
        }

        public void setCommandCount(int commandCount) {
            this.commandCount = commandCount;
        }

        public int getCommandCount() {
            return this.commandCount;
        }

        public void setElementCount(int elementCount) {
            this.elementCount = elementCount;
        }

        public int getElementCount() {
            return this.elementCount;
        }

        public StringBuilder getHeaderBuilder() {
            return this.headerBuilder;
        }

        public StringBuilder getFooterBuilder() {
            return this.footerBuilder;
        }

        public void setIndent(int indent) {
            this.indent = indent;
        }

        public int getIndent() {
            return this.indent;
        }

        public void setCommandIndex(int commandIndex) {
            this.commandIndex = commandIndex;
        }

        public int getCommandIndex() {
            return this.commandIndex;
        }

        public int nextVirtualChainIndex() {
            return nextVirtualChainIndex++;
        }

        public Map<String, String> getTransitionPrefixMapping() {
            return transitionPrefixMapping;
        }

        public void setTransitionPrefixMapping(Map<String, String> transitionPrefixMapping) {
            this.transitionPrefixMapping = transitionPrefixMapping;
        }

        public Set<String> getTransitionExcludeResultPrefixSet() {
            return transitionExcludeResultPrefixSet;
        }

        public void setTransitionExcludeResultPrefixSet(Set<String> transitionExcludeResultPrefixSet) {
            this.transitionExcludeResultPrefixSet = transitionExcludeResultPrefixSet;
        }

        public boolean getExcludeResultPrefixBoundary() {
            return excludeResultPrefixBoundary;
        }

        public void setExcludeResultPrefixBoundary(boolean excludeResultPrefixBoundary) {
            this.excludeResultPrefixBoundary = excludeResultPrefixBoundary;
        }

        public LinkedList<Integer> getElementIndexStack() {
            return elementIndexStack;
        }
    }

    public static enum BodyMode {
        /** The body mode when we are at the top level of a virtual method body. */
        TOP_LEVEL,
        /** The body mode when we are inside a set of chain calls. */
        CHAIN,
        /** The body mode when we are inside a set of handler calls that start elements or send character events. */
        START_ELEMENT_EVENTS,
        /** The body mode when we are inside a set of handler calls that end elements. */
        END_ELEMENT_EVENTS, VIRTUAL_CHAIN
    }

    /**
     * The context for a virtual chain method.
     */
    public static class VirtualChainContext {
        private String name = null;
        private StringBuilder bodyBuilder = new StringBuilder();
        private List<Integer> commandIndexList = new ArrayList<Integer>();
        private List<Integer> elementIndexList = new ArrayList<Integer>();
        private List<Integer> toExecuteIndexList = new ArrayList<Integer>();
        private int elementDepth = 0;
        private BodyMode bodyMode = BodyMode.TOP_LEVEL;

        public VirtualChainContext(String name) {
            this.name = name;
        }

        /** Returns the name of this virtual chain method. */
        public String getName() {
            return name;
        }

        /** Returns the builer for the body of this method. */
        public StringBuilder getBodyBuilder() {
            return bodyBuilder;
        }

        /** Returns the list of child command indecies. */
        public List<Integer> getCommandIndexList() {
            return commandIndexList;
        }

        public List<Integer> getElementIndexList() {
            return elementIndexList;
        }

        public List<Integer> getToExecuteIndexList() {
            return toExecuteIndexList;
        }

        /** Returns the current body mode. */
        public BodyMode getBodyMode() {
            return this.bodyMode;
        }

        /** Sets the current body mode. */
        public void setBodyMode(BodyMode bodyMode) {
            this.bodyMode = bodyMode;
        };

        public int getElementDepth() {
            return this.elementDepth;
        }

        public void setElementDepth(int elementDepth) {
            this.elementDepth = elementDepth;
        }
    }

    public static String stringConstant(String source) {
        // is the source is null, return null.
        if (source == null) {
            return "((String)null)";
        }

        // the string buffer for the result.
        StringBuilder builder = new StringBuilder();

        // the initial double quote.
        builder.append('\"');

        // iterate over all of the characters, encoding all chars over 7 bits into utf escape sequences.
        Matcher matcher = ENCODING_PATTERN.matcher(source);

        while (matcher.find()) {
            if (matcher.group(1) != null) {
                builder.append(matcher.group(1).replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\"")
                        .replaceAll("\\\'", "\\\\\'").replaceAll("\r", "\\\\r").replaceAll("\t", "\\\\t")
                        .replaceAll("\b", "\\\\b").replaceAll("\n", "\\\\n").replaceAll("\f", "\\\\f"));
            } else {
                // there has to be a better way to do this formatting...
                String toUnicode = matcher.group(2);
                for (int i = 0; i < toUnicode.length(); i++) {
                    builder.append("\\u");
                    String codePoint = Integer.toHexString(matcher.group(2).codePointAt(i));
                    for (int j = codePoint.length(); j < 4; j++) {
                        builder.append("0");
                    }
                    builder.append(codePoint);
                }
            }
        }

        // terminating double quote.
        builder.append('\"');

        return builder.toString();
    }
}