apply_parameters_to_operation() (in module secondodb.api.support.secondocommands)

check_identifier() (in module secondodb.api.support.secondoparser)

check_validity_string() (in module secondodb.api.support.secondoparser)

parse_inquiry_algebra() (in module secondodb.api.support.secondoparser)

parse_inquiry_algebras() (in module secondodb.api.support.secondoparser)

parse_inquiry_databases() (in module secondodb.api.support.secondoparser)

parse_inquiry_objects() (in module secondodb.api.support.secondoparser)

parse_inquiry_type_constructors() (in module secondodb.api.support.secondoparser)

parse_inquiry_types() (in module secondodb.api.support.secondoparser)

parse_type_definition() (in module secondodb.api.support.secondoparser)

receive_response() (in module secondodb.api.support.secondoparser)

parse_bool() (in module secondodb.api.algebras.secondostandardalgebra)

parse_int() (in module secondodb.api.algebras.secondostandardalgebra)

parse_longint() (in module secondodb.api.algebras.secondostandardalgebra)

parse_rational() (in module secondodb.api.algebras.secondostandardalgebra)

parse_real() (in module secondodb.api.algebras.secondostandardalgebra)

parse_string() (in module secondodb.api.algebras.secondostandardalgebra)

convert_line_to_list_exp_str() (in module secondodb.api.algebras.secondospatialalgebra)

convert_point_to_list_exp_str() (in module secondodb.api.algebras.secondospatialalgebra)

convert_points_to_list_exp_str() (in module secondodb.api.algebras.secondospatialalgebra)

convert_region_to_list_exp_str() (in module secondodb.api.algebras.secondospatialalgebra)

parse_line() (in module secondodb.api.algebras.secondospatialalgebra)

parse_point() (in module secondodb.api.algebras.secondospatialalgebra)

parse_points() (in module secondodb.api.algebras.secondospatialalgebra)

parse_region() (in module secondodb.api.algebras.secondospatialalgebra)

parse_segment() (in module secondodb.api.algebras.secondospatialalgebra)

BoolInInterval (class in secondodb.api.algebras.secondospatiotemporalalgebra)

IPoint (class in secondodb.api.algebras.secondospatiotemporalalgebra)

IRegion (class in secondodb.api.algebras.secondospatiotemporalalgebra)

IntInInterval (class in secondodb.api.algebras.secondospatiotemporalalgebra)

