Пример #1
0
    def __init__(self, model, baseName, moduleName, actionLocation, fDOM, fInline):

        self._baseName = baseName
        self._fDOM = fDOM
        self._fInline = fInline

        # Generator state
        self._out = None
        self._namespaceStack = []

        # The underlying module.
        self._module = HDKModule(model, moduleName, actionLocation, fDOM)

        # Type information
        structTypes = {}
        arrayTypes = {}
        enumTypes = {}
        for type in model.all_types(user_types = fDOM, state_types = False):
            if type.isArray:
                arrayTypes[type.uri] = type
            elif type.isStruct:
                structTypes[type.uri] = type
            elif type.isEnum:
                enumTypes[type.uri] = type

        self._structTypes = sorted(structTypes.values(), key = lambda x:x.uri)
        self._arrayTypes = sorted(arrayTypes.values(), key = lambda x:x.uri)
        self._enumTypes = sorted(enumTypes.values(), key = lambda x:x.uri)
Пример #2
0
    def __init__(self, model, baseName, moduleName, actionLocation, fDOM, fInline):

        self._baseName = baseName
        self._fDOM = fDOM
        self._fInline = fInline

        # Generator state
        self._out = None
        self._namespaceStack = []

        # The underlying module.
        self._module = HDKModule(model, moduleName, actionLocation, fDOM)

        # Type information
        structTypes = {}
        arrayTypes = {}
        enumTypes = {}
        for type in model.all_types(user_types = fDOM, state_types = False):
            if type.isArray:
                arrayTypes[type.uri] = type
            elif type.isStruct:
                structTypes[type.uri] = type
            elif type.isEnum:
                enumTypes[type.uri] = type

        self._structTypes = sorted(structTypes.values(), key = lambda x:x.uri)
        self._arrayTypes = sorted(arrayTypes.values(), key = lambda x:x.uri)
        self._enumTypes = sorted(enumTypes.values(), key = lambda x:x.uri)
Пример #3
0
    def __init__(self, model, baseName, noid, friendlyName, actionLocation,
                 bGenerateDOMSchemas, bGenerateMethods, bClient, bADIReport):

        self._model = model

        # Options
        self._baseName = baseName
        self._noid = noid
        self._friendlyName = friendlyName
        self._bGenerateMethods = bGenerateMethods
        self._bADIReport = bADIReport

        # Generator state
        self._out = None

        # Module
        self._module = HDKModule(model, baseName, actionLocation,
                                 bGenerateDOMSchemas)

        # Generation options
        self._bGenerateActions = not bClient and self._module.actions
        self._bGenerateServices = not bClient and self._module.services
        self._bGenerateModule = self._module.actions or self._module.events
        self._bGenerateModuleDynamic = not bClient and self._bGenerateModule
Пример #4
0
    def __init__(self, model, baseName, noid, friendlyName,
                 actionLocation, bGenerateDOMSchemas,
                 bGenerateMethods, bClient, bADIReport):

        self._model = model

        # Options
        self._baseName = baseName
        self._noid = noid
        self._friendlyName = friendlyName
        self._bGenerateMethods = bGenerateMethods
        self._bADIReport = bADIReport

        # Generator state
        self._out = None

        # Module
        self._module = HDKModule(model, baseName, actionLocation, bGenerateDOMSchemas)

        # Generation options
        self._bGenerateActions = not bClient and self._module.actions
        self._bGenerateServices = not bClient and self._module.services
        self._bGenerateModule = self._module.actions or self._module.events
        self._bGenerateModuleDynamic = not bClient and self._bGenerateModule
