1#  Copyright 2008-2015 Nokia Networks
2#  Copyright 2016-     Robot Framework Foundation
3#
4#  Licensed under the Apache License, Version 2.0 (the "License");
5#  you may not use this file except in compliance with the License.
6#  You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10#  Unless required by applicable law or agreed to in writing, software
11#  distributed under the License is distributed on an "AS IS" BASIS,
12#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13#  See the License for the specific language governing permissions and
14#  limitations under the License.
15
16import re
17
18from robot.output import LOGGER
19from robot.utils import JYTHON, Utf8Reader, prepr
20
21
22NBSP = u'\xa0'
23
24
25class RobotReader(object):
26    _space_splitter = re.compile(u'[ \t\xa0]{2,}|\t+')
27    _pipe_splitter = re.compile(u'[ \t\xa0]+\|(?=[ \t\xa0]+)')
28    _pipe_starts = ('|', '| ', '|\t', u'|\xa0')
29    _pipe_ends = (' |', '\t|', u'\xa0|')
30
31    def read(self, file, populator, path=None):
32        path = path or getattr(file, 'name', '<file-like object>')
33        process = False
34        for lineno, line in enumerate(Utf8Reader(file).readlines(), start=1):
35            cells = self.split_row(line.rstrip())
36            cells = list(self._check_deprecations(cells, path, lineno))
37            if cells and cells[0].strip().startswith('*') and \
38                    populator.start_table([c.replace('*', '').strip()
39                                           for c in cells]):
40                process = True
41            elif process:
42                populator.add(cells)
43        return populator.eof()
44
45    @classmethod
46    def split_row(cls, row):
47        if row[:2] in cls._pipe_starts:
48            row = row[1:-1] if row[-2:] in cls._pipe_ends else row[1:]
49            return [cls._strip_whitespace(cell)
50                    for cell in cls._pipe_splitter.split(row)]
51        return cls._space_splitter.split(row)
52
53    def _check_deprecations(self, cells, path, line_number):
54        for original in cells:
55            normalized = self._normalize_whitespace(original)
56            if normalized != original:
57                if len(normalized) != len(original):
58                    msg = 'Collapsing consecutive whitespace'
59                else:
60                    msg = 'Converting whitespace characters to ASCII spaces'
61                LOGGER.warn("%s during parsing is deprecated. Fix %s in file "
62                            "'%s' on line %d."
63                            % (msg, prepr(original), path, line_number))
64            yield normalized
65
66    # Jython has issues with non-ASCII spaces https://bugs.jython.org/issue2772
67    if JYTHON:
68
69        _whitespace = re.compile(u'\\s+', re.UNICODE)
70        _trailing_whitespace = re.compile(u'\\s+$', re.UNICODE)
71
72        @classmethod
73        def _strip_whitespace(cls, string):
74            match = cls._whitespace.match(string)
75            if match:
76                string = string[match.end():]
77            match = cls._trailing_whitespace.search(string)
78            if match:
79                string = string[:match.start()]
80            return string
81
82        def _normalize_whitespace(self, string):
83            return ' '.join(self._whitespace.split(string))
84
85    else:
86
87        @classmethod
88        def _strip_whitespace(cls, string):
89            return string.strip()
90
91        def _normalize_whitespace(self, string):
92            return ' '.join(string.split())
93