def writestackedlistenertypedobjectlistimplementation(istate, schema):
    naming = istate.naming()
    subtypenaming = CppParserSubTypeNaming(schema, naming)
    outfile = istate.outputfile()

    outfile.write(
        string.Template("""
${subtype_cppstackedlistlistenerclassname}::${subtype_cppstackedlistlistenerclassname}(
    ${cppsupermainlistenerclassname}* mainListener,
    ${cppsuperstackedlistenerclassname}* parent)
    :
    ${cppsuperstackedlistenerclassname}(mainListener, parent)
{
    fTarget = new List<${subtype_cppmodelclassname}*, true>();
}


${subtype_cppstackedlistlistenerclassname}::~${subtype_cppstackedlistlistenerclassname}()
{
}


List<${subtype_cppmodelclassname}*, true>*
${subtype_cppstackedlistlistenerclassname}::Target()
{
    return fTarget;
}


bool
${subtype_cppstackedlistlistenerclassname}::Handle(const BJsonEvent& event)
{
    switch (event.EventType()) {
        
        case B_JSON_ARRAY_END:
            Pop();
            delete this;
            break;   
        
        case B_JSON_OBJECT_START:
        {
            ${subtype_cppstackedlistenerclassname}* nextListener =
                new ${subtype_cppstackedlistenerclassname}(fMainListener, this);
            fTarget->Add(nextListener->Target());
            Push(nextListener);
            break;
        }
            
        default:
            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
                "illegal state - unexpected json event parsing an array of ${subtype_cppmodelclassname}");
            break;
    }
    
    return ErrorStatus() == B_OK;
}
""").substitute(hdscommon.uniondicts(naming.todict(), subtypenaming.todict())))
def writebulkcontainerstackedlistenerinterface(istate, schema):
    naming = istate.naming()
    subtypenaming = CppParserSubTypeNaming(schema, naming)
    outfile = istate.outputfile()

    # This is a sub-class of the main model object listener.  It will ping out to an item listener
    # when parsing is complete.

    outfile.write(
        string.Template("""
class ${cppitemlistenerstackedlistenerclassname} : public ${subtype_cppstackedlistenerclassname} {
public:
    ${cppitemlistenerstackedlistenerclassname}(
        ${cppsupermainlistenerclassname}* mainListener,
        ${cppsuperstackedlistenerclassname}* parent,
        ${cppitemlistenerclassname}* itemListener);
    ~${cppitemlistenerstackedlistenerclassname}();
    
    void WillPop();
        
private:
    ${cppitemlistenerclassname}* fItemListener;
};


class ${cppbulkcontainerstackedlistenerclassname} : public ${cppsuperstackedlistenerclassname} {
public:
    ${cppbulkcontainerstackedlistenerclassname}(
        ${cppsupermainlistenerclassname}* mainListener,
        ${cppsuperstackedlistenerclassname}* parent,
        ${cppitemlistenerclassname}* itemListener);
    ~${cppbulkcontainerstackedlistenerclassname}();
    
    bool Handle(const BJsonEvent& event);
        
private:
    BString fNextItemName;
    ${cppitemlistenerclassname}* fItemListener;
};


class ${cppbulkcontaineritemliststackedlistenerclassname} : public ${cppsuperstackedlistenerclassname} {
public:
    ${cppbulkcontaineritemliststackedlistenerclassname}(
        ${cppsupermainlistenerclassname}* mainListener,
        ${cppsuperstackedlistenerclassname}* parent,
        ${cppitemlistenerclassname}* itemListener);
    ~${cppbulkcontaineritemliststackedlistenerclassname}();
    
    bool Handle(const BJsonEvent& event);
    void WillPop();
        
private:
    ${cppitemlistenerclassname}* fItemListener;
};
""").substitute(hdscommon.uniondicts(naming.todict(), subtypenaming.todict())))
def writeageneralstackedlistenerinterface(istate, alistenerclassname):
    istate.outputfile().write(
        string.Template("""
class ${alistenerclassname} : public ${cppsuperstackedlistenerclassname} {
public:
    ${alistenerclassname}(
        ${cppsupermainlistenerclassname}* mainListener,
        ${cppsuperstackedlistenerclassname}* parent);
    ~${alistenerclassname}();
    
    bool Handle(const BJsonEvent& event);
};
""").substitute(
            hdscommon.uniondicts(istate.naming().todict(),
                                 {'alistenerclassname': alistenerclassname})))