Interval (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MBool (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MInt (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MPoint (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MReal (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MRegion (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MString (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MapPoint (class in secondodb.api.algebras.secondospatiotemporalalgebra)

MotionVector (class in secondodb.api.algebras.secondospatiotemporalalgebra)

PointInInterval (class in secondodb.api.algebras.secondospatiotemporalalgebra)

RealInInterval (class in secondodb.api.algebras.secondospatiotemporalalgebra)

RegionInInterval (class in secondodb.api.algebras.secondospatiotemporalalgebra)

StringInInterval (class in secondodb.api.algebras.secondospatiotemporalalgebra)

ValueVectorBool (class in secondodb.api.algebras.secondospatiotemporalalgebra)

ValueVectorInt (class in secondodb.api.algebras.secondospatiotemporalalgebra)

ValueVectorReal (class in secondodb.api.algebras.secondospatiotemporalalgebra)

ValueVectorString (class in secondodb.api.algebras.secondospatiotemporalalgebra)

parse_ipoint() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_iregion() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_mbool() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_mint() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_mpoint() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_mreal() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_mregion() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_mstring() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

parse_timestamp() (in module secondodb.api.algebras.secondospatiotemporalalgebra)

Relation (class in secondodb.api.algebras.secondorelationalalgebra)

parse_relation() (in module secondodb.api.algebras.secondorelationalalgebra)

build_list_expr_from_binary() (in module secondodb.api.support.secondoinputhandler)

read_binary_record() (in module secondodb.api.support.secondoinputhandler)

read_bool() (in module secondodb.api.support.secondoinputhandler)

read_byte() (in module secondodb.api.support.secondoinputhandler)

read_double() (in module secondodb.api.support.secondoinputhandler)

read_int() (in module secondodb.api.support.secondoinputhandler)

read_line_of_text() (in module secondodb.api.support.secondoinputhandler)

read_long() (in module secondodb.api.support.secondoinputhandler)

read_real() (in module secondodb.api.support.secondoinputhandler)

read_short() (in module secondodb.api.support.secondoinputhandler)

read_string() (in module secondodb.api.support.secondoinputhandler)

append_to_last_element() (in module secondodb.api.support.secondolistexpr)

build_new_list_node() (in module secondodb.api.support.secondolistexpr)

check_if_atom() (in module secondodb.api.support.secondolistexpr)

create_bool_atom() (in module secondodb.api.support.secondolistexpr)

create_integer_atom() (in module secondodb.api.support.secondolistexpr)

create_real_atom() (in module secondodb.api.support.secondolistexpr)

create_string_atom() (in module secondodb.api.support.secondolistexpr)

create_symbol_atom() (in module secondodb.api.support.secondolistexpr)

create_text_atom() (in module secondodb.api.support.secondolistexpr)

five_element_list() (in module secondodb.api.support.secondolistexpr)

four_element_list() (in module secondodb.api.support.secondolistexpr)

one_element_list() (in module secondodb.api.support.secondolistexpr)

six_element_list() (in module secondodb.api.support.secondolistexpr)

three_element_list() (in module secondodb.api.support.secondolistexpr)

two_element_list() (in module secondodb.api.support.secondolistexpr)

# ----------------------------------------------------------------------------------------------------------------------
# The Secondo Python API (pySecondo)
# Victor Silva (victor.silva@posteo.de)
# October 2019
# Module
# Secondo API
# secondoapi.py
"""
The module Secondo API implements the Application Programming Interface for Python for the interaction with a |sec|
server. The API implements the Python Database API 2.0 Specification (PEP 249).
"""

from socket import socket, AF_INET, SOCK_STREAM, TCP_NODELAY, IPPROTO_TCP
import secondodb.api.support.secondoparser as parser
import secondodb.api.support.secondomessages as messages
import secondodb.api.support.secondocommands as com

apilevel = '2.0'
threadsafety = 1
paramstyle = 'pyformat'
encoding = 'latin-1'
error_encoding = 'ignore'

def connect(host, port, username='username', passwd='passwd', database=''):
    """
    Constructor for creating a connection to the |sec| server. If further data is provided (database and login data),
    a connection to an existing database (if available on the server) will be established.

    Returns a Connection Object.

    :param host: The host of the |sec| server as IP-address or as qualified name (localhost).
    :param port: The port of the |sec| server.
    :param username: The username for the connection (optional).
    :param passwd: The password for the connection (optional).
    :param database: The name of the database (optional).
    :return: A Connection Object.
    """

    # Check if minimum parameters for the connection are available and valid

    if host == '':
        raise InterfaceError('The host is not valid.')

    if port == '':
        raise InterfaceError('The port is not valid.')

    if not parser.check_port(port):
        raise InterfaceError('The port has invalid characters.')

    if not parser.check_identifier(username):
        raise InterfaceError('The username is not valid.')

    if not parser.check_identifier(passwd):
        raise InterfaceError('The password is not valid.')

    if database != '' and not parser.check_identifier(database):
        raise InterfaceError('The database name is not valid.')

    port = int(port)

    # Create Connection object

    connection = Connection(host, port, username, passwd, database)

    if connection.initialized:
        return connection
    else:
        raise InterfaceError('Connection refused.')

# --- Exceptions ---

class Warning(Exception):
    """
    Exception raised for important warnings like data truncations while inserting, etc.
    """
    def __init__(self, message, *args):
        self.message = message
        super(Warning, self).__init__(message, *args)

class Error(Exception):
    """
    Exception that is the base class of all other error exceptions.
    You can use this to catch all errors with one single except statement.
    Warnings are not considered errors and thus should not use this class as base.
    """
        super(Error, self).__init__(message, *args)

class InterfaceError(Error):
    """
    Exception raised for errors that are related to the database interface rather than the database itself.
    """
        super(InterfaceError, self).__init__(message, *args)

class DatabaseError(Error):
    """
    Exception raised for errors that are related to the database.
    """
        super(DatabaseError, self).__init__(message, *args)

class DataError(DatabaseError):
    """
    Exception raised for errors that are due to problems with the processed data like division by zero,
    numeric value out of range, etc.
    """
        super(DataError, self).__init__(message, *args)

class OperationalError(DatabaseError):
    """
    Exception raised for errors that are related to the database's operation and not necessarily under
    the control of the programmer, e.g. an unexpected disconnect occurs, the data source name is not found,
    a transaction could not be processed, a memory allocation error occurred during processing, etc.
    """
        super(OperationalError, self).__init__(message, *args)

class IntegrityError(DatabaseError):
    """
    Exception raised when the relational integrity of the database is affected, e.g. a foreign key check fails.
    """
        super(IntegrityError, self).__init__(message, *args)

class InternalError(DatabaseError):
    """
    Exception raised when the database encounters an internal error, e.g. the cursor is not valid anymore,
    the transaction is out of sync, etc.
    """
        super(InternalError, self).__init__(message, *args)

class ProgrammingError(DatabaseError):
    """
    Exception raised for programming errors, e.g. table not found or already exists, syntax error in the SQL statement,
    wrong number of parameters specified, etc.
    """
        super(ProgrammingError, self).__init__(message, *args)

class NotSupportedError(DatabaseError):
    """
    Exception raised in case a method or database API was used which is not supported by the database,
    e.g. requesting a .rollback() on a connection that does not support transaction or has transactions turned off.
    """
        super(NotSupportedError, self).__init__(message, *args)

# --- Connection Objects ---

class Connection:
    """
    This class implements the connection object of the |sec| API. The connection object manages all operations at server
    level and provides access to the current connection instance with the server. The connection object provides the
    cursor() method to create a cursor for the execution of operations at the database level.
    """

    def __init__(self, host, port, username, passwd, database):
        """
        Constructor of the Connection object.

        :param host: The host of the |sec| Server as IP-address or as qualified name (localhost).
        :param port: The port of the |sec| Server.
        :param username: The username for the connection.
        :param passwd: The password for the connection.
        :param database: The name of the database.
        """
        self.host = host
        self.port = port
        self.username = username
        self.passwd = passwd
        self.database = database
        self.socket_object = socket(AF_INET, SOCK_STREAM)
        self.socket_object.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
        self.initialized = False
        self.server_mode_only = False
        self.transaction_init = False

        self.__initialize()

    def __initialize(self):
        """
        Initializes the connection to the |sec| server.

        :return: None
        """

        # Connect to socket using the host and the port

        try:
            self.socket_object.connect((self.host, self.port))
        except ConnectionRefusedError as e:
            self.socket_object.close()
            raise InterfaceError(e.args[1])
        except OSError as e:
            raise InterfaceError(e.args[1] + ' - Check connection parameters.')

        # Receive initial data from socket

        intro_string = ''

        try:
            intro_string, ok_message = parser.receive_response(self.socket_object)
        except OperationalError:
            raise

        if intro_string == messages.SECONDO_OK:

            # If Secondo accepted the token, proceed with the connection to the server

            conn_string = messages.SECONDO_CONNECT_START + self.username + '\n' \
                                                         + self.passwd + '\n' \
                                                         + messages.SECONDO_CONNECT_END
            self.socket_object.sendall(conn_string.encode())

            # Get response list from socket

            try:
                intro_string, success_message = parser.receive_response(self.socket_object)
            except OperationalError:
                raise

            # Check intro string, print success message from the server

            if intro_string == messages.SECONDO_INTRO_START:
                self.initialized = True
                print(success_message)

                # If a database name was provided, connect to database

                if self.database != '':
                    try:
                        self.open_database(self.database)
                    except OperationalError:
                        self.server_mode_only = True
                        self.database = ''
                        self.username = ''
                        self.passwd = ''
                        self.socket_object.close()
                        raise
                    except InterfaceError:
                        raise
                    else:
                        self.server_mode_only = False
                        return True

                else:
                    self.server_mode_only = True
                    return True
            else:
                raise OperationalError('Error: Bad server response.')

        else:
            try:
                self.socket_object.close()
            except OSError as e:
                raise InterfaceError(e.args[1])
            else:
                self.socket_object = None
                self.initialized = False
                raise InterfaceError('Error: No response from Secondo server')

    def close(self):
        """
        Close the connection now.

        The connection will be unusable from this point forward; an InternalError exception will be raised if any
        operation is attempted with the connection. The same applies to all cursor objects trying to use the connection.
        Note that closing a connection without committing the changes first will cause an implicit rollback to be
        performed.

        :return: True, if the connection was closed successfully.
        """

        if self.initialized:

            # Any pending transaction will be rolled back

            if self.transaction_init:
                try:
                    self.rollback()
                except OperationalError:
                    raise
                except InterfaceError:
                    raise

            close_string = messages.SECONDO_DISCONNECT_END

            # Close open databases

            if not self.server_mode_only:
                try:
                    self.close_database()
                    raise
            try:
                self.socket_object.sendall(close_string.encode())
                raise OperationalError(e.args[1])
            else:
                return True

        else:
            raise InterfaceError('Error: The connection object is not initialized.')

    def start_transaction(self):
        """
        Starts a transaction in the database.

        :return: True, if the transaction was started successfully.
        """

            if self.server_mode_only:
                raise OperationalError('No connection to a database available.')

            command_level = 1
            operation = com.SECONDO_COM_BEGIN_TR
            command_string = messages.SECONDO_COMMAND_START + str(command_level) + '\n' \
                                                                + operation + '\n' \
                                                                + messages.SECONDO_COMMAND_END

            self.socket_object.sendall(command_string.encode())

            try:
                response_string, receive_list, error_dict = parser.receive_response(self.socket_object)
                raise

            if error_dict['code'] != 0:
                if error_dict['code'] == 100:
                    raise InterfaceError('Secondo: Unexpected fatal system error.')
                else:
                    raise InterfaceError(error_dict['message'])
            else:
                self.transaction_init = True

        else:

    def commit(self):
        """
        Commit any pending transaction to the database.

        :return: True, if the current transaction was commit successfully.
        """

                raise InterfaceError('No connection to a database available.')

                command_level = 1
                operation = com.SECONDO_COM_COMMIT_TR
                command_string = messages.SECONDO_COMMAND_START + str(command_level) + '\n' \

                self.socket_object.sendall(command_string.encode())

                try:
                    response_string, receive_list, error_dict = parser.receive_response(self.socket_object)
                    raise

                if error_dict['code'] != 0:
                    if error_dict['code'] == 100:
                        raise InterfaceError('Secondo: Unexpected fatal system error.')
                    else:
                        raise InterfaceError(error_dict['message'])
                else:
                    self.transaction_init = False

            else:
                raise InternalError('No transactions were initiated.')

        else:

    def rollback(self):
        """
        In case a database does provide transactions this method causes the database to roll back to the start of any
        pending transaction. Closing a connection without committing the changes first will cause an implicit rollback
        to be performed.

        :return: True, if the current transaction was rolled back successfully.
        """

                operation = com.SECONDO_COM_ABORT_TR

                try:
                    raise

                    else:
                else:

            else:
                raise OperationalError('No transactions were initiated.')

        else:

    def cursor(self):
        """
        Return a new Cursor Object using the connection.

        If the database does not provide a direct cursor concept, the module will have to emulate cursors using other
        means to the extent needed by this specification.

        :return: A Cursor object.
        """
        cursor = Cursor(self)
        return cursor

    def create_database(self, database_name):
        """
        Creates a database on the |sec| server. The database remains open after creation.

        :param database_name: The name of the new database.
        :return: True, if the database was created successfully.
        """

            database_name.lower()

            if parser.check_identifier(database_name):

                operation = com.SECONDO_COM_CREATE_DB.format(database_name)

                try:
                    raise

                    if error_dict['code'] == 701:
                        raise OperationalError('The database ' + database_name.upper() + ' already exists.')
                    else:
                        raise OperationalError(error_dict['message'])
                else:

            else:
                raise OperationalError('The database name has invalid characters.')

        else:
            raise InterfaceError('The connection has not been initialized.')

    def restore_database(self, database_name, database_location):
        """
        Restores a database from a specific location into the |sec| server. After restoring the database will be
        available for establish a connection.

        :param database_name: The name of the database.
        :param database_location: The location of the database.
        :return: True, if the database was successfully restored.
        """

                operation = com.SECONDO_COM_RESTORE_DB_FROM.format(database_name, database_location)

                try:
                    raise

                    else:
                else:
                    try:
                        self.close_database()
                        raise
                    else:

            else:

        else:

    def delete_database(self, database_name):
        """
        Deletes a database on the |sec| server. Before deletion, all opened databases on the Secondo server will be
        closed. The connection object will be set to server-only mode.

        :return: True, if the database was deleted successfully.
        """

            # Close databases, if any of them are open

                try:
                    raise

                database_name.lower()
                operation = com.SECONDO_COM_DELETE_DB.format(database_name)

                try:
                    raise

                    else:
                else:

            else:

        else:

    def get_list_databases(self):
        """
        Returns a list of the databases in the |sec| server.

        :return: A Python list with the names of the available databases on the |sec| server.
        """

            operation = com.SECONDO_COM_LIST_DB
                                                            + operation + '\n' \
                                                            + messages.SECONDO_COMMAND_END

            try:
                raise

                else:
            else:
                databases = parser.parse_inquiry_databases(receive_list)
                return databases
        else:

    def get_list_objects(self):
        """
        Returns a list of the objects in the |sec| server.

        :return: A Python list with the names available objects of the currently opened database.
        """

            operation = com.SECONDO_COM_LIST_OBJECTS

            try:
                raise

                else:
            else:
                objects = parser.parse_inquiry_objects(receive_list)
                return objects
        else:

    def get_list_types(self):
        """
        Returns a list of the types available in the current open database.

        :return: A list expression object with the available types of the currently opened database.
        """

            operation = com.SECONDO_COM_LIST_TYPES

            try:
                raise

                else:
            else:
                types = parser.parse_inquiry_types(receive_list)
                return types
        else:

    def get_list_type_constructors(self):
        """
        Returns a list of the type constructors available in the |sec| server.

        :return: A Python list with the constructors of the available types of the currently opened database.
        """

            operation = com.SECONDO_COM_LIST_TYPE_CONS

            try:
                raise

                else:
            else:
                type_constructors = parser.parse_inquiry_type_constructors(receive_list)
                return type_constructors
        else:

    def get_list_algebras(self):
        """
        Returns a list of the available algebras in the |sec| server.

        :return: A Python list with the available algebras_1 on the |sec| server.
        """

            operation = com.SECONDO_COM_LIST_ALGEBRAS

            try:
                raise

                else:
            else:
                algebras = parser.parse_inquiry_algebras(receive_list)
                return algebras
        else:

    def get_algebra(self, algebra_name):
        """
        Returns the details for an algebra from the |sec| server.

        :return: A Python list with the available algebras on the |sec| server.
        """

            operation = com.SECONDO_COM_LIST_ALGEBRA.format(algebra_name)

            try:
                raise

                else:
            else:
                algebra = parser.parse_inquiry_algebra(receive_list)
                return algebra
        else:

    def open_database(self, database_name):
        """
        Opens a database on the |sec| server. The server-only mode will be set to False.

        :return: True, if the database was opened successfully.
        """

            operation = com.SECONDO_COM_OPEN_DB.format(database_name)

            try:
                raise

                else:
            else:
                self.server_mode_only = False
                self.database = database_name
        else:

    def close_database(self):
        """
        Closes the currently opened database on the |sec| server. The connection object returns to the server-only
        mode.

        :return: True, if the database was closed successfully.
        """

            operation = com.SECONDO_COM_CLOSE_DB
                                                                                 + operation + '\n' \
                                                                                 + messages.SECONDO_COMMAND_END

            try:
                raise

                else:
            else:
                self.server_mode_only = True
                self.database = ''
        else:

class Cursor:

    description = None
    rowcount = 0

    def __init__(self, connection):
        """
        Constructor of the Cursor object. The database can be initialized during the initialization of the Connection
        object or alternatively in the Cursor object through this constructor. It is important to set previously the
        variable database in the Connection object, otherwise the database won´t be initialized. In that case, an
        InterfaceError will be raised.

        :param connection: A Connection object.
        """
        self.connection = connection
        self.result = []

        # If the connection to the database is not existent, connect to provided database

        if self.connection.server_mode_only:
            if self.connection.database != '':
                try:
                    self.connection.open_database(self.connection.database)
                    raise
                else:
                    self.initialized = True
            else:
                raise OperationalError("No database provided or initialized. The cursor couldn´t be created.")
        else:
            self.initialized = True

        """
        Close the cursor now (rather than whenever __del__ is called).

        The cursor will be unusable from this point forward; an InternalError exception will be raised if any
        operation is attempted with the cursor.

        :return: True, if the cursor was closed successfully.
        """
            try:
                self.connection.close_database()
            except InterfaceError:
                raise
            else:
                self.connection.database = ''
        else:
            raise OperationalError("The cursor has not been initialized")

    def execute(self, operation, parameters=None):
        """
        Prepare and execute a database operation (query or command).

        Parameters may be provided as sequence or mapping and will be bound to variables in the operation. The
        placeholders in the operation must use the following format: {i}, where i is an integer, which
        specifies the position of the parameter in the list of parameters.

        Example 1: Simple Query

        Operation = query {0}
        Parameter list = ['mehringdamm']
        Formatted operation = query mehringdamm

        Example 2: Inserting Single Tuples

        Operation = query {0} inserttuple[{1}, {2}] count;
        Parameter list = ['myfirstrel','"Anna"', '27']
        Formatted operation = query myfirstrel inserttuple["Anna", 27] count;

        :param operation: A string with a |sec| command.
        :param parameters: Further parameters for the execution of the command (currently not in use).
        :return: A |sec| object, a list with |sec| objects or a |sec| response.
        """

        self.description = ''
        self.rowcount = 0

        # Apply parameters, if some are supplied

        if parameters is not None:
            operation = com.apply_parameters_to_operation(operation, parameters)

        # Process operation

            try:
                self.connection.socket_object.sendall(command_string.encode(encoding, error_encoding))
            except UnicodeEncodeError as e:
                raise ProgrammingError(e.args[0])
            else:
                try:
                    response_string, receive_list, error_dict = parser.receive_response(self.connection.socket_object)
                    raise

                if error_dict['code'] == 6:  # No database open
                else:
                    raise ProgrammingError(error_dict['message'])
            else:
                if receive_list.get_list_length() > 1:
                    try:
                        query_response, object_type = parser.parse_query(receive_list)
                    except InterfaceError as e:
                        raise InterfaceError(e.args[0])
                    else:
                        self.rowcount = 1
                        self.description = object_type

                        string_output = str(receive_list)

                        self.result.append([query_response, object_type, string_output])
                        return query_response, object_type, string_output

                else:
                    return 'Success', '', 'Success'

        else:
            raise InternalError('The cursor has not been initialized or is already closed.')

    def executemany(self, operation, seq_of_parameters):
        """
        Prepare a database operation (query or command) and then execute it against all parameter sequences or mappings
        found in the sequence seq_of_parameters. Every entry of the list seq_of_parameters must be declared as a list,
        even when just one parameter is being used.

        The placeholders in the operation must use the following format: {i}, where i is an integer number, which

        :param operation: A |sec| command with placeholders.
        :param seq_of_parameters: A list with further parameters for the execution of the commands of the list.
        :return: A list with entries. Each entry is a list with an |sec| object, the type of the response and the list
                 expression as string.
        """

        response_list = []

        # Apply parameters to operation

        for parameter_list in seq_of_parameters:

            response = self.execute(operation=operation, parameters=parameter_list)
            response_list.append(response)
            self.rowcount += 1

        self.description = 'response list'

        return response_list

    def fetchone(self):
        """
        Fetch the next row of a query result set, returning a single sequence, or None when no more data is available.

        An InterfaceError exception is raised if the previous call to .execute*() did not produce any result set or
        no call was issued yet.

        :return: A single row of a query result set.
        """
            if self.rowcount >= 1:
                try:
                    one_row = self.result.pop(0)
                    return one_row
                except IndexError:
                    return None
            else:
                raise InterfaceError('Error: No rows in cursor available.')
        else:

    def fetchmany(self, size=1):
        """
        Fetch the next set of rows of a query result, returning a sequence of sequences (e.g. a list of tuples). An
        empty sequence is returned when no more rows are available. If there are not enough rows to fulfil the size
        parameter, only the available rows will be returned.

        :param size: The number of the rows to be returned from the cursor.
        :return: A list with the fetched elements.
        """
            if size > 0:
                if self.rowcount >= 1:

                    qty_elements = len(self.result)

                    if qty_elements >= size:
                        try:
                            response_set = self.result[0:size]
                            del self.result[0:size]
                        except IndexError:
                            return None
                        else:
                            return response_set
                    else:
                        try:
                            response_set = self.result[0:qty_elements]
                            del self.result[0:qty_elements]
                        else:
                else:
                    raise InternalError('Error: No rows in cursor available.')
            else:
                raise InterfaceError('Error: Invalid set size.')
        else:

    def fetchall(self):
        """
        Fetch all (remaining) rows of a query result, returning them as a sequence of sequences (e.g. a list of tuples).
        An empty sequence is returned when no more rows are available.

        """
            qty_elements = len(self.result)
                if qty_elements > 0:
                    return self.fetchmany(qty_elements)
                else:
                    return []
            else:
                raise InternalError('Error: No rows in cursor available.')
        else:

    def execute_create_empty_relation(self, relation_name: str, list_attributes: []):
        """
        Creates an empty relation in the database. The list of the attributes must follow the following format:

        list_attributes = [attr]

        where

        attr = [Name, type]

        IMPORTANT: The name of the attributes must begin with an upper case character.

        Example:

        attr1 = ['Att1', 'string']
        attr2 = ['Att2', 'string']

        list_attributes = [attr1, attr2]

        :param relation_name: The name of the relation.
        :param list_attributes: A list with the attributes of the relation (please be aware of the format).
        :return: The string 'Success', if the relation was successfully created, otherwise None.
        """

            if len(list_attributes) > 0:
                str_attr_list = '['
                counter = 0
                for attr in list_attributes:

                    if attr[0] is not None:
                        str_attr_list = str_attr_list + attr[0] + ' : ' + attr[1]
                        if counter < len(list_attributes) - 1:
                            str_attr_list = str_attr_list + ', '
                        counter += 1
                    else:
                        raise ProgrammingError('The attribute at position '
                                               + str(counter) + ' has no valid identifier.')

                str_attr_list = str_attr_list + ']'

                command_string = com.SECONDO_COM_REL_CREATE_EMPTY.format(relation_name, str_attr_list)

                try:
                    response = self.execute(command_string)
                    return response[0]
                except ProgrammingError:
                    raise

            else:
                raise ProgrammingError('Error: List of attributes is empty.')

        else:

    def execute_insert_tuple_into_relation(self, relation_name: str, single_tuple: []):
        """
        Inserts a list of values into a relation.

        :param single_tuple: A single tuple of the relation. The single tuple is a list must contain two elements: the
                             first element is a list with the values of the tuple, and the second element is a list with
                             the types of the relation.
        :return: The string 'Success', if the tuples were successfully added, otherwise None.
        """

            if len(single_tuple) > 0:

                counter = 0

                str_value_list = ''

                for column in range(len(single_tuple[0])):

                    field_type = single_tuple[1][column]
                    value = single_tuple[0][column]

                    const_value = com.SECONDO_COM_REL_CONST_VALUE.format(field_type, value)

                    str_value_list = str_value_list + const_value
                    if counter < len(single_tuple[0]) - 1:
                        str_value_list = str_value_list + ', '
                    counter += 1

                command_string = com.SECONDO_COM_REL_INSERT_TUPLE.format(relation_name, str_value_list)

                try:
                    raise

            else:
                raise ProgrammingError('Error: List of values is empty.')

        else:

    def execute_simple_query(self, value_expression):
        """
        Evaluates the given value expression and displays the result object.

        :param value_expression: A value expression for the command 'query <value expression>'
        :return: A list expression object with the response from the |sec| server.
        """

            command_string = com.SECONDO_COM_QUERY.format(value_expression)

            try:
                response = self.execute(command_string)
            except ProgrammingError:
                raise
            else:
                return response[0]

        else:

    def execute_let(self, identifier, value_expression):
        """
        This command does almost the same as the query command. In contrast, the result of the <value expression> is not
        displayed on the screen. Instead, the result is stored in an object with the name <identifier>. The command only
        runs successfully if the object does not exist yet in the database; otherwise, an error message is displayed.

        :param identifier: The identifier of the object.
        :param value_expression: A value expression for the command 'let <identifier> = <value expression>'
        """

            if not parser.check_identifier(identifier):
                raise OperationalError('Invalid identifier.')

            command_string = com.SECONDO_COM_LET.format(identifier, value_expression)

            try:
                raise
            else:

        else:

    def execute_derive(self, identifier, value_expression):
        """
        This works basically in the same way as the let command. The difference is the handling of the created objects
        during creating and restoring a database dump. The derive command should be used for objects that have no
        external representation, e.g., indexes.

        :param value_expression: A value expression for the command 'derive <identifier> = <value expression>'
        """

            command_string = com.SECONDO_COM_DERIVE.format(identifier, value_expression)

            try:
                raise
            else:

        else:

    def execute_update(self, identifier, value_expression):
        """
        Assigns the result of the value expression to an existing object in the database.

        :param value_expression: A value expression for the command 'update <identifier> := <value expression>'
        :return: The string Success, if the object was updated successfully.
        """

            command_string = com.SECONDO_COM_UPDATE.format(identifier, value_expression)

            try:
                raise
            else:
        else:

    def execute_delete(self, identifier):
        """
        Deletes the object with the name <identifier> from the currently opened database.

        :return: The string Success, if the object was deleted successfully.
        """

            command_string = com.SECONDO_COM_DELETE.format(identifier)

            try:
                raise
            else:
        else:

    def execute_create_type(self, identifier, type_expression):
        """
        Creates a named type in the database.

        :param identifier: The identifier of the type to be created.
        :param type_expression: A type expression for the command 'type <identifier> = <type expression>'
        :return: The string Success, if the named type was created successfully.
        """

            command_string = com.SECONDO_COM_TYPE.format(identifier, type_expression)

            try:
                raise
            else:
        else:

    def execute_delete_type(self, identifier):
        """
        Deletes a named type from the database.

        :param identifier: The identifier of the type to be deleted.
        :return: The string Success, if the named type was deleted successfully.
        """

            command_string = com.SECONDO_COM_DELETE_TYPE.format(identifier)

            try:
                raise
            else:
        else:

    def execute_create(self, identifier, type_expression):
        """
        Creates an object of the given type with undefined value.

        :param identifier: The identifier of the object to be created.
        :param type_expression: A type expression for the command 'create <identifier> = <type expression>'
        :return: The string Success, if the object was created successfully.
        """

            command_string = com.SECONDO_COM_CREATE.format(identifier, type_expression)

            try:
                raise
            else:
        else:

    def execute_kill(self, identifier):
        """
        Removes the object with the name <identifier> from the opened database catalog without removing its data
        structures. Generally, the delete command should be used to remove database objects. The kill command should
        only be used if the delete command crashes the database due to corrupted persistent data structures for this
        object.

        :param identifier: The identifier of the object to be "killed".
        :return: The string Success, if the object was "killed" successfully.
        """

            command_string = com.SECONDO_COM_KILL.format(identifier)

            try:
                raise
            else:
        else:

# Module
# Secondo Commands
# secondocommands.py
"""
The module Secondo Commands contains the strings of the commands to interact with the |sec| server and its objects.
The strings can be formatted using the Python method format() to replace the placeholders (i.e. {0} or {1}).
"""

# ---- Basic commands ---- #
import re
import secondodb.api.secondoapi as api

SECONDO_COM_QUERY = 'query {0}'
SECONDO_COM_LET = 'let {0} = {1}'
SECONDO_COM_DERIVE = 'derive {0} = {1}'
SECONDO_COM_UPDATE = 'update {0} := {1}'
SECONDO_COM_DELETE = 'delete {0}'
SECONDO_COM_TYPE = 'type {0} = {1}'
SECONDO_COM_DELETE_TYPE = 'delete type {0}'
SECONDO_COM_CREATE = 'create {0} : {1}'
SECONDO_COM_KILL = 'kill {0}'

# ---- Databases ----- #

SECONDO_COM_CREATE_DB = 'create database {0}'
SECONDO_COM_OPEN_DB = 'open database {0}'
SECONDO_COM_CLOSE_DB = 'close database'
SECONDO_COM_DELETE_DB = 'delete database {0}'
SECONDO_COM_RESTORE_DB = 'restore database {0}'

# ---- Transactions ----- #

SECONDO_COM_BEGIN_TR = 'begin transaction'
SECONDO_COM_COMMIT_TR = 'commit transaction'
SECONDO_COM_ABORT_TR = 'abort transaction'

# ---- Inquiries ----- #

SECONDO_COM_LIST_DB = 'list databases'
SECONDO_COM_LIST_TYPE_CONS = 'list type constructors'
SECONDO_COM_LIST_OPERATORS = 'list operators'
SECONDO_COM_LIST_ALGEBRAS = 'list algebras'
SECONDO_COM_LIST_ALGEBRA = 'list algebra {0}'
SECONDO_COM_LIST_TYPES = 'list types'
SECONDO_COM_LIST_OBJECTS = 'list objects'

# ---- Relations ----- #

SECONDO_COM_REL_CREATE_EMPTY = 'let {0} = [ const rel (tuple({1})) value () ]'
SECONDO_COM_REL_INSERT_TUPLE = 'query {0} inserttuple[{1}] count'
SECONDO_COM_REL_CONST_VALUE = '[const {0} value {1}]'

# ---- Import and Export ----- #

SECONDO_COM_SAVE_DB = 'save database to {0}'
SECONDO_COM_RESTORE_DB_FROM = 'restore database {0} from {1}'
SECONDO_COM_SAVE_OBJECT = 'save {0} to {1}'
SECONDO_COM_RESTORE_OBJECT = 'restore {0} from {1}'

SECONDO_VALIDITY_STRING = 'bnl'

SECONDO_COM_ID_OBJECT = 'OBJECT'
SECONDO_COM_ID_OBJECTS = 'OBJECTS'

SECONDO_COM_TYPE_REL = 'rel'
SECONDO_COM_TYPE_TUPLE = 'tuple'

def apply_parameters_to_operation(operation, parameter_list):
    """
    Replaces the placeholders of the operation with the parameters of the list.

    :param operation: A string with an operation, which includes placeholders for the parameters.
    :param parameter_list: A list of parameters.
    :return: The formatted operation.
    """

    try:
        formatted_operation = operation.format(*parameter_list)
    except IndexError:
        param_supplied = len(parameter_list)
        param_needed = len(re.findall(r"{(\w+)}", operation))
        raise api.ProgrammingError('Error in parameters: ' + str(param_supplied) + ' parameter(s) supplied, '
                                   + str(param_needed) + ' where expected.')
    else:
        return formatted_operation

# Module
# Secondo Parser
# secondoparser.py
"""
The module Secondo Parser implements functions for parsing a response to an inquiry or a command from the |sec|
server. A list expression object can be constructed through the function receive_response. A list expression object
for a specific inquiry can be then converted to a Python structure (dictionary or list), which can be used to print or
display the results in an ad-hoc format.
"""
from socket import socket
from secondodb.api.support.secondolistexpr import ListExp
import secondodb.api.support.secondoinputhandler as inputhandler
import secondodb.api.algebras.secondostandardalgebra as standardalgebra
import secondodb.api.algebras.secondospatialalgebra as spatialalgebra
import secondodb.api.algebras.secondorelationalalgebra as relationalalgebra
import secondodb.api.algebras.secondospatiotemporalalgebra as spatiotemporalalgebra
import re
from secondodb.api.algebras.secondogeneraldataclasses import Attribute, Type, Operator, Algebra, Object

def receive_response(socket_object: socket):
    """
    This method handles the |sec| response from the server. After processing two parameters will be returned: The first
    parameter contains the opening string of the response (for example <SecondoIntro>). The second parameter depends on
    this string, as it can be a simple string with a message or a List Expression object with the result of an inquiry.

    In case of an error, a dictionary structure with the details (error code, position and message) will be returned. In
    any case, the returned result list corresponds to the fourth element of the obtained list expression object from the
    server response.

    :param socket_object: A socket object with the response from the server.
    :return: The opening string of the response and a list or a message.
    """

    try:
        line = inputhandler.read_line_of_text(socket_object)
    except api.OperationalError:
        raise

    if line == messages.SECONDO_OK:
        ok_message = 'Response from Secondo Server received.'
        return messages.SECONDO_OK, ok_message

    # Handling of the case <SecondoIntro>

    if line == messages.SECONDO_INTRO_START:

        success_message = ''

        while True:
            try:
                message = inputhandler.read_line_of_text(socket_object)
            except api.OperationalError:
                raise
            if message != messages.SECONDO_INTRO_END:
                success_message = message
            else:
                break
        return messages.SECONDO_INTRO_START, success_message

    # Handling of the case <Message>

    if line == messages.SECONDO_MESSAGE_START:

        while line == messages.SECONDO_MESSAGE_START:

            result_list = inputhandler.build_list_expr_from_binary(socket_object)

            first_element = result_list.get_first_element()
            second_element = result_list.get_second_element()

            if first_element.value == messages.SECONDO_PROGRESS:
                __progress_display(second_element.value.value, second_element.next.value, 'completed.')

            try:
                response_end = inputhandler.read_line_of_text(socket_object)
                raise

            # Get the end line of the response

            if response_end != messages.SECONDO_MESSAGE_END:
                raise api.InterfaceError('End of response not reached.')

            try:
                line = inputhandler.read_line_of_text(socket_object)
                raise

        __line_flush()

    # Handling of the case <SecondoError>

    if line == messages.SECONDO_ERROR_START:
        raise api.InterfaceError('Unknown error')

    # Handling of the case <SecondoResponse>

    if line == messages.SECONDO_RESPONSE_START:

        response_string = messages.SECONDO_RESPONSE_START
        result_list = inputhandler.build_list_expr_from_binary(socket_object)

        # Error handling - Error details will be returned as a dictionary structure

        error_code = result_list.get_first_element().value
        error_pos = result_list.get_second_element().value
        error_message = result_list.get_third_element().value

        error_dict = {}

        error_dict.update({'code': error_code})
        error_dict.update({'pos': error_pos})
        error_dict.update({'message': error_message})

        # Set the fourth element of the result list expression object as the returned result list

        result_list = result_list.get_fourth_element()

        try:
            response_end = inputhandler.read_line_of_text(socket_object)
        except api.OperationalError:
            raise

        # Get the end line of the response

        if response_end != messages.SECONDO_RESPONSE_END:
            raise api.InterfaceError('Fatal Error: End of response not reached.')

        return response_string, result_list, error_dict

    else:

        return ListExp()

def parse_inquiry_databases(list_expr: ListExp) -> []:
    """
    Returns a list with the database names of a database inquiry.

    :param list_expr: A List Expression object with a database inquiry.
    :return: A list with the database names.
    """

    db_list = []

    second_element = list_expr.get_second_element()
    databases = second_element.get_second_element()

    next_element = databases

    while next_element.next is not None:
        db_list.append(next_element.value.value)
        next_element = next_element.next

    return db_list

def parse_inquiry_algebras(list_expr: ListExp) -> []:
    """
    Returns a list with the algebras of the server.

    :param list_expr: A list expression object.
    :return: The list of the algebras.
    """

    algebra_list = []

    algebras = second_element.get_second_element()

    next_element = algebras

        algebra_list.append(next_element.value.value)

    return algebra_list

def parse_inquiry_algebra(list_expr: ListExp) -> Algebra:
    """
    Returns a list with the objects of a database.

    :param list_expr: A response list.
    :return: An Algebra object.
    """

    algebra_name = second_element.get_second_element().value.value

    type_constructors = second_element.get_second_element().get_second_element().get_first_element()
    operators = second_element.get_second_element().get_second_element().get_second_element()

    # ------------------------------------------------ Process type constructors

    type_list = []

    next_element = type_constructors

        type_le = next_element.value

        first_element = type_le.get_first_element()  # Type name

        type_name = first_element.value

        # Process properties headers

        second_element = type_le.get_second_element()  # Headers

        headers = []

        while second_element.next is not None:
            headers.append(second_element.value.value)
            second_element = second_element.next

        # Process properties values

        third_element = type_le.get_third_element()  # Values

        values = []

        while third_element.next is not None:
            values.append(third_element.value.value)
            third_element = third_element.next

        properties = {}

        properties_iter = list(zip(headers, values))

        for prop_name, prop_value in properties_iter:
            properties.update({prop_name: prop_value})

        type_list.append(Type(type_name=type_name, properties=properties))

    # ------------------------------------------------ Process operators

    operator_list = []

    next_element = operators

        operator_le = next_element.value

        first_element = operator_le.get_first_element()

        operator_name = first_element.value

        second_element = operator_le.get_second_element()  # Headers

        third_element = operator_le.get_third_element()  # Values

        values = []

        operator_list.append(Operator(operator_name=operator_name, properties=properties))

    return Algebra(algebra_name=algebra_name, type_list=type_list, operator_list=operator_list)

def parse_inquiry_type_constructors(list_expr: ListExp) -> []:
    """
    Parses a types inquiry from a |sec| server response.

    :param list_expr: A list expression object with the types of the response.

    :return: A list containing the type constructors.
    """

    types = second_element.get_second_element()

    next_element = types

        type_list.append(next_element.value.value.value)

    return type_list

def parse_inquiry_types(list_expr: ListExp) -> []:
    """

    :return: A list with objects of the class Type containing the types.
    """

    types = second_element.get_second_element().next

        single_value = next_element.value

        type_name = single_value.get_second_element().value
        properties = single_value.get_third_element().value

def parse_type_definition(list_expr: ListExp):
    """
    Parses a type definition element from a |sec| server response.

    :param list_expr: A list expression object with the type definition of the response.
    :return: A string value with the type and a list with the attributes, if the type corresponds to a relation.
             Otherwise the list parameter will be set to None. Every single attribute is an object of the class
             Attribute.
    """

    attr_list = None

    type_definition = list_expr.get_first_element()

    if isinstance(type_definition, ListExp):

        # Describe relation

        dtype = type_definition.value

        if dtype == 'rel':

            tuple_as_le = list_expr.get_second_element()

            attributes_as_le = tuple_as_le.get_second_element()
            number_of_attributes = attributes_as_le.get_list_length()

            attr_list = []
            for i in range(1, number_of_attributes):

                single_attribute_as_le = attributes_as_le.get_the_n_element(i)

                attribute_name = single_attribute_as_le.get_first_element().value
                attribute_type = single_attribute_as_le.get_second_element().value

                attr_list.append(Attribute(attribute_type=attribute_type, attribute_name=attribute_name))

        else:
            pass

    else:  # Atom
        dtype = type_definition

    return dtype, attr_list

def parse_inquiry_objects(list_expr: ListExp) -> []:
    """
    Parses an objects inquiry from a |sec| server response.

    :return: A list with objects of the class Object containing the types.
    """

    objects = []

    list_objects_as_le = list_expr.get_second_element().get_second_element()
    number_of_objects = list_objects_as_le.get_list_length()

    for i in range(2, number_of_objects):

        single_object_as_le = list_objects_as_le.get_the_n_element(i)

        # Do something with the object, which comes with the format (OBJECT BGrenzenLine () (line))

        object_name_as_le = single_object_as_le.get_second_element()
        object_type_as_le = single_object_as_le.get_fourth_element().value

        dtype, attr_list = parse_type_definition(object_type_as_le)

        object_name = object_name_as_le.value
        object_type = dtype
        attributes = attr_list

        objects.append(Object(object_name=object_name, object_type=object_type, attributes=attributes))

    return objects

def parse_query(list_expr: ListExp):
    """
    Converts the result of a query in list expression format into Python objects.

    :return: A Python object.
    """

    first_element = list_expr.get_first_element()

    dtype, attr_list = parse_type_definition(first_element)

    object_type = dtype
    object_data = list_expr.next.value

    # ---------------------------------------------------- STANDARD ALGEBRA

    if object_type == 'int':
        int_value = standardalgebra.parse_int(object_data)
        return int_value, object_type
    elif object_type == 'real':
        real_value = standardalgebra.parse_real(object_data)
        return real_value, object_type
    elif object_type == 'bool':
        bool_value = standardalgebra.parse_bool(object_data)
        return bool_value, object_type
    elif object_type == 'string':
        string_value = standardalgebra.parse_string(object_data)
        return string_value, object_type
    elif object_type == 'longint':
        longint_value = standardalgebra.parse_longint(object_data)
        return longint_value, object_type
    elif object_type == 'rational':
        rational_value = standardalgebra.parse_rational(object_data)
        return rational_value, object_type

    # ---------------------------------------------------- SPATIAL ALGEBRA

    elif object_type == 'point':
        point = spatialalgebra.parse_point(object_data)
        return point, object_type
    elif object_type == 'points':
        points = spatialalgebra.parse_points(object_data)
        return points, object_type
    elif object_type == 'line':
        line = spatialalgebra.parse_line(object_data)
        return line, object_type
    elif object_type == 'region':
        region = spatialalgebra.parse_region(object_data)
        return region, object_type

    # ---------------------------------------------------- RELATIONAL ALGEBRA

    elif object_type == 'rel':
        rel = relationalalgebra.parse_relation(object_data, attr_list)
        return rel, object_type

    # ---------------------------------------------------- SPATIO TEMPORAL ALGEBRA

    elif object_type == 'mpoint':
        mpoint = spatiotemporalalgebra.parse_mpoint(object_data)
        return mpoint, object_type
    elif object_type == 'mregion':
        mregion = spatiotemporalalgebra.parse_mregion(object_data)
        return mregion, object_type
    elif object_type == 'mreal':
        mreal = spatiotemporalalgebra.parse_mreal(object_data)
        return mreal, object_type
    elif object_type == 'mint':
        mint = spatiotemporalalgebra.parse_mint(object_data)
        return mint, object_type
    elif object_type == 'mstring':
        mstring = spatiotemporalalgebra.parse_mstring(object_data)
        return mstring, object_type
    elif object_type == 'iregion':
        iregion = spatiotemporalalgebra.parse_iregion(object_data)
        return iregion, object_type
    elif object_type == 'ipoint':
        ipoint = spatiotemporalalgebra.parse_ipoint(object_data)
        return ipoint, object_type

    else:
        raise api.InterfaceError('The type of the response (' + object_type + ') is currently not supported')

def check_identifier(identifier: str) -> bool:
    """
    Checks the validity of a given value expression.

    :param identifier:
    :return: True, if the value expression is valid.
    """

    if bool(re.match('^[0-9a-zA-Z_]+$', identifier)):
        if identifier[0] == '_':
            return False
        else:
            if bool(re.match('^[0-9]', identifier)):
                return False
            else:

def check_port(port: str) -> bool:
    """
    Checks the validity of a given port value.

    :param port: The number of the port as string.
    :return: True, if the port value is valid.
    """
    if bool(re.match('^[0-9]+$', port)):
        return True
    else:
        return False

def check_validity_string(validity_string: str) -> bool:
    """
    Checks the validity string of a |sec| response.

    :param validity_string: The string with the value 'bnl'.
    :return: True, if the string has the bnl-value. Otherwise a DataError will be raised.
    """

    if not validity_string == com.SECONDO_VALIDITY_STRING:
        raise api.DataError('The response is not valid.')
    else:

def __progress_display(position, total, message):
    """
    Internal function for displaying the execution progress of a query on the Python console.

    :param position: Integer value representing the time progress of the query.
    :param total: Integer value representing the total time amount of the query.
    :param message: A text message, which will be appended next to the progress indicator.
    :return: None
    """
    if position != 0 and total != 255:
        print("\r" + str(position) + "/" + str(total) + " " + message, end='', flush=True)

def __line_flush():
    """
    Internal function for reseting a line of the Python console.

    """
    print("\r" + "")

# January 2020
# Module
# Secondo Standard Algebra
# secondosstandardalgebra.py
"""
The module Secondo Standard Algebra implements the data types for the conversion of list expression objects with values
of the types contained in the StandardAlgebra of the |sec| system. The data types are implemented in Python
using Data Classes. Data Classes are implemented in the API like normal classes without behaviour.
Like regular classes their attributes can be called through the given names.
"""

def parse_int(list_expr: ListExp) -> int:
    """
    Parses a list expression for an integer object.

    :param list_expr: A list expression object containing an integer.
    :return: An integer.
    """
    return list_expr.value

def parse_real(list_expr: ListExp) -> float:
    """
    Parses a list expression for a real object.

    :param list_expr: A list expression object containing a real.
    :return: A float.
    """

def parse_bool(list_expr: ListExp) -> bool:
    """
    Parses a list expression for a boolean object.

    :param list_expr: A list expression object containing a boolean.
    :return: A boolean.
    """

def parse_string(list_expr: ListExp) -> str:
    """
    Parses a list expression for a string object.

    :param list_expr: A list expression object containing a string.
    :return: A string.
    """

def parse_longint(list_expr: ListExp) -> int:
    """
    Parses a list expression for a long integer object.

    :param list_expr: A list expression object containing a long integer.
    """

def parse_rational(list_expr: ListExp) -> float:
    """
    Parses a list expression for a rational object.

    :param list_expr: A list expression object containing a rational.
    """

# Module
# Secondo Spatial Algebra
# secondospatialalgebra.py
"""
The module Secondo Spatial Algebra implements the data types for the conversion of list expression objects with values
of the types contained in the SpatialAlgebra of the |sec| system. The data types are implemented in Python
"""

from dataclasses import dataclass

@dataclass
class Segment:
    """
    Implements a single segment of a line.
    """

    __slots__ = ["x1", "y1", "x2", "y2"]

    x1: float
    y1: float
    x2: float
    y2: float

class Point:
    """
    Implements a single point expressing two coordinates X and Y.
    """

    __slots__ = ["x", "y"]

    x: float
    y: float

class Line:
    """
    Implements a line as a list of single segments of the class Segment.
    """

    __slots__ = ["segments"]

    segments: []

class Region:
    """
    Implements a region as a list of faces of the class Face.
    """

    __slots__ = ["faces"]

    faces:  []

class Face:
    """
    Implements a single face of a region, containing an outercycle (list of single points) and holecycles (a list of
    cycles, where each cycle is a list of points).
    """

    __slots__ = ["outercycle", "holecycles"]

    outercycle: []
    holecycles: []

def convert_point_to_list_exp_str(point: Point) -> str:
    """
    Converts a point object to a nested list in string format.

    :param point: A point object.
    :return: The nested list as string.
    """

    point: Point

    list_exp_str = '(' \
                   + str(point.x) \
                   + ' ' \
                   + str(point.y) \
                   + ')'
    return list_exp_str

def convert_points_to_list_exp_str(points: []) -> str:
    """
    Converts a points object to a nested list in string format.

    :param points: A points object.
    """

    list_exp_str = '('

    point_count = 1
    for point in points:
        point_le = convert_point_to_list_exp_str(point)
        list_exp_str = list_exp_str + point_le
        if point_count < len(points):
            list_exp_str = list_exp_str + ' '
        else:
            list_exp_str = list_exp_str + ')'
        point_count += 1

def convert_line_to_list_exp_str(line: Line) -> str:
    """
    Converts a line object to a nested list in string format.

    :param line: A line object.
    """

    line: Line

    segments = line.segments

    segment_count = 1
    for segment in segments:
        segment_str = '(' \
                      + str(segment.x1) \
                      + ' ' \
                      + str(segment.y1) \
                      + ' ' \
                      + str(segment.x2) \
                      + ' ' \
                      + str(segment.y2) \
                      + ')'
        list_exp_str = list_exp_str + segment_str
        if segment_count < len(segments):
        else:
        segment_count += 1

def convert_region_to_list_exp_str(region: Region) -> str:
    """
    Converts a region object to a nested list in string format.

    :param region: A region object.
    """

    region: Region

    face_str = ''

    face_count = 1
    for face in region.faces:
        face_str = '('

        outercycle_str = '('
        point_count = 1
        for point in face.outercycle:

            point_str = convert_point_to_list_exp_str(point)
            outercycle_str = outercycle_str + point_str
            if point_count < len(face.outercycle):
                outercycle_str = outercycle_str + ' '
            else:
                outercycle_str = outercycle_str + ')'
            point_count += 1

        face_str = face_str + outercycle_str

        if len(face.holecycles) > 0:

            holecycle_count = 1
            for holecycle in face.holecycles:

                face_str = face_str + ' '

                holecycle_str = '('
                point_count = 1
                for point in holecycle:
                    point_str = convert_point_to_list_exp_str(point)
                    holecycle_str = holecycle_str + point_str
                    if point_count < len(holecycle):
                        holecycle_str = holecycle_str + ' '
                    else:
                        holecycle_str = holecycle_str + ')'
                    point_count += 1

                if holecycle_count < len(face.holecycles):
                    face_str = face_str + holecycle_str + ' '
                else:
                    face_str = face_str + holecycle_str + ')'

                holecycle_count += 1

        else:  # no holes
            face_str = face_str + ')'

        if face_count < len(region.faces):
            list_exp_str = list_exp_str + face_str + ' '
        else:
            list_exp_str = list_exp_str + face_str + ')'

        face_count += 1

def parse_point(list_expr: ListExp) -> Point:
    """
    Transforms a list expression object containing a point (point) to a named tuple.

    :param list_expr: A list expression object containing a point (point).
    :return: A named tuple with the point.
    """

    x = list_expr.get_first_element().value
    y = list_expr.get_second_element().value

    return Point(x=x, y=y)

def parse_points(list_expr: ListExp) -> []:
    """
    Transforms a list expression object containing points (points) to a named tuple.

    :param list_expr: A list expression object containing points (points).
    :return: A named tuple with the points.
    """
    points = []

    next_element = list_expr
        point = parse_point(next_element.value)
        points.append(point)

    return points

def parse_line(list_expr: ListExp) -> Line:
    """
    Transforms a list expression object containing a line (line) to a named tuple.

    :param list_expr: A list expression object containing a line (line).
    :return: A named tuple with the line.
    """

    segments = []
    length = list_expr.get_list_length()

    for i in range(1, length):
        segment = parse_segment(list_expr.get_the_n_element(i))
        segments.append(segment)

    return Line(segments)

def parse_region(list_expr: ListExp) -> Region:
    """
    Transforms a list expression object containing a region (region) to a named tuple.

    :param list_expr: A list expression object containing a region (region).
    :return: A named tuple with the region.
    """

    qty_faces = list_expr.get_list_length()

    faces = []
    for i in range(1, qty_faces):

        face_le: ListExp = list_expr.get_the_n_element(i)

        cycles_count = face_le.get_list_length()

        outercycle_le = face_le.get_first_element()

        if outercycle_le is not None:
            outercycle = parse_points(outercycle_le)
        else:
            outercycle = []

        holecycles = []
        if cycles_count > 1:

            for j in range(2, cycles_count):

                holecycle_le = face_le.get_the_n_element(j)

                if holecycle_le is not None:
                    holecycle = parse_points(holecycle_le)
                else:
                    holecycle = []

                holecycles.append(holecycle)

        face = Face(outercycle, holecycles)

        faces.append(face)

    return Region(faces)

def parse_segment(list_expr: ListExp) -> Segment:
    """
    Transforms a list expression object containing a segment (segment) to a named tuple.

    :param list_expr: A list expression object containing a segment (segment).
    :return: A named tuple with the segment.
    """

    x1 = list_expr.get_first_element().value
    y1 = list_expr.get_second_element().value
    x2 = list_expr.get_third_element().value
    y2 = list_expr.get_fourth_element().value

    return Segment(x1=x1, y1=y1, x2=x2, y2=y2)

# Module
# Secondo Spatio-Temporal Algebra
# secondospatiotemporalalgebra.py
"""
The module Secondo Spatio-Temporal Algebra implements the data types for the conversion of list expression objects with
values of the types contained in the TemporalAlgebra of the |sec| system. The data types are implemented in Python
"""

from collections import namedtuple

import secondodb.api.algebras.secondospatialalgebra as spatial
from datetime import datetime

class Interval:
    """
    Implements a time interval.
    """

    __slots__ = ["start_time", "end_time", "close_left", "close_right"]

    start_time: datetime
    end_time: datetime
    close_left: bool
    close_right: bool

class MotionVector:
    """
    Implements a motion vector of a moving point.
    """

    x1: float
    y1: float
    x2: float
    y2: float

class PointInInterval:
    """
    Implements a structure to store the time interval and the motion vector of a moving point.
    """

    __slots__ = ["interval", "motion_vector"]

    interval: Interval
    motion_vector: MotionVector

class MPoint:
    """
    Implements a moving point as a series of points in intervals.
    """

    __slots__ = ['intervals']

    intervals: []

class MapPoint:
    """
    Implements a map point, i.e. a motion vector for a point of a moving region.
    """

    x1: float
    y1: float
    x2: float
    y2: float

class RegionInInterval:
    """
    Implements a structure to store the time interval and the motion vector of a moving region.
    """

    __slots__ = ["interval", "map_faces"]

    map_faces: []

class MRegion:
    """
    Implements a moving region as a series of regions in intervals.
    """

class MBool:
    """
    Implements a moving boolean as a series of booleans in intervals.
    """

class BoolInInterval:
    """
    Implements a structure to store the time interval and the value vector of a moving boolean.
    """

    __slots__ = ["interval", "value_vector"]

    value_vector: []

class ValueVectorBool:
    """
    Implements the value vector for a moving boolean.
    """

    __slots__ = ["value"]

    value: bool

class RealInInterval:
    """
    Implements a structure to store the time interval and the value vector of a moving real.
    """

class ValueVectorReal:
    """
    Implements the value vector for a moving real.
    """

    __slots__ = ["start_value", "end_value", "boolean"]

    start_value: float
    end_value: float
    boolean: bool

class MReal:
    """
    Implements a moving real as a series of real values in intervals.
    """

class MString:
    """
    Implements a moving string as a series of string values in intervals.
    """

class StringInInterval:
    """
    Implements a structure to store the time interval and the value vector of a moving string.
    """

class ValueVectorString:
    """
    Implements the value vector for a moving string.
    """

    value: str

class MInt:
    """
    Implements a moving integer as a series of integer values in intervals.
    """

class IntInInterval:
    """
    Implements a structure to store the time interval and the value vector of a moving integer.
    """

class ValueVectorInt:
    """
    Implements the value vector for a moving integer.
    """

    value: int

class IRegion:
    """
    Implements an instant region as a region for a specific instant.
    """

    __slots__ = ["instant", "region"]

    instant: datetime
    region: spatial.Region

class IPoint:
    """
    Implements an instant point as a point for a specific instant.
    """

    __slots__ = ["instant", "point"]

    point: spatial.Point

def parse_timestamp(timestamp: str) -> datetime:
    """
    Parses a timestamp string using the Python module datetime.

    Formats supported:

    '%Y-%m-%d-%H:%M'
    '%Y-%m-%d-%H:%M:%S'
    '%Y-%m-%d-%H:%M:%S.%f'

    :param timestamp: A string with a timestamp.
    :return: A datetime object.
    """

    try:
        timestamp_as_datetime = datetime.strptime(timestamp, '%Y-%m-%d-%H:%M')
    except ValueError:
        pass
    else:
        return timestamp_as_datetime

    try:
        timestamp_as_datetime = datetime.strptime(timestamp, '%Y-%m-%d-%H:%M:%S')
        pass
    else:

    try:
        timestamp_as_datetime = datetime.strptime(timestamp, '%Y-%m-%d-%H:%M:%S.%f')
        pass
    else:

    try:
        timestamp_as_datetime = datetime.strptime(timestamp, '%Y-%m-%d')
        pass
    else:

    if timestamp == 'begin of time':
        try:
            timestamp_as_datetime = datetime.strptime('1900-01-01', '%Y-%m-%d')
        except ValueError:
            pass
        else:
            return timestamp_as_datetime

    if timestamp == 'end of time':
        try:
            timestamp_as_datetime = datetime.strptime('9999-12-31', '%Y-%m-%d')
            pass
        else:

def parse_mpoint(list_expr: ListExp) -> MPoint:
    """
    Transforms a list expression object containing a moving point (mpoint) to a named tuple.

    :param list_expr: A list expression object containing a moving point (mpoint).
    :return: An object of the data class MPoint with the moving point.
    """

    number_of_intervals = list_expr.get_list_length()

    intervals = []
    for i in range(1, number_of_intervals):

        point_in_interval_as_le: ListExp = list_expr.get_the_n_element(i)

        # Parse interval

        interval_as_le: ListExp = point_in_interval_as_le.get_first_element()

        start_time = parse_timestamp(interval_as_le.get_first_element().value)
        end_time = parse_timestamp(interval_as_le.get_second_element().value)
        close_left = interval_as_le.get_third_element().value
        close_right = interval_as_le.get_fourth_element().value

        interval = Interval(start_time=start_time, end_time=end_time, close_left=close_left, close_right=close_right)

        # Parse motion vector

        motion_vector_as_le: ListExp = point_in_interval_as_le.get_second_element()

        x1 = motion_vector_as_le.get_first_element().value
        y1 = motion_vector_as_le.get_second_element().value
        x2 = motion_vector_as_le.get_third_element().value
        y2 = motion_vector_as_le.get_fourth_element().value

        motion_vector = MotionVector(x1=x1, y1=y1, x2=x2, y2=y2)

        point_in_interval = PointInInterval(interval, motion_vector)

        intervals.append(point_in_interval)

    return MPoint(intervals)

def parse_mregion(list_expr: ListExp) -> MRegion:
    """
    Transforms a list expression object containing a moving region (mregion) to a named tuple.

    :param list_expr: A list expression object containing a moving region (mregion).
    :return: An object of the data class MRegion with the moving region.
    """

        region_in_interval_as_le: ListExp = list_expr.get_the_n_element(i)

        interval_as_le: ListExp = region_in_interval_as_le.get_first_element()

        # Parse map faces

        map_faces_as_le: ListExp = region_in_interval_as_le.get_second_element()
        number_of_map_faces = map_faces_as_le.get_list_length()

        map_faces = []
        for j in range(1, number_of_map_faces):

            map_face_as_le: ListExp = map_faces_as_le.get_the_n_element(j)

            number_of_cycles = map_face_as_le.get_list_length() - 1

            if number_of_cycles == 1:

                # Parse outercycle

                outercycle_as_le: ListExp = map_face_as_le.get_first_element()
                number_of_map_points = outercycle_as_le.get_list_length()

                map_points = []
                for k in range(1, number_of_map_points):

                    map_point_as_le: ListExp = outercycle_as_le.get_the_n_element(k)
                    x1 = map_point_as_le.get_first_element().value
                    y1 = map_point_as_le.get_second_element().value
                    x2 = map_point_as_le.get_third_element().value
                    y2 = map_point_as_le.get_fourth_element().value
                    map_points.append(MapPoint(x1=x1, y1=y1, x2=x2, y2=y2))

                map_faces.append(spatial.Face(outercycle=map_points, holecycles=[]))

            elif number_of_cycles == 2:

                outercycle = []

                    outercycle.append(MapPoint(x1=x1, y1=y1, x2=x2, y2=y2))

                # Parse holecycle

                holecycle_as_le: ListExp = map_face_as_le.get_second_element()
                number_of_map_points = holecycle_as_le.get_list_length()

                holecycle = []
                    map_point = namedtuple('map_point', ['x1', 'y1', 'x2', 'y2'])
                    map_point_as_le: ListExp = holecycle_as_le.get_the_n_element(k)
                    map_point.x1 = map_point_as_le.get_first_element().value
                    map_point.y1 = map_point_as_le.get_second_element().value
                    map_point.x2 = map_point_as_le.get_third_element().value
                    map_point.y2 = map_point_as_le.get_fourth_element().value
                    holecycle.append(map_point)

                map_faces.append(spatial.Face(outercycle=outercycle, holecycles=[holecycle]))

            intervals.append(RegionInInterval(interval, map_faces))

    return MRegion(intervals)

def parse_mreal(list_expr) -> MReal:
    """
    Transforms a list expression object containing a moving real (mreal) to a named tuple.

    :param list_expr: A list expression object containing a moving real (mreal).
    :return: An object of the data class MReal with the moving real.
    """

        real_in_interval_as_le: ListExp = list_expr.get_the_n_element(i)

        interval_as_le: ListExp = real_in_interval_as_le.get_first_element()

        # Parse value vector

        motion_vector_as_le: ListExp = real_in_interval_as_le.get_second_element()

        start_value = motion_vector_as_le.get_second_element().value
        end_value = motion_vector_as_le.get_third_element().value
        boolean = motion_vector_as_le.get_fourth_element().value

        value_vector = ValueVectorReal(start_value=start_value, end_value=end_value, boolean=boolean)

        intervals.append(RealInInterval(interval=interval, value_vector=value_vector))

    return MReal(intervals=intervals)

def parse_mbool(list_expr: ListExp) -> MBool:
    """
    Transforms a list expression object containing a moving boolean (mbool) to a named tuple.

    :param list_expr: A list expression object containing a moving boolean (mbool).
    :return: An object of the data class MBool with the moving boolean.
    """

        bool_in_interval_as_le: ListExp = list_expr.get_the_n_element(i)

        interval_as_le: ListExp = bool_in_interval_as_le.get_first_element()

        value_vector_as_le: ListExp = bool_in_interval_as_le.get_second_element()

        value = value_vector_as_le.value

        value_vector = ValueVectorBool(value=value)

        bool_in_interval = BoolInInterval(interval=interval, value_vector=value_vector)

        intervals.append(bool_in_interval)

    return MBool(intervals)

def parse_mstring(list_expr: ListExp) -> MString:
    """
    Transforms a list expression object containing a moving string (mstring) to a named tuple.

    :param list_expr: A list expression object containing a moving string (mstring).
    :return: An object of the data class MString with the moving string.
    """

        string_in_interval_as_le: ListExp = list_expr.get_the_n_element(i)

        interval_as_le: ListExp = string_in_interval_as_le.get_first_element()

        value_vector_as_le: ListExp = string_in_interval_as_le.get_second_element()

        value_vector = ValueVectorString(value=value_vector_as_le.value)

        string_in_interval = StringInInterval(interval=interval, value_vector=value_vector)

        intervals.append(string_in_interval)

    return MString(intervals=intervals)

def parse_mint(list_expr: ListExp) -> MInt:
    """
    Transforms a list expression object containing a moving integer (mint) to a named tuple.

    :param list_expr: A list expression object containing a moving integer (mint).
    :return: An object of the data class MInt with the moving integer.
    """

        int_in_interval_as_le: ListExp = list_expr.get_the_n_element(i)

        interval_as_le: ListExp = int_in_interval_as_le.get_first_element()

        value_vector_as_le: ListExp = int_in_interval_as_le.get_second_element()

        value_vector = ValueVectorInt(value_vector_as_le.value)

        int_in_interval = IntInInterval(interval=interval, value_vector=value_vector)

        intervals.append(int_in_interval)

    return MInt(intervals=intervals)

def parse_iregion(list_expr: ListExp) -> IRegion:
    """
    Transforms a list expression object containing an instant region (iregion) to a named tuple.

    :param list_expr: A list expression object containing an instant region (iregion).
    :return: An object of the data class IRegion with the instant region.
    """

    # Parse instant

    instant_as_le: ListExp = list_expr.get_first_element()
    instant = parse_timestamp(instant_as_le.value)

    # Parse region

    region_as_le: ListExp = list_expr.get_second_element()
    region = spatial.parse_region(region_as_le)

    return IRegion(instant=instant, region=region)

def parse_ipoint(list_expr: ListExp) -> IPoint:
    """
    Transforms a list expression object containing an instant point (ipoint) to a named tuple.

    :param list_expr: A list expression object containing an instant region (ipoint).
    :return: An object of the data class IPoint with the instant point.
    """

    # Parse point

    point_as_le: ListExp = list_expr.get_second_element()
    point = spatial.parse_point(point_as_le)

    return IPoint(instant=instant, point=point)

# Module
# Secondo Input Handler
# secondoinputhandler.py
"""
The module Secondo Input Handler handles the conversion of different values from a socket stream in form of binary
values. The implementation is based on the Java-Class MyInputStreamReader of the Secondo JavaGUI and supports the
reception and construction of a list expression object originated through the response to an inquiry (or command) from
the |sec| server.
"""

import struct

import secondodb.api.support.secondolistexpr as listexpr

encoding = 'Latin-1'

# Constants describing codings for binary writing of a nested list

BIN_LONGLIST = 0
BIN_INTEGER = 1
BIN_REAL = 2
BIN_BOOLEAN = 3
BIN_LONGSTRING = 4
BIN_LONGSYMBOL = 5
BIN_LONGTEXT = 6
BIN_LIST = 10
BIN_SHORTLIST = 11
BIN_SHORTINT = 12
BIN_BYTE = 13
BIN_STRING = 14
BIN_SHORTSTRING = 15
BIN_SYMBOL = 16
BIN_SHORTSYMBOL = 17
BIN_TEXT = 18
BIN_SHORTTEXT = 19
BIN_DOUBLE = 20

def read_line_of_text(socket_object: socket) -> str:
    """
    Returns a single text line from a response. The input stream will be read until the occurrence of a new line.

    :param socket_object: A socket object.
    :return: A string with a single text line.
    """

    byte_array = bytearray()

    while True:

        byte = socket_object.recv(1)

        try:
            if byte != b'\n':
                byte_array.append(byte[0])
            else:
                break
        except IndexError:
            raise api.OperationalError('Error: Invalid response from server.')

    try:
        string: str = byte_array.decode(encoding)
    except UnicodeDecodeError as e:
        raise api.InterfaceError(e.args[0])
    else:
        return string

def read_string(socket_object: socket, size: int) -> str:
    """
    Reads a string of a specified size from a socket object.

    :param socket_object: A socket object with the response from the |sec| server.
    :param size: The size of the string.
    :return: A string value.
    """

    for i in range(0, size):
        byte_array.append(byte[0])

    try:
    else:

def read_short(socket_object: socket) -> int:
    """
    Reads a short value from the socket response. A short value takes 2 bytes to be built.

    :return: A short value.
    """

    byte_vector = socket_object.recv(2)
    short_value: int = struct.unpack('>H', byte_vector)[0]

    return short_value

def read_byte(socket_object: socket) -> bytes:
    """
    Reads a single byte from the socket response.

    :return: A byte value.
    """

    byte_value = socket_object.recv(1)

    return byte_value

def read_int(socket_object: socket) -> int:
    """
    Reads an integer value from the socket response. An integer value takes 4 bytes to be built.

    :return: An integer value.
    """

    byte_vector = socket_object.recv(4)
    int_value: int = struct.unpack('>I', byte_vector)[0]

    return int_value

def read_real(socket_object: socket) -> float:
    """
    Reads a real value from the socket response.

    :return: A float representing the real value.
    """

    real_value: float = struct.unpack('>f', byte_vector)[0]

    return real_value

def read_bool(socket_object: socket) -> bool:
    """
    Reads a boolean value from the socket response.

    :return: A boolean value.
    """

    int_value = read_byte(socket_object)[0]

    if int_value == 0:
    else:

def read_double(socket_object: socket) -> float:
    """
    Reads a double value from the socket response.

    :return: A float representing the double value.
    """

    byte_vector = socket_object.recv(8)
    double_value: float = struct.unpack('>d', byte_vector)[0]

    return double_value

def read_long(socket_object: socket) -> int:
    """
    Reads a long value from the socket response.

    :return: A long (int) value.
    """

    long_value: int = struct.unpack('>Q', byte_vector)[0]

    return long_value

def read_binary_record(socket_object: socket) -> listexpr.ListExp:
    """
    Reads on each iteration a single buffered vector of bytes and translates it to the specified type. Some types
    requiere the specification of the length for the buffer. The length is normally expressed as the first binary value
    after the specification of the type integer.

    """

    # Read byte from the socket to identify the type

    type_value = read_byte(socket_object)

    if type_value[0] == BIN_LONGLIST:

        length = read_int(socket_object)

        if length == 0:
            return listexpr.ListExp()
        else:
            le_f = read_binary_record(socket_object)
            le_le = listexpr.one_element_list(le_f)
            le_last = le_le

            for i in range(1, length):
                le_next = read_binary_record(socket_object)
                le_last = listexpr.append_to_last_element(le_last, le_next)

        return le_le

    elif type_value[0] == BIN_INTEGER:

        int_value = read_int(socket_object)
        return listexpr.create_integer_atom(int_value)

    elif type_value[0] == BIN_REAL:

        real_value = read_real(socket_object)
        return listexpr.create_real_atom(real_value)

    elif type_value[0] == BIN_BOOLEAN:

        bool_value = read_bool(socket_object)
        return listexpr.create_bool_atom(bool_value)

    elif type_value[0] == BIN_LONGSTRING:

        string_value = read_string(socket_object, length)
        return listexpr.create_string_atom(string_value)

    elif type_value[0] == BIN_LONGSYMBOL:

        symbol_value = read_string(socket_object, length)
        return listexpr.create_symbol_atom(symbol_value)

    elif type_value[0] == BIN_LONGTEXT:

        text = read_string(socket_object, length)
        return listexpr.create_text_atom(text)

    elif type_value[0] == BIN_LIST:

        length = read_short(socket_object)

        else:

    elif type_value[0] == BIN_SHORTLIST:

        length = read_byte(socket_object)[0]

        else:

    elif type_value[0] == BIN_SHORTINT:

        short_int_value = read_short(socket_object)
        return listexpr.create_integer_atom(short_int_value)

    elif type_value[0] == BIN_BYTE:

        return listexpr.create_integer_atom(read_byte(socket_object)[0])

    elif type_value[0] == BIN_STRING:

        string = read_string(socket_object, length)
        return listexpr.create_string_atom(string)

    elif type_value[0] == BIN_SHORTSTRING:

    elif type_value[0] == BIN_SYMBOL:

    elif type_value[0] == BIN_SHORTSYMBOL:

    elif type_value[0] == BIN_TEXT:

    elif type_value[0] == BIN_SHORTTEXT:

    elif type_value[0] == BIN_DOUBLE:

        double_value = read_double(socket_object)
        return listexpr.create_real_atom(double_value)

def build_list_expr_from_binary(socket_object: socket) -> listexpr.ListExp:
    """
    This method builds a list expression object from a binary response from the |sec| server.
    In javagui: readBinaryFrom()

    :param socket_object:
    :return: A list expression object with the contents of the nested list.
    """

    # Read and check signature string 'bnl'

    signature = read_string(socket_object, 3)

    if signature != com.SECONDO_VALIDITY_STRING:
        raise api.InterfaceError('Wrong validity string')

    # Read and check version

    version_1 = read_short(socket_object)  # major
    version_2 = read_short(socket_object)  # minor

    if version_1 != 1 or (version_2 != 0 and version_2 != 1 and version_2 != 2):
        raise api.InterfaceError('Wrong version number: ' + str(version_1) + "/" + str(version_2))

    # Create list expression from binary

    list_expr = read_binary_record(socket_object)

    return list_expr

 # ----------------------------------------------------------------------------------------------------------------------
# Module
# Secondo List Expression
# secondolistexpr.py
"""
The module Secondo List Expression implements methods for the reception and construction of a list expression object
originated through the response of the |sec| server to an inquiry or a command. The implementation is based on the
Java class ListExpr of the Secondo JavaGUI.
"""

# Constants describing the atom types

NO_ATOM = 0
INT_ATOM = 1
REAL_ATOM = 2
BOOL_ATOM = 3
STRING_ATOM = 4
SYMBOL_ATOM = 5
TEXT_ATOM = 6

def append_to_last_element(last_element, new_node):
    """
    Appends a new element to a list expression object. The new element will be stored in the "value" variable of the
    last element passed and stores an empty list in his "next" variable.

    :param last_element: The list expression object to be extended.
    :param new_node: The list expression object to be appended.
    :return: The new list expression object with the new appended element.
    """

    list_expr = ListExp()
    list_expr.value = new_node
    list_expr.next = ListExp()
    last_element.next = list_expr

def build_new_list_node(left_list, right_list):
    """
    Builds a new node for a list expression. A node is formed by a left and a right list. The right list, which will be
    appended in the "next"-parameter, must not be an atom.

    :param left_list: The left list expression object for the parameter "value".
    :param right_list:  The right list expression object for the parameter "next".
    :return: A list expression object.
    """

    list_expr.value = left_list
    list_expr.next = right_list

def one_element_list(element_1):
    """
    Creates a one element list expression. The left node contains the element value, and the right node an empty list.

    :param element_1: A list expression object.
    :return: A list expression object with one element.
    """
    return build_new_list_node(element_1, ListExp())

def two_element_list(element_1, element_2):
    """
    Creates a two element list expression.

    :param element_2: A list expression object.
    :return: A list expression object with two elements.
    """
    return build_new_list_node(element_1,
                               build_new_list_node(element_2, ListExp()))

def three_element_list(element_1, element_2, element_3):
    """
    Creates a three element list expression.

    :param element_3: A list expression object.
    :return: A list expression object with three elements.
    """
                               build_new_list_node(element_2,
                                                   build_new_list_node(element_3, ListExp())))

def four_element_list(element_1, element_2, element_3, element_4):
    """
    Creates a four element list expression.

    :param element_4: A list expression object.
    :return: A list expression object with four elements.
    """
                                                   build_new_list_node(element_3,
                                                                       build_new_list_node(element_4, ListExp()))))

def five_element_list(element_1, element_2, element_3, element_4, element_5):
    """
    Creates a five element list expression.

    :param element_5: A list expression object.
    :return: A list expression object with five elements.
    """
                                                                       build_new_list_node(element_4,
                               build_new_list_node(element_5, ListExp())))))

def six_element_list(element_1, element_2, element_3, element_4, element_5, element_6):
    """
    Creates a six element list expression.

    :param element_6: A list expression object.
    :return: A list expression object with six elements.
    """
                               build_new_list_node(element_3,
                               build_new_list_node(element_4,
                               build_new_list_node(element_5,
                               build_new_list_node(element_6, ListExp()))))))

