#!/usr/bin/python
# -*- coding: utf-8 -*-
## File fiebdc.py
## This file is part of pyArq-Presupuestos.
##
## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez
##                         <miguelangel@obraencurso.es>
##
## pyArq-Presupuestos is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## pyArq-Presupuestos is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.

# specifications in http://www.fiebdc.org

# Modules
import time
import re
import calendar
import os.path

# pyArq-Presupuestos modules
import base
from Generic import utils
from Generic import globalVars

class Read(object):
    """fiebdc.Read:
    
    Description:
        Reads and parses a fiebdc file
    Constructor:
        fiebdc.Read(filename=None, budget=None)
    Ancestry:
    +-- object
      +-- Read
    Atributes:
        "__budget": budget ("base.Budget" object)
        "__file_format": File format of the fiebdc file
        "__format_list": List of file format that can be readed
        "__character_sets_dict": Dictionary with the character sets supported
        "__character_set": character_set of the file
        "__generator": program which the file is created
        "__cancel": Boolean value, True mean that the read process must stop
        "__filename": The filename of the fiebdc file that is readed
        "__pattern": re compiled pattern dict
    Methods:
        __init__(self, filename=None, budget=None)
        cancel(self)
        eraseControlCharacters(self, string)
        validateCode(self, code)
        parseDate(self, date)
        parseRecord(self,record)
        _parseV(self, field_list)
        _parseC(self, field_list)
        _parseDY(self, field_list)
        _parseMN(self, field_list)
        _parseT(self, field_list)
        _parseK(self, field_list)
        _parseW(self, field_list)
        _parseL(self, field_list)
        _parseQ(self, field_list)
        _parseJ(self, field_list)
        _parseG(self, field_list)
        _parseE(self, field_list)
        _parseX(self, field_list)
        _parseF(self, field_list)
        readFile(self, budget=None, filename=None)
    """
    def __init__(self, filename=None, budget=None):
        """def __init__(self, filename=None, budget=None)
        
        Sets the instance attributes
        """
        self.__budget = budget
        self.__filename = filename
        if not self.__budget is None:
            self.__budget.filename = self.__filename
        self.__cancel = False
        self.__format_list = ["FIEBDC-3/95", "FIEBDC-3/98", "FIEBDC-3/2002",
                              "FIEBDC-3/2004", "FIEBDC-3/2007"]
        # ANSI->¿"ISO-8859-15" or "latin1 ISO-8859-1" or "cp1252 windows-1252"?
        # 850 -> IBM850 -> cp850
        # 437 -> IBM437 -> cp437
        self.__character_sets_dict = {"ANSI" : "cp1252",
                                      "850" : "850",
                                      "437" : "cp437"}
        self.__file_format = "FIEBDC-3/2007"
        self.__generator = globalVars.version
        self.__character_set = "850"
        self.__pattern = {
            "control_tilde" : re.compile("((\r\n)| |\t)+~"),
            "control_vbar" : re.compile("((\r\n)| |\t)+\|"),
            "control_backslash" : re.compile(r"((\r\n)| |\t)+\\"),
            "valid_code" : re.compile("[^A-Za-z0-9ñÑ.$#%&_]"),
            "special_char": re.compile("[#%&]"),
            "no_float": re.compile("[^0-9.]"),
            "formula" : re.compile(".*[^0123456789\.()\+\-\*/\^abcdp ].*"),
            "comment": re.compile("#.*\r\n"),
            "empty_line": re.compile(r"(\r\n) *\r\n"),
            "space_before_backslash" : re.compile(r"( )+\\"),
            "space_after_backslash" : re.compile(r"\\( )+"),
            "start_noend_backslash" : re.compile("(\r\n\\\.*[^\\\])\r\n"),
            "end_oper": re.compile("(\+|-|\*|/|/^|@|&|<|>|<=|>=|=|!) *\r\n"),
            "matricial_var" : re.compile("(\r\n *[%|\$][A-ZÑ].*=.*,) *\r\n"),
            "descomposition" : re.compile("^([^:]+):(.*)$"),
            "var" : re.compile("^([$%][A-ZÑ][()0-9, ]*)=(.*)$"),
            "after_first_tilde" : re.compile("^[^~]*~"),
            "end_control" : re.compile("((\r\n)| |\t)+$"),
            }

    def cancel(self):
        """def cancel(self)
        
        Sets the "__cancel" attribute to True, It stops the read process.
        """
        self.__cancel = True

    def eraseControlCharacters(self, string):
        """eraseControlCharacters(self,string)
        
        Return a copy of the string with the blank characters (32),
        tabs (9) and end of line (13 and 10) before of the separators
        '~', '|' erased.
        Before separator \ not deleted because it affects the reading of the
        record ~P
        """
        # "control_tilde" : "((\r\n)| |\t)+~"
        string = self.__pattern["control_tilde"].sub("~",string)
        # "control_vbar" : "((\r\n)| |\t)+\|"
        string = self.__pattern["control_vbar"].sub("|",string)
        # "control_backslash" : r"((\r\n)| |\t)+\\"
        #string = self.__pattern["control_backslash"].sub(r"\\",string)
        return string

    def validateCode(self, code):
        """validateCode(self, code)
        
        Test if the code have invalid characters and try to erase it,
        if it is posible return a valid code else return a empty string.
        """
        if not isinstance(code, str):
            print _("Invalid code, it must be a string")
            return ""
        # Valid chararcter: A-Z a-z 0-9 ñ Ñ . $ # % & _
        # "valid_code" : "[^A-Za-z0-9ñÑ.$#%&_]"
        _code = self.__pattern["valid_code"].sub("", code)
        if _code != code:
            print utils.mapping(_("The code '$1' have invalid characters."),
                               (code,))
            code = _code
        # the lasts characters can not be <#> or <##>
        # <##> -> root record in FIEFDC-3
        # <#> -> chapter record in FIEFDC-3
        if len(code) > 0:
            while code[-1] == "#":
                code = code[:-1]
            if len(code) > 20:
                code = code[:20]
            # only one charecter # % or &
            if sum([code.count(c) for c in '#%&']) > 1:
                print utils.mapping(_("The code '$1' contains special "\
                                      "characters repeated."),(code,))
                _i = min([code.find(c) for c in '#%&'])
                code = code[:_i+1] + \
                        self.__pattern["special_char"].sub("", code[_i+1:])
        return code

    def parseDate(self, date):
        """parseDate(self, date)
        
        date: in the format:
            uneven len: add a Leading 0
            len = 8  DDMMYYYY
            len <= 6 DDMMYY “80/20”. >80 -> >1980 <80 -> <2080
            len < 5  MMYY
            len < 3  YY
        Test date string and return a tuple (YYYY, MM, DD)
        or None if the date format is invalid
        """
        # All characters must be numbers, len <= 8 and not empty string
        if not date.isdigit() or len(date) > 8 or date == "":
            return None
        else:
            if len(date)%2 == 1: # uneven len: add a leading 0
                date = "0" + date
            if len(date) == 8:
                _d = int(date[:2])
                _m = int(date[2:4])
                _y = int(date[4:8])
            elif len(date) <= 6:
                _y = int(date[-2:])
                if _y < 80: _y = 2000 + _y
                else: _y = 1900 + _y
                if len(date) == 6:
                    _d = int(date[:2])
                    _m = int(date[2:4])
                elif len(date) == 4:
                    _d = 0
                    _m = int(date[:2])
                elif len(date) == 2:
                    _d = 0
                    _m = 0
        if not _d in range(1,31): _d = 0
        if not _m in range(1,12): _m = 0
        if _m == 0: _d = 0
        if _m != 0 and _d != 0:
            if calendar.monthrange(_y, _m)[1] < _d:
                _d = 0
        return (_y, _m, _d)

    def parseRecord(self,record):
        """parseRecord(self,record)
        
        record: the record line readed from the file whith the format:
            type|field|field|subfield\subfield|...
        [a] nothing or "a"
        {a} zero or more #-#twice#-# "a"
        <a> one or more #-#twice#-# "a"
        Types: V C D Y M N T K L Q J G E X B F A
            V: Property and Version
                1- [File_Owner]
                2- Format_Version[\DDMMYYYY]
                3- [Program_Generator]
                4- [Header]\{Title\}
                5- [Chaters_set]
                6- [Comment]
            C: Record:
                1- Code{\Code}
                2- [Unit]
                3- [Summary]
                4- {Price\}
                5- {Date\}
                6- [Type]
            D or Y: DECOMPOSITION or ADD DECOMPOSITION
                1- Parent Code
                2- <Child Code\ [Factor]\ [Yield]>
            M or N: MEASURE or ADD MEASURE
                1- [Parent Code\]Child Code
                2- {Path\}
                3- TOTAL MEASURE
                4- {Type\Comment\Unit\Length\Width\Height\}
                5- [Label]
            T: Text
                1- Code
                2- Description text
            K: Coefficients
                1- { DN \ DD \ DS \ DR \ DI \ DP \ DC \ DM \ DIVISA \ }
                2-  CI \ GG \ BI \ BAJA \ IVA
                3- { DRC \ DC \ DRO \ DFS \ DRS \ DFO \ DUO \ DI \ DES \ DN \
                  DD \ DS \ DIVISA \ }
                4- [ n ]
            L: Sheet of Conditions 1
                A) 
                    1- Empty
                    2- {Section Code\Section Title}
                B)
                    1- Record Code
                    2- {Section Code\Section Text}
                    3- {Section Code\RTF file}
                    4- {Section Code\HTM file}
            Q: Sheet of Conditions 2
                1- Record Code
                2- {Section Code\Paragraph key\{Field key;}\}|
            J: Sheet of Conditions 3
                1- Paragraph code
                2- [Paragraph text]
                3- [RTF file]
                4- [HTML file]
            G: Grafic info
                1- <grafic_file.ext\>
            E: Company
                1- company Code
                2 [ summary ]
                3- [ name ]
                4- { [ type ] \ [ subname ] \ [ address ] \ [ postal_code ]
                  \ [ town ] \ [ province ] \ [ country ] \ { phone; } 
                  \ { fax; }  \ {contact_person; } \ }
                5- [ cif ] \ [ web ] \ [ email ] \
            X: Tecnical information
                A)
                    1- Empty
                    2- < TI_Code \ TI_Descitption \ TI_Unit >
                B)
                    1- Record_code
                    2- < TI_Code \ TI_value >
            F: #-#Adjunto#-# File
                1- Record code
                2- { Type \ { Filenames; } \ [Description] }
            B: Change code
                1- Record Code
                2- New code
            A: Labels
                1- Record Code
                2- <Label\>
        """
        # TODO:  ~L ~J RTF and HTML files
        # TODO:  test ~Q ~J ~G
        # TODO:  ~P. Registro tipo Descripción Paramétrica.
        # TODO:  ~O. Registro tipo Relación Comercial.
        # TODO: test records
        _field_list = record.split("|")
        self._record_number = self._record_number +1
        _budget = self.__budget
        if _field_list[0] == "V":
            self._record_V_number += 1
            self._parseV(_field_list)
        elif _field_list[0] == "C":
            self._record_C_number += 1
            self._parseC(_field_list)
        elif _field_list[0] == "D":
            self._record_D_number += 1
            self._parseDY(_field_list)
        elif _field_list[0] == "Y":
            self._record_Y_number += 1
            self._parseDY(_field_list)
        elif _field_list[0] == "M":
            self._record_M_number += 1
            self._parseMN(_field_list)
        elif _field_list[0] == "N":
            self._record_N_number += 1
            self._parseMN(_field_list)
        elif _field_list[0] == "T":
            self._record_T_number += 1
            self._parseT(_field_list)
        elif _field_list[0] == "K":
            self._record_K_number += 1
            self._parseK(_field_list)
        elif _field_list[0] == "W":
            self._record_W_number += 1
            self._parseW(_field_list)
        elif _field_list[0] == "L":
            self._record_L_number += 1
            self._parseL(_field_list)
        elif _field_list[0] == "Q":
            self._record_Q_number += 1
            self._parseQ(_field_list)
        elif _field_list[0] == "J":
            self._record_J_number += 1
            self._parseJ(_field_list)
        elif _field_list[0] == "G":
            self._record_G_number += 1
            self._parseG(_field_list)
        elif _field_list[0] == "E":
            self._record_E_number += 1
            self._parseE(_field_list)
        elif _field_list[0] == "O":
            self._record_O_number += 1
        elif _field_list[0] == "P":
            self._record_P_number += 1
            self._parseP(_field_list)
        elif _field_list[0] == "X":
            self._record_X_number += 1
            self._parseX(_field_list)
        elif _field_list[0] == "B":
            self._record_B_number += 1
            self._parseB(_field_list)
        elif _field_list[0] == "F":
            self._record_F_number += 1
            self._parseF(_field_list)
        elif _field_list[0] == "A":
            self._record_A_number += 1
            self._parseA(_field_list)
        else:
            self._record_Unknow_number += 1

    def _parseV(self, field_list):
        """_parseV(self, field_list)
        
        field_list: field list of the record
            0- V :Property and Version
            1- [File_Owner]
            2- Format_Version[\DDMMYYYY]
            3- [Program_Generator]
            4- [Header]\{Title\}
            5- [Chaters_set]
            6- [Comment]
            7- [Data type]
            8- [Number budget certificate]
            9- [Date budget certificate]
        """
        if self._record_number != 1:
            print utils.mapping(_("The 'V' record (Property and Version) "\
                    "must be the first record in the file but it is the "\
                    "number: $1"), (self._record_number,))
            print _("The default values were taken and this V record is "\
                  "ignored")
            return
        # _____number of fields_____
        # Any INFORMATION after last field separator is ignored
        if len(field_list) > 10:
            field_list = field_list[:10]
        # If there are no sufficient fields, the fields are added
        # with empty value:""
        else:
            field_list = field_list + [""]*(10-len(field_list))
        # control character are erased: end of line, tab, space
        # only leading and trailing whitespace in owner, generator, comment
        # _____Fields_____
        _record_type = self.delete_control_space(field_list[0])
        _owner = field_list[1].strip()
        _owner = self.delete_control(_owner)
        _version_date = self.delete_control_space(field_list[2])
        _generator = field_list[3].strip()
        _generator = self.delete_control(_generator)
        _header_title = field_list[4].strip()
        _header_title = self.delete_control(_header_title)
        _character_set = self.delete_control_space(field_list[5])
        _comment = field_list[6].strip("\t \n\r")
        _data_type = self.delete_control_space(field_list[7])
        _number_certificate = self.delete_control_space(field_list[8])
        __date_certificate = self.delete_control_space(field_list[9])
        # _____Owner_____
        self.__budget.setOwner(_owner)
        # _____Version-Date_____
        _version_date = _version_date.split("\\")
        _file_format = _version_date[0]
        if _file_format in self.__format_list:
            self.__file_format = _file_format
            print _("FIEBDC format: %s" % _file_format)
        if len(_version_date) > 1:
            _date = _version_date[1]
            if _date != "":
                _parsed_date = self.parseDate(_date)
                if _parsed_date is not  None:
                    self.__budget.setDate(_parsed_date)
        # _____Generator_____
        # ignored field
        print _("FIEBDC file generated by %s" % _generator)
        # _____Header_Title_____
        _header_title = _header_title.split("\\")
        _header_title = [_title.strip() for _title in _header_title]
        _header = _header_title.pop(0)
        _title = [ ]
        for _title_index in _header_title:
            if _title_index != "":
                _title.append(_title_index)
        if _header != "":
            self.__budget.setTitleList([ _header, _title ])
        # _____Characters_set_____
        # field parsed in readFile method
        # _____Comment_____
        if _comment != "":
            self.__budget.setComment(_comment)
        # _____Data type_____
        # 1 -> Base data.
        # 2 -> Budget.
        # 3 -> Budget certificate.
        # 4 -> Base date update.
        try:
            _data_type = int(_data_type)
        except ValueError:
            _data_type = ""
        if _data_type == 3:
            # _____Number budget certificate_____
            try:
                _number_certificate = int(_number_certificate)
            except ValueError:
                _number_certificate = ""
            # _____Date budget certificate_____
            if _date_certificate != "":
                _parsed_date_certificate = self.parseDate(_date_certificate)
                if _parsed_date_certificate is None:
                    _date_certificate = ""
                else:
                    _date_certificate = _parsed_date_certificate
            self.__budget.setBudgetype(_data_type)
            self.__budget.setCertificateOrder(_number_certificate)
            self.__budget.setCertificateDate(_parsed_date_cerfificate)
        elif _data_type != "":
            self.__budget.setBudgeType(_data_type)
        self.num_valid_record = self.num_valid_record + 1

    def _parseK(self, field_list):
        """_parseK(self, field_list)
        
        field_list: field list of the record
            0- K: Coefficients
            1- { DN \ DD \ DS \ DR \ DI \ DP \ DC \ DM \ DIVISA \ }
            2- CI \ GG \ BI \ BAJA \ IVA
            3-
              A){ DRC \ DC \ DRO \ DFS \ DRS \ DFO \ DUO \ DI \ DES \ DN \
                 DD \ DS \ DIVISA \ }
              B){ DRC \ DC \ \ DFS \ DRS \ \ DUO \ DI \ DES \ DN \
                 DD \ DS \ DSP\ DEC\ DIVISA \ }
            4- [ n ]
        """
        # _____Number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        # The last field is ignored, pyArq hate dll's
        if len(field_list) > 4:
             field_list = field_list[1:4]
        # The record must have 3 fields
        else:
             field_list = field_list[1:] + [""]*(4-len(field_list))
        # control character are erased: end of line, tab, space
        # _____Fields_____
        _field0 = self.delete_control_space(field_list[0])
        _field1 = self.delete_control_space(field_list[1])
        _field2 = self.delete_control_space(field_list[2])
        # _____Field 1_____
        if len(_field1) > 0 and _field1[-1] == "\\":
            _field1 = _field1[:-1]
            # if there are a \ character at the end it must be erased
        _percentages = _field1.split("\\")
        if len(_percentages) > 5:
            _percentages = _percentages[:5]
        # If there are no sufficient subfields, the subfields are added
        # with empty value:""
        else:
            _percentages = _percentages + [""]*(5-len(_percentages))
        _percentage_titles = [ "CI", "GG", "BI", "BAJA", "IVA" ]
        _percentage_dict = {}
        for _percentage_index in range(len(_percentages)):
            try:
                _percentage = int(_percentages[_percentage_index])
            except ValueError:
                _percentage = ""
            _percentage_dict[_percentage_titles[_percentage_index]] = \
                _percentage
        self.__budget.setPercentages(_percentage_dict)
        # _____Field 0 and 1_____
        # Default number of decimal places
        # Number of titles in ~V record
        _title_num = len(self.__budget.getTitleList()[1])
        if _title_num == 0: _title_num = 1
        # If the field 2 is empty, the field 0 is readed
        if _field2 == "":
            # _____Field 0_____
            if _field0[-1] == "\\":
                _field0 = _field0[:-1]
                # if there are a \ character at the end it must be erased
            _decimal_list = _field0.split("\\")
            _decimal_index = 0
            if len(_decimal_list)%9 != 0:
                # if it is not multiple of 9, empty subfield are added
                _decimal_list = _decimal_list + [""]*(9 - \
                                len(_decimal_list)%9)
            # The number of decimal values is the same as the numbers of 
            # titles in the V record
            if len(_decimal_list)//9 > _title_num:
                _decimal_list = _decimal_list[:_title_num*9]
            elif len(_decimal_list)//9 < _title_num:
                _decimal_list = _decimal_list + _decimal_list[-9:] * \
                                (_title_num-(len(_decimal_list)//9))
            while _decimal_index <= len(_decimal_list)-9:
                _decimals = _decimal_list[_decimal_index:(_decimal_index + 9)]
                _forlist = range(len(_decimals)-1)
                for _index in range(len(_decimals)):
                    try:
                        #TODO: test this
                        _decimals[_index] = int(_decimals[_index])
                    except ValueError:
                        _decimals[_index] = ""
                _DN = _decimals[0]
                _DD = _decimals[1]
                _DS = _decimals[2]
                _DR = _decimals[3]
                _DI = _decimals[4]
                _DP = _decimals[5]
                _DC = _decimals[6]
                _DM = _decimals[7]
                _DIVISA = _decimals[8]
                _percentage_dict = {"DN" : _DN,
                                    "DD" : _DD,
                                    "DSP" : _DS,
                                    "DS" : _DS,
                                    "DFC" : _DR,
                                    "DFPU" : _DR,
                                    "DFUO" : _DR,
                                    "DFA" : _DR,
                                    "DRC" : _DR,
                                    "DRPU" : _DR,
                                    "DRUO" : _DR,
                                    "DRA" : _DR,
                                    "DP" : _DC,
                                    "DC" : _DC,
                                    "DPU" : _DC,
                                    "DUO" : _DC,
                                    "DEA" : _DC,
                                    "DES" : _DC,
                                    "DIR" : _DI,
                                    "DIRC" : _DI,
                                    "DCD" : _DP,
                                    "DIVISA": _DIVISA }
                _decimal_index = _decimal_index + 9
                self.__budget.setDecimals(_percentage_dict,
                                          (_decimal_index//9))
        else:
            # _____Field 3_____
            if _field2[-1] == "\\":
                _field2 = _field2[:-1]
                # if there are a \ character at the end it must be erased
            _decimal_list = _field2.split("\\")
            # test if the Divisa subfield is 12 or 14 position
            # Divisa is the only Alphanumeric subfield
            # "no_float": "[^0-9.]"
            if len(_decimal_list) >= 13 and \
               self.__pattern["no_float"].search(_decimal_list[12]):
                _multiple = 13
            elif len(_decimal_list) >= 15 and \
               self.__pattern["no_float"].search(_decimal_list[14]):
                _multiple = 15
            else:
                if self.__file_format == "FIEBDC-3/2002":
                    _multiple = 13
                elif self.__file_format == "FIEBDC-3/2004":
                    _multiple = 13
                elif self.__file_format == "FIEBDC-3/2007":
                    _multiple = 15
                else:
                    _multiple = 15
            _decimal_index = 0
            if len(_decimal_list)%_multiple != 0 :
                # if it is not multiple of _multiple, empty subfield are added
                _decimal_list = _decimal_list + \
                                [""]*(_multiple-len(_decimal_list)%_multiple)
            # The number of decimal values is the same as the numbers of 
            # titles in the V record
            if len(_decimal_list)//_multiple > _title_num:
                _decimal_list = _decimal_list[:_title_num*_multiple]
            elif len(_decimal_list)//_multiple < _title_num:
                _decimal_list = _decimal_list + [_decimal_list[-_multiple:]]*\
                             (_title_num-(len(_decimal_list)//_multiple))
            while _decimal_index <= len(_decimal_list)-_multiple:
                _decimals = _decimal_list[_decimal_index:(_decimal_index +\
                            _multiple)]
                for _index in range(len(_decimals)-1):
                    try:
                        _decimals[_index] = int(_decimals[_index])
                    except:
                        _decimals[_index] = ""
                if _multiple == 13:
                    _DRC = _decimals[0]
                    _DC = _decimals[1]
                    _DRO = _decimals[2]
                    _DFS = _decimals[3]
                    _DRS = _decimals[4]
                    _DFO = _decimals[5]
                    _DUO = _decimals[6]
                    _DI = _decimals[7]
                    _DES = _decimals[8]
                    _DN = _decimals[9]
                    _DD = _decimals[10]
                    _DS = _decimals[11]
                    _DIVISA = _decimals[12]
                    _percentage_dict = {
                                  "DN" : _DN,
                                  "DD" : _DD,
                                  "DSP" : _DS,
                                  "DS" : _DS,
                                  "DFC" : _DFS,
                                  "DFPU" : _DRC,
                                  "DFUO" : _DFS,
                                  "DFA" : _DFS,
                                  "DRC" : _DRS,
                                  "DRPU" : _DRC,
                                  "DRUO" : _DRS,
                                  "DRA" : _DRS,
                                  "DP" : _DC,
                                  "DC" : _DC,
                                  "DPU" : _DC,
                                  "DUO" : _DUO,
                                  "DEA" : _DES,
                                  "DES" : _DES,
                                  "DIR" : _DI,
                                  "DIRC" : _DC,
                                  "DCD" : _DI,
                                  "DIVISA": _DIVISA,
                                }
                else: # _multiple == 15:
                    _DRC = _decimals[0]
                    _DC = _decimals[1]
                    _DRO = _decimals[2]
                    _DFS = _decimals[3]
                    _DRS = _decimals[4]
                    _DFO = _decimals[5]
                    _DUO = _decimals[6]
                    _DI = _decimals[7]
                    _DES = _decimals[8]
                    _DN = _decimals[9]
                    _DD = _decimals[10]
                    _DS = _decimals[11]
                    _DSP = _decimals[12]
                    _DEC = _decimals[13]
                    _DIVISA = _decimals[14]
                    _percentage_dict = {
                                  "DN" : _DN,
                                  "DD" : _DD,
                                  "DSP" : _DSP,
                                  "DS" : _DS,
                                  "DFC" : _DFS,
                                  "DFPU" : _DRC,
                                  "DFUO" : _DFS,
                                  "DFA" : _DFS,
                                  "DRC" : _DRS,
                                  "DRPU" : _DRC,
                                  "DRUO" : _DRS,
                                  "DRA" : _DRS,
                                  "DP" : _DC,
                                  "DC" : _DC,
                                  "DPU" : _DC,
                                  "DUO" : _DUO,
                                  "DEA" : _DEC,
                                  "DES" : _DES,
                                  "DIR" : _DI,
                                  "DIRC" : _DC,
                                  "DCD" : _DI,
                                  "DIVISA": _DIVISA}
                _decimal_index = _decimal_index + 13
                self.__budget.setDecimals(_percentage_dict,
                                           (_decimal_index//13))
        self.num_valid_record = self.num_valid_record +1

    def _parseC(self, field_list):
        """_parseC(self, field_list)
        
        field_list: field list of the record
            0- C: Record
            1- Code{\Code}
            2- [Unit]
            3- [Summary]
            4- {Price\}
            5- {Date\}
            6- [Type]
        """
        # _____number of fields_____
        # Any INFORMATION after last field separator is ignored
        if len(field_list) > 7:
            field_list = field_list[:7]
        # If there are no sufficient fields, the fields are added
        # with empty value:""
        else:
            field_list = field_list + [""]*(7-len(field_list))
        # control character are erased: en of line, tab, space 
        # _____Fields_____
        _record_type = field_list[0]
        _codes = self.delete_control_space(field_list[1])
        _unit = self.delete_control_space(field_list[2])
        _summary = self.delete_control(field_list[3])
        _prices = self.delete_control_space(field_list[4])
        _dates = self.delete_control_space(field_list[5])
        _type = self.delete_control_space(field_list[6])
        # _____Code_____
        _codes = _codes.split("\\")
        if len(_codes) > 0:
            # parse the hierarchy of the first code
            # hierarchy: 0->root, 1->Chapter/subchapter, 2->other
            if len(_codes[0]) > 2 and _codes[0][-2:] == "##":
                _hierarchy = 0
            elif len(_codes[0]) > 1 and _codes[0][-1:] == "#":
                _hierarchy = 1
            else:
                _hierarchy = 2
            # "#" and "##" characters at the end of the code are erased
            # invalid characters are also erased
            # maximun len 20 characters
            _codes = [self.validateCode(_code) for _code in _codes]
        # empty codes are ignored
        while "" in _codes:
            _codes.remove("")
        if len(_codes) > 0:
            #TODO: test this
            _code = _codes[0]
            _synonyms = _codes
        else:
            print _("Record C without a valid code")
            return
        # _____Unit_____
        # nothing to do
        # _____Summary_____
        # nothing to do
        # _____Price_____ and _____Dates_____
        # last \ is erased
        if len(_dates) > 0 and _dates[-1] == "\\":
            _dates = _dates[:-1]
        if len(_prices) > 0 and _prices[-1] == "\\":
            _prices = _prices[:-1]
        _dates = _dates.split("\\")
        _prices = _prices.split("\\")
        # number of prices = number of titles in "V" line
        # if there are no sufficient prices it takes the last price defined
        _title_num = len(self.__budget.getTitleList()[1])
        if _title_num == 0: _title_num = 1
        if len(_prices) > _title_num: _prices = _prices[:_title_num]
        elif len(_prices) < _title_num:
            _prices = _prices + [_prices[-1]]*(_title_num-len(_prices))
        # number of dates = number of prices
        # if there are no sufficient dates it takes the last date defined
        if len(_dates) > len(_prices): _dates = _dates[:len(_prices)]
        elif len(_dates) < len(_prices):
            _dates = _dates + [_dates[-1]]*(len(_prices)-len(_dates))
        for _index in range(len(_prices)):
            # TODO: lack to specify the number of decimals of the price
            try:
                _prices[_index] = float(_prices[_index])
            except:
                _prices[_index] = 0.0
            _parsed_date = self.parseDate(_dates[_index])
            if _parsed_date is None:
                _dates[_index] = ""
            else:
                _dates[_index] = _parsed_date
        # _____Type_____
        # 0 Without classifying
        #       EA  Auxiliary element
        #       EU  Unitary element
        #       EC  Complex element
        #       EF  Functional element
        #       OB  Construction site
        #       PA  Cost overrun
        #       PU  Unitary budget
        # 1 Labourforce 
        #       H   Labourforce
        # 2 Machinery and auxiliary equipment
        #       Q   Machinery
        #       %   Auxiliary equipment
        # 3 Building materials
        #       MC  Cement
        #       MCr Ceramic
        #       MM  Wood
        #       MS  Iron and steel
        #       ME  Energy
        #       MCu Copper
        #       MAl Aluminium
        #       ML  Bonding agents
        #       M   Others materials
        #  Hierarchy        type  subtype
        # 0->root         -> 0 -> None,OB
        # 1->[sub]chapter -> 0 -> None,PU
        # 2->Other        -> 0 -> None,EA,EU,EC,EF,PA
        #                    1 -> None,H
        #                    2 -> None,Q,%
        #                    3 -> None,MC,MCr,MM,MS,ME,MCu,Mal,ML,M
        if _hierarchy == 0:
            if _type == "OB":
                _subtype = _type
                _type = 0
            elif _type == "0" or _type == "":
                _subtype = ""
                _type = 0
            else:
                print utils.mapping(_("Incorrect type ($1) in the code $2"),
                      (str(_type), _code))
                _type = 0
                _subtype = ""
        elif _hierarchy == 1:
            if _type == "PU":
                _subtype = _type
                _type = 0
            elif _type == "0" or _type == "":
                _subtype = ""
                _type = 0
            else:
                print utils.mapping(_("Incorrect type ($1) in the code $2"),
                      (str(_type), _code))
                _type = 0
                _subtype = ""
        else:
            if _type == "EA" or _type == "EU" or _type == "EC" or \
               _type == "EF" or _type == "PA":
                _subtype = _type
                _type = 0
            elif _type == "H":
                _subtype = _type
                _type = 1
            elif _type == "Q" or _type == "%":
                _subtype = _type
                _type = 2
            elif _type == "MC" or _type == "MCr" or _type == "MM" or \
                 _type == "MS" or _type == "ME" or _type == "MCu" or \
                 _type == "Mal" or _type == "ML" or _type == "M":
                _subtype = _type
                _type = 3
            elif _type == "0" or _type == "1" or _type == "2" or \
                 _type == "3":
                _subtype = ""
                _type = int(_type)
            elif _type == "":
                _subtype = ""
                _type = 0
            else:
                print utils.mapping(_("Incorrect type ($1) in the code $2"),
                      (str(_type), _code))
                _type = 0
                _subtype = ""
        self.__budget.setRecord(_code, _synonyms, _hierarchy,
            _unit, _summary, _prices, _dates, _type, _subtype)
        self.num_valid_record = self.num_valid_record + 1
    
    def _parseDY(self, field_list):
        """_parseDY(self, field_list)
        
        field_list: field list of the record
            0- D or Y: DECOMPOSITION or ADD DECOMPOSITION
            1- Parent Code
            2- <Child Code\ [Factor]\ [Yield]>
        """
        # _____number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[:3]
        # If there are no sufficient fields, the fields are added
        # with empty value:""
        else:
            field_list = field_list + [""]*(3-len(field_list))
        # control character are erased: end of line, tab, space 
        # _____Fields_____
        _record_type = field_list[0]
        _code = self.delete_control_space(field_list[1])
        _children = self.delete_control_space(field_list[2])
        # _____Code_____
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        _code = self.validateCode(_code) 
        # _____children_____
        # TODO: test the number of decimals in factor an yield values
        _children = _children.split( "\\" )
        _children_list = [ ]
        _child_index = 0
        while _child_index < len(_children)-3:
            # _____subfields_____
            _child_code = _children[_child_index]
            _factor = _children[_child_index+1]
            _yield = _children[_child_index+2]
            # _____child_code_____
            _child_code = self.validateCode(_child_code)
            # _____factor_____
            if _factor != "":
                try:
                    _factor = float(_factor)
                except ValueError:
                    print utils.mapping(_("ValueError loadig the "\
                          "descomposition of the record $1, the factor "\
                          "of the child $2 must be a float number and "\
                          "can not be $3, seted default value 1.0"),
                          (_code, _child_code, _factor))
                    _factor = 1.0
            #____yield___
            if _yield != "":
                try:
                    _yield = float(_yield)
                except ValueError:
                    print utils.mapping(_("ValueError loading the "\
                          "descomposition of the record $1, the yield of "\
                          "the child $2, must be a float number and can"\
                          "not be $3,  seted default value 1.0"),
                           (_code, _child_code, _factor))
                    _yield = 1.0
            if _child_code != "" and _code != "":
                _children_list.append([_child_code, _factor, _yield ])
            if _record_type == "D":
                _position = _child_index / 3
            else: #_record_type == "Y"
                _position = -1
            self.__budget.setTree(_code, _child_code, _position, _factor, 
                _yield, "", "", "", "")
            _child_index = _child_index + 3
        self.num_valid_record = self.num_valid_record +1

    def _parseT(self, field_list):
        """_parseT(self, field_list)
        
        field_list: field list of the record
            0- T: Text
            1- Record code
            2- Description text
        """
        # _____Number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[0:3]
        field_list = field_list[1:]
        if len(field_list) != 2:
            return
        # control character are erased: end of line, tab, space
        # _____Fields_____
        _code = self.delete_control_space(field_list[0])
        _text = field_list[1]
        # _____Code_____
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        _code = self.validateCode(_code) 
        # _____Text_____
        self.__budget.setText(_code, _text)
        self.num_valid_record = self.num_valid_record + 1

    def _parseMN(self, field_list):
        """_parseMN(self, field_list)
        
        field_list: field list of the record
            0- M or N: MEASURE or ADD MEASURE
            1- [Parent Code\]Child Code
            2- {Path\}
            3- TOTAL MEASURE
            4- {Type\Comment\Unit\Length\Width\Height\}
            5- [Label]
        """

        # _____Number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 6 fields
        if len(field_list) > 6:
            field_list = field_list[:6]
        # If there are no sufficient fields, the fields are added
        # with empty value:""
        else:
            field_list = field_list + [""]*(6-len(field_list))
        # control character are erased: end of line, tab, space
        # _____Fields_____
        _record_type = field_list[0]
        _codes = self.delete_control_space(field_list[1])
        _path = self.delete_control_space(field_list[2])
        _total = self.delete_control_space(field_list[3])
        _lines = self.delete_control(field_list[4])
        _label = self.delete_control_space(field_list[5])
        # _____Codes_____
        _code_list = _codes.split( "\\" )
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        if len(_code_list) == 2:
            _parent_code = self.validateCode(_code_list[0]) 
            if _parent_code == "":
                _parent_code = None
            _child_code =  self.validateCode(_code_list[1])
        elif len(_code_list) == 1:
            _child_code =  self.validateCode(_code_list[0])
            _parent_code = None
        else:
            print utils.mapping(_("Invalid codes in $1 record, codes $2"),
                  (_record_type, _codes))
            return
        if _child_code == "":
            print utils.mapping(_("Empty child code in $1 record, codes: "\
                  "$2"), (_record_type, _codes))
            return
        # _____Path_____
        # TODO: path=0, no-estructured measures
        _path_list = _path.split( "\\" )
        if len(_path_list) > 0:
            while _path_list[-1] == "":
                _path_list = _path_list[:-1]
            _path = _path_list[-1]
            try:
                _path = int(_path)
            except ValueError:
                print utils.mapping(_("Invalid path in $1 record, "\
                      "codes $2"), (_record_type, _codes))
                return
            if _path > 0:
                _path -= 1
        else:
            _path = 0
        # _____Total_____
        try:
            _total = float(_total)
        except ValueError:
            print utils.mapping(_("Invalid Total Measure value in $1 "\
                  "record, codes $2"), (_record_type, _codes))
            return
        # _____Measure lines_____
        _lines = _lines.split( "\\" )
        _line_index = 0
        _line_list = [ ]
        while _line_index < len(_lines)-6:
            _linetype = _lines[_line_index]
            if _linetype == "":
                _linetype = 0
            elif _linetype == "1" or _linetype == "2" or \
                   _linetype == "3":
                    _linetype = int(_linetype)
            else:
                _linetype = 0
            _comment= _lines[_line_index + 1]
            if _linetype == 3:
                # "formula": ".*[^0123456789\.()\+\-\*/\^abcdp ].*"
                if self.__pattern["formula"].match(_comment):
                    print utils.mapping(_("The comment is not a formula or "\
                          "its have invalid characters, in the $1 record, "\
                          "codes $2"), (_record_type, _codes))
                    return
                else:
                    _formula = _comment
                    _comment = ""
            else:
                _formula = ""
            _units = _lines[_line_index + 2]
            _length = _lines[_line_index + 3]
            _width = _lines[_line_index + 4]
            _height = _lines[_line_index + 5]
            try:
                if _units != "": _units = float(_units)
                if _length != "": _length = float(_length)
                if _width != "": _width = float(_width)
                if _height != "": _height = float(_height)
            except ValueError:
                print utils.mapping("The measure values are not float "\
                      "numbers, code $1", (_codes,))
                return
            _line_list.append([_linetype, _comment, _units,
                               _length, _width, _height, _formula])
            _line_index = _line_index + 6
        self.__budget.setTree(_parent_code, _child_code, _path, "", "",
                           _total, _line_list, _label, _record_type)
        self.num_valid_record = self.num_valid_record + 1

    def _parseW(self, field_list):
        """_parseW(self, field_list)
        
        field_list: field list of the record
            0- W: Geografical field
            1- Field Code
            2- Field
        """
        # _____Number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 2 fields
        if len(field_list) >= 2:
            field_list = field_list[1:2]
        else:
            return
        # control character are erased: end of line, tab, space
        # _____Fields_____
        _code_fields = field_list[0]
        # last \ is erased
        if len(_code_fields) and _code_fields[-1] == "\\":
            _code_fields = _code_fields[:-1]
        _code_fields = _code_fields.split("\\")
        _field_dict = {}
        _field_index = 0
        while _field_index < len(_code_fields)-1:
            # _____subfields_____
            _field_code = _code_fields[_field_index]
            _field_title = _code_fields[_field_index+1]
            # control character are erased: end of line, tab, space
            # _____section_code_____
            #"control": "[\t \n\r]"
            _field_code = self.delete_control_space(_field_code)
            # _____section_title_____
            if _field_code != "":
                _field_dict[_field_code] = _field_title
            _field_index = _field_index + 2
        self.__budget.setSheetFields(_field_dict)
        self.num_valid_record = self.num_valid_record +1
    
    def _parseL(self, field_list):
        """_parseL(self, field_list)
        
        field_list: field list of the record
            0- L: Sheet of Conditions 1
            A:
                1- Empty
                2- {Section Code\Section Title}
            B:
                1- Record Code
                2- {Section Code\Section Text}
                3- {Section Code\RTF file}
                4- {Section Code\HTM file}
        """
        # _____Number of fields_____
        # The record must have at least 3 fields
        if len(field_list) < 3:
            return
        _code = field_list[1]
        if _code == "":
            # A: Section Titles
            # Any INFORMATION after last field separator is ignored
            # The record must have 3 fields
            if len(field_list) > 3:
                field_list = field_list[0:3]
            field_list = field_list[1:3]
            # _____Fields_____
            _section_codes = field_list[1]
            # last \ is erased
            if len(_section_codes) and _section_codes[-1] == "\\":
                _section_codes = _section_codes[:-1]
            _section_codes = _section_codes.split("\\")
            _section_dict = {}
            _section_index = 0
            while _section_index < len(_section_codes)-1:
                # _____subfields_____
                _section_code = _section_codes[_section_index]
                
                _section_title = _section_codes[_section_index+1]
                # control character are erased: end of line, tab, space
                # _____section_code_____
                _section_code = self.delete_control_space(_section_code)
                # _____section_title_____
                _section_title = self.delete_control_space(_section_title)
                if _section_code != "":
                    _section_dict[_section_code] = _section_title
                _section_index = _section_index + 2
            self.__budget.setSheetSections(_section_dict)
            self.num_valid_record = self.num_valid_record +1
            
        else:
            # Any INFORMATION after last field separator is ignored
            # The record must have 5 fields
            if len(field_list) > 5:
                field_list = field_list[0:5]
            field_list = field_list[1:]
            # _____Fields_____
            # _____Record Code_____
            _record_code = self.delete_control_space(field_list[0])
            # "#" and "##" characters at the end of the code are erased
            # invalid characters are also erased
            _record_code = self.validateCode(_record_code)
            _scodes_text = field_list[1]
            if _scodes_text == "":
                # TODO: rtf and html files
                print "Html and rtf files not implemented in ~L record"
            else:
                # _____Section-code_Section-text_____
                # last \ is erased
                if len(_scodes_text) and _scodes_text[-1] == "\\":
                    _scodes_text = _scodes_text[:-1]
                _scodes_text = _scodes_text.split("\\")
                _paragraph_dict = {}
                _section_dict = {}
                _section_index = 0
                while _section_index < len(_scodes_text)-1:
                    # _____subfields_____
                    _section_code = _scodes_text[_section_index]
                    _section_text = _scodes_text[_section_index+1]
                    # control character are erased: end of line, tab, space
                    # _____section_code_____
                    _section_code = self.delete_control_space(_section_code)
                    # _____section_text_____
                    if _section_code != "" and _section_text != "":
                        #-# paragraph #-#
                        _paragraph_code = _record_code + _section_code + "*"
                        _paragraph_dict[ _paragraph_code ] = _section_text
                        _section_dict[_section_code] = _paragraph_code
                    _section_index = _section_index + 2
                self.__budget.setSheetParagraphs(_paragraph_dict)
                self.__budget.setSheetRecord(_record_code, "*", _section_dict)
                self.num_valid_record = self.num_valid_record +1
    
    def _parseQ(self, field_list):
        """_parseQ(self, field_list)
        
        field_list: field list of the record
            0- Q: Sheet of Conditions 2
            1- Record Code
            2- {Section Code\Paragraph key\{Field key;}\}|
        """
        # _____Number of fields_____
        # The record must have at least 3 fields
        if len(field_list) < 3:
            return
        _code = field_list[1]
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[0:3]
        field_list = field_list[1:]
        # _____Fields_____
        # _____Record Code_____
        _record_code = self.delete_control_space(field_list[0])
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        _record_code = self.validateCode(_record_code)
        _scodes_pkey = field_list[1]
        # last \ is erased
        if len(_scodes_pkey) and _scodes_pkey[-1] == "\\":
            _scodes_pkey = _scodes_pkey[:-1]
        _scodes_pkey = _scodes_pkey.split("\\")
        _field_dict = {}
        _section_index = 0
        while _section_index < len(_scodes_pkey) -1:
            # _____subfields_____
            _section_code = _scodes_pkey[_section_index]
            _paragraph_key = _scodes_text[_section_index+1]
            _field_keys = _scodes_text[_section_index+2]
            # control character are erased: end of line, tab, space
            # _____section_code_____
            _section_code = self.delete_control_space(_section_code)
            # _____section_text_____
            _paragraph_key = self.delete_control_space(_paragraph_key)
            # _____Fields keys_____
            _field_keys = self.delete_control_space(_field_keys)
            # last ; is erased
            if len(_field_keys) and _field_keys[-1] == ";":
                _field_keys = _field_keys[:-1]
            _field_keys_list = _scodes_pkey.split(";")
            for _field_key in _field_keys_list:
                if _field_key != "" and _section_code != "" and \
                   _paragraph_key != "":
                    if _field_key in _field_dict:
                        _section_dict = _field_dict[_field_key]
                    else:
                        _section_dict = {}
                        _field_dict[_field_key] = _section_dict
                    _section_dict[_section_code] = _paragraph_code
            _section_index = _section_index + 3
        for _field, _section_dict in _field_dict.iteritems():
            self.__budget.setSheetRecord(_record_code, _field, _section_dict)
        self.num_valid_record = self.num_valid_record +1
    
    def _parseJ(self, field_list):
        """_parseJ(self, field_list)
        
        field_list: field list of the record
            0- J: Sheet of Conditions 3
            1- Paragraph code
            2- [Paragraph text]
            3- [RTF file]
            4- [HTML file]
        """
        # _____Number of fields_____
        # The record must have at least 3 fields
        if len(field_list) < 3:
            return
        # Any INFORMATION after last field separator is ignored
        # The record must have 5 fields
        if len(field_list) > 5:
            field_list = field_list[0:5]
        field_list = field_list[1:]
        # _____Fields_____
        # _____Paragraph code_____
        _paragraph_code = self.delete_control_space(field_list[0])
        # _____Paragraph text_____
        _paragraph_text = field_list[1]
        if _paragraph_text == "":
            # TODO: rtf and html files
            print "Html and rtf files not implemented in ~J record"
        else:
            self.__budget.setSheetParagraph(paragraph_code, paragraph_text)
            self.num_valid_record = self.num_valid_record +1
    
    def _parseG(self, field_list):
        """_parseG(self, field_list)
        
        field_list: field list of the record
            0- G: Grafic info
            1- record code
            2- <grafic_file.ext\>
        """
        # _____Number of fields_____
        # The record must have at least 3 fields
        if len(field_list) < 3:
            return
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[0:3]
        field_list = field_list[1:]
        # _____Fields_____
        # _____Record Code_____
        _record_code = self.delete_control_space(field_list[0])
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        _record_code = self.validateCode(_record_code)
        # _____Grafic files_____
        _grafic_files = self.delete_control(field_list[1])
        # _____subfields_____
        # last \ is erased
        if len(_grafic_files) and _grafic_files[-1] == "\\":
            _grafic_files = _grafic_files[:-1]
        _grafic_file_list = _grafic_files.split("\\")
        _tested_grafic_file_list = []
        for _grafic_file in _grafic_file_list:
            _path = os.path.dirname(self.__filename)
            _grafic_file_path = os.path.join(_path, _grafic_file)
            if os.path.exists(_grafic_file_path):
                _tested_grafic_file_list.append(_grafic_file_path)
            else:
                _name_ext = os.path.splitext(_grafic_file)
                _grafic_file_name = _name_ext[0]
                _grafic_file_ext = _name_ext[1]
                _grafic_file_name_u = _grafic_file_name.upper()
                _grafic_file_name_l = _grafic_file_name.lower()
                _grafic_file_ext_u = _grafic_file_ext.upper()
                _grafic_file_ext_l = _grafic_file_ext.lower()
                _uu = _grafic_file_name_u + _grafic_file_ext_u
                _ul = _grafic_file_name_u + _grafic_file_ext_l
                _lu = _grafic_file_name_l + _grafic_file_ext_u
                _ll = _grafic_file_name_l + _grafic_file_ext_l
                _grafic_file_path_uu = os.path.join(_path, _uu)
                _grafic_file_path_ul = os.path.join(_path, _ul)
                _grafic_file_path_lu = os.path.join(_path, _lu)
                _grafic_file_path_ll = os.path.join(_path, _ll)
                if os.path.exists(_grafic_file_path_uu):
                    _tested_grafic_file_list.append(_grafic_file_path_uu)
                elif os.path.exists(_grafic_file_path_ul):
                    _tested_grafic_file_list.append(_grafic_file_path_ul)
                elif os.path.exists(_grafic_file_path_lu):
                    _tested_grafic_file_list.append(_grafic_file_path_lu)
                elif os.path.exists(_grafic_file_path_ll):
                    _tested_grafic_file_list.append(_grafic_file_path_ll)
                else:
                    print utils.mapping(_("The file $1 do not exist"),
                        (_grafic_file_path,))
        if len(_grafic_file_list) > 0:
            for _grafic_file in _tested_grafic_file_list:
                self.__budget.addFile(_record_code, _grafic_file, "img", "")
            self.num_valid_record = self.num_valid_record +1
    
    def _parseE(self, field_list):
        """_parseE(self, field_list)
        
        field_list: field list of the record
            0- E: Company
            1- company Code
            2 [ summary ]
            3- [ name ]
            4- { [ type ] \ [ subname ] \ [ address ] \ [ postal_code ]
              \ [ town ] \ [ province ] \ [ country ] \ { phone; } 
              \ { fax; }  \ {contact_person; } \ }
            5- [ cif ] \ [ web ] \ [ email ] \
        """
        
        # _____Number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 6 fields
        if len(field_list) > 6:
            field_list = field_list[1:6]
        # If there are no sufficient fields, the fields are added
        # with empty value:""
        else:
            field_list = field_list[1:] + [""]*(6-len(field_list))
        # _____Fields_____
        # _____company Code_____
        _company_code = self.delete_control_space(field_list[0])
        if _company_code == "":
            return
        # _____Summary_____

        _sumamary = self.delete_control(field_list[1])
        # _____Name_____
        _name = self.delete_control(field_list[2])
        # _____local_offices_____
        _local_offices = self.delete_control(field_list[3])
        # _____subfields of local_offices_____
        # last \ is erased
        if len(_local_offices) and _local_offices[-1] == "\\":
            _local_offices = _local_offices[:-1]
        _local_offices_list = _local_offices.split("\\")
        # If there are no sufficent subfields, the subfields are added 
        # whith empty value
        _nsub = len(_local_offices_list) % 10
        if _nsub != 0:
            _local_offices_list = _local_offices_list + \
                                   [""]*(10-len(field_list))
        _local_offices = []
        _local_offices_index = 0
        while _local_offices_index < len(_local_offices_list)-9:
            # _____subfields_____
            _type = _local_offices_list[_local_offices_index]
            _subname = _local_offices_list[_local_offices_index+1]
            _address = _local_offices_list[_local_offices_index+2]
            _postal_code = _local_offices_list[_local_offices_index+3]
            _town = _local_offices_list[_local_offices_index+4]
            _province = _local_offices_list[_local_offices_index+5]
            _country = _local_offices_list[_local_offices_index+6]
            _phone = _local_offices_list[_local_offices_index+7]
            # last ; is erased
            if len(_phone) and _phone[-1] == ";":
                _phone = _phone[:-1]
            _phone_list = _phone.split(";")
            _fax = _local_offices_list[_local_offices_index+8]
            # last ; is erased
            if len(_fax) and _fax[-1] == ";":
                _fax = _fax[:-1]
            _fax_list = _fax.split(";")
            _contact_person = _local_offices_list[_local_offices_index+9]
            if _type != "" or _subname != "" or _address != "" or \
               _postal_code != "" or _town != "" or _province != "" or \
               _country != "" or _phone != "" or _fax != "" or \
               _contact_person != "":
                _local_offices.append([_type, _subname, _address,
                                       _postal_code, _town, _province,
                                       _country, _phone_list, _fax_list,
                                       _contact_person])
            _local_offices_index = _local_offices_index + 10
        # _____cif web email_____
        _c_w_e = self.delete_control_space(field_list[4])
        # last \ is erased
        if len(_c_w_e) and _c_w_e[-1] == "\\":
            _c_w_e = _c_w_e[:-1]
        _c_w_e_list = _c_w_e.split("\\")
        # _____subfields_____
        # If there are no sufficient fields, the fields are added
        # with empty value:""
        _c_w_e_list = _c_w_e_list + [""]*(3-len(_c_w_e_list))
        _cif = _c_w_e_list[0]
        _web = _c_w_e_list[1]
        _email = _c_w_e_list[2]
        self.__budget.setCompany(_company_code, _sumamary, _name, 
                           _local_offices, _cif, _web, _email)
        self.num_valid_record = self.num_valid_record +1
    
    def _parseX(self, field_list):
        """_parseX(self, field_list)
        
        field_list: field list of the record
            A)
                0- X: Tecnical information
                1- Empty
                2- < TI_Code \ TI_Descitption \ TI_Unit >
            B)
                0- X: Tecnical information
                1- Record_code
                2- < TI_Code \ TI_value >
        """
        # Tecnical information
        # The record must have at least 3 fields
        if len(field_list) < 3:
            return
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[0:3]
        field_list = field_list[1:]
        # _____Fields_____
        # "control": "[\t \n\r]"
        _field_1 = self.delete_control_space(field_list[0])
        _field_2 = self.delete_control_space(field_list[1])
        if _field_1 == "":
            # A)
            _field_2_list = _field_2.split("\\")
            _ti_index = 0
            while _ti_index < len(_field_2_list)-3:
                _ti_code = _field_2_list[_ti_index]
                _ti_description = _field_2_list[_ti_index+1]
                _ti_unit = _field_2_list[_ti_index+2]
                if _ti_code != "":
                    self.__budget.addTecInfo(_ti_code, _ti_description,
                                             _ti_unit)
                _ti_index = _ti_index + 3
        else:
            # B)
            # "#" and "##" characters at the end of the code are erased
            # invalid characters are also erased
            _record_code = self.validateCode(_field_1)
            _field_2_list = _field_2.split("\\")
            _ti_index = 0
            _ti_dict = {}
            while _ti_index < len(_field_2_list)-2:
                _ti_code = _field_2_list[_ti_index]
                _ti_value = _field_2_list[_ti_index+1]
                if _ti_code != "" and _ty_value != "":
                    _ti_dict[_ti_code] = _ty_value
                _ti_index = _ti_index + 2
            self.__budget.setTecnicalInformation(_record_code, _ti_dict)
        self.num_valid_record = self.num_valid_record +1

    def _parseF(self, field_list):
        """_parseF(self, field_list)
        
        field_list: field list of the record
            0- F: Files
            1- Record code
            2- { Type \ { Filenames; } \ [Description] }
        """

        # _____Number of fields_____
        # The record must have at least 3 fields
        if len(field_list) < 3:
            return
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[0:3]
        field_list = field_list[1:]
        # _____Fields_____
        # _____Record Code_____
        _record_code = self.delete_control_space(field_list[0])
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        _record_code = self.validateCode(_record_code)
        # _____Grafic files_____
        _files = self.delete_control(field_list[1])
        # _____subfields_____
        # last \ is erased
        if len(_files) and _files[-1] == "\\":
            _files = _files[:-1]
        _files_list = _files.split("\\")
        # adding empty subfiels if necesary
        if len(_files_list)%3 > 0:
            _files_list.extend[""]*(3 - len(_files_list)%3)
        _file_index = 0
        _tested_files_list = []
        while _file_index < len(_files_list)-3:
            _type = _files_list[_file_index].replace(" ","")
##            _types = {
##                "0": _("others"),
##                "1": _("características técnicas y de fabricación"),
##                "2": _("manual de colocación, uso y mantenimiento"),
##                "3": _("certificado/s de elementos y sistemas"),
##                "4": _("normativa y bibliografía"),
##                "5": _("tarifa de precios"),
##                "6": _("condiciones de venta"),
##                "7": _("carta de colores"),
##                "8": _("ámbito de aplicación y criterios selección"),
##                "9": _("cálculo de elementos y sistemas"),
##                "10": _("presentación, datos generales, objetivos, etc. de "\
##                        "empresa"),
##                "11": _("certificado/s de empresa"),
##                "12": _("obras realizadas")}
            _types = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                      "11", "12"]
            if not _type in _types:
                _type = "0"
            _filenames = _files_list[_file_index + 1]
            _description = _files_list[_file_index + 2]
            _file_index += 3
            if len(_filenames) and _filenames[-1] == ";":
                _files = _files[:-1]
            _filenames_list = _files.split(";")
            _path = os.path.dirname(self.__filename)
            for _filename in filenames_list:
                _file_path = os.path.join(_path, _filename)
                if os.path.exists(_file_path):
                    _tested_files_list.append([_file_path, _type,
                                               _description])
                else:
                    _name_ext = os.path.splitext(_filename)
                    _file_name = _name_ext[0]
                    _file_ext = _name_ext[1]
                    _file_name_u = _file_name.upper()
                    _file_name_l = _file_name.lower()
                    _file_ext_u = _file_ext.upper()
                    _file_ext_l = _file_ext.lower()
                    _uu = _file_name_u + _file_ext_u
                    _ul = _file_name_u + _file_ext_l
                    _lu = _file_name_l + _file_ext_u
                    _ll = _file_name_l + _file_ext_l
                    _file_path_uu = os.path.join(_path, _uu)
                    _file_path_ul = os.path.join(_path, _ul)
                    _file_path_lu = os.path.join(_path, _lu)
                    _file_path_ll = os.path.join(_path, _ll)
                    if os.path.exists(_file_path_uu):
                        _tested_files_list.append([_file_path_uu, _type,
                                                   _description])
                    elif os.path.exists(_grafic_file_path_ul):
                        _tested_files_list.append([_file_path_ul, _type,
                                                   _description])
                    elif os.path.exists(_grafic_file_path_lu):
                        _tested_files_list.append([_file_path_lu, _type,
                                                   _description])
                    elif os.path.exists(_grafic_file_path_ll):
                        _tested_files_list.append([_file_path_ll, _type,
                                                   _description])
                    else:
                        print utils.mapping(_("The file $1 do not exist"),
                            (_file_path,))
        if len(_tested_files_list) > 0:
            for _file in _tested_file_list:
                self.__budget.addFile(_record_code, _file[0], file[1], file[2])
        self.num_valid_record = self.num_valid_record +1

    def _parseB(self, field_list):
        """_parseB(self, field_list)
        
        field_list: field list of the record
            0- B: Change code
            1- Record Code
            2- New code
        """
        # _____Number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[0:3]
        field_list = field_list[1:]
        if len(field_list) != 2:
            return
        # control character are erased: end of line, tab, space
        # _____Fields_____
        _code = self.delete_control_space(field_list[0])
        _new_code = self.delete_control_space(field_list[1])
        # _____Codes_____
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        _code = self.validateCode(_code)
        _new_code = self.validateCode(_new_code)
        # change code
        self.__budget.changeCode(_code, _new_code)
        self.num_valid_record = self.num_valid_record + 1

    def _parseA(self, field_list):
        """_parseA(self, field_list)
        
        field_list: field list of the record
            0- A: Labels
            1- Record Code
            2- <Label\>
        """
        # _____Number of fields_____
        # Any INFORMATION after last field separator is ignored
        # The record must have 3 fields
        if len(field_list) > 3:
            field_list = field_list[0:3]
        field_list = field_list[1:]
        if len(field_list) != 2:
            return
        # control character are erased: end of line, tab, space
        # _____Fields_____
        # "control": "[\t \n\r]"
        _code = self.delete_control_space(field_list[0])
        _labels = self.delete_control_space(field_list[1])
        # _____Codes_____
        # "#" and "##" characters at the end of the code are erased
        # invalid characters are also erased
        _code = self.validateCode(_code)
        # _____Labels_____
        # last \ is erased
        # TODO: change the others parsers to this:
        while len(_labels) > 0 and _labels[-1] == "\\":
            _labels = _labels[:-1]
        # replace "_" to " "
        _labels = _labels.replace("_"," ")
        _label_list = _labels.split("\\")
        for _label in _label_list:
            self.__budget.addLabel(_code, _label)
        self.num_valid_record = self.num_valid_record + 1

    def _parseP(self, field_list):
        """_parseP(self, field_list)
        
        field_list: Parametric record
            A) Global paremetric record
                0- P: Parametric
                1- Empty
                2- [Parametric description]
                3- [library.DLL]
            B) Family Parametric record
                0- P: Parametric
                1- Family Code
                2- [Parametric description]
        """
        # TODO: Use global parametric record
        if len(field_list) > 2:
            # delete control caracters and spaces
            _family_code = self.delete_control_space(field_list[1])
            if _family_code == "": # A)Global paremetric record
                # The record must have 3 or 4 fields
                if len(field_list) > 4:
                    field_list = field_list[0:4]
                field_list = field_list[1:]
                if len(field_list) == 2:
                    field_list.append("")
                if len(field_list) != 3:
                    return
            else: # B)Family Parametric record
                # The record must have 3 fields
                if len(field_list) > 3:
                    field_list = field_list[0:3]
                field_list = field_list[1:]
                if len(field_list) != 2:
                    print _("PyArq hates parametric DLLs")
                    return
        else:
            return
        # _____Description_____
        _description = field_list[1]
        if _description == "":
            print _("PyArq hates parametric DLLs")
            return
        # Adding last end of line
        _description = _description + "\r\n"
        # Delete comments
        # "comment" : "#.*\r\n"
        _description = self.__pattern["comment"].sub("\r\n",_description)
        # Tabs to spaces
        _description = _description.replace("\t"," ")
        # Delete empty lines
        # "empty_line": r"(\r\n) *\r\n"
        while self.__pattern["empty_line"].search(_description):
            _description = self.__pattern["empty_line"].sub(
                            lambda x: x.groups()[0], _description)
        # Delete spaces before and after /
        # "space_before_backslash" : r"( )+\\"
        _description = self.__pattern["space_before_backslash"].sub(
                        r"\\",_description)
        # "space_after_backslash" : r"\\( )+"
        _description = self.__pattern["space_after_backslash"].sub(
                        r"\\",_description)
        # Join lines that start but not end with /
        _description = "\r\n" + _description # add leading end of line
        # "start_noend_backslash": "(\r\n\\\.*[^\\\])\r\n"
        while self.__pattern["start_noend_backslash"].search(_description):
            _description = self.__pattern["start_noend_backslash"].sub(
                            lambda x: x.groups()[0], _description)
        # Join lines that end with a + - * / ^ and @ & < > <= >= = <> !
        # "end_oper" : "(\+|-|\*|/|/^|@|&|<|>|<=|>=|=|!) *\r\n"
        _description = self.__pattern["end_oper"].sub(
                        lambda x: x.groups()[0], _description)
        # Join lines for matricial vars
        # matricial_var : "(\r\n *[%|\$][A-ZÑ].*=.*,) *\r\n"
        while self.__pattern["matricial_var"].search(_description):
            _description = self.__pattern["matricial_var"].sub(
                            lambda x: x.groups()[0], _description)
        _description = _description[2:]  # remove leading end of line
        #_description = re.sub(r"\\( )+",r"\\",_description)
        _lines = _description.split("\r\n")
        _final_description = ""
        _pass_line = 0
        for index in range(len(_lines)):
            _line = _lines[index]
            # Parse lines
            if len(_line) != 0: # Delete empty lines
                if _pass_line > 0:
                    _pass_line = _pass_line -1
                    _line = ""
                elif _line.isspace():
                    _line = ""
                elif  _line[0] != "\\":
                    # Delete spaces out "" delimiter
                    _list = _line.split('"')
                    _final_line = ""
                    for index1 in range(len(_list)):
                        if index1 % 2 != 0:
                            _parcial_line = '"' + _list[index1]
                        else:
                            _parcial_line =  '"' + _list[index1].replace(" ","")
                        _final_line = _final_line + _parcial_line
                    _line = _final_line[1:]
                    _lines[index] = _line
                    # parse data
                    if len(_line) > 2 and _line[:2] == "::":
                        # Delete spaces out " delimiter
                        #print "__PRECIO__" + _line[2:]
                        pass
                    elif len(_line) > 2 and _line[:2] == "%:":
                        # Delete spaces out " delimiter
                        #print "__%AUX__" + _line[2:]
                        pass
                    elif len(_line) > 3 and _line[:2] == "%%:":
                        # Delete spaces out " delimiter
                        #print "__%%AUX__" + _line[2:]
                        pass
                    elif self.__pattern["var"].search(_line):
                        # Delete spaces out " delimiter
                        #print "line =", _line
                        while _line.count('"') % 2 == 1 and \
                              index + _pass_line + 1 < len(_lines) -1:
                            _line = _line + _lines[index + _pass_line + 1]
                            _pass_line = _pass_line + 1
                        _search = self.__pattern["var"].search(_line)
                        if _search is not None:
                            _var = _search.groups()[0] + " = " + _search.groups()[1]
                            #print "__VAR__" + str(_var)
                            pass
                        else:
                            #print "no __VAR__", _line
                            pass
                    elif self.__pattern["descomposition"].search(_line):
                        # Delete spaces out " delimiter
                        #_patern = "(^[^:]*):(.*)$"
                        _search = self.__pattern["descomposition"].search(_line)
                        if _search is not None:
                            _var = _search.groups()[0] + ":" + _search.groups()[1]
                            #print "__Descomposición__" + str(_var)
                            pass
                        else:
                            #print "no __Descomposición__", _line
                            pass
                    else:
                        print "Parametric: code: " + _family_code
                        print "******* Desconocido *** : " + _line
                        if index-10 > 0: print "-11 :", _lines[index-11]
                        if index-10 > 0: print "-10 :", _lines[index-10]
                        if index-9 > 0: print "-9 :", _lines[index-9]
                        if index-8 > 0: print "-8 :", _lines[index-8]
                        if index-7 > 0: print "-7 :", _lines[index-7]
                        if index-6 > 0: print "-6 :", _lines[index-6]
                        if index-5 > 0: print "-5 :", _lines[index-5]
                        if index-4 > 0: print "-4 :", _lines[index-4]
                        if index-3 > 0: print "-3 :", _lines[index-3]
                        if index-2 > 0: print "-2 :", _lines[index-2]
                        if index-1 > 0: print "-1 :", _lines[index-1]
                        print "-0 :", _lines[index-0]
                        pass
                else:
                    _parameter_list = _line.split("\\")[1:-1]
                    if len(_parameter_list) >= 2:
                        if _parameter_list[0] == "C" or \
                           _parameter_list[0] == "COMENTARIO":
                            #print "__COMENTARIO__" + _parameter_list[1]
                            self.__budget.setParametricSelectComment(
                                _family_code, _parameter_list[1])
                        elif _parameter_list[0] == "R" or \
                           _parameter_list[0] == "RESUMEN":
                            #print "__RESUMEN__" + _parameter_list[1]
                            self.__budget.setParametricSummary(_family_code,
                                _parameter_list[1])
                        elif _parameter_list[0] == "T" or \
                           _parameter_list[0] == "TEXTO":
                            #print "__TEXTO__" + _parameter_list[1]
                            self.__budget.setParametricText(_family_code,
                                _parameter_list[1])
                        elif _parameter_list[0] == "P" or \
                           _parameter_list[0] == "PLIEGO":
                            #print "__PLIEGO__" + str(_parameter_list[1:])
                            pass
                        elif _parameter_list[0] == "K" or \
                           _parameter_list[0] == "CLAVES":
                            #print "__CLAVES__" + str(_parameter_list[1:])
                            pass
                        elif _parameter_list[0] == "F" or \
                           _parameter_list[0] == "COMERCIAL":
                            #print "__COMERCIAL__" + str(_parameter_list[1:])
                            pass
                        else:
                            #print "==PARAMETRO==" + str(_parameter_list[:])
                            pass
                _final_description = _final_description + _line + "\r\n"
                
                #print _line
        # Delete last empty line
        _description = _final_description[:-2]
        _lines = _description.split("\r\n")
        for _line in _lines:
            pass
            #print _line
        self.num_valid_record = self.num_valid_record + 1

    def readFile(self, budget=None, filename=None, interface=None):
        """readFile(self, budget=None, filename=None)
        
        filename: the filename of the fiebdc file
        budget: base.obra object
        interface: a object to send messages
            must have printf(message) progress(percent)
                      recordStatistics(...)
        Return the budget objetc or None if the file can be readed
        """
        if not filename is None and not budget is None:
            self.__filename = filename
            self.__budget = budget
            self.__budget.filename = self.__filename
        if self.__filename is None or self.__budget is None or self.__cancel:
            return None
        if not os.path.exists(self.__filename):
            return None
        _time = time.time()
        try:
            _file =  open(self.__filename, 'r')
        except IOError:
            print utils.mapping("IOError: $1", (self.__filename,))
            return None
        self.__budget.filename = self.__filename
        self._record_number = 0
        self.num_valid_record = 0
        self._record_V_number = 0
        self._record_C_number = 0
        self._record_D_number = 0
        self._record_Y_number = 0
        self._record_M_number = 0
        self._record_N_number = 0
        self._record_T_number = 0
        self._record_K_number = 0
        self._record_W_number = 0
        self._record_L_number = 0
        self._record_Q_number = 0
        self._record_J_number = 0
        self._record_G_number = 0
        self._record_E_number = 0
        self._record_O_number = 0
        self._record_P_number = 0
        self._record_X_number = 0
        self._record_B_number = 0
        self._record_F_number = 0
        self._record_A_number = 0
        self._record_Unknow_number = 0
        print utils.mapping(_("Loading file $1"), (self.__filename,))
        _filesize = float(os.path.getsize(self.__filename))
        interface.progress(_file.tell() / _filesize)
        _buffer = _file.read(1000)
        # set codepage from V record
        _record_list = _buffer.split("~")
        registro_V = _record_list[1]
        # ~V|[PROPIEDAD_ARCHIVO]|VERSION_FORMATO[\DDMMAAAA]|[PROGRAMA_EMISION]|
        # [CABECERA]\{ ROTULO_IDENTIFICACION \}|[JUEGO_CARACTERES]|
        # [COMENTARIO]|[TIPO INFORMACIÓN]|[NÚMERO CERTIFICACIÓN]|
        # [FECHA CERTIFICACIÓN ] |
        registro_V = registro_V.split("|")
        if registro_V[0] == "V":
            #_codepage = registro_V[5]
            if len(registro_V) > 5:
                _version = registro_V[5].strip()
                # remove leading spaces
                if _version in self.__character_sets_dict:
                    self.__character_set = self.__character_sets_dict[_version]
                else:
                    print utils.mapping(_("This codepage do not exist in "\
                         "FIEBDC3! Default codepage: $1"),
                         (self.__character_set,))
            else:
                print utils.mapping(_("This V record dot have a codepage! "\
                         "Default codepage: $1"),
                         (self.__character_set,))
        else:
            print utils.mapping(_("Not 'V' record in File! Default codepage: "\
                  "$1"), (self.__character_set,))
        if self.__character_set != "utf8":
            _buffer = unicode(_buffer, self.__character_set)
            _buffer = _buffer.encode("utf8")
        # Any INFORMATION between the beginning of the file and the
        # beginning of the first registry “~” is ignored
        #"after_first_tilde" : "^[^~]*~"
        _buffer = self.__pattern["after_first_tilde"].sub("",_buffer)
        while _buffer != "" and not self.__cancel:
            #-# the blank characters (32), tabs (9) and end of line (13 and 10)
            # before the separators '~', '|' are erased.
            # Before separator \ not deleted because it affects the reading of
            # the record ~P
            _buffer = self.eraseControlCharacters(_buffer)
            _record_list = _buffer.split("~")
            # The last record can be incomplete unless it is the last one of
            # the file
            if len(_record_list) > 1:
                # not the end
                _last_record = _record_list.pop()
            else:
                # the end record
                # The blank characters (32), tabs (9) and end of line
                # (13 and 10) at the end of the file are ignored.
                #"end_control" : "((\r\n)| |\t)+$"
                _record_list[-1] = self.__pattern["end_control"].sub("",
                                           _record_list[-1])
                _last_record = ""
            for record in _record_list:
                if self.__cancel:
                    break
                self.parseRecord(record)
            interface.progress(_file.tell() / _filesize)
            _buffer2 = _file.read(100000)
            if self.__character_set != "utf8":
                _buffer2 = unicode(_buffer2, self.__character_set)
                _buffer2 = _buffer2.encode("utf8")
            _buffer = _last_record + _buffer2
        _file.close()
        if self.__cancel:
            print _("Process terminated")
            return None
        else:
            print utils.mapping(_("Time to load: $1 seconds"),
                 (("%.2f" %(time.time()-_time)),))
            print utils.mapping(_("Records/Valid Records: $1/$2"),
                  (self._record_number, self.num_valid_record))
            if self._record_O_number > 0:
                print utils.mapping(_("$1 unsuported record type O: "\
                  "Comercial Relationship"), (self._record_O_number,))
            if self.num_valid_record == 0:
                print  _("This file is not a valid FIBDC3 file")
                return None
            _str = ""
            for type in \
                     [("V", self._record_V_number),
                     ("C", self._record_C_number),
                     ("D", self._record_D_number),
                     ("Y", self._record_Y_number),
                     ("M", self._record_M_number),
                     ("N", self._record_N_number),
                     ("T", self._record_T_number),
                     ("K", self._record_K_number),
                     ("W", self._record_W_number),
                     ("L", self._record_L_number),
                     ("Q", self._record_Q_number),
                     ("J", self._record_J_number),
                     ("G", self._record_G_number),
                     ("E", self._record_E_number),
                     ("O", self._record_O_number),
                     ("P", self._record_P_number),
                     ("X", self._record_X_number),
                     ("B", self._record_B_number),
                     ("F", self._record_F_number),
                     ("A", self._record_A_number),
                     ("?", self._record_Unknow_number)]:
                _str = _str + "%s: %s\n" %(type[0], type[1])
            print  _str
            self._testBudget(self.__budget)
            return self.__budget

    def _testBudget(self, budget):
        """testBudget(self,budget)
        
        budget: base.obra object
        Test and repair budget object after read it from bc3 file
        """
        # TODO: more to do here
        print _("Testing budget ...")
        # Add price to records without price
        _iter = budget.iter()
        _titlelist = budget.getTitleList()[1]
        if len(_titlelist) == 0:
            _titlenum = 1
        else:
            _titlenum = len(_titlelist)
        for _code in _iter:
            _record = budget.getRecord(_code)
            _prices = _record.getPrices()
            _len_prices = len(_prices)
            if _titlenum > _len_prices:
                _leftprices = _titlenum - _len_prices
                for _index in range(0,_leftprices):
                    _root = budget.getRecord(budget.getRoot())
                    _price = [0.0, _root.getDate(_len_prices + _index)]
                    budget.addPriceToRecord(_price,_record)
        print _("End Test")

    def delete_control_space(self, text):
        text = self.delete_control(text)
        text = text.replace(" ", "")
        return text

    def delete_control(self, text):
        text = text.replace("\t", "")
        text = text.replace("\r", "")
        text = text.replace("\n", "")
        return text