Пример #5
0
class ModuleGenerator:

    # Class initializer
    def __init__(self, model, baseName, noid, friendlyName, actionLocation,
                 bGenerateDOMSchemas, bGenerateMethods, bClient, bADIReport):

        self._model = model

        # Options
        self._baseName = baseName
        self._noid = noid
        self._friendlyName = friendlyName
        self._bGenerateMethods = bGenerateMethods
        self._bADIReport = bADIReport

        # Generator state
        self._out = None

        # Module
        self._module = HDKModule(model, baseName, actionLocation,
                                 bGenerateDOMSchemas)

        # Generation options
        self._bGenerateActions = not bClient and self._module.actions
        self._bGenerateServices = not bClient and self._module.services
        self._bGenerateModule = self._module.actions or self._module.events
        self._bGenerateModuleDynamic = not bClient and self._bGenerateModule

    #
    # Generate code
    #

    def generate(self, dir, fhReport):

        for file, fn, bGenerate in \
                [(os.path.join(dir, self._module.filename('.h')), self._generate_h, not self._bGenerateMethods),
                 (os.path.join(dir, self._module.filename('.c')), self._generate_c, not self._bGenerateMethods),
                 (os.path.join(dir, self._module.filename('_methods.c')), self._generate_methods_c, self._bGenerateMethods),
                 (os.path.join(dir, self._module.filename('_adi.txt')), self._generate_adi_txt, self._bADIReport)]:

            if bGenerate:

                if fhReport is not None:
                    print >> fhReport, 'Generating "%s" ...' % file

                self._out = open(file, "w")
                try:
                    fn()
                except:
                    raise
                finally:
                    self._out.close()

    #
    # Output helper methods
    #

    # Output
    def _write(self, s):

        self._out.write(s)

    # Write .c file header
    def _write_header_c(self):

        self._write('''\
/*
 * Copyright (c) 2008-2010 Cisco Systems, Inc. All rights reserved.
 *
 * Cisco Systems, Inc. retains all right, title and interest (including all
 * intellectual property rights) in and to this computer program, which is
 * protected by applicable intellectual property laws.  Unless you have obtained
 * a separate written license from Cisco Systems, Inc., you are not authorized
 * to utilize all or a part of this computer program for any purpose (including
 * reproduction, distribution, modification, and compilation into object code),
 * and you must immediately destroy or return to Cisco Systems, Inc. all copies
 * of this computer program.  If you are licensed by Cisco Systems, Inc., your
 * rights to utilize this computer program are limited by the terms of that
 * license.  To obtain a license, please contact Cisco Systems, Inc.
 *
 * This computer program contains trade secrets owned by Cisco Systems, Inc.
 * and, unless unauthorized by Cisco Systems, Inc. in writing, you agree to
 * maintain the confidentiality of this computer program and related information
 * and to not disclose this computer program and related information to any
 * other person or entity.
 *
 * THIS COMPUTER PROGRAM IS PROVIDED AS IS WITHOUT ANY WARRANTIES, AND CISCO
 * SYSTEMS, INC. EXPRESSLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * INCLUDING THE WARRANTIES OF MERCHANTIBILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, TITLE, AND NONINFRINGEMENT.
 */
''')

    # Write .h file header
    def _write_header_h(self):

        self._write_header_c()
        self._write('''\

#ifndef %s
#define %s
''' % (self._module.header_sentinel('.h'), self._module.header_sentinel('.h')))

    # Write .h file footer
    def _write_footer_h(self):

        self._write('''\

#endif /* %s */
''' % (self._module.header_sentinel('.h')))

    # Write a section comment
    def _write_section(self, comment):

        self._write('''\


/*
 * %s
 */

''' % (comment))

    # Write enumeration definition
    def _write_enumeration(self,
                           name,
                           values,
                           ix_start=0,
                           ix_delta=1,
                           value_symbols={}):

        # Enum header
        self._write('''\
typedef enum _%s
{
''' % (name))

        # Write enum values
        ix_value = ix_start
        ix_value_last = ix_start + ix_delta * (len(values) - 1)
        sep = ","
        for value in values:

            # Does the value have a symbol?
            if value_symbols.has_key(value):
                str_value = value_symbols[value]
            else:
                str_value = str(ix_value)

            # Don't separate the last value
            if ix_value == ix_value_last:
                sep = ""

            # Write the enum value
            self._write('''\
    %s = %s%s
''' % (value, str_value, sep))

            ix_value += ix_delta

        # Enum footer
        self._write('''\
} %s;
''' % (name))

    # Write schema definition
    def _write_schema(self,
                      schema_nodes_static,
                      schema_static,
                      nodes,
                      element_path_static=None,
                      element_path=None):

        # Schema nodes header
        self._write('''\
static const HDK_XML_SchemaNode %s[] =
{
''' % (schema_nodes_static))

        # Write the schema nodes
        ix = 0
        for node in nodes:

            # Compute the parent index
            if node.parent:
                ix_parent = nodes.index(node.parent)
            else:
                ix_parent = 0

            # Compute the options
            options = []
            if node.is_optional:
                options.append("HDK_XML_SchemaNodeProperty_Optional")
            if node.is_unbounded:
                options.append("HDK_XML_SchemaNodeProperty_Unbounded")
            if node.is_any_element:
                options.append("HDK_XML_SchemaNodeProperty_AnyElement")
            if node.is_error:
                options.append("HDK_XML_SchemaNodeProperty_ErrorOutput")
            if node.is_csv:
                options.append("HDK_XML_SchemaNodeProperty_CSV")
            if options:
                options = " | ".join(options)
            else:
                options = "0"

            # Write the schema node
            self._write('''\
    /* %d */ { %d, %s, %s, %s },
''' % (ix, ix_parent, self._module.element_value(
                node.element()), self._module.type_value(node.type), options))
            ix += 1

        # Schema nodes footer
        self._write('''\
    HDK_XML_Schema_SchemaNodesEnd
};
''')

        # Schema struct element path
        if element_path_static and element_path:

            self._write('''\

static const HDK_XML_Element %s[] =
{
''' % (element_path_static))
            for element in element_path:
                self._write('''\
    %s,
''' % (self._module.element_value(element)))
            self._write('''\
    HDK_MOD_ElementPathEnd
};
''')

        # Schema
        self._write('''\

static const HDK_XML_Schema %s =
{
    s_namespaces,
    s_elements,
    %s,
    %s
};
''' % (schema_static, schema_nodes_static,
        "s_enumTypes" if self._module.schema.enums else "0"))

    # Write action definition
    def _write_action(self, action_loc):

        http_method, http_location, action, noauth = \
            (action_loc.http_method, action_loc.http_location, action_loc.action, action_loc.noauth)

        # Compute the SOAP action
        is_get = (http_method.lower() == "get")
        if is_get:
            soap_action = "0"
        else:
            soap_action = '"' + action.uri + '"'

        # Compute the action function name
        if self._bGenerateActions:
            action_fn = self._module.action_fn(action)
        else:
            action_fn = "0"

        # Compute the schema struct element path symbols
        if not is_get and self._module.schema.struct_nodes_envelope(
                action.inputMember, soap=True):
            input_element_path = self._module.schema_element_path_static(
                action, "_Input")
        else:
            input_element_path = "0"
        if self._module.schema.struct_nodes_envelope(action.outputMember,
                                                     soap=True):
            output_element_path = self._module.schema_element_path_static(
                action, "_Output")
        else:
            output_element_path = "0"

        # Compute the method options
        options = []
        if noauth:
            options.append("HDK_MOD_MethodOption_NoBasicAuth")
        if is_get:
            options.append("HDK_MOD_MethodOption_NoInputStruct")
        if options:
            options = " | ".join(options)
        else:
            options = "0"

        # Compute the OK and REBOOT values
        result_member = action.outputMember.type.members[0]
        if "REBOOT" in action.resultEnum.enumValues:
            result_reboot = "REBOOT"
        else:
            result_reboot = "OK"

        # Write the method struct
        self._write('''\
    {
        "%s",
        "%s",
        %s,
        %s,
        &%s,
        &%s,
        %s,
        %s,
        %s,
        %s,
        %s,
        %s,
        %s
    },
''' % (http_method, http_location, soap_action, action_fn,
        self._module.schema_static(action, suffix="_Input"),
        self._module.schema_static(action, suffix="_Output"),
        input_element_path, output_element_path, options,
        self._module.element_value(
           (result_member.namespace, result_member.name)),
        self._module.type_value(result_member.type),
        self._module.enum_value(result_member.type, "OK"),
        self._module.enum_value(result_member.type, result_reboot)))

    # Write event definition
    def _write_event(self, event):

        # Write the method struct
        self._write('''\
    {
        "%s",
        &%s
    },
''' % (event.uri, self._module.schema_static(event)))

    #
    # *.h code generator
    #

    def _generate_h(self):

        # File header
        self._write_header_h()

        # Includes
        self._write('''\

#include "hdk_mod.h"


/*
 * Macro to control public exports
 */

#ifdef __cplusplus
#  define %s_PREFIX extern "C"
#else
#  define %s_PREFIX extern
#endif
#ifdef HDK_MOD_STATIC
#  define %s %s_PREFIX
#else /* ndef HDK_MOD_STATIC */
#  ifdef _MSC_VER
#    ifdef %s
#      define %s %s_PREFIX __declspec(dllexport)
#    else
#      define %s %s_PREFIX __declspec(dllimport)
#    endif
#  else /* ndef _MSC_VER */
#    ifdef %s
#      define %s %s_PREFIX __attribute__ ((visibility("default")))
#    else
#      define %s %s_PREFIX
#    endif
#  endif /*def _MSC_VER */
#endif /* def HDK_MOD_STATIC */
''' % (self._module.export_macro(), self._module.export_macro(),
        self._module.export_macro(), self._module.export_macro(),
        self._module.build_macro(), self._module.export_macro(),
        self._module.export_macro(), self._module.export_macro(),
        self._module.export_macro(), self._module.build_macro(),
        self._module.export_macro(), self._module.export_macro(),
        self._module.export_macro(), self._module.export_macro()))

        # Element enumeration
        self._write_section("Elements")
        self._write_enumeration(self._module.element_enum(), [
            self._module.element_value(element)
            for element in self._module.schema.elements
        ])

        # Enumeration type definition
        if self._module.schema.enums:

            self._write_section("Enum types enumeration")
            self._write_enumeration(self._module.enum_type_enum(), [
                self._module.enum_type_value(enum)
                for enum in self._module.schema.enums
            ],
                                    ix_start=-1,
                                    ix_delta=-1)

        # Enumeration declarations
        for enum in self._module.schema.enums:

            # Enumeration type definition
            self._write_section("Enumeration %s" % (enum.uri))
            value_unknown = self._module.enum_value(enum,
                                                    None,
                                                    is_unknown=True)
            values = [value_unknown]
            values.extend([
                self._module.enum_value(enum, value)
                for value in enum.enumValues
            ])
            self._write_enumeration(
                self._module.enum_enum(enum),
                values,
                ix_start=-1,
                value_symbols={value_unknown: "HDK_XML_Enum_Unknown"})

            # Enumeration accessor declarations
            self._write('''\

#define %s(pStruct, element, value) HDK_XML_Set_Enum(pStruct, element, %s, 0 ? %s : (value))
#define %s(pStruct, element, value) HDK_XML_Append_Enum(pStruct, element, %s, 0 ? %s : (value))
#define %s(pStruct, element) (%s*)HDK_XML_Get_Enum(pStruct, element, %s)
#define %s(pStruct, element, value) (%s)HDK_XML_GetEx_Enum(pStruct, element, %s, 0 ? %s : (value))
#define %s(pMember) (%s*)HDK_XML_GetMember_Enum(pMember, %s)
''' % (self._module.enum_accessor(enum,
                                  "Set"), self._module.enum_type_value(enum),
            self._module.enum_value(
            enum, enum.enumValues[0]), self._module.enum_accessor(
               enum, "Append"), self._module.enum_type_value(enum),
            self._module.enum_value(
            enum, enum.enumValues[0]), self._module.enum_accessor(enum, "Get"),
            self._module.enum_enum(enum), self._module.enum_type_value(enum),
            self._module.enum_accessor(enum, "GetEx"), self._module.enum_enum(enum),
            self._module.enum_type_value(enum),
            self._module.enum_value(enum, enum.enumValues[0]),
            self._module.enum_accessor(enum, "GetMember"),
            self._module.enum_enum(enum), self._module.enum_type_value(enum)))

        # Action enumeration
        if self._module.actions:
            self._write_section("Method enumeration")
            self._write_enumeration(self._module.action_enum(), [
                self._module.action_value(action_loc)
                for action_loc in self._module.action_locations()
            ])

        # Action sentinels
        if self._bGenerateActions:
            self._write_section("Method sentinels")
            for action in self._module.actions:
                self._write('''\
#define %s
''' % (self._module.action_sentinel(action)))

        # Action declarations
        if self._bGenerateActions:
            self._write_section("Methods")
            for action in self._module.actions:
                self._write('''\
extern void %s(HDK_MOD_MethodContext* pMethodCtx, HDK_XML_Struct* pInput, HDK_XML_Struct* pOutput);
''' % (self._module.action_fn(action)))

        # Event enumeration
        if self._module.events:
            self._write_section("Event enumeration")
            self._write_enumeration(self._module.event_enum(), [
                self._module.event_value(event)
                for event in self._module.events
            ])

        # DOM (struct) schema accessor declarations
        if self._module.schema.structs:
            self._write_section("DOM Schemas")
        for struct in self._module.schema.structs:
            self._write('''\
%s const HDK_XML_Schema* %s();
''' % (self._module.export_macro(), self._module.dom_schema_fn(struct)))

        # State declarations
        if self._module.states:

            self._write_section("ADI")
            self._write_enumeration(self._module.state_enum(), [
                self._module.state_value(state)
                for state in self._module.states
            ],
                                    ix_start=1)

            self._write_section("ADI sentinels")
            for state in self._module.states:

                # Determine get/set
                usages = {}
                for actionState in self._model.actionStates:
                    if state.uri in actionState.stateMembers:
                        stateMember = actionState.stateMembers[state.uri]
                        if stateMember.isGet:
                            usages["get"] = None
                        if stateMember.isSet:
                            usages["set"] = None

                for usage in sorted(usages.iterkeys()):
                    self._write('''\
#define %s
''' % (self._module.state_value_sentinel(state, usage)))

        # Module declaration
        if self._bGenerateModule:

            self._write_section("Module")
            self._write('''\
%s const HDK_MOD_Module* %s(void);
''' % (self._module.export_macro(), self._module.module_fn()))

            if self._bGenerateModuleDynamic:
                self._write('''\

/* Dynamic server module export */
%s const HDK_MOD_Module* HDK_SRV_Module(void);
''' % (self._module.export_macro()))

        # File footer
        self._write_footer_h()

    #
    # *.c code generator
    #

    def _generate_c(self):

        # File header
        self._write_header_c()
        self._write('''\

#include "%s"

#include <string.h>
''' % (self._module.filename('.h')))

        # Namespace table
        self._write_section("Namespaces")
        self._write('''\
static const HDK_XML_Namespace s_namespaces[] =
{
''')
        for namespace in self._module.schema.namespaces:
            self._write('''\
    /* %d */ "%s",
''' % (self._module.schema.namespace_index(namespace), namespace))
        self._write('''\
    HDK_XML_Schema_NamespacesEnd
};
''')

        # Elements table
        self._write_section("Elements")
        self._write('''\
static const HDK_XML_ElementNode s_elements[] =
{
''')
        for element in self._module.schema.elements:
            self._write('''\
    /* %s = %d */ { %d, "%s" },
''' % (self._module.element_value(element),
            self._module.schema.element_index(element),
            self._module.schema.namespace_index(element[0]), element[1]))
        self._write('''\
    HDK_XML_Schema_ElementsEnd
};
''')

        # Enumerations definitions
        for enum in self._module.schema.enums:

            # Enumeration string table
            self._write_section("Enumeration %s" % (enum.uri))
            self._write('''\
static const HDK_XML_EnumValue %s[] =
{
''' % (self._module.enum_type_strings_static(enum)))
            for value in enum.enumValues:
                self._write('''\
    "%s",
''' % (value))
            self._write('''\
    HDK_XML_Schema_EnumTypeValuesEnd
};
''')

        # Enum types array
        if self._module.schema.enums:

            self._write_section("Enumeration types array")
            self._write('''\
static const HDK_XML_EnumType s_enumTypes[] =
{
''')
            sep = ','
            for enum in self._module.schema.enums:
                if enum is self._module.schema.enums[-1]:
                    sep = ""
                self._write('''\
    %s%s
''' % (self._module.enum_type_strings_static(enum), sep))
            self._write('''\
};
''')

        # Action definitions
        for action in self._module.actions:

            self._write_section("Method %s" % (action.uri))

            # Input schema
            self._write_schema(
                self._module.schema_nodes_static(action, suffix="_Input"),
                self._module.schema_static(action, suffix="_Input"),
                self._module.schema.struct_nodes(action.inputMember,
                                                 soap=True),
                element_path_static=self._module.schema_element_path_static(
                    action, suffix="_Input"),
                element_path=self._module.schema.struct_nodes_envelope(
                    action.inputMember, soap=True))

            # Output schema
            self._write('''\

''')
            self._write_schema(
                self._module.schema_nodes_static(action, suffix="_Output"),
                self._module.schema_static(action, suffix="_Output"),
                self._module.schema.struct_nodes(action.outputMember,
                                                 soap=True),
                element_path_static=self._module.schema_element_path_static(
                    action, suffix="_Output"),
                element_path=self._module.schema.struct_nodes_envelope(
                    action.outputMember, soap=True))

        # Actions table
        if self._module.actions:

            self._write_section("Methods")

            # Actions table header
            self._write('''\
static const HDK_MOD_Method s_methods[] =
{
''')

            # Write each action node
            for action_loc in self._module.action_locations():
                self._write_action(action_loc)

            # Actions table footer
            self._write('''\
    HDK_MOD_MethodsEnd
};
''')

        # Event definitions
        for event in self._module.events:

            self._write_section("Event %s" % (event.uri))
            self._write_schema(self._module.schema_nodes_static(event),
                               self._module.schema_static(event),
                               self._module.schema.struct_nodes(event.member))

        # Events table
        if self._module.events:

            self._write_section("Events")

            # Events table header
            self._write('''\
static const HDK_MOD_Event s_events[] =
{
''')

            # Write each event node
            for event in self._module.events:
                self._write_event(event)

            # Events table footer
            self._write('''\
    HDK_MOD_EventsEnd
};
''')

        if self._bGenerateServices:

            # Services action tables
            self._write_section("Service Methods")
            for service in self._module.services:

                # Service actions header
                self._write('''\
static const HDK_MOD_Method* %s[] =
{
''' % (self._module.service_actions_name(service)))

                # Service actions
                for action_uri in service.actions:

                    action_loc = [
                        a for a in self._module.action_locations()
                        if a.action.uri == action_uri
                    ][0]
                    self._write('''\
    &s_methods[%s],
''' % (self._module.action_value(action_loc)))

                # Service actions footer
                self._write('''\
    0
};
''')

            # Services event tables
            self._write_section("Service Events")
            for service in self._module.services:

                # Service events header
                self._write('''\
static const HDK_MOD_Event* %s[] =
{
''' % (self._module.service_events_name(service)))

                # Service events
                for eventURI in service.events:

                    event = [
                        e for e in self._module.events if e.uri == eventURI
                    ][0]
                    self._write('''\
    &s_events[%s],
''' % (self._module.event_value(event)))

                # Service events footer
                self._write('''\
    0
};
''')

            # Services table
            self._write_section("Services")

            # Services table header
            self._write('''\
static const HDK_MOD_Service s_services[] =
{
''')

            # Write each service node
            for service in self._module.services:
                self._write('''\
    {
        "%s",
        %s,
        %s
    },
''' % (service.uri, self._module.service_actions_name(service),
                self._module.service_events_name(service)))

            # Services table footer
            self._write('''\
    HDK_MOD_ServicesEnd
};
''')

        # State definitions
        if self._module.states:

            self._write_section("ADI")
            self._write_schema("s_schemaNodes_ADI", "s_schema_ADI",
                               self._module.schema.state_nodes())

        # Struct schema definitions
        for struct in self._module.schema.structs:
            self._write_section("Struct %s" % (struct.uri))
            self._write_schema(self._module.schema_nodes_static(struct),
                               self._module.schema_static(struct),
                               self._module.schema.struct_nodes(struct))
            self._write('''\

/* extern */ const HDK_XML_Schema* %s()
{
    return &%s;
}
''' % (self._module.dom_schema_fn(struct), self._module.schema_static(struct)))

        # Module definition
        if self._bGenerateModule:

            self._write_section("Module")

            # Network Object ID definition
            if self._noid is not None:
                self._write('''\
/* %s */
static const HDK_XML_UUID s_uuid_NOID =
{
    { %s }
};

''' % (self._noid, ", ".join(
                    ["0x%02x" % ord(byte) for byte in self._noid.bytes])))

            # Module definition and accessors
            self._write('''\
static const HDK_MOD_Module s_module =
{
    %s,
    %s,
    %s,
    %s,
    %s,
    %s
};

const HDK_MOD_Module* %s(void)
{
    return &s_module;
}
''' % ("&s_uuid_NOID" if self._noid else "0", '"' + self._friendlyName + '"'
            if self._friendlyName else "0", "s_services" if self._bGenerateServices
            else "0", "s_methods" if self._module.actions else "0",
            "s_events" if self._module.events else "0", "&s_schema_ADI"
            if self._module.states else "0", self._module.module_fn()))

            if self._bGenerateModuleDynamic:
                self._write('''\

const HDK_MOD_Module* HDK_SRV_Module(void)
{
    return &s_module;
}
''')

    #
    # *_methods.c code generator
    #

    def _generate_methods_c(self):

        # File header
        self._write_header_c()
        self._write('''\

#include "%s"

#include "hdk_srv.h"


/* Helper method for HNAP results */
#define SetHNAPResult(pStruct, prefix, method, result) \\
    prefix##_Set_##method##Result(pStruct, prefix##_Element_##method##Result, prefix##_Enum_##method##Result_##result)
''' % (self._module.filename('.h')))

        # Action declarations
        for action in self._module.actions:

            self._write_section("Method %s" % (action.uri))
            self._write('''\
#ifdef %s

void %s(HDK_MOD_MethodContext* pMethodCtx, HDK_XML_Struct* pInput, HDK_XML_Struct* pOutput)
{
    /* Unused parameters */
    (void)pMethodCtx;
    (void)pInput;
    (void)pOutput;
}

#endif /* %s */
''' % (self._module.action_sentinel(action), self._module.action_fn(action),
            self._module.action_sentinel(action)))

    #
    # *_adi.txt report generator
    #

    def _generate_adi_txt(self):

        # ADI report title
        self._write('''\
======================================================================
ADI Report for the %s Module
======================================================================
''' % (self._baseName.upper()))

        # Output the ADI values used (show get/set)
        self._write('''\


======================================================================
ADI values
======================================================================
''')
        for state in self._model.referenced_states():

            # Determine get/set
            usages = {}
            for actionState in self._model.actionStates:
                if state.uri in actionState.stateMembers:
                    stateMember = actionState.stateMembers[state.uri]
                    if stateMember.isGet:
                        usages["get"] = None
                    if stateMember.isSet:
                        usages["set"] = None
            sUsages = ', '.join(sorted(usages.iterkeys()))

            # Get the base type
            type = state.type
            nArray = 0
            while type.isArray:
                nArray += 1
                type = type.arrayType
            sArray = "[]" * nArray

            # Get the type description
            sDesc = ""
            if type.isEnum:
                sDesc = "enum"
            elif type.isStruct:
                sDesc = "struct"

            # Get the type string
            if sDesc:
                sType = '%s%s (%s, "%s")' % (type.name, sArray, sDesc,
                                             type.namespace)
            else:
                sType = '%s%s' % (type.name, sArray)

            self._write('''\

%s

    Namespace: "%s"
         Name: "%s"
         Type: %s
        Usage: %s
''' % (self._module.state_value(state), state.namespace, state.name, sType,
            sUsages))

        # Output the ADI values used by action
        self._write('''\


======================================================================
ADI values by action
======================================================================
''')
        for actionState in self._model.actionStates:
            self._write('''\

%s

''' % (actionState.uri))
            for stateMember in sorted(actionState.stateMembers.itervalues()):

                # Determine usage
                usages = {}
                if stateMember.isGet:
                    usages["get"] = None
                if stateMember.isSet:
                    usages["set"] = None
                sUsages = ', '.join(sorted(usages.iterkeys()))

                self._write('''\
    [%s] %s
''' % (sUsages, self._module.state_value(stateMember.state)))
Пример #6
0
class CppClientGenerator:

    # Class initializer
    def __init__(self, model, baseName, moduleName, actionLocation, fDOM, fInline):

        self._baseName = baseName
        self._fDOM = fDOM
        self._fInline = fInline

        # Generator state
        self._out = None
        self._namespaceStack = []

        # The underlying module.
        self._module = HDKModule(model, moduleName, actionLocation, fDOM)

        # Type information
        structTypes = {}
        arrayTypes = {}
        enumTypes = {}
        for type in model.all_types(user_types = fDOM, state_types = False):
            if type.isArray:
                arrayTypes[type.uri] = type
            elif type.isStruct:
                structTypes[type.uri] = type
            elif type.isEnum:
                enumTypes[type.uri] = type

        self._structTypes = sorted(structTypes.values(), key = lambda x:x.uri)
        self._arrayTypes = sorted(arrayTypes.values(), key = lambda x:x.uri)
        self._enumTypes = sorted(enumTypes.values(), key = lambda x:x.uri)


    #
    # Generate code
    #

    def generate(self, dir, fhReport):

        # Generate c++ client code
        for file, fn in [(os.path.join(dir, self._filename('.h')), self._generate_h),
                         (os.path.join(dir, self._filename('.cpp')), self._generate_cpp)]:

            if fhReport is not None:
                print >>fhReport, 'Generating "%s" ...' % file

            self._out = open(file, "w")
            try:
                fn()
            except:
                raise
            finally:
                self._out.close()

    #
    # Helpers
    #

    # Is the given type a "blob"
    @staticmethod
    def is_blob_type(type):

        return type.name == "blob"

    @staticmethod
    def is_bool_type(type):

        return type.name == "bool"

    @staticmethod
    def is_int_type(type):

        return type.name == "int"

    @staticmethod
    def is_long_type(type):

        return type.name == "long"

    @staticmethod
    def is_datetime_type(type):

        return type.name == "datetime"

    @staticmethod
    def is_ipaddress_type(type):

        return type.name == "IPAddress"

    @staticmethod
    def is_macaddress_type(type):

        return type.name == "MACAddress"

    @staticmethod
    def is_string_type(type):

        return type.name == "string"

    @staticmethod
    def is_uuid_type(type):

        return type.name == "UUID"

    @staticmethod
    def is_hnap_result_type(type):

        return type.name == "Result"

    # Does the given type support the "blank" value
    @staticmethod
    def supports_blank(type):

        return CppClientGenerator.is_ipaddress_type(type) or CppClientGenerator.is_macaddress_type(type)

    # Retrieve the HDK_XML_ function to use for the given type and access
    def _hdk_struct_accessor(self, type, access):

        if CppClientGenerator.is_datetime_type(type):
            method = "HDK_XML_" + access + "_DateTime"
        elif CppClientGenerator.is_macaddress_type(type) or \
                CppClientGenerator.is_ipaddress_type(type):
            method = "HDK_XML_" + access + "_" + type.name
        elif CppClientGenerator.is_uuid_type(type):
            method = "HDK_XML_" + access + "_UUID"
        elif type.isEnum:
            method = self._module.enum_accessor(type, access)
        elif type.isStruct:
            method = "HDK_XML_" + access + "_Struct"
        else:
            method = "HDK_XML_" + access + "_" + type.name.capitalize()

        return method

    # Retrieve the HDK type cast for the given type
    def _hdk_type_cast(self, type):

        if type.isEnum:
            return "(" + self._module.enum_enum(type) + ")"
        elif CppClientGenerator.is_bool_type(type):
            return "(int)"
        else:
            return ""

    #
    # Symbol helper methods
    #

    # Generated code filename
    def _filename(self, suffix):

        return self._baseName + suffix

    # c++ namespace
    def _namespace(self, uri):

        ns = re.sub("^\w+:/+(www\.)?", "", uri)
        ns = re.sub("\.com", "", ns)
        ns = re.sub("/+HNAPExt", "", ns)

        ns = re.sub("/+$", "", ns)
        if ns:
            ns = ns[0].upper() + ns[1:]

        return re.sub("[^\w]", "_", ns)

    # c++ class name
    def _class_class(self, type):

        assert(type.isStruct)

        if type.isArray:
            match = re.match("ArrayOf(.*)", type.name);
            return makeGlobalSymbol("", "", match.group(1) + "Array")
        else:
            return makeGlobalSymbol("", "", type.name + "Struct")

    def _class_class_iter(self, type):

        return self._class_class(type) + "Iter"

    # c++ class constructor
    def _class_constructor(self, type):

        assert(type.isStruct)
        return self._class_class(type)

    # c++ class set property method
    def _class_set_property_method(self, member):

        return "set_" + member.name

    # c++ class get property method
    def _class_get_property_method(self, member):

        return "get_" + member.name

    # c++ class from file method
    def _class_from_file_method(self):

        return "FromFile"

    # c++ class to file method
    def _class_to_file_method(self):

        return "ToFile"

    def _type_type(self, type, withNS = False):

        typeName = None

        if type.isBuiltin:
            if CppClientGenerator.is_bool_type(type):
               typeName = "bool"
            elif CppClientGenerator.is_int_type(type):
                typeName = "HDK_XML_Int"
            elif CppClientGenerator.is_long_type(type):
                typeName = "HDK_XML_Long"
            elif CppClientGenerator.is_string_type(type):
                typeName = "const char*"
            elif CppClientGenerator.is_datetime_type(type):
                typeName = "time_t"
            elif CppClientGenerator.is_ipaddress_type(type):
                typeName = "IPv4Address"
            elif CppClientGenerator.is_macaddress_type(type):
                typeName = "MACAddress"
            elif CppClientGenerator.is_blob_type(type):
                typeName = "Blob"
            elif CppClientGenerator.is_uuid_type(type):
                typeName = "UUID"
            elif CppClientGenerator.is_hnap_result_type(type):
                typeName = type.name
                if fWithNamespace and self._current_open_namespace() != type.namespace:
                    typeName = self._namespace(type.namespace) + "::" + typeName
            else:
                print>>sys.stderr, "Unhandled builtin type %s" % (type.name)
                typeName = type.name

        elif type.isStruct:
            typeName = self._class_class(type)
            namespace = self._namespace(type.namespace)

            if namespace and withNS and self._current_open_namespace() != type.namespace:
                typeName = namespace + "::" + typeName

        elif type.isEnum:
            typeName = self._enum_enum(type)
            if withNS and self._current_open_namespace() != type.namespace:
                typeName = self._namespace(type.namespace) + "::" + typeName
            typeName = "enum " + typeName

        return typeName

    # c++ type cast
    def _type_cast(self, type):

        if type.isEnum:
            return "(" + self._type_type(type, withNS = True)  + ")"
        elif CppClientGenerator.is_bool_type(type):
            return "(bool)"
        else:
            return ""

    # c++ enum type
    def _enum_enum(self, type):

        return makeGlobalSymbol("", "", type.name)

    # c++ enum value
    def _enum_value(self, type, value, bIsUnknown = False):

        if bIsUnknown:
            return makeGlobalSymbol(type.name, "", "Unknown")
        else:
            return makeGlobalSymbol(type.name, "", value)

    # c++ action
    def _action_fn(self, action_loc, withNS = False):

        (http_method, action) = (action_loc.http_method, action_loc.action)

        fn = action.name
        if withNS:
            fn = self._namespace(action.namespace) + "::" + fn

        # Non-post action?
        if http_method != "POST":
            suffix = "_" + http_method
        else:
            suffix = ""

        return fn + suffix

    #
    # Output helper methods
    #

    # Output
    def _write(self, s):

        self._out.write(s)

    def _write_newline(self):

        self._out.write('''
''')

    def _write_with_indent(self, s, cIndent, bIndentCPreprocessor = False):

        indent = cIndent * ' '
        for line in s.splitlines(True):
            if line[0] == '#' and not bIndentCPreprocessor:
                self._write(line)
            elif re.search("^\s*$", line):
                # Don't write unnecessary whitespace
                self._write(line)
            else:
                self._write(indent + line)

    def _write_with_ns_indent(self, s, extraIndent = 0):

        self._write_with_indent(s, self._namespace_indent() + extraIndent)

    def _write_copyright(self):

        self._write('''\
/*
 * Copyright (c) 2008-2010 Cisco Systems, Inc. All rights reserved.
 *
 * Cisco Systems, Inc. retains all right, title and interest (including all
 * intellectual property rights) in and to this computer program, which is
 * protected by applicable intellectual property laws.  Unless you have obtained
 * a separate written license from Cisco Systems, Inc., you are not authorized
 * to utilize all or a part of this computer program for any purpose (including
 * reproduction, distribution, modification, and compilation into object code),
 * and you must immediately destroy or return to Cisco Systems, Inc. all copies
 * of this computer program.  If you are licensed by Cisco Systems, Inc., your
 * rights to utilize this computer program are limited by the terms of that
 * license.  To obtain a license, please contact Cisco Systems, Inc.
 *
 * This computer program contains trade secrets owned by Cisco Systems, Inc.
 * and, unless unauthorized by Cisco Systems, Inc. in writing, you agree to
 * maintain the confidentiality of this computer program and related information
 * and to not disclose this computer program and related information to any
 * other person or entity.
 *
 * THIS COMPUTER PROGRAM IS PROVIDED AS IS WITHOUT ANY WARRANTIES, AND CISCO
 * SYSTEMS, INC. EXPRESSLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * INCLUDING THE WARRANTIES OF MERCHANTIBILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, TITLE, AND NONINFRINGEMENT.
 */
''')

    # Write .cpp file header
    def _write_header_cpp(self, fileName = ""):

        self._write_copyright()

        self._write('''\

// %s - [Generated by hdkcli_cpp]
''' % (fileName))

    # Write .h file header
    def _write_header_h(self, fileName = ""):

        self._write_copyright()
        self._write('''\

#pragma once

// %s - [Generated by hdkcli_cpp]
''' % (fileName))

    # Write .h file footer
    def _write_footer_h(self):

        # Namespace stack should be empty now.
        assert(not self._namespaceStack)

    # Write the opening of a namespace
    def _write_open_namespace(self, uri, cIndent):

        indent = ' ' * cIndent
        namespace = self._namespace(uri)

        self._write('''\

%snamespace %s
%s{
''' % (indent, namespace,
       indent))

    # Write the closing of a namespace
    def _write_close_namespace(self, uri, cIndent):

        indent = ' ' * cIndent
        namespace = self._namespace(uri)

        self._write('''\
%s} // namespace %s
''' % (indent, namespace))

    # Generate a class forward declaration
    def _write_class_forward_declaration(self, type):

        self._write_with_ns_indent('''\
class %s;
''' % (self._class_class(type)))

    # Generate an enum definition
    def _write_enum(self, type, extraIndent = 0):

        assert(type.isEnum)

        indent = self._namespace_indent() + extraIndent

        self._write_doxygen_section_with_ns_indent_begin(extraIndent)
        self._write_doxygen_string_with_ns_indent('''\
\\enum %s
     <a>%s</a>
''' % (self._enum_enum(type),
       type.uri), extraIndent)
        self._write_doxygen_lines_with_ns_indent(type.doctext, extraIndent)
        self._write_doxygen_section_with_ns_indent_end(extraIndent)

        self._write_with_indent('''\
enum %s
{
    %s = %s /*<! Unknown value */,
''' % (self._enum_enum(type),
       self._enum_value(type, None, True), self._module.enum_value(type, None, True)), indent)

        for value in type.enumValues[0:-1]:
            self._write_with_indent('''\
    %s = %s /*!< %s */,
''' % (self._enum_value(type, value), self._module.enum_value(type, value), value), indent)

        if type.enumValues:
            value = type.enumValues[-1]
            self._write_with_indent('''\
    %s = %s /*!< %s */
''' % (self._enum_value(type, value), self._module.enum_value(type, value), value), indent)
        self._write_with_indent('''\
}; // enum %s

''' % (self._enum_enum(type)), indent)

    #
    # Doxygen-formatted output helpers
    #

    # Doxygen comment prefix.
    _doxygenPrefix = "///"

    def _write_doxygen_line_with_ns_indent(self, line, extraIndent = 0):

        doxygenFormattedLine = CppClientGenerator._doxygenPrefix
        if line and not re.search("^\s$", line):
            doxygenFormattedLine += " " + line

        if not doxygenFormattedLine.endswith("\n"):
            doxygenFormattedLine += "\n"

        self._write_with_indent(doxygenFormattedLine, extraIndent + self._namespace_indent())

    def _write_doxygen_lines_with_ns_indent(self, lines, extraIndent = 0):

        for line in lines:
            self._write_doxygen_line_with_ns_indent(line, extraIndent)

    def _write_doxygen_string_with_ns_indent(self, s, extraIndent = 0):

        for line in s.splitlines(True):
            self._write_doxygen_line_with_ns_indent(line, extraIndent)

    def _write_doxygen_section_with_ns_indent_begin(self, extraIndent = 0):

        self._write_doxygen_line_with_ns_indent(None, extraIndent)

    def _write_doxygen_section_with_ns_indent_end(self, extraIndent = 0):

        self._write_doxygen_line_with_ns_indent(None, extraIndent)

    def _write_doxygen_section_with_ns_indent(self, s, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent_begin(extraIndent)

        self._write_doxygen_string_with_ns_indent(s, extraIndent)

        self._write_doxygen_section_with_ns_indent_end(extraIndent)

    #
    # Namespace scoping
    #

    # Update the current namespace (closing the previous one, if needed)
    def _push_namespace(self, ns):

        if self._namespaceStack:
            if self._namespaceStack[-1].namespace == ns:
                if self._namespaceStack[-1].needs_closing:
                    self._namespaceStack[-1].increment()
                    return
                #else: ns::ns. strange...
            else:
                if self._namespaceStack[-1].needs_closing:
                    self._write_close_namespace(self._namespaceStack[-1].namespace, (len(self._namespaceStack) - 1) * 4)
                    self._namespaceStack.pop()

        self._namespaceStack.append(CppNamespaceScope(ns))
        self._write_open_namespace(ns, (len(self._namespaceStack) - 1) * 4)

    def _pop_namespace(self, bAlwaysClose = False):

        if self._namespaceStack[-1].needs_closing:
            self._write_close_namespace(self._namespaceStack[-1].namespace, (len(self._namespaceStack) - 1) * 4)
            self._namespaceStack.pop()

        if self._namespaceStack:
            self._namespaceStack[-1].decrement()

            if bAlwaysClose and self._namespaceStack[-1].needs_closing:
                self._write_close_namespace(self._namespaceStack[-1].namespace, (len(self._namespaceStack) - 1) * 4)
                self._namespaceStack.pop()

    # Namespace indentation
    def _namespace_indent(self):

        return len(self._namespaceStack) * 4

    # Current open namespace
    def _current_open_namespace(self):

        if self._namespaceStack:
            return self._namespaceStack[-1].namespace
        else:
            return None

    #
    # Helpers for c++ class wrappers of HDK_XML_Structs
    #

    # Generate a default constructor
    def _write_class_default_constructor(self, struct, element = None, fImpl = False, classScope = None, extraIndent = 0):

        if element is None:
            element = "HDK_XML_BuiltinElement_Unknown"

        method = self._class_constructor(struct)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
%s() throw()''' % (method), extraIndent)

        if fImpl:
            self._write(''' :''')
            self._write_with_ns_indent('''
    Struct(%s)
{
}
''' % (element), extraIndent)
        else: # not fImpl
            self._write(''';
''')

    # Generate a constructor with HDK_XML_Struct
    def _write_class_hdk_constructor(self, struct, fImpl = False, classScope = None, extraIndent = 0):

        method = self._class_constructor(struct)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
%s(HDK_XML_Struct* phdkstruct) throw()''' % (method), extraIndent)

        if fImpl:
            self._write(''' :''')
            self._write_with_ns_indent('''
    Struct(phdkstruct)
{
}
''', extraIndent)
        else: # not fImpl
            self._write(''';
''')

    # Generate a get_ accessor implementation.
    def _write_class_get_accessor(self, member, fImpl = False, classScope = None, extraIndent = 0):

        element = self._module.element_value((member.namespace, member.name))

        method = self._class_get_property_method(member)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