def check_if_atom(in_list):
    """
    Verifies if the ListExpr object is an atom. The method verifies the type of the list object.

    :param in_list: A ListExpr Object.
    :return: True, if the list element is not an atom, otherwise false.
    """

    if in_list.type != NO_ATOM:
    else:

def create_text_atom(value):
    """
    Creates a new text atom using a passed text as value.

    :param value: A text.
    :return: A ListExp object with the newly created text atom.
    """

    new_text_atom = ListExp()
    new_text_atom.type = TEXT_ATOM
    new_text_atom.value = str(value)

    return new_text_atom

def create_symbol_atom(value):
    """
    Creates a new symbol atom using its identifier as value.

    :param value: The identifier of the symbol (like DATABASE).
    :return: A ListExp object with the newly created symbol atom.
    """

    new_symbol_atom = ListExp()
    new_symbol_atom.type = SYMBOL_ATOM
    new_symbol_atom.value = str(value)

    return new_symbol_atom

def create_string_atom(value):
    """
    Creates a new string atom using a string as value.

    :param value: A string.
    :return: A ListExp object with the newly created string atom.
    """

    new_string_atom = ListExp()
    new_string_atom.type = STRING_ATOM
    new_string_atom.value = str(value)

    return new_string_atom

def create_integer_atom(value):
    """
    Creates a new integer atom using an integer as value.

    :param value: An integer.
    :return: A ListExp object with the newly created integer atom.
    """

    new_integer_atom = ListExp()
    new_integer_atom.type = INT_ATOM

    new_integer_atom.value = int(value)

    return new_integer_atom

