U
    Ph=                     @  s   d dl mZ d dlZd dlZd dlmZ d dlmZmZ d dl	m
Z
mZmZ d dlmZmZ d dlmZmZ dgZG d	d dZdS )
    )annotationsN)Sequence)AnyIterator)ConfigComponentConfigExpression
ConfigItem)
ID_REF_KEY
ID_SEP_KEY)allow_missing_referencelook_up_optionReferenceResolverc                   @  sJ  e Zd ZdZdZeZeZe	
e de dZeZd?dddd	Zd
d ZddddZdddddZd@dddddddZdAddddddd Zdddd!d"d#Zed$dd%d&d'ZedBd$dd(d)d*d+Zeddd,d-d.d/Zedd0d1d2d3Zedd4dd5d6d7ZedCddd8d0d9d:d;ZedDddd<dd9d=d>ZdS )Er   a  
    Utility class to manage a set of ``ConfigItem`` and resolve the references between them.

    This class maintains a set of ``ConfigItem`` objects and their associated IDs.
    The IDs must be unique within this set. A string in ``ConfigItem``
    starting with ``@`` will be treated as a reference to other ``ConfigItem`` objects by ID.
    Since ``ConfigItem`` may have a nested dictionary or list structure,
    the reference string may also contain the separator ``::`` to refer to a substructure by
    key indexing for a dictionary or integer indexing for a list.

    In this class, resolving references is essentially substitution of the reference strings with the
    corresponding python objects. A typical workflow of resolving references is as follows:

        - Add multiple ``ConfigItem`` objects to the ``ReferenceResolver`` by ``add_item()``.
        - Call ``get_resolved_content()`` to automatically resolve the references. This is done (recursively) by:
            - Convert the items to objects, for those do not have references to other items.
                - If it is instantiable, instantiate it and cache the class instance in ``resolved_content``.
                - If it is an expression, evaluate it and save the value in ``resolved_content``.
            - Substitute the reference strings with the corresponding objects.

    Args:
        items: ``ConfigItem``s to resolve, this could be added later with ``add_item()``.

    Z__local_refsz
(?:\w*)(?:z\w*)*NzSequence[ConfigItem] | None)itemsc                 C  s&   |d kri ndd |D | _ i | _d S )Nc                 S  s   i | ]}|  |qS  )get_id).0ir   r   T/home/dell461/cl/sdc2/HISourceFinder-master-l/src/monai/bundle/reference_resolver.py
<dictcomp>>   s      z.ReferenceResolver.__init__.<locals>.<dictcomp>r   resolved_content)selfr   r   r   r   __init__<   s    zReferenceResolver.__init__c                 C  s   i | _ i | _dS )zQ
        Clear all the added `ConfigItem` and all the resolved content.

        Nr   r   r   r   r   resetA   s    zReferenceResolver.resetbool)returnc                 C  s
   t | jS )N)r   r   r   r   r   r   is_resolvedI   s    zReferenceResolver.is_resolvedr   None)itemr   c                 C  s$   |  }|| jkrdS || j|< dS )zk
        Add a ``ConfigItem`` to the resolver.

        Args:
            item: a ``ConfigItem``.

        N)r   r   )r   r   idr   r   r   add_itemL   s    
zReferenceResolver.add_itemFstrr   zConfigItem | None)r    resolvekwargsr   c                 K  s8   |  |}|r,|| jkr,| jf d|i| | j|S )a  
        Get the ``ConfigItem`` by id.

        If ``resolve=True``, the returned item will be resolved, that is,
        all the reference strings are substituted by the corresponding ``ConfigItem`` objects.

        Args:
            id: id of the expected config item.
            resolve: whether to resolve the item if it is not resolved, default to False.
            kwargs: keyword arguments to pass to ``_resolve_one_item()``.
                Currently support ``instantiate`` and ``eval_expr``. Both are defaulting to True.
        r    )normalize_idr   _resolve_one_itemr   get)r   r    r#   r$   r   r   r   get_itemY   s    
