/**
 *   Copyright &copy; 2008-2013 NetAllied Systems GmbH, Ravensburg, Germany. 
 *       
 *   Licensed under the MIT Open Source License, 
 *   for details please see LICENSE file or the website
 *   http://www.opensource.org/licenses/mit-license.php
*/
package de.netallied.xsd2cppsax;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.xerces.xs.XSAttributeUse;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSNamespaceItem;
import org.apache.xerces.xs.XSObject;
import org.apache.xerces.xs.XSParticle;
import org.apache.xerces.xs.XSSimpleTypeDefinition;
import org.apache.xerces.xs.XSTypeDefinition;

import com.sun.org.apache.xerces.internal.xs.XSConstants;

import de.netallied.xsd2cppsax.TypeMapping.TypeMap;
import de.netallied.xsd2cppsax.statemachine.StateMachineNode;
import de.netallied.xsd2cppsax.statemachine.StateMachineRootNode;

/**
 * Provides methods to fill in code templates.
 * 
 */
public class TemplateEngine {

    /**
     * Creates code to check if required attributes are present.
     * 
     * @param cppElementName
     *            C++ name of element.
     * @param element
     *            Element of which attributes shall be checked.
     * @param dataProvider
     *            DataProvider.
     * @return C++ Code.
     */
    protected static String attributesCheckRequiredPresent(String cppElementName, XSElementDeclaration element,
            IGenerationDataProvider dataProvider) {
        String code = new String();
        Config config = dataProvider.getConfig();

        List<XSAttributeUse> attributes = Util.collectAttributeUses(element);
        for (XSAttributeUse attrUse : attributes) {
            if (attrUse.getRequired()) {
                XSSimpleTypeDefinition attrType = Util.getType(attrUse);
                String codeTmpl = config.getTemplateAttributeCheckRequiredPresent();
                if (codeTmpl.contains(Constants.TMPL_ATTR_NOT_PRESENT_CHECK)) {
                    if (Util.findVariety(attrType) == Variety.ATOMIC) {
                        if (Util.hasFacetEnum(attrType)) {
                            codeTmpl = codeTmpl.replace(Constants.TMPL_ATTR_NOT_PRESENT_CHECK, config
                                    .getTemplateAttributePresentCheckEnumAtomic());
                            String cppEnumTypeName = dataProvider.getEnumOrUnionNameCreator().createEnumTypeName(
                                    attrType, null);
                            codeTmpl = fillInEnumTemplate(codeTmpl, cppEnumTypeName, attrType, null, 0, dataProvider);
                        } else if (Util.isStringType(attrType, config)) {
                            codeTmpl = codeTmpl.replace(Constants.TMPL_ATTR_NOT_PRESENT_CHECK, config
                                    .getTemplateAttributePresentCheckStringAtomic());
                        }
                    }
                }
                code += fillInTemplate(codeTmpl, cppElementName, element, attrUse, null, attrType, null, dataProvider);
            }
        }

        return code;
    }

    /**
     * Creates code to initialize objects in an attributes struct.
     * 
     * @param element
     *            Element of which attributes shall be processed.
     * @param dataProvider
     *            DataProvider.
     * @return Code to be inserted in template.
     */
    protected static String attributesInitObjects(XSElementDeclaration element, IGenerationDataProvider dataProvider) {
        String code = new String();
        Config config = dataProvider.getConfig();
        XSComplexTypeDefinition complexTypeDefi = Util.getComplexType(element.getTypeDefinition());
        if (complexTypeDefi != null) {
            List<XSAttributeUse> attributeUses = Util.objectListToList(complexTypeDefi.getAttributeUses());
            for (XSAttributeUse attrUse : attributeUses) {
                if (Util.isAttributeInitializationRequired(attrUse, dataProvider.getConfig())) {
                    XSSimpleTypeDefinition attrType = Util.getType(attrUse);
                    String codeTmpl = dataProvider.getConfig().getTemplateAttributeInitObject();

                    if (codeTmpl.contains(Constants.TMPL_ATTR_INIT_OBJECT)) {
                        boolean hasDefaultVal = Util.hasDefaultValue(attrUse);
                        String constraintValue = attrUse.getConstraintValue();
                        String attrValue = null;

                        if (codeTmpl.contains(Constants.TMPL_ATTR_INIT_OBJECT_SET_PRESENT)) {
                            String setPresent = null;
                            if (hasDefaultVal) {
                                setPresent = Constants.TMPL_INDENT + Constants.TMPL_ATTR_PRESENT_SET;
                            } else {
                                setPresent = "";
                            }
                            codeTmpl = codeTmpl.replace(Constants.TMPL_ATTR_INIT_OBJECT_SET_PRESENT, setPresent);
                        }

                        switch (Util.findVariety(attrType)) {
                        case UNION:
                            if (hasDefaultVal) {
                                String unionDefault = config.getTemplateAttributeInitObjectUnionDefault();
                                if (unionDefault.contains(Constants.TMPL_ATTR_INIT_OBJECT_DEFAULT_VAL)) {
                                    unionDefault = unionDefault.replace(Constants.TMPL_ATTR_INIT_OBJECT_DEFAULT_VAL,
                                            "\"" + constraintValue + "\"");
                                }
                                if (unionDefault.contains(Constants.TMPL_ATTR_INIT_OBJECT_DEFAULT_VAL_LENGTH)) {
                                    unionDefault = unionDefault.replace(
                                            Constants.TMPL_ATTR_INIT_OBJECT_DEFAULT_VAL_LENGTH, String
                                                    .valueOf(constraintValue.length()));
                                }
                                unionDefault = fillInUnionTemplate(unionDefault, attrType, dataProvider);
                                attrValue = config.getTemplateAttributeInitObjectUnionPreDefault();
                                attrValue += config.getTemplateAttributeInitObjectNoDefaultLHS() + unionDefault;
                            } else {
                                CppEnumNameCreator cppEnumOrUnionNameCreator = dataProvider.getEnumOrUnionNameCreator();
                                String unionType = cppEnumOrUnionNameCreator.createUnionTypeName(attrType, dataProvider
                                        .getElementStack());
                                attrValue = config.getTemplateAttributeInitObjectNoDefaultLHS() + unionType + "();";
                            }
                            break;
                        case LIST:
                            if (hasDefaultVal) {
                                XSSimpleTypeDefinition itemType = attrType.getItemType();
                                String xsdType = Util.findXSDSimpleTypeString(itemType, config);
                                String listType;
                                if (Util.findVariety(itemType) == Variety.UNION) {
                                    listType = dataProvider.getConfig()
                                            .getTemplateUnionAttributeListTypeConversionNoItemValidation();
                                    listType = fillInUnionTemplate(listType, itemType, dataProvider);
                                } else {
                                    listType = config.getTypeMapping().get(xsdType)
                                            .getAttrListTypeConversionNoItemValidation();
                                }
                                String cppElementName = dataProvider.getElementNameMapping().get(element).cppElementName;
                                listType = fillInTypeMappingTemplate(listType, cppElementName, null, dataProvider);
                                // TODO use code templates for these strings
                                if (listType.contains("attributeValue")) {
                                    listType = listType.replace("attributeValue", "\"" + constraintValue + "\"");
                                }
                                attrValue = listType;

                                String preInit = config.getTypeMapping().get(xsdType).getInitializePreList();
                                if (preInit != null) {
                                    attrValue = preInit + attrValue;
                                }

                            } else {
                                attrValue = config.getTemplateAttributeInitObjectNoDefaultLHS()
                                        + dataProvider.findCorrectCppTypeForAttribute(attrType) + "();";
                            }
                            break;
                        case ATOMIC:
                            String cppAtomicType = Util.findXSDSimpleTypeString(attrType, config);
                            String initialize = config.getTypeMapping().get(cppAtomicType).getInitialize();
                            if (initialize != null) {
                                String value = null;
                                if (hasDefaultVal) {
                                    value = constraintValue;
                                } else {
                                    value = "";
                                }
                                attrValue = config.getTemplateAttributeInitObjectNoDefaultLHS()
                                        + TemplateEngine.fillInInitializationTemplate(initialize, value);
                            } else {
                                attrValue = config.getTemplateAttributeInitObjectNoDefaultLHS() + "0";
                            }
                            String preInit = config.getTypeMapping().get(cppAtomicType).getInitializePreAtomic();
                            if (preInit != null && hasDefaultVal) {
                                attrValue = preInit + attrValue;
                            }
                            attrValue += ";";

                            break;
                        }
                        codeTmpl = codeTmpl.replace(Constants.TMPL_ATTR_INIT_OBJECT, attrValue);
                    }
                    code += fillInTemplate(codeTmpl, null, element, attrUse, null, attrType, null, dataProvider);
                }
            }
        }
        return code;
    }

    /**
     * Fills in a code template related to coherency test.
     * 
     * @param tmpl
     *            Code template.
     * @param dataProvider
     *            Generation data provider.
     * @return Filled in code template
     */
    public static String fillInCoherencyTestTemplate(String tmpl, IGenerationDataProvider dataProvider) {
        Config config = dataProvider.getConfig();

        if (tmpl.contains(Constants.TMPL_COHERENCY_TEST_ERROR_HANDLER)) {
            tmpl = tmpl.replace(Constants.TMPL_COHERENCY_TEST_ERROR_HANDLER, config.getCoherencyTestErrorHandlerName());
        }
        if (tmpl.contains(Constants.TMPL_COHERENCY_TEST_ID_COUNTER_MAP)) {
            tmpl = tmpl
                    .replace(Constants.TMPL_COHERENCY_TEST_ID_COUNTER_MAP, config.getCoherencyTestIdCounterMapName());
        }
        if (tmpl.contains(Constants.TMPL_COHERENCY_TEST_COUNT_ATTRIBUTE)) {
            tmpl = tmpl.replace(Constants.TMPL_COHERENCY_TEST_COUNT_ATTRIBUTE, config
                    .getCoherencyTestCountAttributeMemberName());
        }
        if (tmpl.contains(Constants.TMPL_COHERENCY_TEST_LIST_COUNT)) {
            tmpl = tmpl.replace(Constants.TMPL_COHERENCY_TEST_LIST_COUNT, config.getCoherencyTestListCountMemberName());
        }

        return tmpl;
    }

