summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/aria/parser/reading/locator.py
blob: 57b4d5023d02aeb65d67b3bf7abca9634c9251fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ...utils.console import puts, Colored, indent


# We are inheriting the primitive types in order to add the ability to set
# an attribute (_locator) on them.

class LocatableString(unicode):
    pass


class LocatableInt(int):
    pass


class LocatableFloat(float):
    pass


def wrap(value):
    if isinstance(value, basestring):
        return True, LocatableString(value)
    elif isinstance(value, int) and \
            not isinstance(value, bool):  # Note: bool counts as int in Python!
        return True, LocatableInt(value)
    elif isinstance(value, float):
        return True, LocatableFloat(value)
    return False, value


class Locator(object):
    """
    Stores location information (line and column numbers) for agnostic raw data.
    """
    def __init__(self, location, line, column, children=None):
        self.location = location
        self.line = line
        self.column = column
        self.children = children

    def get_child(self, *names):
        if (not names) or (not isinstance(self.children, dict)):
            return self
        name = names[0]
        if name not in self.children:
            return self
        child = self.children[name]
        return child.get_child(names[1:])

    def link(self, raw, path=None):
        if hasattr(raw, '_locator'):
            # This can happen when we use anchors
            return

        try:
            setattr(raw, '_locator', self)
        except AttributeError:
            return

        if isinstance(raw, list):
            for i, raw_element in enumerate(raw):
                wrapped, raw_element = wrap(raw_element)
                if wrapped:
                    raw[i] = raw_element
                child_path = '%s.%d' % (path, i) if path else str(i)
                try:
                    self.children[i].link(raw_element, child_path)
                except KeyError:
                    raise ValueError('location map does not match agnostic raw data: %s' %
                                     child_path)
        elif isinstance(raw, dict):
            for k, raw_element in raw.iteritems():
                wrapped, raw_element = wrap(raw_element)
                if wrapped:
                    raw[k] = raw_element
                child_path = '%s.%s' % (path, k) if path else k
                try:
                    self.children[k].link(raw_element, child_path)
                except KeyError:
                    raise ValueError('location map does not match agnostic raw data: %s' %
                                     child_path)

    def merge(self, locator):
        if isinstance(self.children, dict) and isinstance(locator.children, dict):
            for k, loc in locator.children.iteritems():
                if k in self.children:
                    self.children[k].merge(loc)
                else:
                    self.children[k] = loc

    def dump(self, key=None):
        if key:
            puts('%s "%s":%d:%d' %
                 (Colored.red(key), Colored.blue(self.location), self.line, self.column))
        else:
            puts('"%s":%d:%d' % (Colored.blue(self.location), self.line, self.column))
        if isinstance(self.children, list):
            with indent(2):
                for loc in self.children:
                    loc.dump()
        elif isinstance(self.children, dict):
            with indent(2):
                for k, loc in self.children.iteritems():
                    loc.dump(k)

    def __str__(self):
        # Should be in same format as Issue.locator_as_str
        return '"%s":%d:%d' % (self.location, self.line, self.column)