1# Copyright (c) 2011, Willow Garage, Inc.
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are met:
6#
7#     * Redistributions of source code must retain the above copyright
8#       notice, this list of conditions and the following disclaimer.
9#     * Redistributions in binary form must reproduce the above copyright
10#       notice, this list of conditions and the following disclaimer in the
11#       documentation and/or other materials provided with the distribution.
12#     * Neither the name of the Willow Garage, Inc. nor the names of its
13#       contributors may be used to endorse or promote products derived from
14#       this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26# POSSIBILITY OF SUCH DAMAGE.
27
28# Author Ken Conley/kwc@willowgarage.com
29
30"""
31Underlying model of rosdep data.  The basic data model of rosdep is to
32store a dictionary of data indexed by view name (i.e. ROS stack name).
33This data includes a dictionary mapping rosdep dependency names to
34rules and the view dependencies.
35
36This is a lower-level representation.  Higher-level representation can
37combine these rosdep dependency maps and view dependencies together
38into a combined view on which queries can be made.
39"""
40
41
42class RosdepDatabaseEntry(object):
43    """
44    Stores rosdep data and metadata for a single view.
45    """
46
47    def __init__(self, rosdep_data, view_dependencies, origin):
48        """
49        :param rosdep_data: raw rosdep dictionary map for view
50        :param view_dependencies: list of view dependency names
51        :param origin: name of where data originated, e.g. filename
52        """
53        assert isinstance(rosdep_data, dict), 'RosdepDatabaseEntry() rosdep_data is not a dict: %s' % rosdep_data
54        self.rosdep_data = rosdep_data
55        self.view_dependencies = view_dependencies
56        self.origin = origin
57
58
59class RosdepDatabase(object):
60    """
61    Stores loaded rosdep data for multiple views.
62    """
63
64    def __init__(self):
65        self._rosdep_db = {}  # {view_name: RosdepDatabaseEntry}
66
67    def is_loaded(self, view_name):
68        """
69        :param view_name: name of view to check, ``str``
70        :returns: ``True`` if *view_name* has been loaded into this
71          database.
72        """
73        return view_name in self._rosdep_db
74
75    def mark_loaded(self, view_name):
76        """
77        If view is not already loaded, this will mark it as such.  This in effect sets the data for the view to be empty.
78
79        :param view_name: name of view to mark as loaded
80        """
81        self.set_view_data(view_name, {}, [], None)
82
83    def set_view_data(self, view_name, rosdep_data, view_dependencies, origin):
84        """
85        Set data associated with view.  This will create a new
86        :class:`RosdepDatabaseEntry`.
87
88        :param rosdep_data: rosdep data map to associated with view.
89          This will be copied.
90        :param origin: origin of view data, e.g. filepath of ``rosdep.yaml``
91        """
92        self._rosdep_db[view_name] = RosdepDatabaseEntry(rosdep_data.copy(), view_dependencies, origin)
93
94    def get_view_names(self):
95        """
96        :returns: list of view names that are loaded into this database.
97        """
98        return self._rosdep_db.keys()
99
100    def get_view_data(self, view_name):
101        """
102        :returns: :class:`RosdepDatabaseEntry` of given view.
103
104        :raises: :exc:`KeyError` if no entry for *view_name*
105        """
106        return self._rosdep_db[view_name]
107
108    def get_view_dependencies(self, view_name):
109        """
110        :raises: :exc:`KeyError` if *view_name* is not an entry, or if
111          all of view's dependencies have not been properly loaded.
112        """
113        entry = self.get_view_data(view_name)
114        dependencies = entry.view_dependencies[:]
115        # compute full set of dependencies by iterating over
116        # dependencies in reverse order and prepending.
117        for s in reversed(entry.view_dependencies):
118            dependencies = self.get_view_dependencies(s) + dependencies
119        # make unique preserving order
120        unique_deps = []
121        for d in dependencies:
122            if d not in unique_deps:
123                unique_deps.append(d)
124        return unique_deps
125