'This is a method that you can call from your implementation of
Base.gen_xml or component.  It allows modules to define a type
of component, and benefit from extensibility via Python
entry points and Jenkins Job Builder :ref:`Macros <macro>`.
:arg string component_type: the name of the component
(e.g., `builder`)
:arg YAMLParser parser: the global YAML Parser
:arg Element xml_parent: the parent XML element
:arg dict template_data: values that should be interpolated into
the component definition
See :py:class:`jenkins_jobs.modules.base.Base` for how to register
components of a module.
See the Publishers module for a simple example of how to use
this method.'

def dispatch(self, component_type, xml_parent, component, template_data={}):

    if (component_type not in self.modules_by_component_type):
        raise JenkinsJobsException("Unknown    component    type:    '{0}'.".format(component_type))
    entry_point = self.modules_by_component_type[component_type]
    component_list_type = entry_point.load().component_list_type
    if isinstance(component, dict):
        (name, component_data) = next(iter(component.items()))
        if template_data:
            try:
                component_data = deep_format(component_data, template_data, self.jjb_config.yamlparser['allow_empty_variables'])
            except Exception:
                logging.error("Failure    formatting    component    ('%s')    data    '%s'", name, component_data)
                raise
    else:
        name = component
        component_data = {}
    eps = ModuleRegistry.entry_points_cache.get(component_list_type)
    if (eps is None):
        module_eps = []
        mod = pkg_resources.EntryPoint('__all__', entry_point.module_name, dist=entry_point.dist)
        Mod = mod.load()
        func_eps = [Mod.__dict__.get(a) for a in dir(Mod) if isinstance(Mod.__dict__.get(a), types.FunctionType)]
        for func_ep in func_eps:
            try:
                name_line = func_ep.__doc__.split('\n')
                if (not name_line[0].startswith('yaml:')):
                    logger.debug(("Ignoring    '%s'    as    an    entry    point" % name_line))
                    continue
                ep_name = name_line[0].split('    ')[1]
            except (AttributeError, IndexError):
                logger.debug(("Not    including    func    '%s'    as    an    entry    point" % func_ep.__name__))
                continue
            module_eps.append(pkg_resources.EntryPoint(ep_name, entry_point.module_name, dist=entry_point.dist, attrs=(func_ep.__name__,)))
            logger.debug(("Adding    auto    EP    '%s=%s:%s'" % (ep_name, entry_point.module_name, func_ep.__name__)))
        module_eps.extend(list(pkg_resources.iter_entry_points(group='jenkins_jobs.{0}'.format(component_list_type))))
        eps = {}
        for module_ep in module_eps:
            if (module_ep.name in eps):
                raise JenkinsJobsException("Duplicate    entry    point    found    for    component    type:    '{0}',    '{0}',name:    '{1}'".format(component_type, name))
            eps[module_ep.name] = module_ep
        ModuleRegistry.entry_points_cache[component_list_type] = eps
        logger.debug('Cached    entry    point    group    %s    =    %s', component_list_type, eps)
    component = self.parser_data.get(component_type, {}).get(name)
    if component:
        if ((name in eps) and (name not in self.masked_warned)):
            self.masked_warned[name] = True
            logger.warning(("You    have    a    macro    ('%s')    defined    for    '%s'    component    type    that    is    masking    an    inbuilt    definition" % (name, component_type)))
        for b in component[component_list_type]:
            self.dispatch(component_type, xml_parent, b, component_data)
    elif (name in eps):
        func = eps[name].load()
        func(self, xml_parent, component_data)
    else:
        raise JenkinsJobsException("Unknown    entry    point    or    macro    '{0}'    for    component    type:    '{1}'.".format(name, component_type))