def create_real_atom(value):
    """
    Creates a new real atom using an real as value.

    :param value: A real value.
    :return: A ListExp object with the newly created real atom.
    """

    new_real_atom = ListExp()
    new_real_atom.type = REAL_ATOM
    new_real_atom.value = float(value)

    return new_real_atom

def create_bool_atom(value):
    """
    Creates a new boolean atom using a boolean as value.

    :param value: A boolean.
    :return: A ListExp object with the newly created boolean atom.
    """

    new_boolean_atom = ListExp()
    new_boolean_atom.type = BOOL_ATOM
    new_boolean_atom.value = bool(value)

    return new_boolean_atom

# def write_list_to_string(list_expr, str_rep, identation):
#
#     separator = ' '
#     has_sub_lists = False
#     identation = identation + "    "
#
#     if not list_expr.is_atom():
#         str_rep += '('
#
#     while not list_expr.is_empty():
#
#         if list_expr.type == NO_ATOM:
#             if not has_sub_lists and (not list_expr.value.is_empty() and list_expr.value.type == NO_ATOM):
#                 has_sub_lists = True
#                 separator = "\n" + identation
#                 str_rep += separator
#             str_rep = write_list_to_string(list_expr.value, str_rep, identation)
#         elif list_expr.type == INT_ATOM:
#             str_rep += str(list_expr.value)
#             return str_rep
#         elif list_expr.type == REAL_ATOM:
#         elif list_expr.type == BOOL_ATOM:
#         elif list_expr.type == STRING_ATOM:
#         elif list_expr.type == SYMBOL_ATOM:
#         elif list_expr.type == TEXT_ATOM:
#
#         list_expr = list_expr.next
#
#         if not list_expr.is_empty():
#             str_rep += separator
#
#     str_rep += ')'
#     return str_rep

