<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def add_resource_factory(self, factory_callback: factory_callback_type, types: Union[type, Sequence[Type]], name: str = 'default', context_attr: str = None) -> None: """ Add a resource factory to this context. This will cause a ``resource_added`` event to be dispatched. A resource factory is a callable that generates a "contextual" resource when it is requested by either using any of the methods :meth:`get_resource`, :meth:`require_resource` or :meth:`request_resource` or its context attribute is accessed. When a new resource is created in this manner, it is always bound to the context through it was requested, regardless of where in the chain the factory itself was added to. :param factory_callback: a (non-coroutine) callable that takes a context instance as argument and returns the created resource object :param types: one or more types to register the generated resource as on the target context :param name: name of the resource that will be created in the target context :param context_attr: name of the context attribute the created resource will be accessible as :raises asphalt.core.context.ResourceConflict: if there is an existing resource factory for the given type/name combinations or the given context variable """

        assert check_argument_types()
        self._check_closed()
        if not resource_name_re.fullmatch(name):
            raise ValueError('"name" must be a nonempty string consisting only of alphanumeric '
                             'characters and underscores')
        if iscoroutinefunction(factory_callback):
            raise TypeError('"factory_callback" must not be a coroutine function')
        if not types:
            raise ValueError('"types" must not be empty')

        if isinstance(types, type):
            resource_types = (types,)  # type: Tuple[type, ...]
        else:
            resource_types = tuple(types)

        # Check for a conflicting context attribute
        if context_attr in self._resource_factories_by_context_attr:
            raise ResourceConflict(
                'this context already contains a resource factory for the context attribute {!r}'.
                format(context_attr))

        # Check for conflicts with existing resource factories
        for type_ in resource_types:
            if (type_, name) in self._resource_factories:
                raise ResourceConflict('this context already contains a resource factory for the '
                                       'type {}'.format(qualified_name(type_)))

        # Add the resource factory to the appropriate lookup tables
        resource = ResourceContainer(factory_callback, resource_types, name, context_attr, True)
        for type_ in resource_types:
            self._resource_factories[(type_, name)] = resource

        if context_attr:
            self._resource_factories_by_context_attr[context_attr] = resource

        # Notify listeners that a new resource has been made available
        self.resource_added.dispatch(resource_types, name, True)