%s %s() const throw()''' % (self._type_type(member.type, withNS = True), method), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
''', extraIndent)
            if member.type.isStruct:
                self._write_with_ns_indent('''\
    return %s(GetStruct(), %s);
''' % (self._hdk_struct_accessor(member.type, "Get"), element), extraIndent)
            else:
                # Blob type is special-cased.
                if CppClientGenerator.is_blob_type(member.type):
                    self._write_with_ns_indent('''\
    unsigned int cbBlob = 0;
    char* pbBlob = %s(GetStruct(), %s, &cbBlob);
    return Blob(pbBlob, cbBlob);
''' % (self._hdk_struct_accessor(member.type, "Get"), element), extraIndent)
                else:
                    # Determine the default value
                    if member.type.isEnum:
                        defaultValue = self._module.enum_value(member.type, None, is_unknown = True)
                    elif CppClientGenerator.is_ipaddress_type(member.type):
                        defaultValue = "HDK::IPv4Address::Blank()"
                    elif CppClientGenerator.is_macaddress_type(member.type):
                        defaultValue = "HDK::MACAddress::Blank()"
                    else:
                        defaultValue = "0"

                    # Determine the cast for the type.
                    cast = self._type_cast(member.type)
                    if CppClientGenerator.is_bool_type(member.type):
                        # Avoid Microsoft compiler warning C4800
                        cast = "0 != "
                    self._write_with_ns_indent('''\
    return %s%s(GetStruct(), %s, %s);
''' % (cast, self._hdk_struct_accessor(member.type, "GetEx"), element, defaultValue), extraIndent)
            self._write_with_ns_indent('''\
}
''', extraIndent)

        else: # not fImpl
            self._write(''';
''')

    # Generate a set_ accessor implementation.  If classScope is present, the method will be scoped in the class.
    def _write_class_set_accessor(self, member, fImpl = False, classScope = None, extraIndent = 0):

        element = self._module.element_value((member.namespace, member.name))

        if member.type.isStruct or \
                CppClientGenerator.is_blob_type(member.type) or \
                CppClientGenerator.is_ipaddress_type(member.type) or \
                CppClientGenerator.is_macaddress_type(member.type):

            argType = "const " + self._type_type(member.type, withNS = True) + "&"
        else:
            argType = self._type_type(member.type, withNS = True)

        method = self._class_set_property_method(member)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
void %s(%s value) throw()''' % (method, argType), extraIndent)
        if fImpl:
            self._write_with_ns_indent('''\

{
''', extraIndent)

            if CppClientGenerator.supports_blank(member.type):
                self._write_with_ns_indent('''\
    if (value.IsBlank())
    {
        (void)HDK_XML_Set_Blank(GetStruct(), %s);
        return;
    }
''' % (element), extraIndent)

            # Handle non-standard value sets (struct and blob types)
            if member.type.isStruct:
                self._write_with_ns_indent('''\
    (void)%s(GetStruct(), %s, value);
''' % (self._hdk_struct_accessor(member.type, "SetEx"), element), extraIndent)

            elif CppClientGenerator.is_blob_type(member.type):
                self._write_with_ns_indent('''\
    (void)%s(GetStruct(), %s, value.get_Data(), value.get_Size());
''' % (self._hdk_struct_accessor(member.type, "Set"), element), extraIndent)

            else:
                self._write_with_ns_indent('''\
    (void)%s(GetStruct(), %s, %svalue);
''' % (self._hdk_struct_accessor(member.type, "Set"), element, self._hdk_type_cast(member.type)), extraIndent)

            self._write_with_ns_indent('''\
}
''', extraIndent)

        else: # not fImpl
            self._write(''';
''')

    # Generate a FromFile method implementation.
    def _write_class_method_from_file(self, type, fImpl = False, classScope = None, extraIndent = 0):

        method = self._class_from_file_method()
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
bool %s(const char* pszFile) throw()''' % (method), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
    return HDK::Struct::DeserializeFromFile(%s(), pszFile);
}
''' % (self._module.dom_schema_fn(type)), extraIndent)
        else: # not fImpl
            self._write(''';