def writestackedlistenerinterface(istate, subschema):
    naming = istate.naming()
    subtypenaming = CppParserSubTypeNaming(subschema, naming)

    if not istate.isinterfacehandledcppname(subtypenaming.cppmodelclassname()):
        istate.addinterfacehandledcppname(subtypenaming.cppmodelclassname())

        istate.outputfile().write(
            string.Template("""
class ${subtype_cppstackedlistenerclassname} : public ${cppsuperstackedlistenerclassname} {
public:
    ${subtype_cppstackedlistenerclassname}(
        ${cppsupermainlistenerclassname}* mainListener,
        ${cppsuperstackedlistenerclassname}* parent);
    ~${subtype_cppstackedlistenerclassname}();
    
    bool Handle(const BJsonEvent& event);
    
    ${subtype_cppmodelclassname}* Target();
    
protected:
    ${subtype_cppmodelclassname}* fTarget;
    BString fNextItemName;
};

class ${subtype_cppstackedlistlistenerclassname} : public ${cppsuperstackedlistenerclassname} {
public:
    ${subtype_cppstackedlistlistenerclassname}(
        ${cppsupermainlistenerclassname}* mainListener,
        ${cppsuperstackedlistenerclassname}* parent);
    ~${subtype_cppstackedlistlistenerclassname}();
    
    bool Handle(const BJsonEvent& event);
    
    List<${subtype_cppmodelclassname}*, true>* Target(); // list of %s pointers
    
private:
    List<${subtype_cppmodelclassname}*, true>* fTarget;
};
""").substitute(hdscommon.uniondicts(naming.todict(), subtypenaming.todict())))

        for propname, propmetadata in subschema['properties'].items():
            if propmetadata['type'] == 'array':
                writestackedlistenerinterface(istate, propmetadata['items'])
            elif propmetadata['type'] == 'object':
                writestackedlistenerinterface(istate, propmetadata)
def writegeneralnoopstackedlistenerconstructordestructor(istate, aclassname):

    istate.outputfile().write(
        string.Template("""
${aclassname}::${aclassname}(
    ${cppsupermainlistenerclassname}* mainListener,
    ${cppsuperstackedlistenerclassname}* parent)
    :
    ${cppsuperstackedlistenerclassname}(mainListener, parent)
{
}

${aclassname}::~${aclassname}()
{
}
""").substitute(
            hdscommon.uniondicts(istate.naming().todict(),
                                 {'aclassname': aclassname})))