    /**
     * Fills in a template about complex validation and particles related to
     * current element (e.g. siblings or children).
     * 
     * @param tmpl
     *            Template to fill in. Don't pass a complete function template,
     *            just the particle specific part!
     * @param particle
     *            Particle which provides data to be inserted in template.
     * @param config
     *            Configuration.
     * @return Filled in template.
     */
    public static String fillInComplexValidationParticleTemplate(String tmpl, XSParticle particle, Config config) {
        if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STRUCT_MEMBER_SIBLING_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STRUCT_MEMBER_SIBLING_NAME, Util
                    .createComplexValidationDataStructMemberName(particle.getTerm(), config));
        }
        if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_MIN_OCCURENCE)) {
            tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_MIN_OCCURENCE, String.valueOf(particle.getMinOccurs()));
        }
        if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_SIBLING_NAME)) {
            String siblingName = "";
            if (particle.getTerm() != null && particle.getTerm().getName() != null) {
                siblingName = particle.getTerm().getName();
            }
            tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_SIBLING_NAME, siblingName);
        }
        return tmpl;
    }

    /**
     * Returns given complex type validation related template ready to print.
     * 
     * @param tmpl
     *            Template to process.
     * @param cppElementName
     *            C++ element name.
     * @param cppValidationDataStructType
     *            C++ type name of validation data struct.
     * @param complexType
     *            XSD type of element.
     * @param element
     *            XSD element.
     * @param sm
     *            State machine of parent element or null.
     * @param dataProvider
     *            DataProvider.
     * @return Filled in code template.
     */
    public static String fillInComplexValidationTemplate(String tmpl, String cppElementName,
            String cppValidationDataStructType, XSComplexTypeDefinition complexType, XSElementDeclaration element,
            StateMachineRootNode sm, IGenerationDataProvider dataProvider) {

        XSModelGroup modelGroup = null;
        boolean complexTypeMaxOccursUnbounded = false;
        int complexTypeMinOccurs = -1;
        int complexTypeMaxOccurs = -1;
        if (complexType != null) {
        	XSParticle complexTypeParticle = complexType.getParticle();
            if (complexTypeParticle != null) {
            	complexTypeMaxOccursUnbounded = complexTypeParticle.getMaxOccursUnbounded();
            	complexTypeMinOccurs = complexTypeParticle.getMinOccurs();
            	complexTypeMaxOccurs = complexTypeParticle.getMaxOccurs();
                if (complexTypeParticle.getTerm() instanceof XSModelGroup) {
                	
                    modelGroup = (XSModelGroup) complexType.getParticle().getTerm();
                }
            }
        }
        XSParticle particle = null;
        if (modelGroup != null && modelGroup.getParticles() != null) {
            for (int i = 0; i < modelGroup.getParticles().getLength(); i++) {
                XSObject particleObject = modelGroup.getParticles().item(i);
                if (particleObject instanceof XSParticle) {
                    XSParticle currentParticle = (XSParticle) particleObject;
                    if (currentParticle.getTerm() instanceof XSElementDeclaration) {
                        XSElementDeclaration particleElement = (XSElementDeclaration) currentParticle.getTerm();
                        if (particleElement.getName().equals(element.getName())) {
                            particle = currentParticle;
                            break;
                        }
                    }
                }
            }
        }
        
        Config config = dataProvider.getConfig();
        int iterationCounter = 0;
        while (tmpl.contains(Constants.TEMPLATE_DELIMITER_COMPLEX_VALIDATION)) {
            iterationCounter++;
            if (iterationCounter > config.getMaxTemplateResolvingIterations()) {
                System.err.println("********************************************");
                System.err.println("Could not resolve template:");
                System.err.println(fillInTemplate(tmpl, cppElementName, null, null, null, null, null, dataProvider));
                System.err.println("********************************************");
                break;
            }

            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STRUCT_TYPE)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STRUCT_TYPE, cppValidationDataStructType);
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STRUCT_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STRUCT_NAME, config
                        .getComplexValidationDataStructVariableName());
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_PARENT_STRUCT_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_PARENT_STRUCT_NAME, config
                        .getComplexValidationDataParentStructVariableName());
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STRUCT_MEMBER)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STRUCT_MEMBER, Util
                        .createComplexValidationDataStructMemberName(element, config));
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_CHECK_MAX_OCCURENCE)) {
                String maxOccCheck = config.getTemplateComplexValidationCheckMaxOccurence();
                if (particle == null || (particle != null && particle.getMaxOccursUnbounded()) || complexTypeMaxOccursUnbounded ) {
                    maxOccCheck = "";
                }
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_CHECK_MAX_OCCURENCE, maxOccCheck);
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_GET_VALIDATION_DATA)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_GET_VALIDATION_DATA, config
                        .getTemplateComplexValidationGetTopValidationData());
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_DELETE_VALIDATION_DATA)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_DELETE_VALIDATION_DATA, config
                        .getTemplateComplexValidationDeleteValidationData());
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_GET_PARENT_VALIDATION_DATA)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_GET_PARENT_VALIDATION_DATA, config
                        .getTemplateComplexValidationGetTopParentValidationData());
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_INCREMENT_OCCURENCE)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_INCREMENT_OCCURENCE, config
                        .getTemplateComplexValidationIncrementOccurence());
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_MAX_OCCURENCE)) {
                int maxOccurence = 0;
                if (particle != null) {
                    maxOccurence = particle.getMaxOccurs();
                }
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_MAX_OCCURENCE, String.valueOf(maxOccurence));
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_CHECK_MIN_OCCURENCE_OF_CHILDREN)) {
                String childrenCheck = "";
                if (modelGroup != null && modelGroup.getParticles() != null) {
                    switch (modelGroup.getCompositor()) {
                    case XSModelGroup.COMPOSITOR_ALL:
                    case XSModelGroup.COMPOSITOR_SEQUENCE:
                        for (int i = 0; i < modelGroup.getParticles().getLength(); i++) {
                            XSObject item = modelGroup.getParticles().item(i);
                            if (item instanceof XSParticle) {
                                XSParticle child = (XSParticle) item;
                                if (child.getMinOccurs() == 0) {
                                    continue;
                                }
                                String particleTmpl = config.getTemplateComplexValidationCheckMinOccurencOfChildren();
                                childrenCheck += fillInComplexValidationParticleTemplate(particleTmpl, child, config);
                            }
                        }
                        break;
                    case XSModelGroup.COMPOSITOR_CHOICE:
                        boolean allChildrenHaveMinOccurs = true;
                        for (int i = 0; i < modelGroup.getParticles().getLength(); i++) {
                            XSObject item = modelGroup.getParticles().item(i);
                            if (item instanceof XSParticle) {
                                XSParticle child = (XSParticle) item;
                                if (child.getMinOccurs() == 0) {
                                    allChildrenHaveMinOccurs = false;
                                    continue;
                                }
                                String particleTmpl = config
                                        .getTemplateComplexValidationChoiceCheckMinOccurencOfChildren();
                                childrenCheck += fillInComplexValidationParticleTemplate(particleTmpl, child, config);
                            }
                        }
                        if (allChildrenHaveMinOccurs) {
                            childrenCheck += config.getTemplateComplexValidationChoiceCheckOneChildPresent();
                            if (childrenCheck.contains(Constants.TMPL_COMPLEX_VALI_CHOICE_ONE_CHILD_PER_CHILD)) {
                                for (int i = 0; i < modelGroup.getParticles().getLength(); i++) {
                                    XSObject item = modelGroup.getParticles().item(i);
                                    if (item instanceof XSParticle) {
                                        XSParticle child = (XSParticle) item;
                                        String perChild = config
                                                .getTemplateComplexValidationChoiceCheckOneChildPresentPerChild();
                                        if (i != modelGroup.getParticles().getLength() - 1) {
                                            perChild += " && " + Constants.TMPL_CONTINUE_LOOP;
                                        }
                                        childrenCheck = childrenCheck.replace(
                                                Constants.TMPL_COMPLEX_VALI_CHOICE_ONE_CHILD_PER_CHILD, perChild);
                                        childrenCheck = fillInComplexValidationParticleTemplate(childrenCheck, child,
                                                config);

                                        if (childrenCheck.contains(Constants.TMPL_CONTINUE_LOOP)) {
                                            childrenCheck = childrenCheck.replace(Constants.TMPL_CONTINUE_LOOP,
                                                    Constants.TMPL_COMPLEX_VALI_CHOICE_ONE_CHILD_PER_CHILD);
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_CHECK_MIN_OCCURENCE_OF_CHILDREN, childrenCheck);
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_CHECK_SIBLINGS)) {
                String siblingsCheck = "";
                if (modelGroup != null && modelGroup.getParticles() != null) {
                    switch (modelGroup.getCompositor()) {
                    case XSModelGroup.COMPOSITOR_SEQUENCE:
                        List<XSParticle> previousSiblings = new ArrayList<XSParticle>();
                        List<XSParticle> followingSiblings = new ArrayList<XSParticle>();
                        Util.splitParticlesForSequence(modelGroup, particle, previousSiblings, followingSiblings);
                        for (int i = previousSiblings.size() - 1; i >= 0; i--) {
                            XSParticle prevSib = previousSiblings.get(i);
                            if (prevSib.getMinOccurs() == 0) {
                                continue;
                            }
                            String particleTmpl = config.getTemplateComplexValidationSequCheckPrev();
                            siblingsCheck += fillInComplexValidationParticleTemplate(particleTmpl, prevSib, config);
                            break;
                        }
                        for (XSParticle nextSib : followingSiblings) {
                            String particleTmpl = config.getTemplateComplexValidationSequCheckNext();
                            siblingsCheck += fillInComplexValidationParticleTemplate(particleTmpl, nextSib, config);
                        }
                        break;
                    case XSModelGroup.COMPOSITOR_CHOICE:
                    	if( !complexTypeMaxOccursUnbounded )
                    	{
	                        List<XSParticle> siblings = Util.splitParticlesForChoice(modelGroup, particle);
	                        for (XSParticle sibling : siblings) {
	                            String particleTmpl = config.getTemplateComplexValidationChoiceCheckSiblilngs();
	                            siblingsCheck += fillInComplexValidationParticleTemplate(particleTmpl, sibling, config);
	                        }
                    	}
                        break;
                    }
                }
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_CHECK_SIBLINGS, siblingsCheck);
            }

            // state machine
            if (sm != null) {
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE_END)) {
                    tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE_END, config
                            .getTemplateComplexValidationCheckStateMachineEnd());
                }
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE)) {
                    tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE, config
                            .getTemplateComplexValidationCheckStateMachine());
                }
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_SET_STATE_MACHINE_STATE)) {
                    tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_SET_STATE_MACHINE_STATE, config
                            .getTemplateComplexValidationSetStateMachineState());
                }
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_STRUCT_MEMBER)) {
                    tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_STRUCT_MEMBER, config
                            .getComplexValidationStateMachineStructMember());
                }
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_START_STATE)) {
                    List<StateMachineNode> startNodes = sm.findNodes(Constants.STATE_MACHINE_ROOT_NODE_NAME);
                    tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_START_STATE, String
                            .valueOf(startNodes.get(0).getStateID()));
                }
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_END_STATE)) {
                    List<StateMachineNode> endNodes = sm.findNodes(Constants.STATE_MACHINE_END_NODE_NAME);
                    tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_END_STATE, String.valueOf(endNodes
                            .get(0).getStateID()));
                }
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_CURRENT_STATE)) {
                    List<StateMachineNode> nodes = sm.findNodes(element.getName());
                    tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_CURRENT_STATE, String.valueOf(nodes
                            .get(0).getStateID()));
                }
                if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_CHECK_PER_STATE)
                        || tmpl.contains(Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE_END_PER_STATE)) {
                    boolean handleEnd = tmpl.contains(Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE_END_PER_STATE);
                    List<StateMachineNode> nodes = null;
                    if (!handleEnd) {
                        nodes = sm.findNodes(element.getName());
                    } else {
                        nodes = sm.findNodes(Constants.STATE_MACHINE_END_NODE_NAME);
                    }
                    SortedSet<String> prevStates = new TreeSet<String>();
                    if (config.getDebug()) {
                        System.out.println("***************************************");
                        System.out.println("prevs for element: " + nodes.get(0).getName());
                    }
                    for (StateMachineNode node : nodes) {
                        for (StateMachineNode prevNode : node.getPrecedingStates()) {
                            prevStates.add(prevNode.getStateID());
                            if (config.getDebug()) {
                                System.out.println("  " + prevNode.getName());
                            }
                        }
                    }
                    if (config.getDebug()) {
                        System.out.println("***************************************");
                    }

                    Iterator<String> iterator = prevStates.iterator();
                    while (iterator.hasNext()) {
                        String stateID = iterator.next();
                        String replaceTemplate = null;
                        if (!handleEnd) {
                            replaceTemplate = config.getTemplateComplexValidationCheckStateMachinePerState();
                        } else {
                            replaceTemplate = config.getTemplateComplexValidationCheckStateMachineEndPerState();
                        }
                        if (iterator.hasNext()) {
                            replaceTemplate += " || " + Constants.TMPL_CONTINUE_LOOP;
                        }
                        if (!handleEnd) {
                            tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_CHECK_PER_STATE,
                                    replaceTemplate);
                        } else {
                            tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE_END_PER_STATE,
                                    replaceTemplate);
                        }
                        String perState = String.valueOf(stateID);
                        if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_PREV_STATE)) {
                            tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_PREV_STATE, perState);
                        }
                        if (tmpl.contains(Constants.TMPL_CONTINUE_LOOP)) {
                            if (!handleEnd) {
                                tmpl = tmpl.replace(Constants.TMPL_CONTINUE_LOOP,
                                        Constants.TMPL_COMPLEX_VALI_STATE_MACHINE_CHECK_PER_STATE);
                            } else {
                                tmpl = tmpl.replace(Constants.TMPL_CONTINUE_LOOP,
                                        Constants.TMPL_COMPLEX_VALI_CHECK_STATE_MACHINE_END_PER_STATE);
                            }
                        }
                    }
                }

            }

        }
        return tmpl;
    }

    /**
     * Returns given enum related template ready to print.
     * 
     * @param tmpl
     *            Enum related template to process.
     * @param cppEnumTypeName
     *            Name of C++ enum type.
     * @param simpleType
     *            XSD type representing enum.
     * @param sortedEnumValues
     *            Sorted XSD enum values.
     * @param enumValue
     *            Number of enum value to handle.
     * @param dataProvider
     *            DataProvider.
     * @return Filled in code template.
     */
    public static String fillInEnumTemplate(String tmpl, String cppEnumTypeName, XSSimpleTypeDefinition simpleType,
            List<String> sortedEnumValues, int enumValue, IGenerationDataProvider dataProvider) {

        Config config = dataProvider.getConfig();
        int iterationCounter = 0;
        while (tmpl.contains(Constants.TEMPLATE_DELIMITER_ENUM)) {
            iterationCounter++;
            if (iterationCounter > config.getMaxTemplateResolvingIterations()) {
                System.err.println("********************************************");
                System.err.println("Could not resolve template:");
                System.err.println(fillInTemplate(tmpl, null, null, null, null, null, null, dataProvider));
                System.err.println("********************************************");
                break;
            }

            if (tmpl.contains(Constants.TMPL_ENUM_CPP_TYPE_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_CPP_TYPE_NAME, cppEnumTypeName);
            }
            if (tmpl.contains(Constants.TMPL_ENUM_MAP_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_MAP_NAME, Util.createEnumMapName(cppEnumTypeName, config));
            }
            if (tmpl.contains(Constants.TMPL_ENUM_COUNT)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_COUNT, Util.createEnumCountName(cppEnumTypeName, config));
            }
            if (tmpl.contains(Constants.TMPL_ENUM_MAP_ENTRY)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_MAP_ENTRY, config.getTemplateEnumMapEntry());
            }
            if (tmpl.contains(Constants.TMPL_ENUM_MAP_ENTRY_TYPE)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_MAP_ENTRY_TYPE, config.getTemplateEnumMapEntryType());
            }
            if (tmpl.contains(Constants.TMPL_ENUM_MAP_PAIR_TYPE)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_MAP_PAIR_TYPE, config.getEnumMapPairType());
            }
            if (tmpl.contains(Constants.TMPL_ENUM_BASE_TYPE_VALUE)) {
                String xsdEnumValue = sortedEnumValues.get(enumValue);
                if (Util.isFloatType(simpleType, config)) {
                    double tmp = Double.parseDouble(xsdEnumValue);
                    xsdEnumValue = String.valueOf(tmp);

                    String cppBaseType = Util.findCorrectCppAtomicType(simpleType, config);
                    if (cppBaseType.equals("float")) {
                        xsdEnumValue += "f";
                    }
                } else if (Util.isStringType(simpleType, config)) {
                    xsdEnumValue = Util.calculateHash(xsdEnumValue);
                }
                tmpl = tmpl.replace(Constants.TMPL_ENUM_BASE_TYPE_VALUE, xsdEnumValue);
            }
            if (tmpl.contains(Constants.TMPL_ENUM_CPP_BASE_TYPE)) {
                String cppBaseType = null;
                if (Util.isStringType(simpleType, config)) {
                    cppBaseType = Constants.ENUM_CPP_BASE_TYPE_FOR_STRINGS;
                } else {
                    cppBaseType = Util.findCorrectCppAtomicType(simpleType, config);
                }
                tmpl = tmpl.replace(Constants.TMPL_ENUM_CPP_BASE_TYPE, cppBaseType);
            }
            if (tmpl.contains(Constants.TMPL_ENUM_VALUE)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_VALUE, Util.createEnumValueName(cppEnumTypeName,
                        sortedEnumValues, enumValue, simpleType, config));
            }
            if (tmpl.contains(Constants.TMPL_ENUM_DATA_ATOMIC_XSD_2_CPP)) {
                String xsdTypeString = Util.findXSDSimpleTypeString(simpleType, config);
                String cppCode = config.getTypeMapping().get(xsdTypeString).getEnumDataAtomicTypeConversion();
                cppCode = TemplateEngine.fillInTypeMappingTemplate(cppCode, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_ENUM_DATA_ATOMIC_XSD_2_CPP, cppCode);
            }
            if (tmpl.contains(Constants.TMPL_ENUM_FUNC_TO_ENUM)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_FUNC_TO_ENUM, Util.createEnumFuncNameToEnum(cppEnumTypeName,
                        config));
            }
            if (tmpl.contains(Constants.TMPL_ENUM_FUNC_DATA_PREFIX)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_FUNC_DATA_PREFIX, Util.createEnumFuncNameDataPrefix(
                        cppEnumTypeName, config));
            }
            if (tmpl.contains(Constants.TMPL_ENUM_FUNC_CHAR_DATA)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_FUNC_CHAR_DATA, Util.createEnumFuncNameCharData(
                        cppEnumTypeName, config));
            }
            if (tmpl.contains(Constants.TMPL_ENUM_FUNC_DATA_END)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_FUNC_DATA_END, Util.createEnumFuncNameDataEnd(cppEnumTypeName,
                        config));
            }
            if (tmpl.contains(Constants.TMPL_ENUM_VALUE_NOT_PRESENT)) {
                tmpl = tmpl.replace(Constants.TMPL_ENUM_VALUE_NOT_PRESENT, Util.createEnumNotPresentName(
                        cppEnumTypeName, config));
            }

        }
        return tmpl;
    }

    /**
     * Fills in a hash finding related template.
     * 
     * @param tmpl
     *            Template to fill in.
     * @param cppElementName
     *            C++ element name.
     * @param element
     *            XSD element.
     * @param config
     *            Configuration.
     * @param typeIdMapping
     *            Mapping of XSD types to generated C++ type ID.
     * @return Processed template.
     */
    public static String fillInFindHashTemplate(String tmpl, String cppElementName, XSElementDeclaration element,
            Config config, Map<XSTypeDefinition, String> typeIdMapping) {

        if (tmpl.contains(Constants.TMPL_FINDHASH_TYPEID)) {
            tmpl = tmpl.replace(Constants.TMPL_FINDHASH_TYPEID, typeIdMapping.get(element.getTypeDefinition()));
        }
        if (tmpl.contains(Constants.TMPL_FINDHASH_GENERATED_HASH_VALUE)) {
            tmpl = tmpl.replace(Constants.TMPL_FINDHASH_GENERATED_HASH_VALUE, Util
                    .createElementNameHashIdentifier(cppElementName));
        }
        return tmpl;
    }

    /**
     * Fills in a value initialization code template.
     * 
     * @param tmpl
     *            Template to fill in.
     * @param value
     *            Initial value to set.
     * @return filled in template.
     */
    public static String fillInInitializationTemplate(String tmpl, String value) {
        if (tmpl.contains(Constants.TMPL_INITAL_VALUE)) {
            tmpl = tmpl.replace(Constants.TMPL_INITAL_VALUE, value);
        }
        return tmpl;
    }

    /**
     * Fills in C++ code templates related to simple type validation.
     * 
     * @param tmpl
     *            Code template to process.
     * @param simpleType
     *            XSD simple type current template belongs to.
     * @param config
     *            Configuration.
     * @param xsSimpleType2validationFunctionName
     *            Mapping of XSD type to C++ validation function name.
     * @return Filled in code template.
     */
    public static String fillInSimpleValidationTemplate(String tmpl, XSSimpleTypeDefinition simpleType, Config config,
            Map<XSSimpleTypeDefinition, String> xsSimpleType2validationFunctionName) {

        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_SPECIAL_STREAM_END_VALI)) {
            String code = "";
            if (Util.isSpecialStreamEndValidationRequired(simpleType)) {
                code = config.getTemplateSimpleValidationSpecialStreamEndVali();
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_SPECIAL_STREAM_END_VALI, code);
        }

        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_SPECIAL_STREAM_END_VALI_HANDLE_ERROR)) {
            String code = "";
            if (Util.isSpecialStreamEndValidationRequired(simpleType)) {
                code = config.getTemplateSimpleValidationSpecialStreamEndValiHandleError();
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_SPECIAL_STREAM_END_VALI_HANDLE_ERROR, code);
        }

        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_VALUE)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_VALUE, config
                    .getSimpleValidationFunctionValueParameterName());
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_LENGTH_VALUE)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_LENGTH_VALUE, config
                    .getSimpleValidationFunctionLengthParameterName());
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_MAX_EXCLUSIVE)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_MAX_EXCLUSIVE, simpleType
                    .getLexicalFacetValue(XSSimpleTypeDefinition.FACET_MAXEXCLUSIVE));
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_MAX_INCLUSIVE)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_MAX_INCLUSIVE, simpleType
                    .getLexicalFacetValue(XSSimpleTypeDefinition.FACET_MAXINCLUSIVE));
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_MIN_EXCLUSIVE)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_MIN_EXCLUSIVE, simpleType
                    .getLexicalFacetValue(XSSimpleTypeDefinition.FACET_MINEXCLUSIVE));
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_MIN_INCLUSIVE)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_MIN_INCLUSIVE, simpleType
                    .getLexicalFacetValue(XSSimpleTypeDefinition.FACET_MININCLUSIVE));
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_LENGTH)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_LENGTH, simpleType
                    .getLexicalFacetValue(XSSimpleTypeDefinition.FACET_LENGTH));
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_MIN_LENGTH)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_MIN_LENGTH, simpleType
                    .getLexicalFacetValue(XSSimpleTypeDefinition.FACET_MINLENGTH));
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_MAX_LENGTH)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_MAX_LENGTH, simpleType
                    .getLexicalFacetValue(XSSimpleTypeDefinition.FACET_MAXLENGTH));
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_GET_COUNT_STRUCT_MEMBER)) {
            String funcName = xsSimpleType2validationFunctionName.get(simpleType);
            String getCountCode = null;
            if (funcName != null) {
                getCountCode = config.getTemplateSimpleValidationGetCountStructMemberValidation();
            } else {
                getCountCode = config.getTemplateSimpleValidationGetCountStructMemberNoValidation();
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_GET_COUNT_STRUCT_MEMBER, getCountCode);
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_FUNCTION_NAME)) {
            String funcName = xsSimpleType2validationFunctionName.get(simpleType);
            if (funcName != null) {
                tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_FUNCTION_NAME, funcName);
            }
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_FUNCTION_NAME_STREAM)) {
            String funcName = xsSimpleType2validationFunctionName.get(simpleType);
            String streamFuncName = null;
            if (funcName != null) {
                streamFuncName = Util.createSimpleValidationStreamFunctionName(funcName, config);
                if (Util.findListItemType(simpleType) != null) {
                    streamFuncName = "&" + streamFuncName;
                }
            } else {
                streamFuncName = "0";
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_FUNCTION_NAME_STREAM, streamFuncName);
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_FUNCTION_NAME_STREAM_END)) {
            String funcName = xsSimpleType2validationFunctionName.get(simpleType);
            String streamEndFuncName = null;
            if (funcName != null) {
                streamEndFuncName = Util.createSimpleValidationStreamEndFunctionName(funcName, config);
                if (Util.findListItemType(simpleType) != null) {
                    streamEndFuncName = "&" + streamEndFuncName;
                }
            } else {
                streamEndFuncName = "0";
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_FUNCTION_NAME_STREAM_END, streamEndFuncName);
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_ITEM_TYPE_FUNCTION_NAME)) {
            XSSimpleTypeDefinition itemType = simpleType.getItemType();
            String funcName = xsSimpleType2validationFunctionName.get(itemType);
            if (funcName == null) {
                funcName = "0";
            } else {
                funcName = "&" + funcName;
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_ITEM_TYPE_FUNCTION_NAME, funcName);
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_STREAM_COUNT_STRUCT_MEMBER)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_STREAM_COUNT_STRUCT_MEMBER, config
                    .getSimpleValidationStreamCountStructMember());
        }
        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_UPDATE_STREAM_COUNT)) {
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_UPDATE_STREAM_COUNT, config
                    .getTemplateSimpleValidationUpdateStreamCount());
        }
        if (tmpl.contains(Constants.TMPL_VALI_BLOCK_OPEN)) {
            tmpl = tmpl.replace(Constants.TMPL_VALI_BLOCK_OPEN, config.getTemplateComplexValidationBlockOpen());
        }
        if (tmpl.contains(Constants.TMPL_VALI_BLOCK_ELSE_RUN_TIME)) {
            tmpl = tmpl.replace(Constants.TMPL_VALI_BLOCK_ELSE_RUN_TIME, config
                    .getTemplateComplexValidationBlockElseRunTime());
        }
        if (tmpl.contains(Constants.TMPL_VALI_BLOCK_ELSE_COMPILE_TIME)) {
            tmpl = tmpl.replace(Constants.TMPL_VALI_BLOCK_ELSE_COMPILE_TIME, config
                    .getTemplateComplexValidationBlockElseCompileTime());
        }
        if (tmpl.contains(Constants.TMPL_VALI_BLOCK_CLOSE)) {
            tmpl = tmpl.replace(Constants.TMPL_VALI_BLOCK_CLOSE, config.getTemplateComplexValidationBlockClose());
        }

        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI_PCRE_COMPILER_OUTPUT)) {
            String output = "";
            String pattern = Util.collectPatternFacet(simpleType);

            // TODO make pattern set handling independent of COLLADA 1.5
            if (pattern.contains("[\\c-[:]]")) {
                pattern = pattern.replace("[\\c-[:]]", config.getPcreNameCharactersWithoutColon());
            }
            if (pattern.contains("[\\i-[:]]")) {
                pattern = pattern.replace("[\\i-[:]]", config.getPcreInitialNameCharactersWithoutColon());
            }
            if (pattern.contains("[\\i-[:\\.]]")) {
                pattern = pattern.replace("[\\i-[:\\.]]", config.getPcreInitialNameCharactersWithoutColonAndDot());
            }

            if (pattern.contains("\\c")) {
                pattern = pattern.replace("\\c", config.getPcreNameCharacters());
            }
            if (pattern.contains("\\i")) {
                pattern = pattern.replace("\\i", config.getPcreInitialNameCharacters());
            }

            pattern = "^" + pattern + "$";
            String commandline = "pcre_compiler pattern \"" + pattern + "\"";
            try {
                Process proc = Runtime.getRuntime().exec(commandline);
                InputStream inputStream = proc.getInputStream();
                InputStreamReader inStreamReader = new InputStreamReader(inputStream);
                BufferedReader bufferdReader = new BufferedReader(inStreamReader);
                String line = null;
                while ((line = bufferdReader.readLine()) != null) {
                    output += line + "#nl#";
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI_PCRE_COMPILER_OUTPUT, output);
        }

        return tmpl;
    }

    /**
     * Returns given template ready to print.
     * 
     * Many of the parameters may be null.
     * 
     * @note {@link Util#findCorrectCppAtomicType(XSTypeDefinition)} has a copy
     *       of some of this code.
     * 
     * @param tmpl
     *            Template to process.
     * @param cppElementName
     *            C++ element name to fill into template.
     * @param element
     *            XSD representation of element.
     * @param attrUse
     *            XSD attribute to fill into template.
     * @param dataMethodName
     *            Name of data method to be implemented by subclasses.
     * @param type
     *            XSD Type to fill in to template.
     * @param simpleTypeValiTmpl
     *            Code template to fill in for simple type validation.
     * @param dataProvider
     *            DataProvider.
     * @return Processed template.
     */
    public static String fillInTemplate(String tmpl, String cppElementName, XSElementDeclaration element,
            XSAttributeUse attrUse, String dataMethodName, XSTypeDefinition type, String simpleTypeValiTmpl,
            IGenerationDataProvider dataProvider) {

        XSSimpleTypeDefinition simpleType = Util.findSimpleTypeDefinition(type);
        Variety variety = Util.findVariety(simpleType);
        XSSimpleTypeDefinition listType = null;
        if (variety == Variety.LIST) {
            listType = simpleType;
            simpleType = Util.findListItemType(simpleType);
        }
        String xsdType = Util.findXSDSimpleTypeString(simpleType, dataProvider.getConfig());

        if (tmpl.contains(Constants.TMPL_ATTRIBUTE_PARSING_SWITCH_DEFAULT)) {
            String code = null;
            if (Util.isUnknownAttributesMemberRequired(element.getTypeDefinition())) {
                code = dataProvider.getConfig().getTemplateValidateBeginSwitchDefaultUnknownAttributes();
            } else {
                code = dataProvider.getConfig().getTemplateValidateBeginSwitchDefault();
            }
            tmpl = tmpl.replace(Constants.TMPL_ATTRIBUTE_PARSING_SWITCH_DEFAULT, code);
        }

        if (variety == Variety.ATOMIC) {
            if (tmpl.contains(Constants.TMPL_PRE_ATTR_ATOMIC_XSD_2_CPP)) {
                String conversion = null;
                if (Util.hasFacetEnum(simpleType)) {
                    conversion = fillInEnumTemplate(dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getEnumAttrAtomicTypePreConversion(), dataProvider.getEnumOrUnionNameCreator()
                            .createEnumTypeName(simpleType, null), simpleType, null, 0, dataProvider);
                } else {
                    conversion = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getAttrAtomicTypePreConversion();
                }
                conversion = fillInTypeMappingTemplate(conversion, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_PRE_ATTR_ATOMIC_XSD_2_CPP, conversion);
            }
            if (tmpl.contains(Constants.TMPL_ATTR_ATOMIC_XSD_2_CPP)) {
                String conversion = null;
                if (Util.hasFacetEnum(simpleType)) {
                    conversion = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getEnumAttrAtomicTypeConversion();
                    conversion = fillInTypeMappingTemplate(conversion, null, null, dataProvider);
                    conversion = fillInEnumTemplate(conversion, dataProvider.getEnumOrUnionNameCreator()
                            .createEnumTypeName(simpleType, null), simpleType, null, 0, dataProvider);
                } else {
                    conversion = dataProvider.getConfig().getTypeMapping().get(xsdType).getAttrAtomicTypeConversion();
                }
                conversion = fillInTypeMappingTemplate(conversion, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_ATTR_ATOMIC_XSD_2_CPP, conversion);
            }
            if (tmpl.contains(Constants.TMPL_POST_ATTR_ATOMIC_XSD_2_CPP)) {
                String conversion = null;
                if (Util.hasFacetEnum(simpleType)) {
                    conversion = fillInEnumTemplate(dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getEnumAttrAtomicTypePostConversion(), dataProvider.getEnumOrUnionNameCreator()
                            .createEnumTypeName(simpleType, null), simpleType, null, 0, dataProvider);
                } else {
                    conversion = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getAttrAtomicTypePostConversion();
                }
                conversion = fillInTypeMappingTemplate(conversion, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_POST_ATTR_ATOMIC_XSD_2_CPP, conversion);
            }

            if (tmpl.contains(Constants.TMPL_PRE_DATA_ATOMIC_XSD_2_CPP)) {
                String replaceValue = null;
                if (Util.hasFacetEnum(simpleType) && Util.isStringType(simpleType, dataProvider.getConfig())) {
                    replaceValue = Constants.TMPL_TYPE_MAP_DEFAULT_PRE_ATTR_ATOMIC_XSD2CPP;
                } else {
                    replaceValue = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getDataAtomicTypePreConversion();
                }
                replaceValue = fillInTypeMappingTemplate(replaceValue, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_PRE_DATA_ATOMIC_XSD_2_CPP, replaceValue);
            }
            if (tmpl.contains(Constants.TMPL_DATA_ATOMIC_XSD_2_CPP)) {
                String replaceValue = dataProvider.getConfig().getTypeMapping().get(xsdType)
                        .getDataAtomicTypeConversion();
                replaceValue = fillInTypeMappingTemplate(replaceValue, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_DATA_ATOMIC_XSD_2_CPP, replaceValue);
            }
            if (tmpl.contains(Constants.TMPL_POST_DATA_ATOMIC_XSD_2_CPP)) {
                String replaceValue = null;
                if (Util.hasFacetEnum(simpleType) && Util.isStringType(simpleType, dataProvider.getConfig())) {
                    replaceValue = Constants.TMPL_TYPE_MAP_DEFAULT_POST_DATA_ATOMIC_XSD2CPP;
                } else {
                    replaceValue = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getDataAtomicTypePostConversion();
                }
                replaceValue = fillInTypeMappingTemplate(replaceValue, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_POST_DATA_ATOMIC_XSD_2_CPP, replaceValue);
            }

            if (tmpl.contains(Constants.TMPL_DATA_ATOMIC_DATA_METHOD_IMPL)) {
                TypeMap typeMap = dataProvider.getConfig().getTypeMapping().get(xsdType);
                String cppImpl = null;
                if (Util.isFacetPatternSupported(simpleType, dataProvider.getConfig())) {
                    cppImpl = typeMap.getDataAtomicDataMethodImplFacetPattern();
                } else {
                    cppImpl = typeMap.getDataAtomicDataMethodImpl();
                }
                if (cppImpl != null && cppImpl.length() > 0) {
                    cppImpl = fillInTypeMappingTemplate(cppImpl, null, null, dataProvider);
                    tmpl = tmpl.replace(Constants.TMPL_DATA_ATOMIC_DATA_METHOD_IMPL, cppImpl);
                } else {
                    tmpl = tmpl.replace(Constants.TMPL_DATA_ATOMIC_DATA_METHOD_IMPL,
                            Constants.TMPL_DATA_ATOMIC_DATA_METHOD_IMPL_DEFAULT);
                }
            }
        }

        if (variety == Variety.LIST) {
            if (tmpl.contains(Constants.TMPL_PRE_ATTR_LIST_XSD_2_CPP)) {
                String cppCode = dataProvider.getConfig().getTypeMapping().get(xsdType).getAttrListTypePreConversion();
                cppCode = fillInTypeMappingTemplate(cppCode, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_PRE_ATTR_LIST_XSD_2_CPP, cppCode);
            }
            if (tmpl.contains(Constants.TMPL_ATTR_LIST_XSD_2_CPP)) {
                String itemConversion = null;
                if (Util.findVariety(simpleType) == Variety.UNION) {
                    itemConversion = dataProvider.getConfig()
                            .getTemplateUnionAttributeListTypeConversionNoItemValidation();
                    itemConversion = fillInUnionTemplate(itemConversion, simpleType, dataProvider);
                } else {
                    itemConversion = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getAttrListTypeConversionNoItemValidation();
                }
                itemConversion = fillInTypeMappingTemplate(itemConversion, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_ATTR_LIST_XSD_2_CPP, itemConversion);
            }
            if (tmpl.contains(Constants.TMPL_ATTR_LIST_XSD_2_CPP_ITEM_VALIDATION)) {
                String itemConversionVali = null;
                if (Util.findVariety(simpleType) == Variety.UNION) {
                    itemConversionVali = dataProvider.getConfig()
                            .getTemplateUnionAttributeListTypeConversionItemValidation();
                    itemConversionVali = fillInUnionTemplate(itemConversionVali, simpleType, dataProvider);
                } else {
                    itemConversionVali = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getAttrListTypeConversionItemValidation();
                }
                itemConversionVali = fillInTypeMappingTemplate(itemConversionVali, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_ATTR_LIST_XSD_2_CPP_ITEM_VALIDATION, itemConversionVali);
                tmpl = fillInSimpleValidationTemplate(tmpl, listType, dataProvider.getConfig(), dataProvider
                        .getXsSimpleType2validationFunctionName());

            }
            if (tmpl.contains(Constants.TMPL_POST_ATTR_LIST_XSD_2_CPP)) {
                String cppCode = dataProvider.getConfig().getTypeMapping().get(xsdType).getAttrListTypePostConversion();
                cppCode = fillInTypeMappingTemplate(cppCode, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_POST_ATTR_LIST_XSD_2_CPP, cppCode);
            }

            if (tmpl.contains(Constants.TMPL_DATA_LIST_XSD_2_CPP_VALIDATION)) {
                if (Util.findVariety(simpleType) == Variety.UNION) {
                    String cppConversion = dataProvider.getConfig().getTemplateUnionDataListTypeConversionValidation();
                    cppConversion = fillInUnionTemplate(cppConversion, simpleType, dataProvider);
                    cppConversion = prepareSimpleTypeValidationTemplate(cppConversion, simpleType, cppElementName,
                            element, dataProvider);
                    tmpl = tmpl.replace(Constants.TMPL_DATA_LIST_XSD_2_CPP_VALIDATION, cppConversion);
                } else if (Util.hasFacetEnum(simpleType)) {
                    tmpl = tmpl.replace(Constants.TMPL_DATA_LIST_XSD_2_CPP_VALIDATION,
                            Constants.TMPL_DATA_LIST_XSD_2_CPP);
                } else {
                    String cppConversion = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getDataListTypeConversionValidation();
                    cppConversion = fillInTypeMappingTemplate(cppConversion, null, null, dataProvider);
                    cppConversion = prepareSimpleTypeValidationTemplate(cppConversion, listType, cppElementName,
                            element, dataProvider);
                    tmpl = tmpl.replace(Constants.TMPL_DATA_LIST_XSD_2_CPP_VALIDATION, cppConversion);
                }
            }
            if (tmpl.contains(Constants.TMPL_DATA_LIST_XSD_2_CPP)) {
                String cppConversion = null;
                if (Util.findVariety(simpleType) == Variety.UNION) {
                    cppConversion = dataProvider.getConfig().getTemplateUnionDataListTypeConversion();
                    cppConversion = fillInUnionTemplate(cppConversion, simpleType, dataProvider);
                } else if (Util.hasFacetEnum(simpleType)) {
                    cppConversion = dataProvider.getConfig().getTypeMapping().get(xsdType)
                            .getEnumDataListTypeConversion();
                    cppConversion = fillInTypeMappingTemplate(cppConversion, cppElementName, attrUse, dataProvider);
                    cppConversion = fillInEnumTemplate(cppConversion, dataProvider.getEnumOrUnionNameCreator()
                            .createEnumTypeName(simpleType, null), simpleType, null, 0, dataProvider);
                } else {
                    cppConversion = dataProvider.getConfig().getTypeMapping().get(xsdType).getDataListTypeConversion();
                }
                cppConversion = fillInTypeMappingTemplate(cppConversion, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_DATA_LIST_XSD_2_CPP, cppConversion);
            }
            if (tmpl.contains(Constants.TMPL_DATA_LIST_VALIDATE_END)) {
                String cppConversion = null;
                if (Util.findVariety(simpleType) == Variety.UNION) {
                    cppConversion = dataProvider.getConfig().getTemplateUnionDataListEnd();
                    cppConversion = fillInUnionTemplate(cppConversion, simpleType, dataProvider);
                } else {
                    cppConversion = dataProvider.getConfig().getTypeMapping().get(xsdType).getDataListEnd();
                }
                cppConversion = fillInTypeMappingTemplate(cppConversion, null, null, dataProvider);
                cppConversion = prepareSimpleTypeValidationTemplate(cppConversion, listType, cppElementName, element,
                        dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_DATA_LIST_VALIDATE_END, cppConversion);
            }
            if (tmpl.contains(Constants.TMPL_DATA_LIST_VALIDATE_END_VALIDATION)) {
                String cppConversion = null;
                if (Util.findVariety(simpleType) == Variety.UNION) {
                    cppConversion = dataProvider.getConfig().getTemplateUnionDataListEndValidation();
                    cppConversion = fillInUnionTemplate(cppConversion, simpleType, dataProvider);
                } else {
                    cppConversion = dataProvider.getConfig().getTypeMapping().get(xsdType).getDataListEndValidation();
                }
                cppConversion = fillInTypeMappingTemplate(cppConversion, null, null, dataProvider);
                cppConversion = prepareSimpleTypeValidationTemplate(cppConversion, listType, cppElementName, element,
                        dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_DATA_LIST_VALIDATE_END_VALIDATION, cppConversion);
            }
        }

        if (variety == Variety.UNION) {
            if (tmpl.contains(Constants.TMPL_UNION_ATTR_ATOMIC_XSD_2_CPP)) {
                String unionTmpl = dataProvider.getConfig().getTemplateUnionAttributeAtomicXsd2Cpp();
                unionTmpl = fillInUnionTemplate(unionTmpl, simpleType, dataProvider);
                unionTmpl = fillInTypeMappingTemplate(unionTmpl, cppElementName, attrUse, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_ATTR_ATOMIC_XSD_2_CPP, unionTmpl);
            }
            if (tmpl.contains(Constants.TMPL_UNION_ATTR_ATOMIC_PRE_XSD_2_CPP)) {
                String unionTmpl = dataProvider.getConfig().getTemplateUnionAttributeAtomicPreXsd2Cpp();
                unionTmpl = fillInUnionTemplate(unionTmpl, simpleType, dataProvider);
                unionTmpl = fillInTypeMappingTemplate(unionTmpl, cppElementName, attrUse, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_ATTR_ATOMIC_PRE_XSD_2_CPP, unionTmpl);
            }
            if (tmpl.contains(Constants.TMPL_UNION_ATTR_ATOMIC_POST_XSD_2_CPP)) {
                String unionTmpl = dataProvider.getConfig().getTemplateUnionAttributeAtomicPostXsd2Cpp();
                unionTmpl = fillInUnionTemplate(unionTmpl, simpleType, dataProvider);
                unionTmpl = fillInTypeMappingTemplate(unionTmpl, cppElementName, attrUse, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_ATTR_ATOMIC_POST_XSD_2_CPP, unionTmpl);
            }
            if (tmpl.contains(Constants.TMPL_UNION_DATA_ATOMIC_DATA_METHOD_IMPL)) {
                String unionTmpl = dataProvider.getConfig().getTemplateUnionDataAtomicDataMethodImpl();
                unionTmpl = fillInUnionTemplate(unionTmpl, simpleType, dataProvider);
                unionTmpl = fillInTypeMappingTemplate(unionTmpl, cppElementName, attrUse, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_DATA_ATOMIC_DATA_METHOD_IMPL, unionTmpl);
            }
            if (tmpl.contains(Constants.TMPL_UNION_DATA_ATOMIC_XSD2CPP)) {
                String unionTmpl = dataProvider.getConfig().getTemplateUnionDataAtomicXsd2Cpp();
                unionTmpl = fillInUnionTemplate(unionTmpl, simpleType, dataProvider);
                unionTmpl = fillInTypeMappingTemplate(unionTmpl, cppElementName, attrUse, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_DATA_ATOMIC_XSD2CPP, unionTmpl);
            }
            if (tmpl.contains(Constants.TMPL_UNION_DATA_ATOMIC_PRE_XSD2CPP)) {
                String unionTmpl = dataProvider.getConfig().getTemplateUnionDataAtomicPreXsd2Cpp();
                unionTmpl = fillInUnionTemplate(unionTmpl, simpleType, dataProvider);
                unionTmpl = fillInTypeMappingTemplate(unionTmpl, cppElementName, attrUse, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_DATA_ATOMIC_PRE_XSD2CPP, unionTmpl);
            }
            if (tmpl.contains(Constants.TMPL_UNION_DATA_ATOMIC_POST_XSD2CPP)) {
                String unionTmpl = dataProvider.getConfig().getTemplateUnionDataAtomicPostXsd2Cpp();
                unionTmpl = fillInUnionTemplate(unionTmpl, simpleType, dataProvider);
                unionTmpl = fillInTypeMappingTemplate(unionTmpl, cppElementName, attrUse, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_DATA_ATOMIC_POST_XSD2CPP, unionTmpl);
            }
        }

        if (tmpl.contains(Constants.TMPL_ATTR_INIT_OBJECTS)) {
            tmpl = tmpl.replace(Constants.TMPL_ATTR_INIT_OBJECTS, attributesInitObjects(element, dataProvider));
        }
        if (tmpl.contains(Constants.TMPL_ATTR_CHECK_REQUIRED_PRESENT)) {
            tmpl = tmpl.replace(Constants.TMPL_ATTR_CHECK_REQUIRED_PRESENT, attributesCheckRequiredPresent(
                    cppElementName, element, dataProvider));
        }

        if (tmpl.contains(Constants.TMPL_CPP_ATOMIC_TYPE)) {
            String cppType = null;
            if (Util.hasFacetEnum(simpleType)) {
                cppType = dataProvider.getEnumOrUnionNameCreator().createEnumTypeName(simpleType, null);
            } else {
                cppType = dataProvider.getConfig().getTypeMapping().get(xsdType).getAttrAtomicType();
            }
            cppType = fillInTypeMappingTemplate(cppType, null, null, dataProvider);
            tmpl = tmpl.replace(Constants.TMPL_CPP_ATOMIC_TYPE, cppType);
        }

        if (tmpl.contains(Constants.TMPL_SIMPLE_VALI)) {
            if (variety == Variety.LIST) {
                if (dataProvider.getXsSimpleType2validationFunctionName().get(listType) != null
                        && simpleTypeValiTmpl != null) {
                    simpleTypeValiTmpl = fillInSimpleValidationTemplate(simpleTypeValiTmpl, listType, dataProvider
                            .getConfig(), dataProvider.getXsSimpleType2validationFunctionName());
                } else {
                    simpleTypeValiTmpl = "";
                }
            } else {
                if (dataProvider.getXsSimpleType2validationFunctionName().get(simpleType) != null
                        && simpleTypeValiTmpl != null) {
                    simpleTypeValiTmpl = fillInSimpleValidationTemplate(simpleTypeValiTmpl, simpleType, dataProvider
                            .getConfig(), dataProvider.getXsSimpleType2validationFunctionName());
                } else {
                    simpleTypeValiTmpl = "";
                }
            }
            tmpl = tmpl.replace(Constants.TMPL_SIMPLE_VALI, simpleTypeValiTmpl);
        }

        if (tmpl.contains(Constants.TMPL_FPRINTF_PARSER_CLASS_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_FPRINTF_PARSER_CLASS_NAME, dataProvider.getConfig()
                    .getFPrintfParserClassName());
        }
        if (tmpl.contains(Constants.TMPL_FPRINTF_PARSER_FILE_MEMBER_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_FPRINTF_PARSER_FILE_MEMBER_NAME, dataProvider.getConfig()
                    .getFprintfParserFileMemberName());
        }
        if (tmpl.contains(Constants.TMPL_FPRINTF_PARSER_PRINT_FLOAT_TEMPLATE_METHOD_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_FPRINTF_PARSER_PRINT_FLOAT_TEMPLATE_METHOD_NAME, dataProvider
                    .getConfig().getFprintfParserPrintFloatTemplateMethodName());
        }
        if (tmpl.contains(Constants.TMPL_FPRINTF_PARSER_PRINT_FLOAT_METHOD_PARAMETER_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_FPRINTF_PARSER_PRINT_FLOAT_METHOD_PARAMETER_NAME, dataProvider
                    .getConfig().getFprintfParserPrintFloatMethodParameterName());
        }

        if (tmpl.contains(Constants.TMPL_ELEMENT_NAME)) {
            // note: use XSD Element Name here, NOT C++ Name
            tmpl = tmpl.replace(Constants.TMPL_ELEMENT_NAME, element.getName());
        }
        if (tmpl.contains(Constants.TMPL_ELEMENT_NAME_HASH)) {
            // note: use XSD Element Name here, NOT C++ Name
            tmpl = tmpl.replace(Constants.TMPL_ELEMENT_NAME_HASH, Util.createElementNameHashIdentifier(element
                    .getName()));
        }

        if (attrUse != null) {
            if (tmpl.contains(Constants.TMPL_ATTRIBUTE_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_ATTRIBUTE_NAME, Util.getAttributeName(attrUse, dataProvider
                        .getConfig()));
            }
            if (tmpl.contains(Constants.TMPL_ATTRIBUTE_NAME_HASH)) {
                tmpl = tmpl.replace(Constants.TMPL_ATTRIBUTE_NAME_HASH, Util.createAttributeNameHashIdentifier(Util
                        .getAttributeOriginalName(attrUse, dataProvider.getConfig())));
            }
            if (tmpl.contains(Constants.TMPL_ATTR_PRESENT_MEMBER)) {
                tmpl = tmpl.replace(Constants.TMPL_ATTR_PRESENT_MEMBER, dataProvider.getConfig()
                        .getAttributesPresentMemberName());
            }
            if (tmpl.contains(Constants.TMPL_ATTR_PRESENT_MASK)) {
                tmpl = tmpl.replace(Constants.TMPL_ATTR_PRESENT_MASK, Util.createAttributePresentMaskName(attrUse,
                        dataProvider.getConfig()));
            }
            if (tmpl.contains(Constants.TMPL_ATTR_PRESENT_SET)) {
                String setAttrPresentTmpl = null;
                if (Util.presentMaskRequired(attrUse, dataProvider.getConfig())) {
                    setAttrPresentTmpl = dataProvider.getConfig().getTemplateAttributePresentSet();
                    setAttrPresentTmpl = fillInTemplate(setAttrPresentTmpl, cppElementName, element, attrUse,
                            dataMethodName, listType, simpleTypeValiTmpl, dataProvider);
                } else {
                    setAttrPresentTmpl = "";
                }
                tmpl = tmpl.replace(Constants.TMPL_ATTR_PRESENT_SET, setAttrPresentTmpl);
            }
            if (tmpl.contains(Constants.TMPL_ATTR_PRESENT_CHECK)) {
                String checkAttrPresentTmpl = null;
                if (Util.presentMaskRequired(attrUse, dataProvider.getConfig())) {
                    checkAttrPresentTmpl = dataProvider.getConfig().getTemplateAttributePresentCheck();
                    checkAttrPresentTmpl = fillInTemplate(checkAttrPresentTmpl, cppElementName, element, attrUse,
                            dataMethodName, listType, simpleTypeValiTmpl, dataProvider);
                } else {
                    checkAttrPresentTmpl = "";
                }
                tmpl = tmpl.replace(Constants.TMPL_ATTR_PRESENT_CHECK, checkAttrPresentTmpl);
            }
            if (tmpl.contains(Constants.TMPL_ATTR_NOT_PRESENT_CHECK)) {
                String checkAttrNotPresentTmpl = null;
                if (Util.presentMaskRequired(attrUse, dataProvider.getConfig())) {
                    checkAttrNotPresentTmpl = dataProvider.getConfig().getTemplateAttributeNotPresentCheck();
                    checkAttrNotPresentTmpl = fillInTemplate(checkAttrNotPresentTmpl, cppElementName, element, attrUse,
                            dataMethodName, listType, simpleTypeValiTmpl, dataProvider);
                } else {
                    checkAttrNotPresentTmpl = "";
                }
                tmpl = tmpl.replace(Constants.TMPL_ATTR_NOT_PRESENT_CHECK, checkAttrNotPresentTmpl);
            }
        }

        if (element != null && cppElementName != null) {
            if (tmpl.contains(Constants.TMPL_ATTRIBUTE_STRUCT_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_ATTRIBUTE_STRUCT_NAME, Util.createAttributeStructName(
                        cppElementName, element, dataProvider.getConfig(), dataProvider.getCompleteElementUsage()));
            }
        }

        if (tmpl.contains(Constants.TMPL_ATTRIBUTE_STRUCT_SUFFIX)) {
            tmpl = tmpl.replace(Constants.TMPL_ATTRIBUTE_STRUCT_SUFFIX, dataProvider.getConfig()
                    .getAttributeStructSuffix());
        }

        if (tmpl.contains(Constants.TMPL_UNKNOWN_ATTRIBUTES_MEMBER_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_UNKNOWN_ATTRIBUTES_MEMBER_NAME, dataProvider.getConfig()
                    .getUnknownAttributesMemberName());
        }
        if (tmpl.contains(Constants.TMPL_UNKNOWN_ATTRIBUTES_MEMBER_TYPE)) {
            tmpl = tmpl.replace(Constants.TMPL_UNKNOWN_ATTRIBUTES_MEMBER_TYPE, dataProvider.getConfig()
                    .getUnknownAttributesMemberType());
        }
        if (tmpl.contains(Constants.TMPL_UNKNOWN_ATTRIBUTE_TYPE)) {
            tmpl = tmpl.replace(Constants.TMPL_UNKNOWN_ATTRIBUTE_TYPE, dataProvider.getConfig()
                    .getUnknownAttributeType());
        }

        if (tmpl.contains(Constants.TMPL_CLASS_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_CLASS_NAME, dataProvider.getConfig().getClassNamePublic());
        }
        if (tmpl.contains(Constants.TMPL_PRIVATE_CLASS_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_PRIVATE_CLASS_NAME, dataProvider.getConfig().getClassNamePrivate());
        }
        if (tmpl.contains(Constants.TMPL_MIMPL)) {
            tmpl = tmpl.replace(Constants.TMPL_MIMPL, dataProvider.getConfig().getPublicImplMember());
        }
        if (tmpl.contains(Constants.TMPL_TYPED_ATTRIBUTE_STRUCT_VARIABLE_NAME)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPED_ATTRIBUTE_STRUCT_VARIABLE_NAME, dataProvider.getConfig()
                    .getTypedAttributeDataVariableName());
        }

        if (dataMethodName != null) {
            if (tmpl.contains(Constants.TMPL_DATA_METHOD)) {
                tmpl = tmpl.replace(Constants.TMPL_DATA_METHOD, dataMethodName);
            }
        }

        if (tmpl.contains(Constants.TMPL_NEWLINE)) {
            tmpl = tmpl.replace(Constants.TMPL_NEWLINE, "\n");
        }
        if (tmpl.contains(Constants.TMPL_INDENT)) {
            tmpl = tmpl.replace(Constants.TMPL_INDENT, dataProvider.getConfig().getIndentation());
        }

        return tmpl;
    }

    /**
     * Fills in code templates related to type mapping.
     * 
     * @param tmpl
     *            Template to fill in.
     * @param cppElementName
     *            C++ name of element given template belongs to.
     * @param attrUse
     *            Attribute given template belongs to.
     * @param dataProvider
     *            Data provider.
     * @return Filled in template.
     */
    public static String fillInTypeMappingTemplate(String tmpl, String cppElementName, XSAttributeUse attrUse,
            IGenerationDataProvider dataProvider) {
        Config config = dataProvider.getConfig();

        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_ATTR_ATOMIC_XSD2CPP_RHS)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_ATTR_ATOMIC_XSD2CPP_RHS, config
                    .getTemplateTypeMappingDefaultAttrAtomicXsd2cppRhs());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_ATTR_LIST_XSD2CPP_RHS)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_ATTR_LIST_XSD2CPP_RHS, config
                    .getTemplateTypeMappingDefaultAttrListXsd2cppRhs());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_DATA_METHOD_IMPL)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_DATA_METHOD_IMPL, config
                    .getTemplateTypeMappingDefaultDataAtomicDataMethodImpl());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_POST_ATTR_ATOMIC_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_POST_ATTR_ATOMIC_XSD2CPP, config
                    .getTemplateTypeMappingDefaultPostAttrAtomicXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_POST_DATA_ATOMIC_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_POST_DATA_ATOMIC_XSD2CPP, config
                    .getTemplateTypeMappingDefaultPostDataAtomicXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_PRE_ATTR_ATOMIC_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_PRE_ATTR_ATOMIC_XSD2CPP, config
                    .getTemplateTypeMappingDefaultPreAttrAtomicXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_PRE_DATA_ATOMIC_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_PRE_DATA_ATOMIC_XSD2CPP, config
                    .getTemplateTypeMappingDefaultPreDataAtomicXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_UNION_DATA_ATOMIC_XSD2CPP_RHS)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_UNION_DATA_ATOMIC_XSD2CPP_RHS, config
                    .getTemplateTypeMappingDefaultUnionDataAtomicXsd2cppRhs());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_ATTR_ATOMIC_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_ATTR_ATOMIC_XSD2CPP, config
                    .getTemplateTypeMappingDefaultEnumSringAttrAtomicXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_DATA_ATOMIC_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_DATA_ATOMIC_XSD2CPP, config
                    .getTemplateTypeMappingDefaultEnumSringDataAtomicXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_DATA_LIST_DATAEND)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_DATA_LIST_DATAEND, config
                    .getTemplateTypeMappingDefaultEnumSringDataListDataend());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_DATA_LIST_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_ENUM_STRING_DATA_LIST_XSD2CPP, config
                    .getTemplateTypeMappingDefaultEnumSringDataListXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_STRING_DATA_XSD2CPP)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_STRING_DATA_XSD2CPP, config
                    .getTemplateTypeMappingDefaultSringDataXsd2cpp());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_CONVERSION)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_CONVERSION, config
                    .getTemplateTypeMappingDefaultDataAtomicConversion());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_VALIDATE_END_FACET_PATTERN)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_VALIDATE_END_FACET_PATTERN, config
                    .getTemplateTypeMappingDefaultDataAtomicValidateEndFacetPattern());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_XSD2CPP_RHS)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_ATOMIC_XSD2CPP_RHS, config
                    .getTemplateTypeMappingDefaultDataAtomicXsd2cppRhs());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_LIST_XSD2CPP_LHS)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_LIST_XSD2CPP_LHS, config
                    .getTemplateTypeMappingDefaultDataListXsd2cppLhs());
        }
        if (tmpl.contains(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_LIST_XSD2CPP_VALIDATION_LHS)) {
            tmpl = tmpl.replace(Constants.TMPL_TYPE_MAP_DEFAULT_DATA_LIST_XSD2CPP_VALIDATION_LHS, config
                    .getTemplateTypeMappingDefaultDataListXsd2cppValidationLhs());
        }

        return tmpl;
    }

    /**
     * Fills in a C++ code template related to XSD union member types.
     * 
     * @param tmpl
     *            Code template to process.
     * @param unionType
     *            XSD union type given template belongs to.
     * @param memberType
     *            XSD type of union member given template belongs to.
     * @param memberNumber
     *            Position of given member in list of all union members.
     * @param dataProvider
     *            DataProvider.
     * 
     * @return Filled in code template.
     */
    public static String fillInUnionMemberTemplate(String tmpl, XSSimpleTypeDefinition unionType,
            XSSimpleTypeDefinition memberType, int memberNumber, IGenerationDataProvider dataProvider) {

        Config config = dataProvider.getConfig();
        CppEnumNameCreator cppUnionNameCreator = dataProvider.getEnumOrUnionNameCreator();
        Map<XSSimpleTypeDefinition, Map<XSSimpleTypeDefinition, StringPair>> unionMemberTypeMap = dataProvider
                .getUnionMemberTypeMap();

        int iterationCounter = 0;
        while (tmpl.contains(Constants.TEMPLATE_DELIMITER_UNION)) {
            iterationCounter++;
            if (iterationCounter > config.getMaxTemplateResolvingIterations()) {
                System.err.println("********************************************");
                System.err.println("Could not resolve template:");
                System.err.println(fillInTemplate(tmpl, null, null, null, null, null, null, dataProvider));
                System.err.println("********************************************");
                break;
            }

            tmpl = fillInUnionTemplate(tmpl, unionType, dataProvider);

            String cppMemberName = cppUnionNameCreator.createUnionMemberTypeName(memberType, unionType);
            Map<XSSimpleTypeDefinition, StringPair> map = unionMemberTypeMap.get(unionType);
            StringPair stringPair = map.get(memberType);
            if (tmpl.contains(Constants.TMPL_UNION_MEMBER_CPP_TYPE)) {
                String cppType = stringPair.str2;
                tmpl = tmpl.replace(Constants.TMPL_UNION_MEMBER_CPP_TYPE, cppType);
            }
            if (tmpl.contains(Constants.TMPL_UNION_MEMBER_TMP_VAR)) {
                tmpl = tmpl.replace(Constants.TMPL_UNION_MEMBER_TMP_VAR, "tmp" + memberNumber);
            }
            if (tmpl.contains(Constants.TMPL_UNION_MEMBER_TYPE_ENUM)) {
                tmpl = tmpl.replace(Constants.TMPL_UNION_MEMBER_TYPE_ENUM, cppMemberName);
            }
            if (tmpl.contains(Constants.TMPL_UNION_MEMBER_CONVERSION_FUNC_NAME)) {
                String cppConversion = null;
                if (Util.hasFacetEnum(memberType)) {
                    String toEnum = config.getTemplateUnionEnumMemberConversion();
                    String cppEnumTypeName = cppUnionNameCreator.createEnumTypeName(memberType, null);
                    cppConversion = fillInEnumTemplate(toEnum, cppEnumTypeName, memberType, null, 0, dataProvider);

                } else {
                    String xsdType = stringPair.str1;
                    cppConversion = config.getTypeMapping().get(xsdType).getUnionAtomicTypeConversion();
                }
                cppConversion = fillInTypeMappingTemplate(cppConversion, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_MEMBER_CONVERSION_FUNC_NAME, cppConversion);
            }
            if (tmpl.contains(Constants.TMPL_UNION_MEMBER_CONVERSION_FUNC_PARAS)) {
                String paras = config.getUnionMemberTypeConversionFunctionParameters();
                if (Util.hasFacetEnum(memberType)) {
                    paras += config.getTemplateUnionMemberTypeConversionFunctionParametersEnumAddon();
                    String cppEnumTypeName = cppUnionNameCreator.createEnumTypeName(memberType, null);
                    paras = fillInEnumTemplate(paras, cppEnumTypeName, memberType, null, 0, dataProvider);
                }
                tmpl = tmpl.replace(Constants.TMPL_UNION_MEMBER_CONVERSION_FUNC_PARAS, paras);
            }
            if (tmpl.contains(Constants.TMPL_UNION_ENUM_BASE_FUNC)) {
                String xsdType = stringPair.str1;
                String cppConversion = config.getTypeMapping().get(xsdType).getUnionEnumBaseTypeConversion();
                cppConversion = fillInTypeMappingTemplate(cppConversion, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_ENUM_BASE_FUNC, cppConversion);
            }
            if (tmpl.contains(Constants.TMPL_UNION_ENUM_BASE_TYPE)) {
                String xsdType = stringPair.str1;
                String enumBaseType = config.getTypeMapping().get(xsdType).getUnionEnumBaseType();
                enumBaseType = fillInTypeMappingTemplate(enumBaseType, null, null, dataProvider);
                tmpl = tmpl.replace(Constants.TMPL_UNION_ENUM_BASE_TYPE, enumBaseType);
            }

        }
        return tmpl;
    }

    /**
     * Fills in C++ code templates related to XSD unions.
     * 
     * @param tmpl
     *            Code template to fill in.
     * @param unionType
     *            XSD union type given template belongs to.
     * @param dataProvider
     *            DataProvider.
     * 
     * @return Filled in code template.
     */
    public static String fillInUnionTemplate(String tmpl, XSSimpleTypeDefinition unionType,
            IGenerationDataProvider dataProvider) {

        Config config = dataProvider.getConfig();
        CppEnumNameCreator cppUnionNameCreator = dataProvider.getEnumOrUnionNameCreator();

        int iterationCounter = 0;
        while (tmpl.contains(Constants.TEMPLATE_DELIMITER_UNION)) {
            iterationCounter++;
            if (iterationCounter > config.getMaxTemplateResolvingIterations()) {
                break;
            }

            String cppUnionName = cppUnionNameCreator.createUnionTypeName(unionType, null);
            if (tmpl.contains(Constants.TMPL_UNION_CPP_TYPE_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_UNION_CPP_TYPE_NAME, cppUnionName);
            }
            if (tmpl.contains(Constants.TMPL_UNION_TYPE_MEMBER_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_UNION_TYPE_MEMBER_NAME, config.getUnionTypeMemberName());
            }
            if (tmpl.contains(Constants.TMPL_UNION_DATA_MEMBER_NAME)) {
                tmpl = tmpl.replace(Constants.TMPL_UNION_DATA_MEMBER_NAME, config.getUnionDataMemberName());
            }
            if (tmpl.contains(Constants.TMPL_UNION_FUNC_TO_UNION)) {
                tmpl = tmpl.replace(Constants.TMPL_UNION_FUNC_TO_UNION, Util.createUnionFuncNameToUnion(cppUnionName,
                        config));
            }
            if (tmpl.contains(Constants.TMPL_UNION_FUNC_DATA_PREFIX)) {
                tmpl = tmpl.replace(Constants.TMPL_UNION_FUNC_DATA_PREFIX, Util.createUnionFuncNameToUnionPrefix(
                        cppUnionName, config));
            }

            if (tmpl.contains(Constants.TMPL_UNION_TO_UNION_PER_MEMBER)) {
                String allMembersCode = new String();
                List<XSSimpleTypeDefinition> memberTypes = Util.objectListToList(unionType.getMemberTypes());
                for (int i = 0; i < memberTypes.size(); i++) {
                    String perMemberTmpl = config.getTemplateUnionFuncToUnionPerMember();
                    XSSimpleTypeDefinition memberType = memberTypes.get(i);
                    perMemberTmpl = fillInUnionMemberTemplate(perMemberTmpl, unionType, memberType, i, dataProvider);
                    allMembersCode += perMemberTmpl;
                }
                tmpl = tmpl.replace(Constants.TMPL_UNION_TO_UNION_PER_MEMBER, allMembersCode);
            }
        }
        return tmpl;
    }

    /**
     * Prepares a simple type validation code template.
     * 
     * Calls Complex validation method for simple type validation. Needed
     * because a complex type validation code template is reused. Call
     * {@link #fillInSimpleValidationTemplate(String, XSSimpleTypeDefinition, Config, Map)}
     * first for recursive replacement.
     * 
     * @param tmpl
     *            Code template to process.
     * @param simpleType
     *            XSD type given template belongs to.
     * @param cppElementName
     *            C++ name of element given template belongs to.
     * @param element
     *            XSD element given template belongs to.
     * @param dataProvider
     *            DataProvider.
     * 
     * @return Prepared code template.
     */
    public static String prepareSimpleTypeValidationTemplate(String tmpl, XSSimpleTypeDefinition simpleType,
            String cppElementName, XSElementDeclaration element, IGenerationDataProvider dataProvider) {

        String cppValiStructName = dataProvider.getXsType2validationDataStructName().get(simpleType);
        tmpl = fillInSimpleValidationTemplate(tmpl, simpleType, dataProvider.getConfig(), dataProvider
                .getXsSimpleType2validationFunctionName());
        if (cppValiStructName != null) {
            tmpl = fillInComplexValidationTemplate(tmpl, cppElementName, cppValiStructName, null, element, null,
                    dataProvider);
        }
        tmpl = fillInSimpleValidationTemplate(tmpl, simpleType, dataProvider.getConfig(), dataProvider
                .getXsSimpleType2validationFunctionName());
        if (cppValiStructName != null) {
            tmpl = fillInComplexValidationTemplate(tmpl, cppElementName, cppValiStructName, null, element, null,
                    dataProvider);
        } else {
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_GET_VALIDATION_DATA)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_GET_VALIDATION_DATA, dataProvider.getConfig()
                        .getTemplateSimpleValidationGetValidationDataNoValidation());
            }
            if (tmpl.contains(Constants.TMPL_COMPLEX_VALI_DELETE_VALIDATION_DATA)) {
                tmpl = tmpl.replace(Constants.TMPL_COMPLEX_VALI_DELETE_VALIDATION_DATA, dataProvider.getConfig()
                        .getTemplateSimpleValidationDeleteValidationDataNoValidation());
            }
        }
        return tmpl;
    }
}
