def _test_with_response_content(self,
                                    validator,
                                    content_file,
                                    expected_error_line_count=None):
        if DEBUG:
            print('\nTest with response from: %s' % content_file)
        validator.state = _state_read_response_tag
        validator.decoder = ApiDecoder()
        with open(content_file, 'r') as f:
            lines = f.readlines()

        self._test_with_lines_of_content(validator, lines,
                                         expected_error_line_count)
 def __init__(self):
     self.state = _state_init
     self.decoder = ContentDecoder()
     self._read_parameter_string = False
     self._read_newline = False
     self._parameters = list()
class ApiaryValidator:
    def __init__(self):
        self.state = _state_init
        self.decoder = ContentDecoder()
        self._read_parameter_string = False
        self._read_newline = False
        self._parameters = list()

    def validate_file(self, file, verbose=False):
        validation_result = True
        error = None
        assert isinstance(file, str)
        try:
            with open(file, 'r') as f:
                lines = f.readlines()

        except FileNotFoundError:
            print('Error: could not find the file %s' % file)
            lines = list()
            validation_result = False
            error = ApiaryError(message='could not find the file: %s' % file)

        line_count = 0
        for line in lines:
            line_count += 1
            if verbose:
                print('%d %s' % (line_count, line))
            valid, error = self._read_line(line)
            if not valid:
                print('ValError: %s (@ %d)' % (error.message, line_count))
                validation_result = False
                break

        return validation_result, error

    def _read_line(self, line):
        error = None
        assert not self.state < 0, 'StateError: the validator is in the error state with line %s' % line

        if self.state == _state_read_group_title:
            if _api_title(line):
                if not _api_url(line):
                    error = ApiarySyntaxError(
                        message='Cannot find the api url in line: %s' % line)
                    self.state = _state_error
                else:
                    self._parameters = ApiaryValidator._get_parameters_from_api_title(
                        line)
                    self.state = _state_read_api_title

            elif _api_method(line) or _param_title(line) or _param_string(line) or _request_title(line) \
                    or _response_title(line):
                error = ApiarySyntaxError(message='Missing api title string')
                self.state = _state_error

        elif self.state == _state_read_api_title:
            if _api_method(line):
                self.state = _state_read_api_method

            elif _param_title(line) or _param_string(line) or _request_title(
                    line) or _response_title(line):
                error = ApiarySyntaxError(message='Missing api method string')
                self.state = _state_error

        elif self.state == _state_read_api_method:
            if _param_title(line):
                self._read_parameter_string = False
                self.state = _state_read_param_tag

            elif _request_title(line):
                self._prepare_for_scanning_request_content()

            elif _response_title(line):
                self._prepare_for_scanning_response_content()

            elif _param_string(line):
                error = ApiarySyntaxError(message='Missing parameter title')
                self.state = _state_error

        elif self.state == _state_read_param_tag:
            if _param_string(line):
                self._read_parameter_string = True
                parameter = ApiaryValidator._get_parameter_from_parameter_string(
                    line)
                if parameter not in self._parameters:
                    error = ApiaryParameterNotDefinedError(parameter=parameter)
                    self.state = _state_error

            elif _request_title(line):
                if not self._read_parameter_string:
                    error = ApiarySyntaxError(message='Missing parameter info')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_request_content()

            elif _response_title(line):
                if not self._read_parameter_string:
                    error = ApiarySyntaxError(message='Missing parameter info')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_response_content()

            elif not re.match(r'\s+', line):
                error = ApiarySyntaxError(
                    message='The lines should contain the parameter info')
                self.state = _state_error

        elif self.state == _state_read_request_tag:
            test_line = line
            if _response_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(
                        message='Missing request content')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_response_content()

            elif _request_title(line):
                self._prepare_for_scanning_request_content()

            else:
                error = self._scan_line_by_decoder(line)

        elif self.state == _state_read_response_tag:
            if _group_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(
                        message='Missing response content')
                    self.state = _state_error
                else:
                    self.decoder.clear()
                    self._read_newline = False
                    self.state = _state_read_group_title

            elif _api_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(
                        message='Missing response content')
                    self.state = _state_error
                else:
                    self.decoder.clear()
                    self._parameters = ApiaryValidator._get_parameters_from_api_title(
                        line)
                    self.state = _state_read_api_title

            elif _api_method(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(
                        message='Missing response content')
                    self.state = _state_error
                else:
                    self.decoder.clear()
                    self.state = _state_read_api_method

            elif _request_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError('Missing the request content')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_request_content()

            elif _response_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError('Missing the response content')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_response_content()

            else:
                error = self._scan_line_by_decoder(line)

        else:  # _state_init
            if _group_title(line):
                self.state = _state_read_group_title

        return (error is None), error

    def _scan_line_by_decoder(self, line):
        assert self.decoder is not None
        assert isinstance(line, str)
        error = None

        # validation for the newline constrain:
        if not self._read_newline:
            if re.match(r'^\s*[\r\n]?$', line):
                self._read_newline = True
                error = None
            else:
                error = ApiarySyntaxError(
                    message=
                    'SyntaxError: the code block should be started with a newline'
                )
            return error

        # validation for the indent constrain:
        if not ApiaryValidator._indent_validation(line):
            self.state = _state_error
            return ApiarySyntaxError(
                message=
                'SyntaxError: each line in the code block should be indented by 8 spaces ot 2 tabs'
            )

        # scan line with the decoder:
        try:
            self.decoder.scan_line(line)

        except AssertionError as e:
            error = ApiarySyntaxError(message='DecoderAssertion: %s' % e.args)
            self.state = _state_error

        except Exception as e:
            error = ApiarySyntaxError(message='DecodeException: %s' %
                                      getattr(e, 'message', None))
            self.state = _state_error

        return error

    def _prepare_for_scanning_response_content(self):
        self.state = _state_read_response_tag
        self._prepare_for_scanning_code_block()

    def _prepare_for_scanning_request_content(self):
        self.state = _state_read_request_tag
        self._prepare_for_scanning_code_block()

    def _prepare_for_scanning_code_block(self):
        self.decoder.clear()
        self._read_newline = False

    @staticmethod
    def _get_parameters_from_api_title(title):
        assert isinstance(title, str) and _api_title(title)
        url_search = re.search(r'\[.+\]', title)
        assert url_search is not None, 'Cannot find the url content in the title %s' % title
        parameters = list()
        url_elements = url_search.group()[1:-1].split('/')
        for element in url_elements:
            buffer = None
            for chart in element:
                if chart == '{':
                    buffer = ''

                elif chart == '}':
                    sub_parameters = buffer.split(',')
                    for param in sub_parameters:
                        parameters.append(param)
                    buffer = None

                elif chart == '?':
                    continue

                elif buffer is not None:
                    buffer += chart

        return parameters

    @staticmethod
    def _get_parameter_from_parameter_string(parameter_string):
        space = ' \t'
        accepted_characters = string.ascii_letters + string.digits + '_-'
        buffer = None
        for chart in parameter_string:
            if chart in space and buffer is not None:
                break
            if chart in accepted_characters:
                if buffer:
                    buffer += chart
                else:
                    buffer = chart
        return buffer

    @staticmethod
    def _indent_validation(line):
        space_count = 0
        for chart in line:
            if chart == ' ':
                space_count += 1
            elif chart == '\t':
                space_count += 4
            else:
                break

        return (not space_count < 8) or re.match(r'^\s*[\r\n]?$', line)
 def __init__(self):
     self.state = _state_init
     self.decoder = ContentDecoder()
     self._read_parameter_string = False
     self._read_newline = False
     self._parameters = list()
class ApiaryValidator:
    def __init__(self):
        self.state = _state_init
        self.decoder = ContentDecoder()
        self._read_parameter_string = False
        self._read_newline = False
        self._parameters = list()

    def validate_file(self, file, verbose=False):
        validation_result = True
        error = None
        assert isinstance(file, str)
        try:
            with open(file, 'r') as f:
                lines = f.readlines()

        except FileNotFoundError:
            print('Error: could not find the file %s' % file)
            lines = list()
            validation_result = False
            error = ApiaryError(message='could not find the file: %s' % file)

        line_count = 0
        for line in lines:
            line_count += 1
            if verbose:
                print('%d %s' % (line_count, line))
            valid, error = self._read_line(line)
            if not valid:
                print('ValError: %s (@ %d)' % (error.message, line_count))
                validation_result = False
                break

        return validation_result, error

    def _read_line(self, line):
        error = None
        assert not self.state < 0, 'StateError: the validator is in the error state with line %s' % line

        if self.state == _state_read_group_title:
            if _api_title(line):
                if not _api_url(line):
                    error = ApiarySyntaxError(message='Cannot find the api url in line: %s' % line)
                    self.state = _state_error
                else:
                    self._parameters = ApiaryValidator._get_parameters_from_api_title(line)
                    self.state = _state_read_api_title

            elif _api_method(line) or _param_title(line) or _param_string(line) or _request_title(line) \
                    or _response_title(line):
                error = ApiarySyntaxError(message='Missing api title string')
                self.state = _state_error

        elif self.state == _state_read_api_title:
            if _api_method(line):
                self.state = _state_read_api_method

            elif _param_title(line) or _param_string(line) or _request_title(line) or _response_title(line):
                error = ApiarySyntaxError(message='Missing api method string')
                self.state = _state_error

        elif self.state == _state_read_api_method:
            if _param_title(line):
                self._read_parameter_string = False
                self.state = _state_read_param_tag

            elif _request_title(line):
                self._prepare_for_scanning_request_content()

            elif _response_title(line):
                self._prepare_for_scanning_response_content()

            elif _param_string(line):
                error = ApiarySyntaxError(message='Missing parameter title')
                self.state = _state_error

        elif self.state == _state_read_param_tag:
            if _param_string(line):
                self._read_parameter_string = True
                parameter = ApiaryValidator._get_parameter_from_parameter_string(line)
                if parameter not in self._parameters:
                    error = ApiaryParameterNotDefinedError(parameter=parameter)
                    self.state = _state_error

            elif _request_title(line):
                if not self._read_parameter_string:
                    error = ApiarySyntaxError(message='Missing parameter info')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_request_content()

            elif _response_title(line):
                if not self._read_parameter_string:
                    error = ApiarySyntaxError(message='Missing parameter info')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_response_content()

            elif not re.match(r'\s+', line):
                error = ApiarySyntaxError(message='The lines should contain the parameter info')
                self.state = _state_error

        elif self.state == _state_read_request_tag:
            test_line = line
            if _response_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(message='Missing request content')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_response_content()

            elif _request_title(line):
                self._prepare_for_scanning_request_content()

            else:
                error = self._scan_line_by_decoder(line)

        elif self.state == _state_read_response_tag:
            if _group_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(message='Missing response content')
                    self.state = _state_error
                else:
                    self.decoder.clear()
                    self._read_newline = False
                    self.state = _state_read_group_title

            elif _api_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(message='Missing response content')
                    self.state = _state_error
                else:
                    self.decoder.clear()
                    self._parameters = ApiaryValidator._get_parameters_from_api_title(line)
                    self.state = _state_read_api_title

            elif _api_method(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError(message='Missing response content')
                    self.state = _state_error
                else:
                    self.decoder.clear()
                    self.state = _state_read_api_method

            elif _request_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError('Missing the request content')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_request_content()

            elif _response_title(line):
                if not self.decoder.get_parsed_objects():
                    error = ApiarySyntaxError('Missing the response content')
                    self.state = _state_error
                else:
                    self._prepare_for_scanning_response_content()

            else:
                error = self._scan_line_by_decoder(line)

        else:  # _state_init
            if _group_title(line):
                self.state = _state_read_group_title

        return (error is None), error

    def _scan_line_by_decoder(self, line):
        assert self.decoder is not None
        assert isinstance(line, str)
        error = None

        # validation for the newline constrain:
        if not self._read_newline:
            if re.match(r'^\s*[\r\n]?$', line):
                self._read_newline = True
                error = None
            else:
                error = ApiarySyntaxError(message='SyntaxError: the code block should be started with a newline')
            return error

        # validation for the indent constrain:
        if not ApiaryValidator._indent_validation(line):
            self.state = _state_error
            return ApiarySyntaxError(
                message='SyntaxError: each line in the code block should be indented by 8 spaces ot 2 tabs')

        # scan line with the decoder:
        try:
            self.decoder.scan_line(line)

        except AssertionError as e:
            error = ApiarySyntaxError(message='DecoderAssertion: %s' % e.args)
            self.state = _state_error

        except Exception as e:
            error = ApiarySyntaxError(message='DecodeException: %s' % getattr(e, 'message', None))
            self.state = _state_error

        return error

    def _prepare_for_scanning_response_content(self):
        self.state = _state_read_response_tag
        self._prepare_for_scanning_code_block()

    def _prepare_for_scanning_request_content(self):
        self.state = _state_read_request_tag
        self._prepare_for_scanning_code_block()

    def _prepare_for_scanning_code_block(self):
        self.decoder.clear()
        self._read_newline = False

    @staticmethod
    def _get_parameters_from_api_title(title):
        assert isinstance(title, str) and _api_title(title)
        url_search = re.search(r'\[.+\]', title)
        assert url_search is not None, 'Cannot find the url content in the title %s' % title
        parameters = list()
        url_elements = url_search.group()[1:-1].split('/')
        for element in url_elements:
            buffer = None
            for chart in element:
                if chart == '{':
                    buffer = ''

                elif chart == '}':
                    sub_parameters = buffer.split(',')
                    for param in sub_parameters:
                        parameters.append(param)
                    buffer = None

                elif chart == '?':
                    continue

                elif buffer is not None:
                    buffer += chart

        return parameters

    @staticmethod
    def _get_parameter_from_parameter_string(parameter_string):
        space = ' \t'
        accepted_characters = string.ascii_letters + string.digits + '_-'
        buffer = None
        for chart in parameter_string:
            if chart in space and buffer is not None:
                break
            if chart in accepted_characters:
                if buffer:
                    buffer += chart
                else:
                    buffer = chart
        return buffer

    @staticmethod
    def _indent_validation(line):
        space_count = 0
        for chart in line:
            if chart == ' ':
                space_count += 1
            elif chart == '\t':
                space_count += 4
            else:
                break

        return (not space_count < 8) or re.match(r'^\s*[\r\n]?$', line)