def write_list_to_string(list_expr, str_list: [], identation) -> []:

    separator = ' '
    has_sub_lists = False
    identation = identation + "    "

    if not list_expr.is_atom():
        str_list.append('(')

    while not list_expr.is_empty():

        if list_expr.type == NO_ATOM:
            if not has_sub_lists and (not list_expr.value.is_empty() and list_expr.value.type == NO_ATOM):
                has_sub_lists = True
                separator = "\n" + identation
                str_list.append(separator)
            str_list = write_list_to_string(list_expr.value, str_list, identation)
        else:
            str_list.append(str(list_expr.value))
            return str_list

        list_expr = list_expr.next

        if not list_expr.is_empty():
            str_list.append(separator)

    str_list.append(')')
    return str_list

class ListExp:

    def __init__(self):
        """
        Constructor of the list expression for handling |sec| objects.
        """
        self.type = NO_ATOM
        self.value = None
        self.next = None

    def __str__(self):

        str_list = []
        identation = ''

        str_list = write_list_to_string(self, str_list, identation)
        string = "".join(str_list)

    def is_empty(self):

        if self.type == NO_ATOM and self.value is None and self.next is None:
            return True
        else:

    def is_atom(self):

        if self.type == NO_ATOM:
        else:

    def end_of_list(self):

        if self.type == NO_ATOM and not self.is_empty() and self.next.is_empty():
        else:

    def get_the_n_element(self, n):
        """
        Returns the n element of the list.

        :param n: The number of the n-element to be retrieved.
        :return: A list expression object representing the requested n-element.
        """

        i = 1
        tmp_le = self

        while i < n:
            tmp_le = tmp_le.next
            i += 1

        return tmp_le.value

    def get_first_element(self):
        """
        Returns the first element of the list.

        :return: A ListExp object with the first element of the current list.
        """
        return self.get_the_n_element(1)

    def get_second_element(self):
        """
        Returns the second element of the list.

        :return: A ListExp object with the second element of the current list.
        """
        return self.get_the_n_element(2)

    def get_third_element(self):
        """
        Returns the third element of the list.

        :return: A ListExp object with the third element of the current list.
        """
        return self.get_the_n_element(3)

    def get_fourth_element(self):
        """
        Returns the fourth element of the list.

        :return: A ListExp object with the fourth element of the current list.
        """
        return self.get_the_n_element(4)

    def get_fifth_element(self):
        """
        Returns the fifth element of the list.

        :return: A ListExp object with the fifth element of the current list.
        """
        return self.get_the_n_element(5)

    def get_sixth_element(self):
        """
        Returns the sixth element of the list.

        :return: A ListExp object with the sixth element of the current list.
        """
        return self.get_the_n_element(6)

    def get_list_length(self):
        """
        Determines the length of the current list.

        :return: An integer with the depth of the list.
        """

        i = 1

        while tmp_le.next is not None:
            i += 1

        return i

    def get_last_node(self):
        """
        Returns the last node of the list expression object.

        :return: The last node of the list expression object.
        """

        last_node = self

        if self.next is None:
            return last_node
        else:
            while last_node.next is not None:
                last_node = self.next

    def add(self, in_list):
        """
        Adds a new node to the list.

        :param in_list: A list expression object.
        :return: The current instance of the list expression object.
        """

        if self.get_list_length() == 1 and self.type == NO_ATOM:
            # The list has the original node only
            self.value = in_list.value
            self.type = in_list.value
            self.next = None
        else:
            # The list has more than one node
            last_node = self.get_last_node()
            last_node.next = in_list