def writemainlistenerimplementation(istate, schema, supportbulkcontainer):
    outfile = istate.outputfile()
    naming = istate.naming()
    subtypenaming = CppParserSubTypeNaming(schema, istate.naming())

    # super (abstract) listener

    outfile.write(
        string.Template("""
${cppsupermainlistenerclassname}::${cppsupermainlistenerclassname}()
{
    fStackedListener = NULL;
    fErrorStatus = B_OK;
}


${cppsupermainlistenerclassname}::~${cppsupermainlistenerclassname}()
{
}


void
${cppsupermainlistenerclassname}::HandleError(status_t status, int32 line, const char* message)
{
    fprintf(stderr, "an error has arisen processing json for '${cpprootmodelclassname}'; %s\\n", message);
    fErrorStatus = status;
}


void
${cppsupermainlistenerclassname}::Complete()
{
}


status_t
${cppsupermainlistenerclassname}::ErrorStatus()
{
    return fErrorStatus;
}

void
${cppsupermainlistenerclassname}::SetStackedListener(
    ${cppsuperstackedlistenerclassname}* stackedListener)
{
    fStackedListener = stackedListener;
}

""").substitute(naming.todict()))

    # single parser

    outfile.write(
        string.Template("""
${cppsinglemainlistenerclassname}::${cppsinglemainlistenerclassname}()
:
${cppsupermainlistenerclassname}()
{
    fTarget = NULL;
}


${cppsinglemainlistenerclassname}::~${cppsinglemainlistenerclassname}()
{
}


bool
${cppsinglemainlistenerclassname}::Handle(const BJsonEvent& event)
{
    if (fErrorStatus != B_OK)
       return false;
       
    if (fStackedListener != NULL)
        return fStackedListener->Handle(event);
    
    switch (event.EventType()) {
        
        case B_JSON_OBJECT_START:
        {
            ${subtype_cppstackedlistenerclassname}* nextListener = new ${subtype_cppstackedlistenerclassname}(
                this, NULL);
            fTarget = nextListener->Target();
            SetStackedListener(nextListener);
            break;
        }
              
        default:
            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
                "illegal state - unexpected json event parsing top level for ${cpprootmodelclassname}");
            break;
    }
    
    return ErrorStatus() == B_OK;
}


${cpprootmodelclassname}*
${cppsinglemainlistenerclassname}::Target()
{
    return fTarget;
}

""").substitute(hdscommon.uniondicts(naming.todict(), subtypenaming.todict())))

    if supportbulkcontainer:

        # create a main listener that can work through the list of top level model objects and ping the listener

        outfile.write(
            string.Template("""
${cppbulkcontainermainlistenerclassname}::${cppbulkcontainermainlistenerclassname}(
    ${cppitemlistenerclassname}* itemListener) : ${cppsupermainlistenerclassname}()
{
    fItemListener = itemListener;
}


${cppbulkcontainermainlistenerclassname}::~${cppbulkcontainermainlistenerclassname}()
{
}


bool
${cppbulkcontainermainlistenerclassname}::Handle(const BJsonEvent& event)
{
    if (fErrorStatus != B_OK)
       return false;
       
    if (fStackedListener != NULL)
        return fStackedListener->Handle(event);
    
    switch (event.EventType()) {
        
        case B_JSON_OBJECT_START:
        {
            ${cppbulkcontainerstackedlistenerclassname}* nextListener =
                new ${cppbulkcontainerstackedlistenerclassname}(
                    this, NULL, fItemListener);
            SetStackedListener(nextListener);
            return true;
            break;
        }
              
        default:
            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
                "illegal state - unexpected json event parsing top level for ${cppbulkcontainermainlistenerclassname}");
            break;
    }
    
    return ErrorStatus() == B_OK;
}

""").substitute(hdscommon.uniondicts(naming.todict(), subtypenaming.todict())))
def writebulkcontainerstackedlistenerimplementation(istate, schema):
    naming = istate.naming()
    subtypenaming = CppParserSubTypeNaming(schema, naming)
    outfile = istate.outputfile()

    outfile.write(
        string.Template("""
${cppitemlistenerstackedlistenerclassname}::${cppitemlistenerstackedlistenerclassname}(
    ${cppsupermainlistenerclassname}* mainListener, ${cppsuperstackedlistenerclassname}* parent,
    ${cppitemlistenerclassname}* itemListener)
:
${subtype_cppstackedlistenerclassname}(mainListener, parent)
{
    fItemListener = itemListener;
}


${cppitemlistenerstackedlistenerclassname}::~${cppitemlistenerstackedlistenerclassname}()
{
}


void
${cppitemlistenerstackedlistenerclassname}::WillPop()
{
    fItemListener->Handle(fTarget);
    delete fTarget;
}


${cppbulkcontainerstackedlistenerclassname}::${cppbulkcontainerstackedlistenerclassname}(
    ${cppsupermainlistenerclassname}* mainListener, ${cppsuperstackedlistenerclassname}* parent,
    ${cppitemlistenerclassname}* itemListener)
:
${cppsuperstackedlistenerclassname}(mainListener, parent)
{
    fItemListener = itemListener;
}


${cppbulkcontainerstackedlistenerclassname}::~${cppbulkcontainerstackedlistenerclassname}()
{
}


bool
${cppbulkcontainerstackedlistenerclassname}::Handle(const BJsonEvent& event)
{
    switch (event.EventType()) {
        
        case B_JSON_ARRAY_END:
            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected start of array");
            break;
        
        case B_JSON_OBJECT_NAME:
            fNextItemName = event.Content();
            break;
            
        case B_JSON_OBJECT_START:
            Push(new ${cppgeneralobjectstackedlistenerclassname}(fMainListener, this));
            break;
            
        case B_JSON_ARRAY_START:
            if (fNextItemName == "items")
                Push(new ${cppbulkcontaineritemliststackedlistenerclassname}(fMainListener, this, fItemListener));
            else
                Push(new ${cppgeneralarraystackedlistenerclassname}(fMainListener, this));
            break;
            
        case B_JSON_OBJECT_END:
            Pop();
            delete this;
            break;
            
        default:
                // ignore
            break;
    }
    
    return ErrorStatus() == B_OK;
}


${cppbulkcontaineritemliststackedlistenerclassname}::${cppbulkcontaineritemliststackedlistenerclassname}(
    ${cppsupermainlistenerclassname}* mainListener, ${cppsuperstackedlistenerclassname}* parent,
    ${cppitemlistenerclassname}* itemListener)
:
${cppsuperstackedlistenerclassname}(mainListener, parent)
{
    fItemListener = itemListener;
}


${cppbulkcontaineritemliststackedlistenerclassname}::~${cppbulkcontaineritemliststackedlistenerclassname}()
{
}


bool
${cppbulkcontaineritemliststackedlistenerclassname}::Handle(const BJsonEvent& event)
{
    switch (event.EventType()) {
        
        case B_JSON_OBJECT_START:
            Push(new ${cppitemlistenerstackedlistenerclassname}(fMainListener, this, fItemListener));
            break;
            
        case B_JSON_ARRAY_END:
            Pop();
            delete this;
            break;
            
        default:
            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected json event");
            break;
    }
    
    return ErrorStatus() == B_OK;
}


void
${cppbulkcontaineritemliststackedlistenerclassname}::WillPop()
{
    fItemListener->Complete();
}


""").substitute(hdscommon.uniondicts(naming.todict(), subtypenaming.todict())))
def writestackedlistenertypedobjectimplementation(istate, schema):
    outfile = istate.outputfile()
    naming = istate.naming()
    subtypenaming = CppParserSubTypeNaming(schema, naming)

    outfile.write(
        string.Template("""
${subtype_cppstackedlistenerclassname}::${subtype_cppstackedlistenerclassname}(
    ${cppsupermainlistenerclassname}* mainListener,
    ${cppsuperstackedlistenerclassname}* parent)
    :
    ${cppsuperstackedlistenerclassname}(mainListener, parent)
{
    fTarget = new ${subtype_cppmodelclassname}();
}


${subtype_cppstackedlistenerclassname}::~${subtype_cppstackedlistenerclassname}()
{
}


${subtype_cppmodelclassname}*
${subtype_cppstackedlistenerclassname}::Target()
{
    return fTarget;
}


bool
${subtype_cppstackedlistenerclassname}::Handle(const BJsonEvent& event)
{
    switch (event.EventType()) {
        
        case B_JSON_ARRAY_END:
            HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, "illegal state - unexpected start of array");
            break;
        
        case B_JSON_OBJECT_NAME:
            fNextItemName = event.Content();
            break;
            
        case B_JSON_OBJECT_END:
            Pop();
            delete this;
            break;

""").substitute(hdscommon.uniondicts(naming.todict(), subtypenaming.todict())))

    # now extract the fields from the schema that need to be fed in.

    writestackedlistenerfieldsimplementation(istate, schema,
                                             jscom.CPP_TYPE_STRING,
                                             'B_JSON_STRING',
                                             'new BString(event.Content())')

    writestackedlistenerfieldsimplementation(istate, schema,
                                             jscom.CPP_TYPE_BOOLEAN,
                                             'B_JSON_TRUE', 'true')

    writestackedlistenerfieldsimplementation(istate, schema,
                                             jscom.CPP_TYPE_BOOLEAN,
                                             'B_JSON_FALSE', 'false')

    outfile.write('        case B_JSON_NULL:\n')
    outfile.write('        {\n')

    for propname, propmetadata in schema['properties'].items():
        # TODO; deal with array case somehow.
        if 'array' != propmetadata['type']:
            writestackedlistenerfieldimplementation(istate, propname, 'NULL')
    outfile.write('            fNextItemName.SetTo("");\n')
    outfile.write('            break;\n')
    outfile.write('        }\n')

    # number type is a bit complex because it can either be a double or it can be an
    # integral value.

    outfile.write('        case B_JSON_NUMBER:\n')
    outfile.write('        {\n')

    for propname, propmetadata in schema['properties'].items():
        propcpptypename = jscom.propmetadatatocpptypename(propmetadata)
        if propcpptypename == jscom.CPP_TYPE_INTEGER:
            writestackedlistenerfieldimplementation(istate, propname,
                                                    'event.ContentInteger()')
        elif propcpptypename == jscom.CPP_TYPE_NUMBER:
            writestackedlistenerfieldimplementation(istate, propname,
                                                    'event.ContentDouble()')
    outfile.write('            fNextItemName.SetTo("");\n')
    outfile.write('            break;\n')
    outfile.write('        }\n')

    # object type; could be a sub-type or otherwise just drop into a placebo consumer to keep the parse
    # structure working.  This would most likely be additional sub-objects that are additional to the
    # expected schema.

    outfile.write('        case B_JSON_OBJECT_START:\n')
    outfile.write('        {\n')

    objectifclausekeyword = 'if'

    for propname, propmetadata in schema['properties'].items():
        if propmetadata['type'] == jscom.JSON_TYPE_OBJECT:
            subtypenaming = CppParserSubTypeNaming(propmetadata, naming)

            outfile.write('            %s (fNextItemName == "%s") {\n' %
                          (objectifclausekeyword, propname))
            outfile.write(
                '                %s* nextListener = new %s(fMainListener, this);\n'
                % (subtypenaming.cppstackedlistenerclassname(),
                   subtypenaming.cppstackedlistenerclassname()))
            outfile.write(
                '                fTarget->Set%s(nextListener->Target());\n' %
                (subtypenaming.cppmodelclassname()))
            outfile.write('                Push(nextListener);\n')
            outfile.write('            }\n')

            objectifclausekeyword = 'else if'

    outfile.write('            %s (1 == 1) {\n' % objectifclausekeyword)
    outfile.write(
        '                %s* nextListener = new %s(fMainListener, this);\n' %
        (naming.cppgeneralobjectstackedlistenerclassname(),
         naming.cppgeneralobjectstackedlistenerclassname()))
    outfile.write('                Push(nextListener);\n')
    outfile.write('            }\n')
    outfile.write('            fNextItemName.SetTo("");\n')
    outfile.write('            break;\n')
    outfile.write('        }\n')

    # array type; could be an array of objects or otherwise just drop into a placebo consumer to keep
    # the parse structure working.

    outfile.write('        case B_JSON_ARRAY_START:\n')
    outfile.write('        {\n')

    objectifclausekeyword = 'if'

    for propname, propmetadata in schema['properties'].items():
        if propmetadata['type'] == jscom.JSON_TYPE_ARRAY:
            subtypenaming = CppParserSubTypeNaming(propmetadata['items'],
                                                   naming)

            outfile.write('            %s (fNextItemName == "%s") {\n' %
                          (objectifclausekeyword, propname))
            outfile.write(
                '                %s* nextListener = new %s(fMainListener, this);\n'
                % (subtypenaming.cppstackedlistlistenerclassname(),
                   subtypenaming.cppstackedlistlistenerclassname()))
            outfile.write(
                '                fTarget->Set%s(nextListener->Target());\n' %
                (jscom.propnametocppname(propname)))
            outfile.write('                Push(nextListener);\n')
            outfile.write('            }\n')

            objectifclausekeyword = 'else if'

    outfile.write('            %s (1 == 1) {\n' % objectifclausekeyword)
    outfile.write(
        '                %s* nextListener = new %s(fMainListener, this);\n' %
        (naming.cppsuperstackedlistenerclassname(),
         naming.cppgeneralarraystackedlistenerclassname()))
    outfile.write('                Push(nextListener);\n')
    outfile.write('            }\n')
    outfile.write('            fNextItemName.SetTo("");\n')
    outfile.write('            break;\n')
    outfile.write('        }\n')

    outfile.write("""
    }
    
    return ErrorStatus() == B_OK;
}
""")