zReferenceResolver.get_itemzset[str] | Nonez#ConfigExpression | str | Any | None)r    waiting_listr$   r   c                 K  sf  |  |}|| jkr| j| S zt|| jd|ddd}W n4 tk
rp } ztd| d|W 5 d}~X Y nX t|ts|S |	 }|dkrt
 }|| | j D ]H\}}|| jkrt|tr||	 r|dd	r| n|| j|< q| j||d
 D ]}	|	|kr*td|	 d| d|	| jkrzt|	| jdd W nV tk
r } z6d|	 d}
| js|t|
|t|
 W Y qW 5 d}~X Y nX | jf |	|d| ||	 q| j||| jd}|j|d t|tr|dd	r| n|| j|< nJt|trR|dd	}|rF|j| j | jidn|| j|< n
|| j|< | j| S )aC  
        Resolve and return one ``ConfigItem`` of ``id``, cache the resolved result in ``resolved_content``.
        If it has unresolved references, recursively resolve the referring items first.

        Args:
            id: id name of ``ConfigItem`` to be resolved.
            waiting_list: set of ids pending to be resolved.
                It's used to detect circular references such as:
                `{"name": "A", "dep": "@B"}` and `{"name": "B", "dep": "@A"}`.
            kwargs: keyword arguments to pass to ``_resolve_one_item()``.
                Currently support ``instantiate``, ``eval_expr`` and ``default``.
                `instantiate` and `eval_expr` are defaulting to True, `default` is the target config item
                if the `id` is not in the config content, must be a `ConfigItem` object.

        Fdefault