''')


    # Generate a ToFile method implementation.
    def _write_class_method_to_file(self, type, fImpl = False, classScope = None, extraIndent = 0):

        method = self._class_to_file_method()
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
bool %s(const char* pszFile) const throw()''' % (method), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
    return HDK::Struct::SerializeToFile(%s(), pszFile, 0);
}
''' % (self._module.dom_schema_fn(type)), extraIndent)
        else: # not fImpl
            self._write(''';
''')

    # Generate a c++ class declaration from an HDK_XML_Struct
    def _write_class_declaration(self, struct,
                                 fGenerateSets = True, fConstructFromStruct = True,
                                 fInline = False, rootElement = None, fDOM = False):

        self._write_doxygen_section_with_ns_indent_begin()
        self._write_doxygen_string_with_ns_indent('''\
\\brief %s
     <a>%s</a>
''' % (struct.name, struct.uri))
        self._write_doxygen_lines_with_ns_indent(struct.doctext)
        self._write_doxygen_section_with_ns_indent_end()

        self._write_with_ns_indent('''\
class %s : public Struct
{
''' % (self._class_class(struct)))

        # Generate the class constructors and destructor.
        self._write_with_ns_indent('''\
public:
    //
    // Constructors/Destructor.
    //
''')
        self._write_class_default_constructor(struct, element = rootElement, fImpl = fInline, extraIndent = 4)
        self._write_newline()

        if fConstructFromStruct:
            self._write_class_hdk_constructor(struct, fImpl = fInline, extraIndent = 4)
            self._write_newline()

        # Write the accessor methods.
        for member in struct.members:

            self._write_doxygen_section_with_ns_indent_begin(4)
            self._write_doxygen_string_with_ns_indent('''\
\\brief Get the %s value.
''' % (member.name), 4)
            if member.doctext:
                self._write_doxygen_line_with_ns_indent('''\
     \\retval %s
''' % (member.doctext[0]), 4)

                self._write_doxygen_lines_with_ns_indent(member.doctext[1:], 4)
            self._write_doxygen_section_with_ns_indent_end(4)

            self._write_class_get_accessor(member, fImpl = fInline, extraIndent = 4)
            self._write_newline()

            if fGenerateSets:
                self._write_doxygen_section_with_ns_indent_begin(4)
                self._write_doxygen_string_with_ns_indent('''\
\\brief Set the %s value.
''' % (member.name), 4)
                if member.doctext:
                    self._write_doxygen_string_with_ns_indent('''\
     \\arg %s
''' % (member.doctext[0]), 4)
                    self._write_doxygen_lines_with_ns_indent(member.doctext[1:], 4)
                self._write_doxygen_section_with_ns_indent_end(4)

                self._write_class_set_accessor(member, fImpl = fInline, extraIndent = 4)
                self._write_newline()

        if fDOM:
            self._write_doxygen_section_with_ns_indent_begin(4)
            self._write_doxygen_string_with_ns_indent('''\
\\brief Serialize to/from an XML file.
''', 4)
            self._write_doxygen_section_with_ns_indent_end(4)

            self._write_class_method_from_file(struct, fImpl = fInline, extraIndent = 4)
            self._write_class_method_to_file(struct, fImpl = fInline, extraIndent = 4)
            self._write_newline()

        self._write_with_ns_indent('''\
}; // class %s : public Struct

''' % self._class_class(struct))

    # Generate a wrapper for an array class using the array template
    def _write_array_template_typedef(self, type, includeClassDeclaration = True):

        assert(type.isArray)

        self._write_doxygen_section_with_ns_indent('''\
\\class %s
     Wrapper class for accessing arrays of %s values.
''' % (self._class_class(type),
       type.arrayType.name))

        # element represents the XML element of each array item
        element = self._module.element_value((type.members[0].namespace, type.members[0].name))

        if type.arrayType.isStruct:
            if includeClassDeclaration:
                self._write_with_ns_indent('''\
class %s; // forward declaration
''' % (self._class_class(type.arrayType)))
            self._write_with_ns_indent('''\
typedef HDK::StructArray<%s, %s> %s;
typedef HDK::StructArray<%s, %s>::StructArrayIter %sIter;

''' % (self._type_type(type.arrayType, withNS = True), element, self._class_class(type),
       self._type_type(type.arrayType, withNS = True), element, self._class_class(type)))
        elif type.arrayType.isEnum:
            self._write_with_ns_indent('''\
typedef HDK::EnumArray<%s, %s, %s> %s;
typedef HDK::EnumArray<%s, %s, %s>::EnumArrayIter %sIter;

''' % (self._type_type(type.arrayType, withNS = True), self._module.enum_type_value(type.arrayType), element, self._class_class(type),
       self._type_type(type.arrayType, withNS = True), self._module.enum_type_value(type.arrayType), element, self._class_class(type)))
        elif type.arrayType.name == "datetime":
            self._write_with_ns_indent('''\
typedef HDK::DateTimeArray<%s> %s;
typedef HDK::DateTimeArray<%s>::DateTimeArrayIter %sIter;

''' % (element, self._class_class(type),
       element, self._class_class(type)))
        else:
            templateClass = type.arrayType.name.capitalize() + "Array"
            self._write_with_ns_indent('''\
typedef HDK::%s<%s> %s;
typedef HDK::%s<%s>::%sIter %sIter;

''' % (templateClass, element, self._class_class(type),
       templateClass, element, self._class_class(type), templateClass))

    #
    # Generate a c++ class definition from an HDK_XML_Struct
    #
    def _write_class_definition(self, struct,
                                fGenerateSets = True, fConstructFromStruct = True,
                                rootElement = None,
                                fDOM = False):

        assert(struct.isStruct)

        # Constructors.
        self._write_class_default_constructor(struct, element = rootElement, fImpl = True, classScope = struct)

        if fConstructFromStruct:
            self._write_newline()
            self._write_class_hdk_constructor(struct, fImpl = True, classScope = struct)

        for member in struct.members:

            element = self._module.element_value((member.namespace, member.name))

            # Get accessor
            self._write_newline()
            self._write_class_get_accessor(member, fImpl = True, classScope = struct)

            # Set accessor
            if fGenerateSets:
                self._write_newline()
                self._write_class_set_accessor(member, fImpl = True, classScope = struct)

        if fDOM:
            self._write_newline()
            self._write_class_method_from_file(struct, fImpl = True, classScope = struct)

            self._write_newline()
            self._write_class_method_to_file(struct, fImpl = True, classScope = struct)

    # Helper to generate the action method documentation
    def _write_action_method_documentation(self, action_loc, extraIndent = 0):

        (http_method, http_location, action, noauth) = \
            (action_loc.http_method, action_loc.http_location, action_loc.action, action_loc.noauth)

        bInput = (len(action.inputMember.type.members) > 0)
        bOutput = (len(action.outputMember.type.members) > 1)

        self._write_doxygen_section_with_ns_indent_begin(extraIndent)
        self._write_doxygen_string_with_ns_indent('''\
\\brief Call the %s method on a given device.
    <a>%s</a>
    This method uses HTTP method %s and location '%s'
''' % (action.name,
       action.uri,
       http_method, http_location), extraIndent)
        if noauth:
            self._write_doxygen_string_with_ns_indent('''\
    \\note This method does NOT require HTTP Basic Authorization.

''', extraIndent)
        self._write_doxygen_lines_with_ns_indent(action.doctext)
        self._write_doxygen_line_with_ns_indent("\n")

        # Write out the potential result values.
        if action.resultEnum.enumValues:
            self._write_doxygen_line_with_ns_indent('''\
    Possible result values:
''', extraIndent)
            for enumValue in action.resultEnum.enumValues:
                self._write_doxygen_line_with_ns_indent('''\
         - #%s
''' % (self._enum_value(action.resultEnum, enumValue)), extraIndent)
        self._write_doxygen_string_with_ns_indent('''\
    \\arg pTarget The target on which to call this method.
''', extraIndent)
        if bInput:
            self._write_doxygen_string_with_ns_indent('''\
    \\arg input The input argument data to the %s HNAP method.
''' % (action.name), extraIndent)

        if bOutput:
            self._write_doxygen_line_with_ns_indent('''\
    \\arg output The output argument data from the %s HNAP method.
''' % (action.name), extraIndent)

        self._write_doxygen_string_with_ns_indent('''\
    \\arg[optional] result The HNAP result of the %s HNAP method.
    \\arg timeoutSecs An optional timeout, in seconds, to use for the HNAP call.
    \\retval The result of the HNAP method call.
''' % (action.name), extraIndent)
        self._write_doxygen_section_with_ns_indent_end(extraIndent)

    # Helper to generate the action method declarations
    def _write_action_method(self, action_loc, fImpl = False, withNS = False, fNoDefaultArgs = False, extraIndent = 0):

        action = action_loc.action

        bInput = (len(action.inputMember.type.members) > 0)
        bOutput = (len(action.outputMember.type.members) > 1)

        self._write_with_ns_indent('''\
