Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / pyreverse / diadefslib.py @ 1026

History | View | Annotate | Download (8.15 KB)

1 745 jjdelcerro
# Copyright (c) 2000-2013 LOGILAB S.A. (Paris, FRANCE).
2
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
3
#
4
# This program is free software; you can redistribute it and/or modify it under
5
# the terms of the GNU General Public License as published by the Free Software
6
# Foundation; either version 2 of the License, or (at your option) any later
7
# version.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License along with
14
# this program; if not, write to the Free Software Foundation, Inc.,
15
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
"""handle diagram generation options for class diagram or default diagrams
17
"""
18
19
from six.moves import builtins
20
21
import astroid
22
23
from pylint.pyreverse.diagrams import PackageDiagram, ClassDiagram
24
from pylint.pyreverse.utils import LocalsVisitor
25
26
BUILTINS_NAME = builtins.__name__
27
28
# diagram generators ##########################################################
29
30
class DiaDefGenerator(object):
31
    """handle diagram generation options"""
32
33
    def __init__(self, linker, handler):
34
        """common Diagram Handler initialization"""
35
        self.config = handler.config
36
        self._set_default_options()
37
        self.linker = linker
38
        self.classdiagram = None # defined by subclasses
39
40
    def get_title(self, node):
41
        """get title for objects"""
42
        title = node.name
43
        if self.module_names:
44
            title = '%s.%s' % (node.root().name, title)
45
        return title
46
47
    def _set_option(self, option):
48
        """activate some options if not explicitly deactivated"""
49
        # if we have a class diagram, we want more information by default;
50
        # so if the option is None, we return True
51
        if option is None:
52
            return bool(self.config.classes)
53
        return option
54
55
    def _set_default_options(self):
56
        """set different default options with _default dictionary"""
57
        self.module_names = self._set_option(self.config.module_names)
58
        all_ancestors = self._set_option(self.config.all_ancestors)
59
        all_associated = self._set_option(self.config.all_associated)
60
        anc_level, ass_level = (0, 0)
61
        if  all_ancestors:
62
            anc_level = -1
63
        if all_associated:
64
            ass_level = -1
65
        if self.config.show_ancestors is not None:
66
            anc_level = self.config.show_ancestors
67
        if self.config.show_associated is not None:
68
            ass_level = self.config.show_associated
69
        self.anc_level, self.ass_level = anc_level, ass_level
70
71
    def _get_levels(self):
72
        """help function for search levels"""
73
        return self.anc_level, self.ass_level
74
75
    def show_node(self, node):
76
        """true if builtins and not show_builtins"""
77
        if self.config.show_builtin:
78
            return True
79
        return node.root().name != BUILTINS_NAME
80
81
    def add_class(self, node):
82
        """visit one class and add it to diagram"""
83
        self.linker.visit(node)
84
        self.classdiagram.add_object(self.get_title(node), node)
85
86
    def get_ancestors(self, node, level):
87
        """return ancestor nodes of a class node"""
88
        if level == 0:
89
            return
90
        for ancestor in node.ancestors(recurs=False):
91
            if not self.show_node(ancestor):
92
                continue
93
            yield ancestor
94
95
    def get_associated(self, klass_node, level):
96
        """return associated nodes of a class node"""
97
        if level == 0:
98
            return
99
        for ass_nodes in list(klass_node.instance_attrs_type.values()) + \
100
                         list(klass_node.locals_type.values()):
101
            for ass_node in ass_nodes:
102
                if isinstance(ass_node, astroid.Instance):
103
                    ass_node = ass_node._proxied
104
                if not (isinstance(ass_node, astroid.ClassDef)
105
                        and self.show_node(ass_node)):
106
                    continue
107
                yield ass_node
108
109
    def extract_classes(self, klass_node, anc_level, ass_level):
110
        """extract recursively classes related to klass_node"""
111
        if self.classdiagram.has_node(klass_node) or not self.show_node(klass_node):
112
            return
113
        self.add_class(klass_node)