# March 2020
# Module
"""
The module Secondo Relational Algebra implements the data types for the conversion of list expression objects with
values of the types contained in the RelationalAlgebra of the |sec| system. The data types are implemented in Python
"""

import secondodb.api.algebras.secondostandardalgebra as standard
import secondodb.api.algebras.secondospatiotemporalalgebra as spatiotemporal
from secondodb.api.algebras.secondogeneraldataclasses import Attribute

class Relation:
    """
    Implements the type relation (rel) of the relational algebra of |sec|.
    """

    __slots__ = ["attributes", "data"]

    attributes: []
    data: []

def parse_relation(list_expr: ListExp, attr_list: []) -> Relation:
    """
    Parses a relation (rel) object. A relation named tuple with two attributes (attributes and data) will be returned.
    The attribute "attributes" contains the fields and types of the relation. The attribute "data" contains the entries.

    :param list_expr: A list expression object with a relation.
    :param attr_list: The list of the attributes. Every attribute is an object of the class Attribute.
    :return: An object of the class Relation with the data. Every entry of the relation data is a Python dictionary,
             which allows to call the single fields of the tuple using the corresponding attribute name. The retrieval
             of the value using an index (like in lists) is allowed as well.
    """

    relation_data = []

    while list_expr.next is not None:

        single_row = {}
        single_row_as_le = list_expr.value

        for j in range(0, len(attr_list)):

            single_attribute: Attribute = attr_list[j]

            attr_name = single_attribute.attribute_name

            if single_attribute.attribute_type == 'string':
                attr_value = standard.parse_string(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'text':

            elif single_attribute.attribute_type == 'int':
                attr_value = standard.parse_int(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'longint':
                attr_value = standard.parse_longint(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'real':
                attr_value = standard.parse_real(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'rational':
                attr_value = standard.parse_rational(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'bool':
                attr_value = standard.parse_bool(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'point':
                attr_value = spatial.parse_point(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'points':
                attr_value = spatial.parse_points(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'line':
                attr_value = spatial.parse_line(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'sline':

            elif single_attribute.attribute_type == 'region':
                attr_value = spatial.parse_region(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'mpoint':
                attr_value = spatiotemporal.parse_mpoint(single_row_as_le.get_the_n_element(j + 1))

            elif single_attribute.attribute_type == 'mregion':
                attr_value = spatiotemporal.parse_mregion(single_row_as_le.get_the_n_element(j + 1))

            else:
                raise api.InterfaceError('Error: Attribute type ' + single_attribute.attribute_type + ' not supported.')

            single_row.update({attr_name: attr_value})

        relation_data.append(single_row)

    return Relation(attr_list, relation_data)