no_default)print_all_optionsr*   zid='z&' is not found in the config resolver.N	eval_exprT)configr    zdetected circular references 'z
' for id='z' in the config content.)r,   zthe referring item `@z'` is not defined in the config content.)r    r)   )r.   r    refs)r.   instantiate)globals)r%   r   r   r   r'   
ValueErrorKeyError
isinstancer   
get_configsetaddr   is_import_statementevaluatefind_refs_in_configkeysr   warningswarnr&   discardupdate_config_with_refsupdate_configr   r0   _vars)r   r    r)   r$   r   errZitem_configtvdmsgZ
new_configrun_evalr   r   r   r&   k   sZ    


$

 


"

z#ReferenceResolver._resolve_one_item)r    r$   r   c                 K  s   | j f d|i|S )a  
        Get the resolved ``ConfigItem`` by id.

        Args:
            id: id name of the expected item.
            kwargs: keyword arguments to pass to ``_resolve_one_item()``.
                Currently support ``instantiate``, ``eval_expr`` and ``default``.
                `instantiate` and `eval_expr` are defaulting to True, `default` is the target config item
                if the `id` is not in the config content, must be a `ConfigItem` object.

        r    )r&   )r   r    r$   r   r   r   get_resolved_content   s    z&ReferenceResolver.get_resolved_contentz	str | int)r    r   c                 C  s   t |d| jS )z
        Normalize the id string to consistently use `cls.sep`.

        Args:
            id: id string to be normalized.
        #)r"   replacesep)clsr    r   r   r   r%      s    zReferenceResolver.normalize_idz	list[str])r    lastr   c                 C  sD   |s|  || jS |  || jd}d|dd |d gS )z
        Split the id string into a list of strings by `cls.sep`.

        Args:
            id: id string to be split.
            last: whether to split the rightmost part of the id. default is False (split all parts).
            N)r%   splitrK   rsplitjoin)rL   r    rM   resr   r   r   split_id   s    	zReferenceResolver.split_idzIterator[tuple[str, str, Any]])r    r.   r   c                 c  sT   t |tr| nt|D ]4\}}|dkr<| | j | n| }|||fV  qdS )z
        Iterate over the sub-configs of the input config, the output `sub_id` uses `cls.sep` to denote substructure.

        Args:
            id: id string of the current input config.
            config: input config to be iterated.
        rO   N)r4   dictr   	enumeraterK   )rL   r    r.   krD   sub_idr   r   r   iter_subconfigs   s    	" z!ReferenceResolver.iter_subconfigszdict[str, int])valuer   c                 C  sd   i }|  |}| j|}t|}|D ]6}|s8||kr(|t| jd }||dd ||< q(|S )z
        Match regular expression for the input string to find the references.
        The reference string starts with ``"@"``, like: ``"@XXX::YYY::ZZZ"``.

        Args:
            value: input value to match regular expression.

        Nr   rN   )r%   
id_matcherfindallr   is_expressionlenrefr'   )rL   r[   r/   resultvalue_is_exprr   r    r   r   r   match_refs_pattern   s    


z$ReferenceResolver.match_refs_patternrV   )r[   r/   r   c                 C  s   |  |}| j|}|jtdd t|}|D ]}|sB||kr2|t| jd }||krd| d}| jsvt	|t
| q2|r||| j d| d}q2||kr2|| }q2|S )a  
        Match regular expression for the input string to update content with the references.
        The reference part starts with ``"@"``, like: ``"@XXX::YYY::ZZZ"``.
        References dictionary must contain the referring IDs as keys.

        Args:
            value: input value to match regular expression.
            refs: all the referring components with ids as keys, default to `None`.

        T)keyreverseNzcan not find expected ID 'z' in the references.z['z'])r%   r\   r]   sortr_   r   r^   r`   r   r3   r<   r=   rJ   rA   )rL   r[   r/   ra   rb   r   Zref_idrF   r   r   r   update_refs_pattern   s$    



z%ReferenceResolver.update_refs_patternzdict[str, int] | None)r.   r    r/   r   c           	      C  s   |pi }t |tr@| j|d D ]\}}||d| ||< q"t |ttfsR|S | ||D ]<\}}}t	|st
|r||krd||< | |||}q^|S )a7  
        Recursively search all the content of input config item to get the ids of references.
        References mean: the IDs of other config items (``"@XXX"`` in this config item), or the
        sub-item in the config is `instantiable`, or the sub-item in the config is `expression`.
        For `dict` and `list`, recursively check the sub-items.

        Args:
            config: input config content to search.
            id: ID name for the input config item.
            refs: dict of the ID name and count of found references, default to `None`.

        )r[   r   rN   )r4   r"   rc   r   r'   listrV   rZ   r   is_instantiabler   r^   r:   )	rL   r.   r    r/   refs_count_rY   rD   r   r   r   r:   $  s    
z%ReferenceResolver.find_refs_in_configzdict | Nonec           
      C  s   |pi }t |tr| ||S t |ttfs0|S t| }| ||D ]l\}}}t|sdt	
|r|| }	t|r|	dkrqFn| |||}	t |tr|||	in||	 qF|S )aD  
        With all the references in ``refs``, update the input config content with references
        and return the new config.

        Args:
            config: input config content to update.
            id: ID name for the input config.
            refs: all the referring content with ids, default to `None`.

        N)r4   r"   rg   rh   rV   typerZ   r   ri   r   r^   r?   updateappend)
rL   r.   r    r/   rj   retidxrY   rD   updatedr   r   r   r?   >  s    

$z)ReferenceResolver.update_config_with_refs)N)F)N)F)N)N)__name__
__module____qualname____doc__rA   r
   rK   r	   r`   recompiler\   r   r   r   r   r!   r(   r&   rH   classmethodr%   rU   rZ   rc   rg   r:   r?   r   r   r   r   r      s8    J	%)
__future__r   rw   r<   collections.abcr   typingr   r   monai.bundle.config_itemr   r   r   monai.bundle.utilsr	   r
   monai.utilsr   r   __all__r   r   r   r   r   <module>   s   