114
115
        for ancestor in self.get_ancestors(klass_node, anc_level):
116
            self.extract_classes(ancestor, anc_level-1, ass_level)
117
118
        for ass_node in self.get_associated(klass_node, ass_level):
119
            self.extract_classes(ass_node, anc_level, ass_level-1)
120
121
122
class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator):
123
    """generate minimum diagram definition for the project :
124

125
    * a package diagram including project's modules
126
    * a class diagram including project's classes
127
    """
128
129
    def __init__(self, linker, handler):
130
        DiaDefGenerator.__init__(self, linker, handler)
131
        LocalsVisitor.__init__(self)
132
133
    def visit_project(self, node):
134
        """visit an pyreverse.utils.Project node
135

136
        create a diagram definition for packages
137
        """
138
        mode = self.config.mode
139
        if len(node.modules) > 1:
140
            self.pkgdiagram = PackageDiagram('packages %s' % node.name, mode)
141
        else:
142
            self.pkgdiagram = None
143
        self.classdiagram = ClassDiagram('classes %s' % node.name, mode)
144
145
    def leave_project(self, node): # pylint: disable=unused-argument
146
        """leave the pyreverse.utils.Project node
147

148
        return the generated diagram definition
149
        """
150
        if self.pkgdiagram:
151
            return self.pkgdiagram, self.classdiagram
152
        return self.classdiagram,
153
154
    def visit_module(self, node):
155
        """visit an astroid.Module node
156

157
        add this class to the package diagram definition
158
        """
159
        if self.pkgdiagram:
160
            self.linker.visit(node)
161
            self.pkgdiagram.add_object(node.name, node)
162
163
    def visit_classdef(self, node):
164
        """visit an astroid.Class node
165

166
        add this class to the class diagram definition
167
        """
168
        anc_level, ass_level = self._get_levels()
169
        self.extract_classes(node, anc_level, ass_level)
170
171
    def visit_importfrom(self, node):
172
        """visit astroid.From  and catch modules for package diagram
173
        """
174
        if self.pkgdiagram:
175
            self.pkgdiagram.add_from_depend(node, node.modname)
176
177
178
class ClassDiadefGenerator(DiaDefGenerator):
179
    """generate a class diagram definition including all classes related to a
180
    given class
181
    """
182
183
    def __init__(self, linker, handler):
184
        DiaDefGenerator.__init__(self, linker, handler)
185
186
    def class_diagram(self, project, klass):
187
        """return a class diagram definition for the given klass and its
188
        related klasses
189
        """
190
191
        self.classdiagram = ClassDiagram(klass, self.config.mode)
192
        if len(project.modules) > 1:
193
            module, klass = klass.rsplit('.', 1)
194
            module = project.get_module(module)
195
        else:
196
            module = project.modules[0]
197
            klass = klass.split('.')[-1]
198
        klass = next(module.ilookup(klass))
199
200
        anc_level, ass_level = self._get_levels()
201
        self.extract_classes(klass, anc_level, ass_level)
202
        return self.classdiagram
203
204
# diagram handler #############################################################
205
206
class DiadefsHandler(object):
207
    """handle diagram definitions :
208

209
    get it from user (i.e. xml files) or generate them
210
    """
211
212
    def __init__(self, config):
213
        self.config = config
214
215
    def get_diadefs(self, project, linker):
216
        """get the diagrams configuration data
217
        :param linker: pyreverse.inspector.Linker(IdGeneratorMixIn, LocalsVisitor)
218
        :param project: pyreverse.utils.Project
219
        """
220
221
        #  read and interpret diagram definitions (Diadefs)
222
        diagrams = []
223
        generator = ClassDiadefGenerator(linker, self)
224
        for klass in self.config.classes:
225
            diagrams.append(generator.class_diagram(project, klass))
226
        if not diagrams:
227
            diagrams = DefaultDiadefGenerator(linker, self).visit(project)
228
        for diagram in diagrams:
229
            diagram.extract_relationships()
230
        return  diagrams