HDK::ClientError %s
(
    HDK::ITarget* pTarget,
''' % (self._action_fn(action_loc, withNS = withNS)), extraIndent)
        if bInput:
            self._write_with_ns_indent('''\
    const %s & input,
''' % (self._type_type(action.inputMember.type, withNS = True)), extraIndent)
        if bOutput:
            self._write_with_ns_indent('''\
    %s & output,
''' % (self._type_type(action.outputMember.type, withNS = True)), extraIndent)

        if fNoDefaultArgs:
            presultDefault = "/* = NULL */"
            timeoutSecsDefault = "/* = 0 */"
        else:
            presultDefault = "= NULL"
            timeoutSecsDefault = "= 0"
        self._write_with_ns_indent('''\
    %s* presult %s,
    unsigned int timeoutSecs %s
) throw()''' % (self._type_type(action.outputMember.type.members[0].type, withNS = True), presultDefault,
                timeoutSecsDefault), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
    if (!pTarget)
    {
        return ClientError_InvalidArg;
    }
''', extraIndent)
            if not bInput:
                self._write_with_ns_indent('''\
    %s input;
''' % (self._type_type(action.inputMember.type, withNS = True)), extraIndent)
            if not bOutput:
                self._write_with_ns_indent('''\

    %s output;
''' % (self._type_type(action.outputMember.type, withNS = True)), extraIndent)
            self._write_with_ns_indent('''\

    ClientError error = pTarget->Request(timeoutSecs,
                                         %s(),
                                         %s,
                                         input,
                                         &output);
''' % (self._module.module_fn(),
       self._module.action_value(action_loc)), extraIndent)
            self._write_with_ns_indent('''\

    const HDK_MOD_Method* pMethod = HDK_MOD_GetMethod(%s(), %s);
''' % (self._module.module_fn(), self._module.action_value(action_loc)), extraIndent)
            self._write_with_ns_indent('''\

    // Get the result value.
    %s result = output.%s();
    if (NULL != presult)
    {
        *presult = result;
    }

    // Determine if there was an HNAP-result, and whether it was an error or not.
    if ((ClientError_OK == error) && (HDK_XML_BuiltinElement_Unknown != pMethod->hnapResultElement))
    {
        if ((pMethod->hnapResultOK != (int)result) && (pMethod->hnapResultREBOOT != (int)result))
        {
            // An HNAP error response.
            error = HDK::ClientError_HnapMethod;
        }
    }

    return error;
}
''' % (self._type_type(action.outputMember.type.members[0].type, withNS = True), \
           self._class_get_property_method(action.outputMember.type.members[0])), extraIndent)

        else: # not fImpl
            self._write(''';
''')

    # Helper to write HDK Init documentation
    def _write_init_function_documentation(self, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent('''\
\\fn InitializeClient
     Initialize the HDK client library.  This should be called once per application instance.
     Each call to InitializeClient should be matched by a call to UninitializeClient.
     \\retval true if initialization was successful, false if not.
''', extraIndent)

    # Helper to write HDK Init method
    def _write_init_function(self, fImpl = False, withNS = False, extraIndent = 0):

        method = "InitializeClient"
        if withNS:
            method = "HDK::" + method

        self._write_with_ns_indent('''\
bool %s() throw()''' % (method), extraIndent)

        if fImpl:

            self._write_with_ns_indent('''\

{
    return !!HDK_CLI_Init();
}
''', extraIndent)

        else: # not fImpl

            self._write(''';
''')

    # Helper to write HDK uninit documentation
    def _write_uninit_function_documentation(self, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent('''\
\\fn UninitializeClient
     Cleanup the HDK client library.  This should be called if true was returned from InitializeClient
     When the caller is done using the HDK client library.
''', extraIndent)

    # Helper to write HDK uninit method
    def _write_uninit_function(self, fImpl = False, withNS = False, extraIndent = 0):

        method = "UninitializeClient"
        if withNS:
            method = "HDK::" + method

        self._write_with_ns_indent('''\
void %s() throw()''' % (method), extraIndent)

        if fImpl:

            self._write_with_ns_indent('''\

{
    HDK_CLI_Cleanup();
}
''', extraIndent)

        else: # not fImpl

            self._write(''';
''')

    # Helper to write URI accessor macro documentation
    def _write_uri_accessor_macro_documentation(self, action_loc, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent('''\
\\brief SOAP method URI for action %s
''' % (action_loc.action.name), extraIndent)

    # Helper to write URI accessor macro
    def _write_uri_accessor_macro(self, action_loc, extraIndent = 0):

        self._write_with_ns_indent('''\
#define %s (HDK_MOD_GetMethod(%s(), %s)->pszSOAPAction)
''' % (makeGlobalSymbol("", action_loc.action.namespace, action_loc.action.name, nameSuffix = "_URI"),
       self._module.module_fn(), self._module.action_value(action_loc)), extraIndent)

    #
    # .h code generator
    #
    def _generate_h(self):

        self._write_header_h(self._filename('.h'))

        self._write('''\

// Non-generated client code.
#include "hdk_cli_cpp.h"

// Underlying schema module.
#include "%s"

''' % (self._module.filename('.h')))

        self._push_namespace("HDK")

        # Write HDK global functions.
        if self._module.action_locations():
            self._write_init_function_documentation()
            self._write_init_function()
            self._write_newline()
            self._write_uninit_function_documentation()
            self._write_uninit_function()
            self._write_newline()

        # Generate c++-style enums
        for typeEnum in self._enumTypes:

            self._push_namespace(typeEnum.namespace)
            self._write_enum(typeEnum)
            self._pop_namespace()

        # Declare array types.
        for typeArray in self._arrayTypes:

            self._push_namespace(typeArray.namespace)
            self._write_array_template_typedef(typeArray)
            self._pop_namespace()

        # Generate c++ wrapper classes for each struct
        for typeStruct in self._structTypes:

            # Generate any required forward declarations.
            for member in typeStruct.members:
                # The types are sorted alphabetically, so anything greater than this one has yet to be defined.
                forwDeclType = member.type
                if forwDeclType.isArray:
                    forwDeclType = forwDeclType.arrayType
                if forwDeclType.isStruct and not forwDeclType.isArray:
                   if forwDeclType.name > typeStruct.name:
                       self._push_namespace(forwDeclType.namespace)
                       self._write_class_forward_declaration(forwDeclType)
                       self._pop_namespace()

            self._push_namespace(typeStruct.namespace)

            element = None
            if self._fDOM:
                element = self._module.element_value((typeStruct.namespace, typeStruct.name))
            self._write_class_declaration(typeStruct,
                                          fDOM = self._fDOM,
                                          fInline = self._fInline,
                                          rootElement = element)
            self._pop_namespace()

        # Generate class implementations for each action input and output structure.
        for action in self._module.actions:

            for (fGenerateSets, struct) in \
                    [(True, action.inputMember.type),
                     (False, action.outputMember.type)]:
                self._push_namespace(struct.namespace)
                self._write_class_declaration(struct,
                                              fGenerateSets, fConstructFromStruct = False,
                                              fInline = self._fInline,
                                              rootElement = self._module.element_value((action.namespace, action.name)))
                self._pop_namespace()

        # Generate the action method declarations.
        for action_loc in self._module.action_locations():
            self._push_namespace(action_loc.action.namespace)

            # Generate accessors for macros URIs.
            if action_loc.http_method == "POST":
                self._write_uri_accessor_macro_documentation(action_loc)
                self._write_uri_accessor_macro(action_loc)
                self._write_newline()

            self._write_action_method_documentation(action_loc)
            self._write_action_method(action_loc)
            self._write_newline()
            self._pop_namespace()

        # Make sure the previous namespace is closed.
        self._pop_namespace()

        # Should be in the HDK namespace scope
        assert(self._namespaceStack[-1].namespace == "HDK")

        # Close HDK namespace
        self._pop_namespace(True)

    #
    # .cpp code generator
    #
    def _generate_cpp(self):

        self._write_header_cpp(self._filename('.cpp'))

        self._write('''\

// Local header.
#include "%s"

using namespace HDK;
''' % (self._filename('.h')))

        if not self._fInline:
            # Generate class implementations for each structure type.
            for type in self._structTypes:
                element = None
                if self._fDOM:
                    element = self._module.element_value((type.namespace, type.name))

                self._write_newline()
                self._write_class_definition(type, fDOM = self._fDOM, rootElement = element)

            # Generate class implementations for each action input and output structure.
            for action in self._module.actions:
                for (fGenerateSets, member) in \
                        [(True, action.inputMember),
                         (False, action.outputMember)]:
                    self._write_newline()
                    self._write_class_definition(member.type,
                                                 fGenerateSets,
                                                 fConstructFromStruct = False,
                                                 rootElement = self._module.element_value((action.namespace, action.name)))

        # Write the action method definitions.
        if self._module.action_locations():
            # Write HDK global init/cleanup function definitions.
            self._write_newline()
            self._write_init_function(fImpl = True, withNS = True)
            self._write_newline()
            self._write_uninit_function(fImpl = True, withNS = True)

            for action_loc in self._module.action_locations():
                self._write_newline()
                self._write_action_method(action_loc, fImpl = True, withNS = True, fNoDefaultArgs = True)
Пример #7
0
class CppClientGenerator:

    # Class initializer
    def __init__(self, model, baseName, moduleName, actionLocation, fDOM, fInline):

        self._baseName = baseName
        self._fDOM = fDOM
        self._fInline = fInline

        # Generator state
        self._out = None
        self._namespaceStack = []

        # The underlying module.
        self._module = HDKModule(model, moduleName, actionLocation, fDOM)

        # Type information
        structTypes = {}
        arrayTypes = {}
        enumTypes = {}
        for type in model.all_types(user_types = fDOM, state_types = False):
            if type.isArray:
                arrayTypes[type.uri] = type
            elif type.isStruct:
                structTypes[type.uri] = type
            elif type.isEnum:
                enumTypes[type.uri] = type

        self._structTypes = sorted(structTypes.values(), key = lambda x:x.uri)
        self._arrayTypes = sorted(arrayTypes.values(), key = lambda x:x.uri)
        self._enumTypes = sorted(enumTypes.values(), key = lambda x:x.uri)


    #
    # Generate code
    #

    def generate(self, dir, fhReport):

        # Generate c++ client code
        for file, fn in [(os.path.join(dir, self._filename('.h')), self._generate_h),
                         (os.path.join(dir, self._filename('.cpp')), self._generate_cpp)]:

            if fhReport is not None:
                print >>fhReport, 'Generating "%s" ...' % file

            self._out = open(file, "w")
            try:
                fn()
            except:
                raise
            finally:
                self._out.close()

    #
    # Helpers
    #

    # Is the given type a "blob"
    @staticmethod
    def is_blob_type(type):

        return type.name == "blob"

    @staticmethod
    def is_bool_type(type):

        return type.name == "bool"

    @staticmethod
    def is_int_type(type):

        return type.name == "int"

    @staticmethod
    def is_long_type(type):

        return type.name == "long"

    @staticmethod
    def is_datetime_type(type):

        return type.name == "datetime"

    @staticmethod
    def is_ipaddress_type(type):

        return type.name == "IPAddress"

    @staticmethod
    def is_macaddress_type(type):

        return type.name == "MACAddress"

    @staticmethod
    def is_string_type(type):

        return type.name == "string"

    @staticmethod
    def is_uuid_type(type):

        return type.name == "UUID"

    @staticmethod
    def is_hnap_result_type(type):

        return type.name == "Result"

    # Does the given type support the "blank" value
    @staticmethod
    def supports_blank(type):

        return CppClientGenerator.is_ipaddress_type(type) or CppClientGenerator.is_macaddress_type(type)

    # Retrieve the HDK_XML_ function to use for the given type and access
    def _hdk_struct_accessor(self, type, access):

        if CppClientGenerator.is_datetime_type(type):
            method = "HDK_XML_" + access + "_DateTime"
        elif CppClientGenerator.is_macaddress_type(type) or \
                CppClientGenerator.is_ipaddress_type(type):
            method = "HDK_XML_" + access + "_" + type.name
        elif CppClientGenerator.is_uuid_type(type):
            method = "HDK_XML_" + access + "_UUID"
        elif type.isEnum:
            method = self._module.enum_accessor(type, access)
        elif type.isStruct:
            method = "HDK_XML_" + access + "_Struct"
        else:
            method = "HDK_XML_" + access + "_" + type.name.capitalize()

        return method

    # Retrieve the HDK type cast for the given type
    def _hdk_type_cast(self, type):

        if type.isEnum:
            return "(" + self._module.enum_enum(type) + ")"
        elif CppClientGenerator.is_bool_type(type):
            return "(int)"
        else:
            return ""

    #
    # Symbol helper methods
    #

    # Generated code filename
    def _filename(self, suffix):

        return self._baseName + suffix

    # c++ namespace
    def _namespace(self, uri):

        ns = re.sub("^\w+:/+(www\.)?", "", uri)
        ns = re.sub("\.com", "", ns)
        ns = re.sub("/+HNAPExt", "", ns)

        ns = re.sub("/+$", "", ns)
        if ns:
            ns = ns[0].upper() + ns[1:]

        return re.sub("[^\w]", "_", ns)

    # c++ class name
    def _class_class(self, type):

        assert(type.isStruct)

        if type.isArray:
            match = re.match("ArrayOf(.*)", type.name);
            return makeGlobalSymbol("", "", match.group(1) + "Array")
        else:
            return makeGlobalSymbol("", "", type.name + "Struct")

    def _class_class_iter(self, type):

        return self._class_class(type) + "Iter"

    # c++ class constructor
    def _class_constructor(self, type):

        assert(type.isStruct)
        return self._class_class(type)

    # c++ class set property method
    def _class_set_property_method(self, member):

        return "set_" + member.name

    # c++ class get property method
    def _class_get_property_method(self, member):

        return "get_" + member.name

    # c++ class from file method
    def _class_from_file_method(self):

        return "FromFile"

    # c++ class to file method
    def _class_to_file_method(self):

        return "ToFile"

    def _type_type(self, type, withNS = False):

        typeName = None

        if type.isBuiltin:
            if CppClientGenerator.is_bool_type(type):
               typeName = "bool"
            elif CppClientGenerator.is_int_type(type):
                typeName = "HDK_XML_Int"
            elif CppClientGenerator.is_long_type(type):
                typeName = "HDK_XML_Long"
            elif CppClientGenerator.is_string_type(type):
                typeName = "const char*"
            elif CppClientGenerator.is_datetime_type(type):
                typeName = "time_t"
            elif CppClientGenerator.is_ipaddress_type(type):
                typeName = "IPv4Address"
            elif CppClientGenerator.is_macaddress_type(type):
                typeName = "MACAddress"
            elif CppClientGenerator.is_blob_type(type):
                typeName = "Blob"
            elif CppClientGenerator.is_uuid_type(type):
                typeName = "UUID"
            elif CppClientGenerator.is_hnap_result_type(type):
                typeName = type.name
                if fWithNamespace and self._current_open_namespace() != type.namespace:
                    typeName = self._namespace(type.namespace) + "::" + typeName
            else:
                print>>sys.stderr, "Unhandled builtin type %s" % (type.name)
                typeName = type.name

        elif type.isStruct:
            typeName = self._class_class(type)
            namespace = self._namespace(type.namespace)

            if namespace and withNS and self._current_open_namespace() != type.namespace:
                typeName = namespace + "::" + typeName

        elif type.isEnum:
            typeName = self._enum_enum(type)
            if withNS and self._current_open_namespace() != type.namespace:
                typeName = self._namespace(type.namespace) + "::" + typeName
            typeName = "enum " + typeName

        return typeName

    # c++ type cast
    def _type_cast(self, type):

        if type.isEnum:
            return "(" + self._type_type(type, withNS = True)  + ")"
        elif CppClientGenerator.is_bool_type(type):
            return "(bool)"
        else:
            return ""

    # c++ enum type
    def _enum_enum(self, type):

        return makeGlobalSymbol("", "", type.name)

    # c++ enum value
    def _enum_value(self, type, value, bIsUnknown = False):

        if bIsUnknown:
            return makeGlobalSymbol(type.name, "", "Unknown")
        else:
            return makeGlobalSymbol(type.name, "", value)

    # c++ action
    def _action_fn(self, action_loc, withNS = False):

        (http_method, action) = (action_loc.http_method, action_loc.action)

        fn = action.name
        if withNS:
            fn = self._namespace(action.namespace) + "::" + fn

        # Non-post action?
        if http_method != "POST":
            suffix = "_" + http_method
        else:
            suffix = ""

        return fn + suffix

    #
    # Output helper methods
    #

    # Output
    def _write(self, s):

        self._out.write(s)

    def _write_newline(self):

        self._out.write('''
''')

    def _write_with_indent(self, s, cIndent, bIndentCPreprocessor = False):

        indent = cIndent * ' '
        for line in s.splitlines(True):
            if line[0] == '#' and not bIndentCPreprocessor:
                self._write(line)
            elif re.search("^\s*$", line):
                # Don't write unnecessary whitespace
                self._write(line)
            else:
                self._write(indent + line)

    def _write_with_ns_indent(self, s, extraIndent = 0):

        self._write_with_indent(s, self._namespace_indent() + extraIndent)

    def _write_copyright(self):

        self._write('''\
/*
 * Copyright (c) 2008-2010 Cisco Systems, Inc. All rights reserved.
 *
 * Cisco Systems, Inc. retains all right, title and interest (including all
 * intellectual property rights) in and to this computer program, which is
 * protected by applicable intellectual property laws.  Unless you have obtained
 * a separate written license from Cisco Systems, Inc., you are not authorized
 * to utilize all or a part of this computer program for any purpose (including
 * reproduction, distribution, modification, and compilation into object code),
 * and you must immediately destroy or return to Cisco Systems, Inc. all copies
 * of this computer program.  If you are licensed by Cisco Systems, Inc., your
 * rights to utilize this computer program are limited by the terms of that
 * license.  To obtain a license, please contact Cisco Systems, Inc.
 *
 * This computer program contains trade secrets owned by Cisco Systems, Inc.
 * and, unless unauthorized by Cisco Systems, Inc. in writing, you agree to
 * maintain the confidentiality of this computer program and related information
 * and to not disclose this computer program and related information to any
 * other person or entity.
 *
 * THIS COMPUTER PROGRAM IS PROVIDED AS IS WITHOUT ANY WARRANTIES, AND CISCO
 * SYSTEMS, INC. EXPRESSLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * INCLUDING THE WARRANTIES OF MERCHANTIBILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, TITLE, AND NONINFRINGEMENT.
 */
''')

    # Write .cpp file header
    def _write_header_cpp(self, fileName = ""):

        self._write_copyright()

        self._write('''\

// %s - [Generated by hdkcli_cpp]
''' % (fileName))

    # Write .h file header
    def _write_header_h(self, fileName = ""):

        self._write_copyright()
        self._write('''\

#pragma once

// %s - [Generated by hdkcli_cpp]
''' % (fileName))

    # Write .h file footer
    def _write_footer_h(self):

        # Namespace stack should be empty now.
        assert(not self._namespaceStack)

    # Write the opening of a namespace
    def _write_open_namespace(self, uri, cIndent):

        indent = ' ' * cIndent
        namespace = self._namespace(uri)

        self._write('''\

%snamespace %s
%s{
''' % (indent, namespace,
       indent))

    # Write the closing of a namespace
    def _write_close_namespace(self, uri, cIndent):

        indent = ' ' * cIndent
        namespace = self._namespace(uri)

        self._write('''\
%s} // namespace %s
''' % (indent, namespace))

    # Generate a class forward declaration
    def _write_class_forward_declaration(self, type):

        self._write_with_ns_indent('''\
class %s;
''' % (self._class_class(type)))

    # Generate an enum definition
    def _write_enum(self, type, extraIndent = 0):

        assert(type.isEnum)

        indent = self._namespace_indent() + extraIndent

        self._write_doxygen_section_with_ns_indent_begin(extraIndent)
        self._write_doxygen_string_with_ns_indent('''\
\\enum %s
     <a>%s</a>
''' % (self._enum_enum(type),
       type.uri), extraIndent)
        self._write_doxygen_lines_with_ns_indent(type.doctext, extraIndent)
        self._write_doxygen_section_with_ns_indent_end(extraIndent)

        self._write_with_indent('''\
enum %s
{
    %s = %s /*<! Unknown value */,
''' % (self._enum_enum(type),
       self._enum_value(type, None, True), self._module.enum_value(type, None, True)), indent)

        for value in type.enumValues[0:-1]:
            self._write_with_indent('''\
    %s = %s /*!< %s */,
''' % (self._enum_value(type, value), self._module.enum_value(type, value), value), indent)

        if type.enumValues:
            value = type.enumValues[-1]
            self._write_with_indent('''\
    %s = %s /*!< %s */
''' % (self._enum_value(type, value), self._module.enum_value(type, value), value), indent)
        self._write_with_indent('''\
}; // enum %s

''' % (self._enum_enum(type)), indent)

    #
    # Doxygen-formatted output helpers
    #

    # Doxygen comment prefix.
    _doxygenPrefix = "///"

    def _write_doxygen_line_with_ns_indent(self, line, extraIndent = 0):

        doxygenFormattedLine = CppClientGenerator._doxygenPrefix
        if line and not re.search("^\s$", line):
            doxygenFormattedLine += " " + line

        if not doxygenFormattedLine.endswith("\n"):
            doxygenFormattedLine += "\n"

        self._write_with_indent(doxygenFormattedLine, extraIndent + self._namespace_indent())

    def _write_doxygen_lines_with_ns_indent(self, lines, extraIndent = 0):

        for line in lines:
            self._write_doxygen_line_with_ns_indent(line, extraIndent)

    def _write_doxygen_string_with_ns_indent(self, s, extraIndent = 0):

        for line in s.splitlines(True):
            self._write_doxygen_line_with_ns_indent(line, extraIndent)

    def _write_doxygen_section_with_ns_indent_begin(self, extraIndent = 0):

        self._write_doxygen_line_with_ns_indent(None, extraIndent)

    def _write_doxygen_section_with_ns_indent_end(self, extraIndent = 0):

        self._write_doxygen_line_with_ns_indent(None, extraIndent)

    def _write_doxygen_section_with_ns_indent(self, s, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent_begin(extraIndent)

        self._write_doxygen_string_with_ns_indent(s, extraIndent)

        self._write_doxygen_section_with_ns_indent_end(extraIndent)

    #
    # Namespace scoping
    #

    # Update the current namespace (closing the previous one, if needed)
    def _push_namespace(self, ns):

        if self._namespaceStack:
            if self._namespaceStack[-1].namespace == ns:
                if self._namespaceStack[-1].needs_closing:
                    self._namespaceStack[-1].increment()
                    return
                #else: ns::ns. strange...
            else:
                if self._namespaceStack[-1].needs_closing:
                    self._write_close_namespace(self._namespaceStack[-1].namespace, (len(self._namespaceStack) - 1) * 4)
                    self._namespaceStack.pop()

        self._namespaceStack.append(CppNamespaceScope(ns))
        self._write_open_namespace(ns, (len(self._namespaceStack) - 1) * 4)

    def _pop_namespace(self, bAlwaysClose = False):

        if self._namespaceStack[-1].needs_closing:
            self._write_close_namespace(self._namespaceStack[-1].namespace, (len(self._namespaceStack) - 1) * 4)
            self._namespaceStack.pop()

        if self._namespaceStack:
            self._namespaceStack[-1].decrement()

            if bAlwaysClose and self._namespaceStack[-1].needs_closing:
                self._write_close_namespace(self._namespaceStack[-1].namespace, (len(self._namespaceStack) - 1) * 4)
                self._namespaceStack.pop()

    # Namespace indentation
    def _namespace_indent(self):

        return len(self._namespaceStack) * 4

    # Current open namespace
    def _current_open_namespace(self):

        if self._namespaceStack:
            return self._namespaceStack[-1].namespace
        else:
            return None

    #
    # Helpers for c++ class wrappers of HDK_XML_Structs
    #

    # Generate a default constructor
    def _write_class_default_constructor(self, struct, element = None, fImpl = False, classScope = None, extraIndent = 0):

        if element is None:
            element = "HDK_XML_BuiltinElement_Unknown"

        method = self._class_constructor(struct)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
%s() throw()''' % (method), extraIndent)

        if fImpl:
            self._write(''' :''')
            self._write_with_ns_indent('''
    Struct(%s)
{
}
''' % (element), extraIndent)
        else: # not fImpl
            self._write(''';
''')

    # Generate a constructor with HDK_XML_Struct
    def _write_class_hdk_constructor(self, struct, fImpl = False, classScope = None, extraIndent = 0):

        method = self._class_constructor(struct)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
%s(HDK_XML_Struct* phdkstruct) throw()''' % (method), extraIndent)

        if fImpl:
            self._write(''' :''')
            self._write_with_ns_indent('''
    Struct(phdkstruct)
{
}
''', extraIndent)
        else: # not fImpl
            self._write(''';
''')

    # Generate a get_ accessor implementation.
    def _write_class_get_accessor(self, member, fImpl = False, classScope = None, extraIndent = 0):

        element = self._module.element_value((member.namespace, member.name))

        method = self._class_get_property_method(member)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
%s %s() const throw()''' % (self._type_type(member.type, withNS = True), method), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
''', extraIndent)
            if member.type.isStruct:
                self._write_with_ns_indent('''\
    return %s(GetStruct(), %s);
''' % (self._hdk_struct_accessor(member.type, "Get"), element), extraIndent)
            else:
                # Blob type is special-cased.
                if CppClientGenerator.is_blob_type(member.type):
                    self._write_with_ns_indent('''\
    unsigned int cbBlob = 0;
    char* pbBlob = %s(GetStruct(), %s, &cbBlob);
    return Blob(pbBlob, cbBlob);
''' % (self._hdk_struct_accessor(member.type, "Get"), element), extraIndent)
                else:
                    # Determine the default value
                    if member.type.isEnum:
                        defaultValue = self._module.enum_value(member.type, None, is_unknown = True)
                    elif CppClientGenerator.is_ipaddress_type(member.type):
                        defaultValue = "HDK::IPv4Address::Blank()"
                    elif CppClientGenerator.is_macaddress_type(member.type):
                        defaultValue = "HDK::MACAddress::Blank()"
                    else:
                        defaultValue = "0"

                    # Determine the cast for the type.
                    cast = self._type_cast(member.type)
                    if CppClientGenerator.is_bool_type(member.type):
                        # Avoid Microsoft compiler warning C4800
                        cast = "0 != "
                    self._write_with_ns_indent('''\
    return %s%s(GetStruct(), %s, %s);
''' % (cast, self._hdk_struct_accessor(member.type, "GetEx"), element, defaultValue), extraIndent)
            self._write_with_ns_indent('''\
}
''', extraIndent)

        else: # not fImpl
            self._write(''';
''')

    # Generate a set_ accessor implementation.  If classScope is present, the method will be scoped in the class.
    def _write_class_set_accessor(self, member, fImpl = False, classScope = None, extraIndent = 0):

        element = self._module.element_value((member.namespace, member.name))

        if member.type.isStruct or \
                CppClientGenerator.is_blob_type(member.type) or \
                CppClientGenerator.is_ipaddress_type(member.type) or \
                CppClientGenerator.is_macaddress_type(member.type):

            argType = "const " + self._type_type(member.type, withNS = True) + "&"
        else:
            argType = self._type_type(member.type, withNS = True)

        method = self._class_set_property_method(member)
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
void %s(%s value) throw()''' % (method, argType), extraIndent)
        if fImpl:
            self._write_with_ns_indent('''\

{
''', extraIndent)

            if CppClientGenerator.supports_blank(member.type):
                self._write_with_ns_indent('''\
    if (value.IsBlank())
    {
        (void)HDK_XML_Set_Blank(GetStruct(), %s);
        return;
    }
''' % (element), extraIndent)

            # Handle non-standard value sets (struct and blob types)
            if member.type.isStruct:
                self._write_with_ns_indent('''\
    (void)%s(GetStruct(), %s, value);
''' % (self._hdk_struct_accessor(member.type, "SetEx"), element), extraIndent)

            elif CppClientGenerator.is_blob_type(member.type):
                self._write_with_ns_indent('''\
    (void)%s(GetStruct(), %s, value.get_Data(), value.get_Size());
''' % (self._hdk_struct_accessor(member.type, "Set"), element), extraIndent)

            else:
                self._write_with_ns_indent('''\
    (void)%s(GetStruct(), %s, %svalue);
''' % (self._hdk_struct_accessor(member.type, "Set"), element, self._hdk_type_cast(member.type)), extraIndent)

            self._write_with_ns_indent('''\
}
''', extraIndent)

        else: # not fImpl
            self._write(''';
''')

    # Generate a FromFile method implementation.
    def _write_class_method_from_file(self, type, fImpl = False, classScope = None, extraIndent = 0):

        method = self._class_from_file_method()
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
bool %s(const char* pszFile) throw()''' % (method), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
    return HDK::Struct::DeserializeFromFile(%s(), pszFile);
}
''' % (self._module.dom_schema_fn(type)), extraIndent)
        else: # not fImpl
            self._write(''';
''')


    # Generate a ToFile method implementation.
    def _write_class_method_to_file(self, type, fImpl = False, classScope = None, extraIndent = 0):

        method = self._class_to_file_method()
        if classScope is not None:
            method = self._type_type(classScope, withNS = True) + "::" + method

        self._write_with_ns_indent('''\
bool %s(const char* pszFile) const throw()''' % (method), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
    return HDK::Struct::SerializeToFile(%s(), pszFile, 0);
}
''' % (self._module.dom_schema_fn(type)), extraIndent)
        else: # not fImpl
            self._write(''';
''')

    # Generate a c++ class declaration from an HDK_XML_Struct
    def _write_class_declaration(self, struct,
                                 fGenerateSets = True, fConstructFromStruct = True,
                                 fInline = False, rootElement = None, fDOM = False):

        self._write_doxygen_section_with_ns_indent_begin()
        self._write_doxygen_string_with_ns_indent('''\
\\brief %s
     <a>%s</a>
''' % (struct.name, struct.uri))
        self._write_doxygen_lines_with_ns_indent(struct.doctext)
        self._write_doxygen_section_with_ns_indent_end()

        self._write_with_ns_indent('''\
class %s : public Struct
{
''' % (self._class_class(struct)))

        # Generate the class constructors and destructor.
        self._write_with_ns_indent('''\
public:
    //
    // Constructors/Destructor.
    //
''')
        self._write_class_default_constructor(struct, element = rootElement, fImpl = fInline, extraIndent = 4)
        self._write_newline()

        if fConstructFromStruct:
            self._write_class_hdk_constructor(struct, fImpl = fInline, extraIndent = 4)
            self._write_newline()

        # Write the accessor methods.
        for member in struct.members:

            self._write_doxygen_section_with_ns_indent_begin(4)
            self._write_doxygen_string_with_ns_indent('''\
\\brief Get the %s value.
''' % (member.name), 4)
            if member.doctext:
                self._write_doxygen_line_with_ns_indent('''\
     \\retval %s
''' % (member.doctext[0]), 4)

                self._write_doxygen_lines_with_ns_indent(member.doctext[1:], 4)
            self._write_doxygen_section_with_ns_indent_end(4)

            self._write_class_get_accessor(member, fImpl = fInline, extraIndent = 4)
            self._write_newline()

            if fGenerateSets:
                self._write_doxygen_section_with_ns_indent_begin(4)
                self._write_doxygen_string_with_ns_indent('''\
\\brief Set the %s value.
''' % (member.name), 4)
                if member.doctext:
                    self._write_doxygen_string_with_ns_indent('''\
     \\arg %s
''' % (member.doctext[0]), 4)
                    self._write_doxygen_lines_with_ns_indent(member.doctext[1:], 4)
                self._write_doxygen_section_with_ns_indent_end(4)

                self._write_class_set_accessor(member, fImpl = fInline, extraIndent = 4)
                self._write_newline()

        if fDOM:
            self._write_doxygen_section_with_ns_indent_begin(4)
            self._write_doxygen_string_with_ns_indent('''\
\\brief Serialize to/from an XML file.
''', 4)
            self._write_doxygen_section_with_ns_indent_end(4)

            self._write_class_method_from_file(struct, fImpl = fInline, extraIndent = 4)
            self._write_class_method_to_file(struct, fImpl = fInline, extraIndent = 4)
            self._write_newline()

        self._write_with_ns_indent('''\
}; // class %s : public Struct

''' % self._class_class(struct))

    # Generate a wrapper for an array class using the array template
    def _write_array_template_typedef(self, type, includeClassDeclaration = True):

        assert(type.isArray)

        self._write_doxygen_section_with_ns_indent('''\
\\class %s
     Wrapper class for accessing arrays of %s values.
''' % (self._class_class(type),
       type.arrayType.name))

        # element represents the XML element of each array item
        element = self._module.element_value((type.members[0].namespace, type.members[0].name))

        if type.arrayType.isStruct:
            if includeClassDeclaration:
                self._write_with_ns_indent('''\
class %s; // forward declaration
''' % (self._class_class(type.arrayType)))
            self._write_with_ns_indent('''\
typedef HDK::StructArray<%s, %s> %s;
typedef HDK::StructArray<%s, %s>::StructArrayIter %sIter;

''' % (self._type_type(type.arrayType, withNS = True), element, self._class_class(type),
       self._type_type(type.arrayType, withNS = True), element, self._class_class(type)))
        elif type.arrayType.isEnum:
            self._write_with_ns_indent('''\
typedef HDK::EnumArray<%s, %s, %s> %s;
typedef HDK::EnumArray<%s, %s, %s>::EnumArrayIter %sIter;

''' % (self._type_type(type.arrayType, withNS = True), self._module.enum_type_value(type.arrayType), element, self._class_class(type),
       self._type_type(type.arrayType, withNS = True), self._module.enum_type_value(type.arrayType), element, self._class_class(type)))
        elif type.arrayType.name == "datetime":
            self._write_with_ns_indent('''\
typedef HDK::DateTimeArray<%s> %s;
typedef HDK::DateTimeArray<%s>::DateTimeArrayIter %sIter;

''' % (element, self._class_class(type),
       element, self._class_class(type)))
        else:
            templateClass = type.arrayType.name.capitalize() + "Array"
            self._write_with_ns_indent('''\
typedef HDK::%s<%s> %s;
typedef HDK::%s<%s>::%sIter %sIter;

''' % (templateClass, element, self._class_class(type),
       templateClass, element, self._class_class(type), templateClass))

    #
    # Generate a c++ class definition from an HDK_XML_Struct
    #
    def _write_class_definition(self, struct,
                                fGenerateSets = True, fConstructFromStruct = True,
                                rootElement = None,
                                fDOM = False):

        assert(struct.isStruct)

        # Constructors.
        self._write_class_default_constructor(struct, element = rootElement, fImpl = True, classScope = struct)

        if fConstructFromStruct:
            self._write_newline()
            self._write_class_hdk_constructor(struct, fImpl = True, classScope = struct)

        for member in struct.members:

            element = self._module.element_value((member.namespace, member.name))

            # Get accessor
            self._write_newline()
            self._write_class_get_accessor(member, fImpl = True, classScope = struct)

            # Set accessor
            if fGenerateSets:
                self._write_newline()
                self._write_class_set_accessor(member, fImpl = True, classScope = struct)

        if fDOM:
            self._write_newline()
            self._write_class_method_from_file(struct, fImpl = True, classScope = struct)

            self._write_newline()
            self._write_class_method_to_file(struct, fImpl = True, classScope = struct)

    # Helper to generate the action method documentation
    def _write_action_method_documentation(self, action_loc, extraIndent = 0):

        (http_method, http_location, action, noauth) = \
            (action_loc.http_method, action_loc.http_location, action_loc.action, action_loc.noauth)

        bInput = (len(action.inputMember.type.members) > 0)
        bOutput = (len(action.outputMember.type.members) > 1)

        self._write_doxygen_section_with_ns_indent_begin(extraIndent)
        self._write_doxygen_string_with_ns_indent('''\
\\brief Call the %s method on a given device.
    <a>%s</a>
    This method uses HTTP method %s and location '%s'
''' % (action.name,
       action.uri,
       http_method, http_location), extraIndent)
        if noauth:
            self._write_doxygen_string_with_ns_indent('''\
    \\note This method does NOT require HTTP Basic Authorization.

''', extraIndent)
        self._write_doxygen_lines_with_ns_indent(action.doctext)
        self._write_doxygen_line_with_ns_indent("\n")

        # Write out the potential result values.
        if action.resultEnum.enumValues:
            self._write_doxygen_line_with_ns_indent('''\
    Possible result values:
''', extraIndent)
            for enumValue in action.resultEnum.enumValues:
                self._write_doxygen_line_with_ns_indent('''\
         - #%s
''' % (self._enum_value(action.resultEnum, enumValue)), extraIndent)
        self._write_doxygen_string_with_ns_indent('''\
    \\arg pTarget The target on which to call this method.
''', extraIndent)
        if bInput:
            self._write_doxygen_string_with_ns_indent('''\
    \\arg input The input argument data to the %s HNAP method.
''' % (action.name), extraIndent)

        if bOutput:
            self._write_doxygen_line_with_ns_indent('''\
    \\arg output The output argument data from the %s HNAP method.
''' % (action.name), extraIndent)

        self._write_doxygen_string_with_ns_indent('''\
    \\arg[optional] result The HNAP result of the %s HNAP method.
    \\arg timeoutSecs An optional timeout, in seconds, to use for the HNAP call.
    \\retval The result of the HNAP method call.
''' % (action.name), extraIndent)
        self._write_doxygen_section_with_ns_indent_end(extraIndent)

    # Helper to generate the action method declarations
    def _write_action_method(self, action_loc, fImpl = False, withNS = False, fNoDefaultArgs = False, extraIndent = 0):

        action = action_loc.action

        bInput = (len(action.inputMember.type.members) > 0)
        bOutput = (len(action.outputMember.type.members) > 1)

        self._write_with_ns_indent('''\
HDK::ClientError %s
(
    HDK::ITarget* pTarget,
''' % (self._action_fn(action_loc, withNS = withNS)), extraIndent)
        if bInput:
            self._write_with_ns_indent('''\
    const %s & input,
''' % (self._type_type(action.inputMember.type, withNS = True)), extraIndent)
        if bOutput:
            self._write_with_ns_indent('''\
    %s & output,
''' % (self._type_type(action.outputMember.type, withNS = True)), extraIndent)

        if fNoDefaultArgs:
            presultDefault = "/* = NULL */"
            timeoutSecsDefault = "/* = 0 */"
        else:
            presultDefault = "= NULL"
            timeoutSecsDefault = "= 0"
        self._write_with_ns_indent('''\
    %s* presult %s,
    unsigned int timeoutSecs %s
) throw()''' % (self._type_type(action.outputMember.type.members[0].type, withNS = True), presultDefault,
                timeoutSecsDefault), extraIndent)

        if fImpl:
            self._write_with_ns_indent('''\

{
    if (!pTarget)
    {
        return ClientError_InvalidArg;
    }
''', extraIndent)
            if not bInput:
                self._write_with_ns_indent('''\
    %s input;
''' % (self._type_type(action.inputMember.type, withNS = True)), extraIndent)
            if not bOutput:
                self._write_with_ns_indent('''\

    %s output;
''' % (self._type_type(action.outputMember.type, withNS = True)), extraIndent)
            self._write_with_ns_indent('''\

    ClientError error = pTarget->Request(timeoutSecs,
                                         %s(),
                                         %s,
                                         input,
                                         &output);
''' % (self._module.module_fn(),
       self._module.action_value(action_loc)), extraIndent)
            self._write_with_ns_indent('''\

    const HDK_MOD_Method* pMethod = HDK_MOD_GetMethod(%s(), %s);
''' % (self._module.module_fn(), self._module.action_value(action_loc)), extraIndent)
            self._write_with_ns_indent('''\

    // Get the result value.
    %s result = output.%s();
    if (NULL != presult)
    {
        *presult = result;
    }

    // Determine if there was an HNAP-result, and whether it was an error or not.
    if ((ClientError_OK == error) && (HDK_XML_BuiltinElement_Unknown != pMethod->hnapResultElement))
    {
        if ((pMethod->hnapResultOK != (int)result) && (pMethod->hnapResultREBOOT != (int)result))
        {
            // An HNAP error response.
            error = HDK::ClientError_HnapMethod;
        }
    }

    return error;
}
''' % (self._type_type(action.outputMember.type.members[0].type, withNS = True), \
           self._class_get_property_method(action.outputMember.type.members[0])), extraIndent)

        else: # not fImpl
            self._write(''';
''')

    # Helper to write HDK Init documentation
    def _write_init_function_documentation(self, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent('''\
\\fn InitializeClient
     Initialize the HDK client library.  This should be called once per application instance.
     Each call to InitializeClient should be matched by a call to UninitializeClient.
     \\retval true if initialization was successful, false if not.
''', extraIndent)

    # Helper to write HDK Init method
    def _write_init_function(self, fImpl = False, withNS = False, extraIndent = 0):

        method = "InitializeClient"
        if withNS:
            method = "HDK::" + method

        self._write_with_ns_indent('''\
bool %s() throw()''' % (method), extraIndent)

        if fImpl:

            self._write_with_ns_indent('''\

{
    return !!HDK_CLI_Init();
}
''', extraIndent)

        else: # not fImpl

            self._write(''';
''')

    # Helper to write HDK uninit documentation
    def _write_uninit_function_documentation(self, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent('''\
\\fn UninitializeClient
     Cleanup the HDK client library.  This should be called if true was returned from InitializeClient
     When the caller is done using the HDK client library.
''', extraIndent)

    # Helper to write HDK uninit method
    def _write_uninit_function(self, fImpl = False, withNS = False, extraIndent = 0):

        method = "UninitializeClient"
        if withNS:
            method = "HDK::" + method

        self._write_with_ns_indent('''\
void %s() throw()''' % (method), extraIndent)

        if fImpl:

            self._write_with_ns_indent('''\

{
    HDK_CLI_Cleanup();
}
''', extraIndent)

        else: # not fImpl

            self._write(''';
''')

    # Helper to write URI accessor macro documentation
    def _write_uri_accessor_macro_documentation(self, action_loc, extraIndent = 0):

        self._write_doxygen_section_with_ns_indent('''\
\\brief SOAP method URI for action %s
''' % (action_loc.action.name), extraIndent)

    # Helper to write URI accessor macro
    def _write_uri_accessor_macro(self, action_loc, extraIndent = 0):

        self._write_with_ns_indent('''\
#define %s (HDK_MOD_GetMethod(%s(), %s)->pszSOAPAction)
''' % (makeGlobalSymbol("", action_loc.action.namespace, action_loc.action.name, nameSuffix = "_URI"),
       self._module.module_fn(), self._module.action_value(action_loc)), extraIndent)

    #
    # .h code generator
    #
    def _generate_h(self):

        self._write_header_h(self._filename('.h'))

        self._write('''\

// Non-generated client code.
#include "hdk_cli_cpp.h"

// Underlying schema module.
#include "%s"

''' % (self._module.filename('.h')))

        self._push_namespace("HDK")

        # Write HDK global functions.
        if self._module.action_locations():
            self._write_init_function_documentation()
            self._write_init_function()
            self._write_newline()
            self._write_uninit_function_documentation()
            self._write_uninit_function()
            self._write_newline()

        # Generate c++-style enums
        for typeEnum in self._enumTypes:

            self._push_namespace(typeEnum.namespace)
            self._write_enum(typeEnum)
            self._pop_namespace()

        # Declare array types.
        for typeArray in self._arrayTypes:

            self._push_namespace(typeArray.namespace)
            self._write_array_template_typedef(typeArray)
            self._pop_namespace()

        # Generate c++ wrapper classes for each struct
        for typeStruct in self._structTypes:

            # Generate any required forward declarations.
            for member in typeStruct.members:
                # The types are sorted alphabetically, so anything greater than this one has yet to be defined.
                forwDeclType = member.type
                if forwDeclType.isArray:
                    forwDeclType = forwDeclType.arrayType
                if forwDeclType.isStruct and not forwDeclType.isArray:
                   if forwDeclType.name > typeStruct.name:
                       self._push_namespace(forwDeclType.namespace)
                       self._write_class_forward_declaration(forwDeclType)
                       self._pop_namespace()

            self._push_namespace(typeStruct.namespace)

            element = None
            if self._fDOM:
                element = self._module.element_value((typeStruct.namespace, typeStruct.name))
            self._write_class_declaration(typeStruct,
                                          fDOM = self._fDOM,
                                          fInline = self._fInline,
                                          rootElement = element)
            self._pop_namespace()

        # Generate class implementations for each action input and output structure.
        for action in self._module.actions:

            for (fGenerateSets, struct) in \
                    [(True, action.inputMember.type),
                     (False, action.outputMember.type)]:
                self._push_namespace(struct.namespace)
                self._write_class_declaration(struct,
                                              fGenerateSets, fConstructFromStruct = False,
                                              fInline = self._fInline,
                                              rootElement = self._module.element_value((action.namespace, action.name)))
                self._pop_namespace()

        # Generate the action method declarations.
        for action_loc in self._module.action_locations():
            self._push_namespace(action_loc.action.namespace)

            # Generate accessors for macros URIs.
            if action_loc.http_method == "POST":
                self._write_uri_accessor_macro_documentation(action_loc)
                self._write_uri_accessor_macro(action_loc)
                self._write_newline()

            self._write_action_method_documentation(action_loc)
            self._write_action_method(action_loc)
            self._write_newline()
            self._pop_namespace()

        # Make sure the previous namespace is closed.
        self._pop_namespace()

        # Should be in the HDK namespace scope
        assert(self._namespaceStack[-1].namespace == "HDK")

        # Close HDK namespace
        self._pop_namespace(True)

    #
    # .cpp code generator
    #
    def _generate_cpp(self):

        self._write_header_cpp(self._filename('.cpp'))

        self._write('''\

// Local header.
#include "%s"

using namespace HDK;
''' % (self._filename('.h')))

        if not self._fInline:
            # Generate class implementations for each structure type.
            for type in self._structTypes:
                element = None
                if self._fDOM:
                    element = self._module.element_value((type.namespace, type.name))

                self._write_newline()
                self._write_class_definition(type, fDOM = self._fDOM, rootElement = element)

            # Generate class implementations for each action input and output structure.
            for action in self._module.actions:
                for (fGenerateSets, member) in \
                        [(True, action.inputMember),
                         (False, action.outputMember)]:
                    self._write_newline()
                    self._write_class_definition(member.type,
                                                 fGenerateSets,
                                                 fConstructFromStruct = False,
                                                 rootElement = self._module.element_value((action.namespace, action.name)))

        # Write the action method definitions.
        if self._module.action_locations():
            # Write HDK global init/cleanup function definitions.
            self._write_newline()
            self._write_init_function(fImpl = True, withNS = True)
            self._write_newline()
            self._write_uninit_function(fImpl = True, withNS = True)

            for action_loc in self._module.action_locations():
                self._write_newline()
                self._write_action_method(action_loc, fImpl = True, withNS = True, fNoDefaultArgs = True)
Пример #8
0
class ModuleGenerator:

    # Class initializer
    def __init__(self, model, baseName, noid, friendlyName,
                 actionLocation, bGenerateDOMSchemas,
                 bGenerateMethods, bClient, bADIReport):

        self._model = model

        # Options
        self._baseName = baseName
        self._noid = noid
        self._friendlyName = friendlyName
        self._bGenerateMethods = bGenerateMethods
        self._bADIReport = bADIReport

        # Generator state
        self._out = None

        # Module
        self._module = HDKModule(model, baseName, actionLocation, bGenerateDOMSchemas)

        # Generation options
        self._bGenerateActions = not bClient and self._module.actions
        self._bGenerateServices = not bClient and self._module.services
        self._bGenerateModule = self._module.actions or self._module.events
        self._bGenerateModuleDynamic = not bClient and self._bGenerateModule

    #
    # Generate code
    #

    def generate(self, dir, fhReport):

        for file, fn, bGenerate in \
                [(os.path.join(dir, self._module.filename('.h')), self._generate_h, not self._bGenerateMethods),
                 (os.path.join(dir, self._module.filename('.c')), self._generate_c, not self._bGenerateMethods),
                 (os.path.join(dir, self._module.filename('_methods.c')), self._generate_methods_c, self._bGenerateMethods),
                 (os.path.join(dir, self._module.filename('_adi.txt')), self._generate_adi_txt, self._bADIReport)]:

            if bGenerate:

                if fhReport is not None:
                    print >>fhReport, 'Generating "%s" ...' % file

                self._out = open(file, "w")
                try:
                    fn()
                except:
                    raise
                finally:
                    self._out.close()


    #
    # Output helper methods
    #

    # Output
    def _write(self, s):

        self._out.write(s)

    # Write .c file header
    def _write_header_c(self):

        self._write('''\
/*
 * Copyright (c) 2008-2010 Cisco Systems, Inc. All rights reserved.
 *
 * Cisco Systems, Inc. retains all right, title and interest (including all
 * intellectual property rights) in and to this computer program, which is
 * protected by applicable intellectual property laws.  Unless you have obtained
 * a separate written license from Cisco Systems, Inc., you are not authorized
 * to utilize all or a part of this computer program for any purpose (including
 * reproduction, distribution, modification, and compilation into object code),
 * and you must immediately destroy or return to Cisco Systems, Inc. all copies
 * of this computer program.  If you are licensed by Cisco Systems, Inc., your
 * rights to utilize this computer program are limited by the terms of that
 * license.  To obtain a license, please contact Cisco Systems, Inc.
 *
 * This computer program contains trade secrets owned by Cisco Systems, Inc.
 * and, unless unauthorized by Cisco Systems, Inc. in writing, you agree to
 * maintain the confidentiality of this computer program and related information
 * and to not disclose this computer program and related information to any
 * other person or entity.
 *
 * THIS COMPUTER PROGRAM IS PROVIDED AS IS WITHOUT ANY WARRANTIES, AND CISCO
 * SYSTEMS, INC. EXPRESSLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * INCLUDING THE WARRANTIES OF MERCHANTIBILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, TITLE, AND NONINFRINGEMENT.
 */
''')

    # Write .h file header
    def _write_header_h(self):

        self._write_header_c();
        self._write('''\

#ifndef %s
#define %s
''' % (self._module.header_sentinel('.h'), self._module.header_sentinel('.h')))

    # Write .h file footer
    def _write_footer_h(self):

        self._write('''\

#endif /* %s */
''' % (self._module.header_sentinel('.h')))

    # Write a section comment
    def _write_section(self, comment):

            self._write('''\


/*
 * %s
 */

''' % (comment))

    # Write enumeration definition
    def _write_enumeration(self, name, values, ix_start = 0, ix_delta = 1, value_symbols = {}):

        # Enum header
        self._write('''\
typedef enum _%s
{
''' % (name))

        # Write enum values
        ix_value = ix_start
        ix_value_last = ix_start + ix_delta * (len(values) - 1)
        sep = ","
        for value in values:

            # Does the value have a symbol?
            if value_symbols.has_key(value):
                str_value = value_symbols[value]
            else:
                str_value = str(ix_value)

            # Don't separate the last value
            if ix_value == ix_value_last:
                sep = ""

            # Write the enum value
            self._write('''\
    %s = %s%s
''' % (value, str_value, sep))

            ix_value += ix_delta

        # Enum footer
        self._write('''\
} %s;
''' % (name))

    # Write schema definition
    def _write_schema(self, schema_nodes_static, schema_static, nodes,
                      element_path_static = None, element_path = None):

        # Schema nodes header
        self._write('''\
static const HDK_XML_SchemaNode %s[] =
{
''' % (schema_nodes_static))

        # Write the schema nodes
        ix = 0
        for node in nodes:

            # Compute the parent index
            if node.parent:
                ix_parent = nodes.index(node.parent)
            else:
                ix_parent = 0

            # Compute the options
            options = []
            if node.is_optional:
                options.append("HDK_XML_SchemaNodeProperty_Optional")
            if node.is_unbounded:
                options.append("HDK_XML_SchemaNodeProperty_Unbounded")
            if node.is_any_element:
                options.append("HDK_XML_SchemaNodeProperty_AnyElement")
            if node.is_error:
                options.append("HDK_XML_SchemaNodeProperty_ErrorOutput")
            if node.is_csv:
                options.append("HDK_XML_SchemaNodeProperty_CSV")
            if options:
                options = " | ".join(options)
            else:
                options = "0"

            # Write the schema node
            self._write('''\
    /* %d */ { %d, %s, %s, %s },
''' % (ix, ix_parent,
       self._module.element_value(node.element()),
       self._module.type_value(node.type),
       options))
            ix += 1

        # Schema nodes footer
        self._write('''\
    HDK_XML_Schema_SchemaNodesEnd
};
''')

        # Schema struct element path
        if element_path_static and element_path:

            self._write('''\

static const HDK_XML_Element %s[] =
{
''' % (element_path_static))
            for element in element_path:
                self._write('''\
    %s,
''' % (self._module.element_value(element)))
            self._write('''\
    HDK_MOD_ElementPathEnd
};
''')

        # Schema
        self._write('''\

static const HDK_XML_Schema %s =
{
    s_namespaces,
    s_elements,
    %s,
    %s
};
''' % (schema_static,
       schema_nodes_static,
       "s_enumTypes" if self._module.schema.enums else "0"))

    # Write action definition
    def _write_action(self, action_loc):

        http_method, http_location, action, noauth = \
            (action_loc.http_method, action_loc.http_location, action_loc.action, action_loc.noauth)

        # Compute the SOAP action
        is_get = (http_method.lower() == "get")
        if is_get:
            soap_action = "0"
        else:
            soap_action = '"' + action.uri + '"'

        # Compute the action function name
        if self._bGenerateActions:
            action_fn = self._module.action_fn(action)
        else:
            action_fn = "0"

        # Compute the schema struct element path symbols
        if not is_get and self._module.schema.struct_nodes_envelope(action.inputMember, soap = True):
            input_element_path = self._module.schema_element_path_static(action, "_Input")
        else:
            input_element_path = "0"
        if self._module.schema.struct_nodes_envelope(action.outputMember, soap = True):
            output_element_path = self._module.schema_element_path_static(action, "_Output")
        else:
            output_element_path = "0"

        # Compute the method options
        options = []
        if noauth:
            options.append("HDK_MOD_MethodOption_NoBasicAuth")
        if is_get:
            options.append("HDK_MOD_MethodOption_NoInputStruct")
        if options:
            options = " | ".join(options)
        else:
            options = "0"

        # Compute the OK and REBOOT values
        result_member = action.outputMember.type.members[0]
        if "REBOOT" in action.resultEnum.enumValues:
            result_reboot = "REBOOT"
        else:
            result_reboot = "OK"

        # Write the method struct
        self._write('''\
    {
        "%s",
        "%s",
        %s,
        %s,
        &%s,
        &%s,
        %s,
        %s,
        %s,
        %s,
        %s,
        %s,
        %s
    },
''' % (http_method,
       http_location,
       soap_action,
       action_fn,
       self._module.schema_static(action, suffix = "_Input"),
       self._module.schema_static(action, suffix = "_Output"),
       input_element_path,
       output_element_path,
       options,
       self._module.element_value((result_member.namespace, result_member.name)),
       self._module.type_value(result_member.type),
       self._module.enum_value(result_member.type, "OK"),
       self._module.enum_value(result_member.type, result_reboot)))

    # Write event definition
    def _write_event(self, event):

        # Write the method struct
        self._write('''\
    {
        "%s",
        &%s
    },
''' % (event.uri,
       self._module.schema_static(event)))


    #
    # *.h code generator
    #

    def _generate_h(self):

        # File header
        self._write_header_h()

        # Includes
        self._write('''\

#include "hdk_mod.h"


/*
 * Macro to control public exports
 */

#ifdef __cplusplus
#  define %s_PREFIX extern "C"
#else
#  define %s_PREFIX extern
#endif
#ifdef HDK_MOD_STATIC
#  define %s %s_PREFIX
#else /* ndef HDK_MOD_STATIC */
#  ifdef _MSC_VER
#    ifdef %s
#      define %s %s_PREFIX __declspec(dllexport)
#    else
#      define %s %s_PREFIX __declspec(dllimport)
#    endif
#  else /* ndef _MSC_VER */
#    ifdef %s
#      define %s %s_PREFIX __attribute__ ((visibility("default")))
#    else
#      define %s %s_PREFIX
#    endif
#  endif /*def _MSC_VER */
#endif /* def HDK_MOD_STATIC */
''' % (self._module.export_macro(),
       self._module.export_macro(),
       self._module.export_macro(), self._module.export_macro(),
       self._module.build_macro(),
       self._module.export_macro(), self._module.export_macro(),
       self._module.export_macro(), self._module.export_macro(),
       self._module.build_macro(),
       self._module.export_macro(), self._module.export_macro(),
       self._module.export_macro(), self._module.export_macro()))

        # Element enumeration
        self._write_section("Elements")
        self._write_enumeration(self._module.element_enum(),
                                [ self._module.element_value(element) for element in self._module.schema.elements ])

        # Enumeration type definition
        if self._module.schema.enums:

            self._write_section("Enum types enumeration")
            self._write_enumeration(self._module.enum_type_enum(),
                                    [ self._module.enum_type_value(enum) for enum in self._module.schema.enums ],
                                    ix_start = -1, ix_delta = -1)

        # Enumeration declarations
        for enum in self._module.schema.enums:

            # Enumeration type definition
            self._write_section("Enumeration %s" % (enum.uri))
            value_unknown = self._module.enum_value(enum, None, is_unknown = True)
            values = [ value_unknown ]
            values.extend([ self._module.enum_value(enum, value) for value in enum.enumValues ])
            self._write_enumeration(self._module.enum_enum(enum), values, ix_start = -1,
                                    value_symbols = { value_unknown: "HDK_XML_Enum_Unknown" })

            # Enumeration accessor declarations
            self._write('''\

#define %s(pStruct, element, value) HDK_XML_Set_Enum(pStruct, element, %s, 0 ? %s : (value))
#define %s(pStruct, element, value) HDK_XML_Append_Enum(pStruct, element, %s, 0 ? %s : (value))
#define %s(pStruct, element) (%s*)HDK_XML_Get_Enum(pStruct, element, %s)
#define %s(pStruct, element, value) (%s)HDK_XML_GetEx_Enum(pStruct, element, %s, 0 ? %s : (value))
#define %s(pMember) (%s*)HDK_XML_GetMember_Enum(pMember, %s)
''' % (self._module.enum_accessor(enum, "Set"), self._module.enum_type_value(enum), self._module.enum_value(enum, enum.enumValues[0]),
       self._module.enum_accessor(enum, "Append"), self._module.enum_type_value(enum), self._module.enum_value(enum, enum.enumValues[0]),
       self._module.enum_accessor(enum, "Get"), self._module.enum_enum(enum), self._module.enum_type_value(enum),
       self._module.enum_accessor(enum, "GetEx"), self._module.enum_enum(enum), self._module.enum_type_value(enum), self._module.enum_value(enum, enum.enumValues[0]),
       self._module.enum_accessor(enum, "GetMember"), self._module.enum_enum(enum), self._module.enum_type_value(enum)))

        # Action enumeration
        if self._module.actions:
            self._write_section("Method enumeration")
            self._write_enumeration(self._module.action_enum(),
                                    [ self._module.action_value(action_loc) for action_loc in self._module.action_locations() ])

        # Action sentinels
        if self._bGenerateActions:
            self._write_section("Method sentinels")
            for action in self._module.actions:
                self._write('''\
#define %s
''' % (self._module.action_sentinel(action)))

        # Action declarations
        if self._bGenerateActions:
            self._write_section("Methods")
            for action in self._module.actions:
                self._write('''\
extern void %s(HDK_MOD_MethodContext* pMethodCtx, HDK_XML_Struct* pInput, HDK_XML_Struct* pOutput);
''' % (self._module.action_fn(action)))

        # Event enumeration
        if self._module.events:
            self._write_section("Event enumeration")
            self._write_enumeration(self._module.event_enum(),
                                    [ self._module.event_value(event) for event in self._module.events ])

        # DOM (struct) schema accessor declarations
        if self._module.schema.structs:
            self._write_section("DOM Schemas")
        for struct in self._module.schema.structs:
            self._write('''\
%s const HDK_XML_Schema* %s();
''' % (self._module.export_macro(), self._module.dom_schema_fn(struct)))

        # State declarations
        if self._module.states:

            self._write_section("ADI")
            self._write_enumeration(self._module.state_enum(),
                                    [ self._module.state_value(state) for state in self._module.states ],
                                    ix_start = 1)

            self._write_section("ADI sentinels")
            for state in self._module.states:

                # Determine get/set
                usages = {}
                for actionState in self._model.actionStates:
                    if state.uri in actionState.stateMembers:
                        stateMember = actionState.stateMembers[state.uri]
                        if stateMember.isGet:
                            usages["get"] = None
                        if stateMember.isSet:
                            usages["set"] = None

                for usage in sorted(usages.iterkeys()):
                    self._write('''\
#define %s
''' % (self._module.state_value_sentinel(state, usage)))

        # Module declaration
        if self._bGenerateModule:

            self._write_section("Module")
            self._write('''\
%s const HDK_MOD_Module* %s(void);
''' % (self._module.export_macro(), self._module.module_fn()))

            if self._bGenerateModuleDynamic:
                self._write('''\

/* Dynamic server module export */
%s const HDK_MOD_Module* HDK_SRV_Module(void);
''' % (self._module.export_macro()))

        # File footer
        self._write_footer_h()


    #
    # *.c code generator
    #

    def _generate_c(self):

        # File header
        self._write_header_c()
        self._write('''\

#include "%s"

#include <string.h>
''' % (self._module.filename('.h')))

        # Namespace table
        self._write_section("Namespaces")
        self._write('''\
static const HDK_XML_Namespace s_namespaces[] =
{
''')
        for namespace in self._module.schema.namespaces:
            self._write('''\
    /* %d */ "%s",
''' % (self._module.schema.namespace_index(namespace),
       namespace))
        self._write('''\
    HDK_XML_Schema_NamespacesEnd
};
''')

        # Elements table
        self._write_section("Elements")
        self._write('''\
static const HDK_XML_ElementNode s_elements[] =
{
''')
        for element in self._module.schema.elements:
            self._write('''\
    /* %s = %d */ { %d, "%s" },
''' % (self._module.element_value(element),
       self._module.schema.element_index(element),
       self._module.schema.namespace_index(element[0]),
       element[1]))
        self._write('''\
    HDK_XML_Schema_ElementsEnd
};
''')

        # Enumerations definitions
        for enum in self._module.schema.enums:

            # Enumeration string table
            self._write_section("Enumeration %s" % (enum.uri))
            self._write('''\
static const HDK_XML_EnumValue %s[] =
{
''' % (self._module.enum_type_strings_static(enum)))
            for value in enum.enumValues:
                self._write('''\
    "%s",
''' % (value))
            self._write('''\
    HDK_XML_Schema_EnumTypeValuesEnd
};
''')

        # Enum types array
        if self._module.schema.enums:

            self._write_section("Enumeration types array")
            self._write('''\
static const HDK_XML_EnumType s_enumTypes[] =
{
''')
            sep = ','
            for enum in self._module.schema.enums:
                if enum is self._module.schema.enums[-1]:
                    sep = ""
                self._write('''\
    %s%s
''' % (self._module.enum_type_strings_static(enum), sep))
            self._write('''\
};
''')

        # Action definitions
        for action in self._module.actions:

            self._write_section("Method %s" % (action.uri))

            # Input schema
            self._write_schema(self._module.schema_nodes_static(action, suffix = "_Input"),
                               self._module.schema_static(action, suffix = "_Input"),
                               self._module.schema.struct_nodes(action.inputMember, soap = True),
                               element_path_static = self._module.schema_element_path_static(action, suffix = "_Input"),
                               element_path = self._module.schema.struct_nodes_envelope(action.inputMember, soap = True))

            # Output schema
            self._write('''\

''')
            self._write_schema(self._module.schema_nodes_static(action, suffix = "_Output"),
                               self._module.schema_static(action, suffix = "_Output"),
                               self._module.schema.struct_nodes(action.outputMember, soap = True),
                               element_path_static = self._module.schema_element_path_static(action, suffix = "_Output"),
                               element_path = self._module.schema.struct_nodes_envelope(action.outputMember, soap = True))

        # Actions table
        if self._module.actions:

            self._write_section("Methods")

            # Actions table header
            self._write('''\
static const HDK_MOD_Method s_methods[] =
{
''')

            # Write each action node
            for action_loc in self._module.action_locations():
                self._write_action(action_loc)

            # Actions table footer
            self._write('''\
    HDK_MOD_MethodsEnd
};
''')

        # Event definitions
        for event in self._module.events:

            self._write_section("Event %s" % (event.uri))
            self._write_schema(self._module.schema_nodes_static(event),
                               self._module.schema_static(event),
                               self._module.schema.struct_nodes(event.member))

        # Events table
        if self._module.events:

            self._write_section("Events")

            # Events table header
            self._write('''\
static const HDK_MOD_Event s_events[] =
{
''')

            # Write each event node
            for event in self._module.events:
                self._write_event(event)

            # Events table footer
            self._write('''\
    HDK_MOD_EventsEnd
};
''')

        if self._bGenerateServices:

            # Services action tables
            self._write_section("Service Methods")
            for service in self._module.services:

                # Service actions header
                self._write('''\
static const HDK_MOD_Method* %s[] =
{
''' % (self._module.service_actions_name(service)))

                # Service actions
                for action_uri in service.actions:

                    action_loc = [a for a in self._module.action_locations() if a.action.uri == action_uri][0]
                    self._write('''\
    &s_methods[%s],
''' % (self._module.action_value(action_loc)))

                # Service actions footer
                self._write('''\
    0
};
''')

            # Services event tables
            self._write_section("Service Events")
            for service in self._module.services:

                # Service events header
                self._write('''\
static const HDK_MOD_Event* %s[] =
{
''' % (self._module.service_events_name(service)))

                # Service events
                for eventURI in service.events:

                    event = [e for e in self._module.events if e.uri == eventURI][0]
                    self._write('''\
    &s_events[%s],
''' % (self._module.event_value(event)))

                # Service events footer
                self._write('''\
    0
};
''')

            # Services table
            self._write_section("Services")

            # Services table header
            self._write('''\
static const HDK_MOD_Service s_services[] =
{
''')

            # Write each service node
            for service in self._module.services:
                self._write('''\
    {
        "%s",
        %s,
        %s
    },
''' % (service.uri,
       self._module.service_actions_name(service),
       self._module.service_events_name(service)))

            # Services table footer
            self._write('''\
    HDK_MOD_ServicesEnd
};
''')

        # State definitions
        if self._module.states:

            self._write_section("ADI")
            self._write_schema("s_schemaNodes_ADI", "s_schema_ADI",
                               self._module.schema.state_nodes())

        # Struct schema definitions
        for struct in self._module.schema.structs:
            self._write_section("Struct %s" % (struct.uri))
            self._write_schema(self._module.schema_nodes_static(struct),
                               self._module.schema_static(struct),
                               self._module.schema.struct_nodes(struct))
            self._write('''\

/* extern */ const HDK_XML_Schema* %s()
{
    return &%s;
}
''' % (self._module.dom_schema_fn(struct),
       self._module.schema_static(struct)))

        # Module definition
        if self._bGenerateModule:

            self._write_section("Module");

            # Network Object ID definition
            if self._noid is not None:
                self._write('''\
/* %s */
static const HDK_XML_UUID s_uuid_NOID =
{
    { %s }
};

''' % (self._noid, ", ".join(["0x%02x" % ord(byte) for byte in self._noid.bytes])))

            # Module definition and accessors
            self._write('''\
static const HDK_MOD_Module s_module =
{
    %s,
    %s,
    %s,
    %s,
    %s,
    %s
};

const HDK_MOD_Module* %s(void)
{
    return &s_module;
}
''' % (
    "&s_uuid_NOID" if self._noid else "0",
    '"' + self._friendlyName + '"' if self._friendlyName else "0",
    "s_services" if self._bGenerateServices else "0",
    "s_methods" if self._module.actions else "0",
    "s_events" if self._module.events else "0",
    "&s_schema_ADI" if self._module.states else "0",
    self._module.module_fn()
    ))

            if self._bGenerateModuleDynamic:
                self._write('''\

const HDK_MOD_Module* HDK_SRV_Module(void)
{
    return &s_module;
}
''')


    #
    # *_methods.c code generator
    #

    def _generate_methods_c(self):

        # File header
        self._write_header_c()
        self._write('''\

#include "%s"

#include "hdk_srv.h"


/* Helper method for HNAP results */
#define SetHNAPResult(pStruct, prefix, method, result) \\
    prefix##_Set_##method##Result(pStruct, prefix##_Element_##method##Result, prefix##_Enum_##method##Result_##result)
''' % (self._module.filename('.h')))

        # Action declarations
        for action in self._module.actions:

            self._write_section("Method %s" % (action.uri))
            self._write('''\
#ifdef %s

void %s(HDK_MOD_MethodContext* pMethodCtx, HDK_XML_Struct* pInput, HDK_XML_Struct* pOutput)
{
    /* Unused parameters */
    (void)pMethodCtx;
    (void)pInput;
    (void)pOutput;
}

#endif /* %s */
''' % (self._module.action_sentinel(action),
       self._module.action_fn(action),
       self._module.action_sentinel(action)))


    #
    # *_adi.txt report generator
    #

    def _generate_adi_txt(self):

        # ADI report title
        self._write('''\
======================================================================
ADI Report for the %s Module
======================================================================
''' % (self._baseName.upper()))

        # Output the ADI values used (show get/set)
        self._write('''\


======================================================================
ADI values
======================================================================
''')
        for state in self._model.referenced_states():

            # Determine get/set
            usages = {}
            for actionState in self._model.actionStates:
                if state.uri in actionState.stateMembers:
                    stateMember = actionState.stateMembers[state.uri]
                    if stateMember.isGet:
                        usages["get"] = None
                    if stateMember.isSet:
                        usages["set"] = None
            sUsages = ', '.join(sorted(usages.iterkeys()))

            # Get the base type
            type = state.type
            nArray = 0
            while type.isArray:
                nArray += 1
                type = type.arrayType
            sArray = "[]" * nArray

            # Get the type description
            sDesc = ""
            if type.isEnum:
                sDesc = "enum"
            elif type.isStruct:
                sDesc = "struct"

            # Get the type string
            if sDesc:
                sType = '%s%s (%s, "%s")' % (type.name, sArray, sDesc, type.namespace)
            else:
                sType = '%s%s' % (type.name, sArray)

            self._write('''\

%s

    Namespace: "%s"
         Name: "%s"
         Type: %s
        Usage: %s
''' % (self._module.state_value(state), state.namespace, state.name, sType, sUsages))

        # Output the ADI values used by action
        self._write('''\


======================================================================
ADI values by action
======================================================================
''')
        for actionState in self._model.actionStates:
            self._write('''\

%s

''' % (actionState.uri))
            for stateMember in sorted(actionState.stateMembers.itervalues()):

                # Determine usage
                usages = {}
                if stateMember.isGet:
                    usages["get"] = None
                if stateMember.isSet:
                    usages["set"] = None
                sUsages = ', '.join(sorted(usages.iterkeys()))

                self._write('''\
    [%s] %s
''' % (sUsages, self._module.state_value(stateMember.state)))