1# Copyright 2014-2017 Insight Software Consortium.
2# Copyright 2004-2009 Roman Yakovenko.
3# Distributed under the Boost Software License, Version 1.0.
4# See http://www.boost.org/LICENSE_1_0.txt
5
6"""Defines :class:`scopedef_t` class"""
7
8import timeit
9try:
10    from collections.abc import Callable
11except ImportError:
12    from collections import Callable
13
14from . import algorithm
15from . import templates
16from . import declaration
17from . import mdecl_wrapper
18from . import byte_info
19from . import elaborated_info
20from . import runtime_errors
21from .. import utils
22
23
24class matcher(object):
25
26    """Class-namespace, contains implementation of a few "find" algorithms"""
27
28    @staticmethod
29    def find(decl_matcher, decls, recursive=True):
30        """
31        Returns a list of declarations that match `decl_matcher` defined
32        criteria or None
33
34        :param decl_matcher: Python callable object, that takes one argument -
35            reference to a declaration
36        :param decls: the search scope, :class:declaration_t object or
37            :class:declaration_t objects list t
38        :param recursive: boolean, if True, the method will run `decl_matcher`
39            on the internal declarations too
40        """
41
42        where = []
43        if isinstance(decls, list):
44            where.extend(decls)
45        else:
46            where.append(decls)
47        if recursive:
48            where = make_flatten(where)
49        return list(filter(decl_matcher, where))
50
51    @staticmethod
52    def find_single(decl_matcher, decls, recursive=True):
53        """
54        Returns a reference to the declaration, that match `decl_matcher`
55        defined criteria.
56
57        if a unique declaration could not be found the method will return None.
58
59        :param decl_matcher: Python callable object, that takes one argument -
60            reference to a declaration
61        :param decls: the search scope, :class:declaration_t object or
62            :class:declaration_t objects list t
63        :param recursive: boolean, if True, the method will run `decl_matcher`
64            on the internal declarations too
65        """
66        answer = matcher.find(decl_matcher, decls, recursive)
67        if len(answer) == 1:
68            return answer[0]
69
70    @staticmethod
71    def get_single(decl_matcher, decls, recursive=True):
72        """
73        Returns a reference to declaration, that match `decl_matcher` defined
74        criteria.
75
76        If a unique declaration could not be found, an appropriate exception
77        will be raised.
78
79        :param decl_matcher: Python callable object, that takes one argument -
80            reference to a declaration
81        :param decls: the search scope, :class:declaration_t object or
82            :class:declaration_t objects list t
83        :param recursive: boolean, if True, the method will run `decl_matcher`
84            on the internal declarations too
85        """
86        answer = matcher.find(decl_matcher, decls, recursive)
87        if len(answer) == 1:
88            return answer[0]
89        elif not answer:
90            raise runtime_errors.declaration_not_found_t(decl_matcher)
91        else:
92            raise runtime_errors.multiple_declarations_found_t(decl_matcher)
93
94
95class scopedef_t(declaration.declaration_t):
96
97    """
98    Base class for :class:`namespace_t` and :class:`class_t` classes.
99
100    This is the base class for all declaration classes that may have
101    children nodes. The children can be accessed via the
102    :attr:`scopedef_t.declarations` property.
103
104    Also this class provides "get/select/find" interface. Using this class you
105    can get instance or instances of internal declaration(s).
106
107    You can find declaration(s) using next criteria:
108
109        1. ``name`` - declaration name, could be full qualified name
110
111        2. ``header_dir`` - directory, to which belongs file, that the
112           declaration was declared in. ``header_dir`` should be absolute path.
113
114        3. ``header_file`` - file that the declaration was declared in.
115
116        4. ``function`` - user ( your ) custom criteria. The interesting
117           thing is that this function will be joined with
118           other arguments (criteria).
119
120        5. ``recursive`` - the search declaration range, if True will be search
121           in internal declarations too.
122
123    Every ""query"" API, takes name or function as the first argument.
124
125    .. code-block:: python
126
127        global_namespace.member_function("do_something)
128
129    the statement returns reference to member function named "do_something".
130    If there the function doesn't exist or more than one function exists,
131    an exception is raised.
132
133    If you want to query for many declarations, use other function(s):
134
135    .. code-block:: python
136
137        do_something = global_namespace.member_functions("do_something")
138
139    the statement returns :class:`mdecl_wrapper_t` instance. That object will
140    save you writing `for` loops. For more information see
141    :class:`the class <mdecl_wrapper_t>` documentation.
142    """
143
144    RECURSIVE_DEFAULT = True
145    ALLOW_EMPTY_MDECL_WRAPPER = False
146
147    # this class variable is used to prevent recursive imports
148    _impl_matchers = {}
149    # this class variable is used to prevent recursive imports
150    _impl_decl_types = {}
151    # this class variable is used to prevent recursive imports
152    _impl_all_decl_types = []
153
154    def __init__(self, name=''):
155        declaration.declaration_t.__init__(self, name)
156
157        self._optimized = False
158        self._type2decls = {}
159        self._type2name2decls = {}
160        self._type2decls_nr = {}
161        self._type2name2decls_nr = {}
162        self._all_decls = None
163        self._all_decls_not_recursive = []
164
165    @property
166    def _logger(self):
167        """reference to :attr:`pygccxml.utils.loggers.queries_engine` logger"""
168        return utils.loggers.queries_engine
169
170    def _get__cmp__scope_items(self):
171        """implementation details"""
172        raise NotImplementedError()
173
174    def _get__cmp__items(self):
175        """implementation details"""
176        items = []
177        if self._optimized:
178            # in this case we don't need to build class internal declarations
179            # list
180            items.append(self._all_decls_not_recursive.sort())
181        else:
182            items.append(self.declarations.sort())
183        items.extend(self._get__cmp__scope_items())
184        return items
185
186    def __eq__(self, other):
187        if not declaration.declaration_t.__eq__(self, other):
188            return False
189        return self.declarations[:].sort() == other.declarations[:].sort()
190
191    __hash__ = declaration.declaration_t.__hash__
192
193    def _get_declarations_impl(self):
194        raise NotImplementedError()
195
196    @property
197    def declarations(self):
198        """
199        List of children declarations.
200
201        Returns:
202            List[declarations.declaration_t]
203        """
204        if self._optimized:
205            return self._all_decls_not_recursive
206
207        return self._get_declarations_impl()
208
209    @declarations.setter
210    def declarations(self, declarations):
211        """
212        Set list of all declarations defined in the namespace.
213
214        Args:
215            List[declarations.declaration_t]: list of declarations
216
217        Not implemented.
218
219        """
220        raise NotImplementedError()
221
222    def remove_declaration(self, decl):
223        raise NotImplementedError()
224
225    @staticmethod
226    def __decl_types(decl):
227        """implementation details"""
228        types = []
229        bases = list(decl.__class__.__bases__)
230        if 'pygccxml' in decl.__class__.__module__:
231            types.append(decl.__class__)
232        while bases:
233            base = bases.pop()
234            if base is declaration.declaration_t:
235                continue
236            if base is byte_info.byte_info:
237                continue
238            if base is elaborated_info.elaborated_info:
239                continue
240            if 'pygccxml' not in base.__module__:
241                continue
242            types.append(base)
243            bases.extend(base.__bases__)
244        return types
245
246    def clear_optimizer(self):
247        """Cleans query optimizer state"""
248        self._optimized = False
249        self._type2decls = {}
250        self._type2name2decls = {}
251        self._type2decls_nr = {}
252        self._type2name2decls_nr = {}
253        self._all_decls = None
254        self._all_decls_not_recursive = None
255
256        for decl in self.declarations:
257            if isinstance(decl, scopedef_t):
258                decl.clear_optimizer()
259
260    def init_optimizer(self):
261        """
262        Initializes query optimizer state.
263
264        There are 4 internals hash tables:
265            1. from type to declarations
266            2. from type to declarations for non-recursive queries
267            3. from type to name to declarations
268            4. from type to name to declarations for non-recursive queries
269
270        Almost every query includes declaration type information. Also very
271        common query is to search some declaration(s) by name or full name.
272        Those hash tables allows to search declaration very quick.
273        """
274        if self.name == '::':
275            self._logger.debug(
276                "preparing data structures for query optimizer - started")
277        start_time = timeit.default_timer()
278
279        self.clear_optimizer()
280
281        for dtype in scopedef_t._impl_all_decl_types:
282            self._type2decls[dtype] = []
283            self._type2decls_nr[dtype] = []
284            self._type2name2decls[dtype] = {}
285            self._type2name2decls_nr[dtype] = {}
286
287        self._all_decls_not_recursive = self.declarations
288        self._all_decls = make_flatten(
289            self._all_decls_not_recursive)
290        for decl in self._all_decls:
291            types = self.__decl_types(decl)
292            for type_ in types:
293                self._type2decls[type_].append(decl)
294                name2decls = self._type2name2decls[type_]
295                if decl.name not in name2decls:
296                    name2decls[decl.name] = []
297                name2decls[decl.name].append(decl)
298                if self is decl.parent:
299                    self._type2decls_nr[type_].append(decl)
300                    name2decls_nr = self._type2name2decls_nr[type_]
301                    if decl.name not in name2decls_nr:
302                        name2decls_nr[decl.name] = []
303                    name2decls_nr[decl.name].append(decl)
304
305        for decl in self._all_decls_not_recursive:
306            if isinstance(decl, scopedef_t):
307                decl.init_optimizer()
308        if self.name == '::':
309            self._logger.debug((
310                "preparing data structures for query optimizer - " +
311                "done( %f seconds ). "), (timeit.default_timer() - start_time))
312        self._optimized = True
313
314    @staticmethod
315    def _build_operator_function(name, function):
316        if isinstance(name, Callable):
317            return name
318
319        return function
320
321    @staticmethod
322    def _build_operator_name(name, function, symbol):
323        """implementation details"""
324        def add_operator(sym):
325            if 'new' in sym or 'delete' in sym:
326                return 'operator ' + sym
327            return 'operator' + sym
328        if isinstance(name, Callable) and None is function:
329            name = None
330        if name:
331            if 'operator' not in name:
332                name = add_operator(name)
333            return name
334        elif symbol:
335            return add_operator(symbol)
336        return name  # both name and symbol are None
337
338    def _on_rename(self):
339        for decl in self.decls(allow_empty=True):
340            decl.cache.reset_name_based()
341        # I am not sure whether to introduce this or not?
342        # It could be very time consuming operation + it changes optimize query
343        # data structures.
344        # if self.parent:
345        #    if self.parent._optimized:
346        #        self.parent.init_optimizer()
347
348    @staticmethod
349    def __normalize_args(**keywds):
350        """implementation details"""
351        if isinstance(keywds['name'], Callable) and \
352                None is keywds['function']:
353            keywds['function'] = keywds['name']
354            keywds['name'] = None
355        return keywds
356
357    def __findout_recursive(self, **keywds):
358        """implementation details"""
359        if None is keywds['recursive']:
360            return self.RECURSIVE_DEFAULT
361
362        return keywds['recursive']
363
364    def __findout_allow_empty(self, **keywds):
365        """implementation details"""
366        if None is keywds['allow_empty']:
367            return self.ALLOW_EMPTY_MDECL_WRAPPER
368
369        return keywds['allow_empty']
370
371    @staticmethod
372    def __findout_decl_type(match_class, **keywds):
373        """implementation details"""
374        if 'decl_type' in keywds:
375            return keywds['decl_type']
376
377        matcher_args = keywds.copy()
378        del matcher_args['function']
379        del matcher_args['recursive']
380        if 'allow_empty' in matcher_args:
381            del matcher_args['allow_empty']
382
383        decl_matcher = match_class(**matcher_args)
384        if decl_matcher.decl_type:
385            return decl_matcher.decl_type
386        return None
387
388    def __create_matcher(self, match_class, **keywds):
389        """implementation details"""
390        matcher_args = keywds.copy()
391        del matcher_args['function']
392        del matcher_args['recursive']
393        if 'allow_empty' in matcher_args:
394            del matcher_args['allow_empty']
395
396        decl_matcher = decl_matcher = match_class(**matcher_args)
397        if keywds['function']:
398            self._logger.debug(
399                'running query: %s and <user defined function>',
400                str(decl_matcher))
401            return lambda decl: decl_matcher(decl) and keywds['function'](decl)
402
403        self._logger.debug('running query: %s', str(decl_matcher))
404        return decl_matcher
405
406    def __findout_range(self, name, decl_type, recursive):
407        """implementation details"""
408        if not self._optimized:
409            self._logger.debug(
410                'running non optimized query - optimization has not been done')
411            decls = self.declarations
412            if recursive:
413                decls = make_flatten(self.declarations)
414            if decl_type:
415                decls = [d for d in decls if isinstance(d, decl_type)]
416            return decls
417
418        if name and templates.is_instantiation(name):
419            # templates has tricky mode to compare them, so lets check the
420            # whole range
421            name = None
422
423        if name and decl_type:
424            impl_match = scopedef_t._impl_matchers[scopedef_t.decl](name=name)
425            if impl_match.is_full_name():
426                name = impl_match.decl_name_only
427            if recursive:
428                self._logger.debug(
429                    'query has been optimized on type and name')
430                return self._type2name2decls[decl_type].get(name, [])
431
432            self._logger.debug(
433                'non recursive query has been optimized on type and name')
434            return self._type2name2decls_nr[decl_type].get(name, [])
435        elif decl_type:
436            if recursive:
437                self._logger.debug('query has been optimized on type')
438                return self._type2decls[decl_type]
439
440            self._logger.debug(
441                'non recursive query has been optimized on type')
442            return self._type2decls_nr[decl_type]
443        else:
444            if recursive:
445                self._logger.debug((
446                    'query has not been optimized ( hint: query does not ' +
447                    'contain type and/or name )'))
448                return self._all_decls
449
450            self._logger.debug((
451                'non recursive query has not been optimized ( hint: ' +
452                'query does not contain type and/or name )'))
453            return self._all_decls_not_recursive
454
455    def _find_single(self, match_class, **keywds):
456        """implementation details"""
457        self._logger.debug('find single query execution - started')
458        start_time = timeit.default_timer()
459        norm_keywds = self.__normalize_args(**keywds)
460        decl_matcher = self.__create_matcher(match_class, **norm_keywds)
461        dtype = self.__findout_decl_type(match_class, **norm_keywds)
462        recursive_ = self.__findout_recursive(**norm_keywds)
463        decls = self.__findout_range(norm_keywds['name'], dtype, recursive_)
464        found = matcher.get_single(decl_matcher, decls, False)
465        self._logger.debug(
466            'find single query execution - done( %f seconds )',
467            (timeit.default_timer() - start_time))
468        return found
469
470    def _find_multiple(self, match_class, **keywds):
471        """implementation details"""
472        self._logger.debug('find all query execution - started')
473        start_time = timeit.default_timer()
474        norm_keywds = self.__normalize_args(**keywds)
475        decl_matcher = self.__create_matcher(match_class, **norm_keywds)
476        dtype = self.__findout_decl_type(match_class, **norm_keywds)
477        recursive_ = self.__findout_recursive(**norm_keywds)
478        allow_empty = self.__findout_allow_empty(**norm_keywds)
479        decls = self.__findout_range(norm_keywds['name'], dtype, recursive_)
480        found = matcher.find(decl_matcher, decls, False)
481        mfound = mdecl_wrapper.mdecl_wrapper_t(found)
482        self._logger.debug('%d declaration(s) that match query', len(mfound))
483        self._logger.debug(
484            'find single query execution - done( %f seconds )',
485            (timeit.default_timer() - start_time))
486        if not mfound and not allow_empty:
487            raise RuntimeError(
488                "Multi declaration query returned 0 declarations.")
489        return mfound
490
491    def decl(
492            self,
493            name=None,
494            function=None,
495            decl_type=None,
496            header_dir=None,
497            header_file=None,
498            recursive=None):
499        """returns reference to declaration, that is matched defined
500        criteria"""
501        return (
502            self._find_single(
503                self._impl_matchers[
504                    scopedef_t.decl],
505                name=name,
506                function=function,
507                decl_type=decl_type,
508                header_dir=header_dir,
509                header_file=header_file,
510                recursive=recursive)
511        )
512
513    def decls(
514            self,
515            name=None,
516            function=None,
517            decl_type=None,
518            header_dir=None,
519            header_file=None,
520            recursive=None,
521            allow_empty=None):
522        """returns a set of declarations, that are matched defined criteria"""
523        return (
524            self._find_multiple(
525                self._impl_matchers[
526                    scopedef_t.decl],
527                name=name,
528                function=function,
529                decl_type=decl_type,
530                header_dir=header_dir,
531                header_file=header_file,
532                recursive=recursive,
533                allow_empty=allow_empty)
534        )
535
536    def class_(
537            self,
538            name=None,
539            function=None,
540            header_dir=None,
541            header_file=None,
542            recursive=None):
543        """returns reference to class declaration, that is matched defined
544        criteria"""
545        return (
546            self._find_single(
547                self._impl_matchers[scopedef_t.class_],
548                name=name,
549                function=function,
550                decl_type=self._impl_decl_types[
551                    scopedef_t.class_],
552                header_dir=header_dir,
553                header_file=header_file,
554                recursive=recursive)
555        )
556
557    def classes(
558            self,
559            name=None,
560            function=None,
561            header_dir=None,
562            header_file=None,
563            recursive=None,
564            allow_empty=None):
565        """returns a set of class declarations, that are matched defined
566        criteria"""
567        return (
568            self._find_multiple(
569                self._impl_matchers[scopedef_t.class_],
570                name=name,
571                function=function,
572                decl_type=self._impl_decl_types[
573                    scopedef_t.class_],
574                header_dir=header_dir,
575                header_file=header_file,
576                recursive=recursive,
577                allow_empty=allow_empty)
578        )
579
580    def variable(
581            self,
582            name=None,
583            function=None,
584            decl_type=None,
585            header_dir=None,
586            header_file=None,
587            recursive=None):
588        """returns reference to variable declaration, that is matched defined
589        criteria"""
590
591        return (
592            self._find_single(
593                self._impl_matchers[
594                    scopedef_t.variable],
595                name=name,
596                function=function,
597                decl_type=decl_type,
598                header_dir=header_dir,
599                header_file=header_file,
600                recursive=recursive)
601        )
602
603    def variables(
604            self,
605            name=None,
606            function=None,
607            decl_type=None,
608            header_dir=None,
609            header_file=None,
610            recursive=None,
611            allow_empty=None):
612
613        """returns a set of variable declarations, that are matched defined
614        criteria"""
615        return (
616            self._find_multiple(
617                self._impl_matchers[
618                    scopedef_t.variable],
619                name=name,
620                function=function,
621                decl_type=decl_type,
622                header_dir=header_dir,
623                header_file=header_file,
624                recursive=recursive,
625                allow_empty=allow_empty)
626        )
627
628    def calldef(
629            self,
630            name=None,
631            function=None,
632            return_type=None,
633            arg_types=None,
634            header_dir=None,
635            header_file=None,
636            recursive=None):
637        """returns reference to "calldef" declaration, that is matched defined
638        criteria"""
639        return (
640            self._find_single(
641                self._impl_matchers[scopedef_t.calldef],
642                name=name,
643                function=function,
644                decl_type=self._impl_decl_types[
645                    scopedef_t.calldef],
646                return_type=return_type,
647                arg_types=arg_types,
648                header_dir=header_dir,
649                header_file=header_file,
650                recursive=recursive)
651        )
652
653    def calldefs(
654            self,
655            name=None,
656            function=None,
657            return_type=None,
658            arg_types=None,
659            header_dir=None,
660            header_file=None,
661            recursive=None,
662            allow_empty=None):
663        """returns a set of :class:`calldef_t` declarations, that are matched
664        defined criteria"""
665        return (
666            self._find_multiple(
667                self._impl_matchers[scopedef_t.calldef],
668                name=name,
669                function=function,
670                decl_type=self._impl_decl_types[
671                    scopedef_t.calldef],
672                return_type=return_type,
673                arg_types=arg_types,
674                header_dir=header_dir,
675                header_file=header_file,
676                recursive=recursive,
677                allow_empty=allow_empty)
678        )
679
680    def operator(
681            self,
682            name=None,
683            function=None,
684            symbol=None,
685            return_type=None,
686            arg_types=None,
687            header_dir=None,
688            header_file=None,
689            recursive=None):
690        """returns reference to operator declaration, that is matched
691        defined criteria"""
692        return (
693            self._find_single(
694                self._impl_matchers[scopedef_t.operator],
695                name=self._build_operator_name(name,
696                                               function,
697                                               symbol),
698                symbol=symbol,
699                function=self._build_operator_function(name,
700                                                       function),
701                decl_type=self._impl_decl_types[scopedef_t.operator],
702                return_type=return_type,
703                arg_types=arg_types,
704                header_dir=header_dir,
705                header_file=header_file,
706                recursive=recursive)
707        )
708
709    def operators(
710            self,
711            name=None,
712            function=None,
713            symbol=None,
714            return_type=None,
715            arg_types=None,
716            header_dir=None,
717            header_file=None,
718            recursive=None,
719            allow_empty=None):
720        """returns a set of operator declarations, that are matched
721        defined criteria"""
722        return (
723            self._find_multiple(
724                self._impl_matchers[scopedef_t.operator],
725                name=self._build_operator_name(name,
726                                               function,
727                                               symbol),
728                symbol=symbol,
729                function=self._build_operator_function(name,
730                                                       function),
731                decl_type=self._impl_decl_types[scopedef_t.operator],
732                return_type=return_type,
733                arg_types=arg_types,
734                header_dir=header_dir,
735                header_file=header_file,
736                recursive=recursive,
737                allow_empty=allow_empty)
738        )
739
740    def member_function(
741            self,
742            name=None,
743            function=None,
744            return_type=None,
745            arg_types=None,
746            header_dir=None,
747            header_file=None,
748            recursive=None):
749        """returns reference to member declaration, that is matched
750        defined criteria"""
751        return (
752            self._find_single(
753                self._impl_matchers[scopedef_t.member_function],
754                name=name,
755                function=function,
756                decl_type=self._impl_decl_types[
757                    scopedef_t.member_function],
758                return_type=return_type,
759                arg_types=arg_types,
760                header_dir=header_dir,
761                header_file=header_file,
762                recursive=recursive)
763        )
764
765    def member_functions(
766            self,
767            name=None,
768            function=None,
769            return_type=None,
770            arg_types=None,
771            header_dir=None,
772            header_file=None,
773            recursive=None,
774            allow_empty=None):
775        """returns a set of member function declarations, that are matched
776        defined criteria"""
777        return (
778            self._find_multiple(
779                self._impl_matchers[scopedef_t.member_function],
780                name=name,
781                function=function,
782                decl_type=self._impl_decl_types[
783                    scopedef_t.member_function],
784                return_type=return_type,
785                arg_types=arg_types,
786                header_dir=header_dir,
787                header_file=header_file,
788                recursive=recursive,
789                allow_empty=allow_empty)
790        )
791
792    def constructor(
793            self,
794            name=None,
795            function=None,
796            return_type=None,
797            arg_types=None,
798            header_dir=None,
799            header_file=None,
800            recursive=None):
801        """returns reference to constructor declaration, that is matched
802        defined criteria"""
803        return (
804            self._find_single(
805                self._impl_matchers[scopedef_t.constructor],
806                name=name,
807                function=function,
808                decl_type=self._impl_decl_types[
809                    scopedef_t.constructor],
810                return_type=return_type,
811                arg_types=arg_types,
812                header_dir=header_dir,
813                header_file=header_file,
814                recursive=recursive)
815        )
816
817    def constructors(
818            self,
819            name=None,
820            function=None,
821            return_type=None,
822            arg_types=None,
823            header_dir=None,
824            header_file=None,
825            recursive=None,
826            allow_empty=None):
827        """returns a set of constructor declarations, that are matched
828        defined criteria"""
829        return (
830            self._find_multiple(
831                self._impl_matchers[scopedef_t.constructor],
832                name=name,
833                function=function,
834                decl_type=self._impl_decl_types[
835                    scopedef_t.constructor],
836                return_type=return_type,
837                arg_types=arg_types,
838                header_dir=header_dir,
839                header_file=header_file,
840                recursive=recursive,
841                allow_empty=allow_empty)
842        )
843
844    def member_operator(
845            self,
846            name=None,
847            function=None,
848            symbol=None,
849            return_type=None,
850            arg_types=None,
851            header_dir=None,
852            header_file=None,
853            recursive=None):
854        """returns reference to member operator declaration, that is matched
855        defined criteria"""
856        return (
857            self._find_single(
858                self._impl_matchers[scopedef_t.member_operator],
859                name=self._build_operator_name(name,
860                                               function,
861                                               symbol),
862                symbol=symbol,
863                function=self._build_operator_function(name,
864                                                       function),
865                decl_type=self._impl_decl_types[scopedef_t.member_operator],
866                return_type=return_type,
867                arg_types=arg_types,
868                header_dir=header_dir,
869                header_file=header_file,
870                recursive=recursive)
871        )
872
873    def member_operators(
874            self,
875            name=None,
876            function=None,
877            symbol=None,
878            return_type=None,
879            arg_types=None,
880            header_dir=None,
881            header_file=None,
882            recursive=None,
883            allow_empty=None):
884        """returns a set of member operator declarations, that are matched
885        defined criteria"""
886        return (
887            self._find_multiple(
888                self._impl_matchers[scopedef_t.member_operator],
889                name=self._build_operator_name(name,
890                                               function,
891                                               symbol),
892                symbol=symbol,
893                function=self._build_operator_function(name,
894                                                       function),
895                decl_type=self._impl_decl_types[scopedef_t.member_operator],
896                return_type=return_type,
897                arg_types=arg_types,
898                header_dir=header_dir,
899                header_file=header_file,
900                recursive=recursive,
901                allow_empty=allow_empty)
902        )
903
904    def casting_operator(
905            self,
906            name=None,
907            function=None,
908            return_type=None,
909            arg_types=None,
910            header_dir=None,
911            header_file=None,
912            recursive=None):
913        """returns reference to casting operator declaration, that is matched
914        defined criteria"""
915        return (
916            self._find_single(
917                self._impl_matchers[scopedef_t.casting_operator],
918                name=name,
919                function=function,
920                decl_type=self._impl_decl_types[
921                    scopedef_t.casting_operator],
922                return_type=return_type,
923                arg_types=arg_types,
924                header_dir=header_dir,
925                header_file=header_file,
926                recursive=recursive)
927        )
928
929    def casting_operators(
930            self,
931            name=None,
932            function=None,
933            return_type=None,
934            arg_types=None,
935            header_dir=None,
936            header_file=None,
937            recursive=None,
938            allow_empty=None):
939        """returns a set of casting operator declarations, that are matched
940        defined criteria"""
941        return (
942            self._find_multiple(
943                self._impl_matchers[scopedef_t.casting_operator],
944                name=name,
945                function=function,
946                decl_type=self._impl_decl_types[
947                    scopedef_t.casting_operator],
948                return_type=return_type,
949                arg_types=arg_types,
950                header_dir=header_dir,
951                header_file=header_file,
952                recursive=recursive,
953                allow_empty=allow_empty)
954        )
955
956    def enumeration(
957            self,
958            name=None,
959            function=None,
960            header_dir=None,
961            header_file=None,
962            recursive=None):
963        """returns reference to enumeration declaration, that is matched
964        defined criteria"""
965        return (
966            self._find_single(
967                self._impl_matchers[scopedef_t.enumeration],
968                name=name,
969                function=function,
970                decl_type=self._impl_decl_types[
971                    scopedef_t.enumeration],
972                header_dir=header_dir,
973                header_file=header_file,
974                recursive=recursive)
975        )
976
977    def enumerations(
978            self,
979            name=None,
980            function=None,
981            header_dir=None,
982            header_file=None,
983            recursive=None,
984            allow_empty=None):
985        """returns a set of enumeration declarations, that are matched
986        defined criteria"""
987        return (
988            self._find_multiple(
989                self._impl_matchers[scopedef_t.enumeration],
990                name=name,
991                function=function,
992                decl_type=self._impl_decl_types[
993                    scopedef_t.enumeration],
994                header_dir=header_dir,
995                header_file=header_file,
996                recursive=recursive,
997                allow_empty=allow_empty)
998        )
999
1000    def typedef(
1001            self,
1002            name=None,
1003            function=None,
1004            header_dir=None,
1005            header_file=None,
1006            recursive=None):
1007        """returns reference to typedef declaration, that is matched
1008        defined criteria"""
1009        return (
1010            self._find_single(
1011                self._impl_matchers[scopedef_t.typedef],
1012                name=name,
1013                function=function,
1014                decl_type=self._impl_decl_types[
1015                    scopedef_t.typedef],
1016                header_dir=header_dir,
1017                header_file=header_file,
1018                recursive=recursive)
1019        )
1020
1021    def typedefs(
1022            self,
1023            name=None,
1024            function=None,
1025            header_dir=None,
1026            header_file=None,
1027            recursive=None,
1028            allow_empty=None):
1029        """returns a set of typedef declarations, that are matched
1030        defined criteria"""
1031        return (
1032            self._find_multiple(
1033                self._impl_matchers[scopedef_t.typedef],
1034                name=name,
1035                function=function,
1036                decl_type=self._impl_decl_types[
1037                    scopedef_t.typedef],
1038                header_dir=header_dir,
1039                header_file=header_file,
1040                recursive=recursive,
1041                allow_empty=allow_empty)
1042        )
1043
1044    def __getitem__(self, name_or_function):
1045        """
1046        Allow simple name based find of declarations.  Internally just calls
1047        `decls` method.
1048        :param name_or_function:  Name of `decl` to lookup or finder function.
1049        """
1050        return self.decls(name_or_function)
1051
1052    def i_depend_on_them(self, recursive=True):
1053        raise NotImplementedError()
1054
1055
1056def make_flatten(decl_or_decls):
1057    """
1058    Converts tree representation of declarations to flatten one.
1059
1060    :param decl_or_decls: reference to list of declaration's or single
1061        declaration
1062    :type decl_or_decls: :class:`declaration_t` or [ :class:`declaration_t` ]
1063    :rtype: [ all internal declarations ]
1064
1065    """
1066
1067    def proceed_single(decl):
1068        answer = [decl]
1069        if not isinstance(decl, scopedef_t):
1070            return answer
1071        for elem in decl.declarations:
1072            if isinstance(elem, scopedef_t):
1073                answer.extend(proceed_single(elem))
1074            else:
1075                answer.append(elem)
1076        return answer
1077
1078    decls = []
1079    if isinstance(decl_or_decls, list):
1080        decls.extend(decl_or_decls)
1081    else:
1082        decls.append(decl_or_decls)
1083    answer = []
1084    for decl in decls:
1085        answer.extend(proceed_single(decl))
1086    return answer
1087
1088
1089def find_all_declarations(
1090        declarations,
1091        decl_type=None,
1092        name=None,
1093        parent=None,
1094        recursive=True,
1095        fullname=None):
1096    """
1097    Returns a list of all declarations that match criteria, defined by
1098    developer.
1099
1100    For more information about arguments see :class:`match_declaration_t`
1101    class.
1102
1103    :rtype: [ matched declarations ]
1104
1105    """
1106
1107    if recursive:
1108        decls = make_flatten(declarations)
1109    else:
1110        decls = declarations
1111
1112    return list(
1113        filter(
1114            algorithm.match_declaration_t(
1115                decl_type=decl_type,
1116                name=name,
1117                fullname=fullname,
1118                parent=parent),
1119            decls))
1120
1121
1122def find_declaration(
1123        declarations,
1124        decl_type=None,
1125        name=None,
1126        parent=None,
1127        recursive=True,
1128        fullname=None):
1129    """
1130    Returns single declaration that match criteria, defined by developer.
1131    If more the one declaration was found None will be returned.
1132
1133    For more information about arguments see :class:`match_declaration_t`
1134    class.
1135
1136    :rtype: matched declaration :class:`declaration_t` or None
1137
1138    """
1139
1140    decl = find_all_declarations(
1141        declarations,
1142        decl_type=decl_type,
1143        name=name,
1144        parent=parent,
1145        recursive=recursive,
1146        fullname=fullname)
1147    if len(decl) == 1:
1148        return decl[0]
1149
1150
1151def find_first_declaration(
1152        declarations,
1153        decl_type=None,
1154        name=None,
1155        parent=None,
1156        recursive=True,
1157        fullname=None):
1158    """
1159    Returns first declaration that match criteria, defined by developer.
1160
1161    For more information about arguments see :class:`match_declaration_t`
1162    class.
1163
1164    :rtype: matched declaration :class:`declaration_t` or None
1165
1166    """
1167
1168    decl_matcher = algorithm.match_declaration_t(
1169        decl_type=decl_type,
1170        name=name,
1171        fullname=fullname,
1172        parent=parent)
1173    if recursive:
1174        decls = make_flatten(declarations)
1175    else:
1176        decls = declarations
1177    for decl in decls:
1178        if decl_matcher(decl):
1179            return decl
1180    return None
1181
1182
1183def declaration_files(decl_or_decls):
1184    """
1185    Returns set of files
1186
1187    Every declaration is declared in some file. This function returns set, that
1188    contains all file names of declarations.
1189
1190    :param decl_or_decls: reference to list of declaration's or single
1191        declaration
1192    :type decl_or_decls: :class:`declaration_t` or [:class:`declaration_t`]
1193    :rtype: set(declaration file names)
1194
1195    """
1196
1197    files = set()
1198    decls = make_flatten(decl_or_decls)
1199    for decl in decls:
1200        if decl.location:
1201            files.add(decl.location.file_name)
1202    return files
1203