#-*- coding: utf-8 -*-

# Copyright 2008-2010 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import os
import sys
from cl_utils import convertStrListDict, fillstr, getpathenv, runOsCommand,\
                     pathJoin, _toUNICODE
from cl_vars_share import varsShare as glob_attr
import re
from cl_lang import lang
from cl_template import iniParser
from cl_string import columnWrite
import cl_overriding

# Перевод модуля на другой язык
tr = lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])

class var:
    '''Объект "Переменная окружения"'''
    # название сервиса которому принадлежит переменная
    #(Main, Builder, Client итд)
    # была ли установлена переменная
    isSet = False
    # Для какой программы переменная
    service = None
    # значение переменной
    value = ""
    # режим записи (атрибут mode)
    mode = "r"
    # скрытая переменная
    hide = False
    # был ли вызван метод заполнения
    isCallFill = False
    # объект в котором создан этот объект
    parentObj = None
    # запускать или нет метод заполнения
    fillStart = True
    # dynamic = True то переменная динамическая при повтороном запуске
    # запускается метод заполнения
    # метод заполнения не запускается только если fillStart = False
    # (осторожно  возможно зацикливание программы если методы заполнения
    # переменных используют методы друг друга)
    dynamic = False

    def __init__(self, parentObj):
        # словарь зависимых  переменных {имя:значение}
        self.dependValues = {}
        # объект который создал этот объект
        self.parentObj = parentObj

    def is_update(self):
        #Нужно ли перезапускать метод заполнения (если зависимые переменные
        #обновились то нужно)
        upd = False
        for depVarName in self.dependValues.keys():
            value = getattr(self.parentObj, depVarName).Get()
            if self.dependValues[depVarName] != value:
                self.dependValues[depVarName] =\
                    getattr(self.parentObj, depVarName).value
                upd = True
                break
        return upd

    def Get(self):
        """Получение значения переменной"""
        if not self.fillStart:
            self.isSet = True
            return self.value
        if self.dynamic:
            self.value = self.Fill()
            self.isSet = True
            return self.value
        if not self.isSet:
            if self.isCallFill:
                self.isSet = True
                return self.value
            self.isCallFill = True
            self.value = self.Fill()
        if self.dependValues and self.is_update():
            self.isCallFill = True
            self.value = self.Fill()
        self.isSet = True
        return self.value

    def Set(self, value):
        """Запись значения переменной"""
        self.value = value
        self.isSet = True
        return self.value

    def Fill(self):
        """Заполнение переменной в дальнейшем заменяем методом заполнения"""
        return self.value

class DataVars(object):
    """Класс хранения переменных шаблонов"""

    class DataVarsError(Exception):
        """Класс ошибок"""
        pass

    # Импортируемые модули - (раздел: модуль переменных, модуль заполнения
    #переменных)
    __modlist={'Main':('cl_vars','cl_fill')}
    def __init__(self):
        # Для нахождения зависимостей переменных
        self.__levelNumber = 0
        self.__LevelsVar = []
        # Для хранения импортированных модулей и объектов
        #[(cекция,импортированный модуль переменных, объект заполнения),]
        self._importList = []
        self.importData("Main")
        # Переменные которые нужно записать
        self.varsNeedWritten = []

    def importData(self, section, modlist=[]):
        """Импортирует модули с переменными и модули с функциями заполнения

        section секция раздела (Main, Client итд)
        создает необходимые структуры данных
        """
        if modlist:
            self.__modlist.update({section:modlist})
        if not section in self.__modlist.keys():
            raise self.DataVarsError(_("Unsupported section %s")%section)
        modVar = self.__modlist[section][0]
        modFill = self.__modlist[section][1]
        # Импортируем класс описания переменных и класс заполнения
        try:
            globals()[modVar] = __import__(modVar)
        except (ImportError, AttributeError), e:
            err1 = _("Error in import module %s")%modVar
            err2 = _("error")  + ": " +str(e)
            raise self.DataVarsError("%s\n%s"%(err1,err2))

        flagFindFillModule = True
        try:
            globals()[modFill] = __import__(modFill)
        except (ImportError, AttributeError), e:
            if "No module named" in str(e):
                flagFindFillModule = False
            else:
                err1 = _("Error in import module %s")%modFill
                err2 = _("error")  + ": " +str(e)
                raise self.DataVarsError("%s\n%s"%(err1,err2))

        if flagFindFillModule:
            # Создаем объект с методами заполнения переменных
            fillObj = globals()[modFill].fillVars()
            # Подключаем методы получения и записи переменных
            fillObj.Get = self.Get
            fillObj.Set = self.Set
            fillObj.GetList = self.GetList
            fillObj.SetList = self.SetList
        else:
            fillObj = False
        # Заполняем self._importList
        self._importList.insert(0,(section,globals()[modVar],fillObj))

    def __findVarData(self, nameVar):
        """Находит данные для создания объекта переменная в модулях и

        объектах
        """
        # Ищем переменную в модуле
        dataVar = False
        dataVarWork = False
        e = False
        for sectionWork, moduleVar, fillobj in self._importList:
            if not hasattr(moduleVar.Data, nameVar):
                continue
            dataVarWork = getattr(moduleVar.Data, nameVar)
            section = sectionWork
            if dataVar is False:
                dataVar = dataVarWork
        if dataVar == False:
            err1 = _("Not found variable %s")%nameVar
            err2 = ""
            if e:
                err2 = _("error")  + ": " +str(e)
            raise self.DataVarsError("%s\n%s"%(err1,err2))
        dataVar['service'] = section
        # Ищем метод в объекте методов заполнения
        nameMethod = "get_" + nameVar
        flagFindMetod = False
        for section, moduleVar, fillobj in self._importList:
            if fillobj and hasattr(fillobj, nameMethod):
                flagFindMetod = True
                method = getattr(fillobj, nameMethod)
                break
        if flagFindMetod:
            return (dataVar,method)
        else:
            return (dataVar,False)

    def __setAttributesVar(self, var, nameVar, dictVar):
        """Установка аттрибутов для созданного объекта var

        название аттрибута и его значение берется из словаря dictVar
        """
        if not set(dictVar.keys()) <= set(dir(var)):
            raise self.DataVarsError(\
                _("error initalize variable %s, incorrect data")%nameVar)
        for nameAttr in dictVar.keys():
            setattr(var, nameAttr, dictVar[nameAttr])
        return True

    def __Get(self, nameVar):
        ret = ""
        self.__LevelsVar.append((self.__levelNumber, nameVar))
        self.__levelNumber += 1
        #nameMethod = "get_" + nameVar
        if hasattr(self, nameVar):
           ret = getattr(self, nameVar).Get()
        elif self.__findVarData(nameVar):
            dictVar, methodFill =self.__findVarData(nameVar)
            varobj = var(self)
            # Устанавливаем аттрибуты
            self.__setAttributesVar(varobj, nameVar, dictVar)
            if methodFill:
                varobj.Fill = methodFill
            setattr(self, nameVar, varobj)
            ret = getattr(self, nameVar).Get()
        self.__levelNumber -= 1
        if  self.__levelNumber == 0 and\
            getattr(self, nameVar).fillStart and\
            len(self.__LevelsVar)>1:
            links = self.__getLinks(self.__LevelsVar)
            for name in links.keys():
                for nameLink in links[name].keys():
                    val = getattr(self, nameLink).Get()
                    getattr(self, name).dependValues[nameLink] = val
        if  self.__levelNumber == 0:
            self.__LevelsVar = []
        return ret

    def Get(self, nameVar):
        return self.__Get(nameVar)


    def __Set(self, nameVar, value, force=False):
        nameMethod = "get_" + nameVar
        if not hasattr(self, nameVar) and self.__findVarData(nameVar):
            dictVar, methodFill =self.__findVarData(nameVar)
            varobj = var(self)
            # Устанавливаем аттрибуты
            self.__setAttributesVar(varobj, nameVar, dictVar)
            if methodFill:
                varobj.Fill = methodFill
            setattr(self, nameVar, varobj)
        if hasattr(self, nameVar):
            if not force and "r" in getattr(self, nameVar).mode:
                cl_overriding.printERROR(\
                    _("Attempt to rewrite a variable for reading") +\
                    ": %s"%nameVar)
                return False
            getattr(self, nameVar).fillStart = False
            return getattr(self, nameVar).Set(value)

    def Set(self, nameVar, value, force=False):
        return self.__Set(nameVar, value, force)

    def SetWriteVar(self, nameVar, value, force=False):
        """Установка значения переменной,

        и запись в список сохраняемых переменных"""
        ret = self.__Set(nameVar, value, force)
        if ret and not nameVar in self.varsNeedWritten:
            self.varsNeedWritten.append(nameVar)
        return ret

    def WriteVars(self, location='default', header=False):
        """Запись переменных установленных командой SetWriteVar"""
        flagSuccess = True
        for nameVar in self.varsNeedWritten:
            if not self.Write(nameVar, self.Get(nameVar), force=False,
                              location=location, header=header):
                flagSuccess = False
                break
        return flagSuccess

    def __frame(self, lVar):
        """получить список областей зависимости переменных"""
        data = []
        if not lVar:
            return data
        firstLevel = lVar[0][0]
        for level, name in lVar[1:]:
            if level> firstLevel:
                data.append((level, name))
            else:
                break
        return data

    def __getLinks(self, lVar):
        """Получить список переменных и от каких переменных они зависят

        на вход список [(уровень рекурсии, название переменной),]
        """
        links = {}
        frames = {}
        levelLinks = {}
        lVarFr = lVar
        for level, name in lVar:
            fr = self.__frame(lVarFr)
            if not frames.has_key(name):
                frames[name] = fr
                levelLinks[name] = level+1
            lVarFr = lVarFr[1:]
        for name in frames.keys():
            level = levelLinks[name]
            fr = frames[name]
            links[name] = {}
            for lv, nm in fr:
                if level == lv:
                    links[name][nm] = ""
        return links

    def __getPathCalculateIni(self, location):
        """Получить путь к ini файлу по алиасу пути"""
        retData = self.Get('cl_env_data')
        ini_dict = {}
        ini_dict.update(retData)
        if location in ini_dict.keys():
            name_calculate_ini = ini_dict[location]
        else:
            cl_overriding.printERROR(\
                _("Unable to find the alias '%s' of the file path for \
storage of variables templates")%location)
            cl_overriding.exit(1)
        return pathJoin(self.Get('cl_chroot_path'), name_calculate_ini)

    def __getSection(self, vname):
        """секция для записи в ini файл переменной

        vname - имя переменной
        """
        if not hasattr(self, vname):
            try:
                self.Get(vname)
            except self.DataVarsError, e:
                cl_overriding.printERROR(_("Not found template variable %s")\
                                         %vname)
                cl_overriding.printERROR(e)
                cl_overriding.exit(1)
        return getattr(self, vname).service.lower()

    def __writeVarValue(self, vname, val, location, header):
        '''Записать значение в calculate.ini

        Параметры:
        vname       имя переменной
        val         значение переменной
        location    расположение ini файла ('default', 'local', 'remote')
        header      раздел ini файла ('client', 'server', 'main')

        Возвращаемые значение:
        True        запись успешна
        False       запись не удалaсь
        '''
        # получаем путь до ini файла
        name_calculate_ini = self.__getPathCalculateIni(location)
        # извлекаем из полного имени файла путь
        onlydir = os.path.split(name_calculate_ini)[0]
        try:
            # проверяем чтобы путь до ини файла существовал
            if not os.path.exists(onlydir):
                # создаем его если отсутствует
                os.makedirs(onlydir)
        except OSError (nerr,msg):
            cl_overriding.printERROR(str(nerr) + " " + str(msg))
            return False
        config = iniParser(name_calculate_ini)
        # Получаем секцию конфигурационного файла
        if not header:
            header = self.__getSection(vname)
        return config.setVar(header,{vname: convertStrListDict(val)})

    def __deleteVarValue(self, vname, location, header):
        '''Удалить переменную в calculate.ini

        Параметры:
        vname       имя переменной
        location    расположение ini файла ('default', 'local', 'remote')

        Возвращаемые значение:
        True        удалено успешна
        False       удаление не удалось
        '''
        # получаем путь до ini файла
        name_calculate_ini = self.__getPathCalculateIni(location)
        # извлекаем из полного имени файла путь
        onlydir = os.path.split(name_calculate_ini)[0]
        # проверяем чтобы путь до ини файла существовал
        if not os.path.exists(onlydir):
            return False
        config = iniParser(name_calculate_ini)
        # Получаем секцию конфигурационного файла
        if not header:
            header = self.__getSection(vname)
        # Удаляем переменную
        allVars = config.getAreaVars(header)
        if allVars is False:
            return False
        if vname in allVars.keys():
            retDelVar = config.delVar(header, vname)
            retDelArea = True
            if not config.getAreaVars(header):
                retDelArea = config.delArea(header)
            if retDelArea and retDelVar:
                return True
            else:
                return False
        return True

    def Write(self,vname,val=None,force=False,location='default',header=False):
        '''Установить и записать значение переменной в ini файл

        Параметры:
        vname     имя переменной
        val       значение переменной
        force     "принудительный режим"
        location    расположение ini файла ('default', 'local', 'remote')
        header      раздел ini файла ('client', 'server', 'main')
        '''
        if val is None:
            val = self.Get(vname)
        else:
            if self.__Set(vname, val, force) is False:
                return False
        if self.__writeVarValue(vname, val, location, header):
            return True
        return False

    def Delete(self, vname, location='default', header=False):
        '''Удалить переменную в calculate.ini

        Параметры:
        vname       имя переменной
        location    расположение ini файла ('default', 'local', 'remote')

        Возвращаемые значение:
        True        удалено успешна
        False       удаление не удалсь
        '''
        return self.__deleteVarValue(vname, location, header)

    def __getActiveSections(self):
        """активные секции в ini файле"""
        act_section = []
        for service,t,t in self._importList:
            act_section.append(service.lower())
        return act_section

    def GetIniVar(self, section_dot_nameVar):
        """Получить значение переменной из конфигурационного файла

        section_dot_nameVar - "имя_секции.имя_переменной_профиля"
        """
        calculate_ini_files = self.Get('cl_env_path')
        section, spl, name_var = section_dot_nameVar.rpartition(".")
        if section and name_var:
            pass
        elif name_var:
            section = "main"
        else:
            cl_overriding.printERROR(\
                _("error Datavars.GetIniVar: empty section"))
            return False
        # Значение переменной в env файлах
        valueVar = ""
        for name_calculate_ini in calculate_ini_files:
            # проверить сущестование ini файла
            if os.path.exists(name_calculate_ini):
                # получить объект настроенный на ini
                config = iniParser(name_calculate_ini)
                # получаем значение переменной из секции
                data = config.getVar(section, name_var, checkExistVar=True)
                if data is False:
                    return False
                existsVar, value = data
                if existsVar:
                    valueVar = value
        return valueVar.encode("UTF-8")

    def GetRemoteInfo(self, envFile):
        """Получение информационных переменных

        из файла envFile"""
        optionsInfo = {}
        # получить объект настроенный на ini
        config = iniParser(envFile)
        # получаем все секции из конфигурационного файла
        allsect = config.getAllSectionNames()
        if allsect:
            # Секция (название сервиса)
            for section in allsect:
                allvars = config.getAreaVars(section)
                if allvars == False:
                    return False
                # Опции сервиса
                options = {}
                for varName, value in allvars.items():
                    varName = varName.encode("UTF-8")
                    value=convertStrListDict(value.encode("UTF-8"))
                    options[varName] = value
                if options:
                    optionsInfo[section] = options
        return optionsInfo

    def flIniFile(self):
        '''Заместить значение переменных значениями из ини файлов

        Возвращаемые значения:
        cловарь импортированных переменных -    переменные считаны
        False  -  файл не был обнаружен
        '''
        #Cловарь переменных из ini файлов
        importVars = {}
        calculate_ini_files = self.Get('cl_env_path')
        # активные секции (секции из которых будут использованы переменные)
        act_section = self.__getActiveSections()
        set_act_section = set(act_section)
        i = 0
        # Алиасы к путям env
        locations = self.Get('cl_env_location')
        for name_calculate_ini in calculate_ini_files:
            # проверить сущестование ini файла
            if os.path.exists(name_calculate_ini):
                # получить объект настроенный на ini
                config = iniParser(name_calculate_ini)
                # получаем все секции из конфигурационного файла
                allsect = config.getAllSectionNames()
                if not allsect:
                    continue
                # находим встречающиеся у обоих секции
                act_sect = tuple(set(allsect)& set_act_section)
                # словарь переменных для ini - файла
                importFileVars = {}
                # получаем все переменные из всех секций
                for section in act_sect:
                    allvars = config.getAreaVars(section)
                    if allvars == False:
                        return False
                    # словарь переменных для ini - файла
                    importFileVars = {}
                    # принудительно переписать все переменные окружения
                    # полученные из ini
                    for (k,v) in allvars.items():
                        k = k.encode("UTF-8")
                        value = convertStrListDict(v.encode("UTF-8"))
                        self.Set(k, value, True)
                        importFileVars[k] = value
                if i < 3:
                    importVars[locations[i]] = importFileVars
            i += 1
        return importVars


    def defined(self, vname):
        """Имеет ли значение переменная"""
        try:
            value = self.Get(vname)
        except:
            cl_overriding.printERROR(_("error var %s not found")%str(vname))
            cl_overriding.exit(1)
        if value:
            return True
        else:
            return False

    #def defined(self, vname):
        #return True

    def exists(self, nameVar):
        """ Определяет существует ли переменная с таким имененм
        """
        if hasattr(self, nameVar):
            return True
        foundVar = False
        # Ищем переменную в импортируемых модулях
        for section, moduleVar, fillobj in self._importList:
            if hasattr(moduleVar.Data, nameVar):
                foundVar = True
                break
        return foundVar

    def getVars(self, varsFilter=None, varsNames=[], verbose=1):
        """Словарь переменных для печати"""
        # проверка фильтра
        reFilter = False
        if varsFilter:
            try:
                reFilter = re.compile("^%s$"%varsFilter)
            except:
                cl_overriding.printERROR(_("wrong vars filter '%s'")\
                                         %str(varsFilter))
                cl_overriding.exit(1)
        ret = {}
        dictServicesVars = {}
        for section, moduleVar, fillobj in self._importList:
            dataVar = moduleVar.Data
            dictVars = dir(dataVar)
            for nameVar in dictVars:
                if not "__" in nameVar and (not\
                    (getattr(dataVar,nameVar).has_key("hide") and\
                     getattr(dataVar,nameVar)['hide']) or verbose>1 \
                     or varsFilter):
                    if varsNames and not nameVar in varsNames:
                        continue
                    if reFilter and not reFilter.search(nameVar):
                        continue
                    self.Get(nameVar)
                    variable = getattr(self, nameVar)
                    service = variable.service.lower()
                    ret[nameVar] = variable
                    if not service in dictServicesVars:
                        dictServicesVars[service] = {}
                    dictServicesVars[service][nameVar] = variable
        return ret, dictServicesVars

    def printVars(self, varsFilter=None, varsNames=[], outFormat="default",
                  verbose=1):
        """распечатать список переменных с значениями"""
        def getLenElements(varsDict):
            mlen_name=0;
            mlen_type=0;
            mlen_mode=0;
            for i,j in varsDict.items():
                if len(i)>mlen_name:
                    mlen_name=len(i)
                if not '[' in varsDict[i].mode:
                    mode="[%s]"%(varsDict[i].mode.lower())
                varsDict[i].mode=mode
                if len(mode)>mlen_mode:
                    mlen_mode=len(mode)
            return mlen_name, mlen_type, mlen_mode

        def printVarsTable(varsDict, title, lenList=()):
            """print variables table"""
            if lenList:
                mlen_name, mlen_type, mlen_mode = lenList;
            else:
                mlen_name, mlen_type, mlen_mode = getLenElements(varsDict)
            plist=varsDict.keys()
            plist.sort()
            br = fillstr("-",mlen_name) + " " +\
                fillstr("-",mlen_mode) + " " +  fillstr("-",10)
            cl_overriding.printSUCCESS(title)
            cl_overriding.printSUCCESS(br)
            cl_overriding.printSUCCESS(\
            _toUNICODE(_("Variable name")).center(mlen_name).encode('UTF-8') +\
                                       " " + _("Mode") + " " +_("Value"))
            cl_overriding.printSUCCESS(br)
            for i in plist:
                if i.endswith("_pw"):
                    p_val = "***"
                else:
                    p_val=varsDict[i].value
                columnWrite(i, mlen_name, varsDict[i].mode.lower(),
                            mlen_mode, p_val)
        var, dictPkgVars = self.getVars(varsFilter, varsNames, verbose=verbose)
        if outFormat == "default":
            lenList = getLenElements(var)
            libService = 'main'
            if libService in dictPkgVars:
                var = dictPkgVars.pop(libService)
                printVarsTable(var, _("The list of %s variables:")%'library',
                               lenList=lenList)
            for service in dictPkgVars.keys():
                var = dictPkgVars[service]
                cl_overriding.printSUCCESS("")
                printVarsTable(var, _("The list of %s variables:")%service,
                               lenList=lenList)
        elif outFormat == "xml":
            xmlObj = _varsXML()
            varNames = sorted(var.keys())
            for name in varNames:
                if name.endswith("_pw"):
                    value = "***"
                else:
                    value = var[name].value
                typeVar = type(value) 
                if typeVar in (str, int, float):
                    xmlObj.addVar(name, value)
                elif typeVar == list:
                    valueList = value
                    xmlObj.addList(name, valueList)
                elif typeVar == dict:
                    valueDict = value
                    xmlObj.addDict(name, valueDict)
            cl_overriding.printSUCCESS(xmlObj.toXML(),printBR=False)

    def GetList(self, nameVar, spl=","):
        """Получить значение переменной в виде списка

        если значение переменной:
            строка - разделяем spl (по умолчанию ',') и выводим список,
            кроме строки - выводим как есть
        """
        value = self.Get(nameVar)
        if type(value) in (str, unicode):
            return map(lambda x: x.strip(), value.split(spl))
        return value

    def SetList(self, nameVar, value, force=False, spl=","):
        """Записать список в переменную в виде строки, элементы разделены spl

        если значение переменной:
            список - объединяем через spl (по умолчанию ',')
                     список в строку, записываем
            кроме списка - записываем
        """
        if type(value) == list:
            strValue = ",".join(value)
        else:
            strValue = value
        return self.Set(nameVar, strValue, force)

    def WriteList(self, vname, val=None, force=False,
                  location='default', header=False, spl=","):
        """Записать список в переменную в виде строки и записать в ini файл
            (элементы разделены spl)

        если значение переменной:
            список - объединяем через spl (по умолчанию ',')
                     список в строку, записываем
            кроме списка - записываем
        """
        if val is None:
            val = self.Get(vname)
        if type(val) == list:
            strValue = ",".join(val)
        else:
            strValue = val
        if not strValue and type(strValue) in (str, unicode):
            if self.__Set(vname, strValue, force) is False:
                return False
            if not self.__deleteVarValue(vname, location, header):
                return False
            return True
        return self.Write(vname, strValue, force, location, header)

    def AppendToList(self, nameVar, value, force=False, spl=",",
                     unique=True):
        """Добавление в переменную со строковым значением элемента

        Разделитель элементов spl (по умолчанию ',')
        """
        listValues = self.GetList(nameVar, spl)
        if type(listValues) == list:
            listValues = filter(lambda x: x, listValues)
            if unique:
                if not value in listValues:
                    listValues.append(value)
            else:
                listValues.append(value)
        return self.SetList(nameVar, listValues, force, spl)

    def RemoveToList(self, nameVar, value, force=False, spl=","):
        """Удаление из переменной со строковым значением элементов

        со значением value

        Разделитель элементов spl (по умолчанию ',')
        """
        listValues = self.GetList(nameVar, spl)
        if type(listValues) == list:
            listValues = filter(lambda x: x, listValues)
            listValues = filter(lambda x: x!=value, listValues)
        self.SetList(nameVar, listValues, force, spl)
        return True

class _varsXML:
    declaration = '<?xml version="1.0" encoding="UTF-8"?>\n'
    rootElementStart = '<variables>\n'
    rootElementEnd = '</variables>\n'
    indent = "  "
    templateVar = """%(indent)s<var type="var" name="%(name)s">\n\
%(indent)s%(indent)s<value>%(value)s</value>\n\
%(indent)s</var>\n"""
    templateListStart = """%(indent)s<var type="list" name="%(name)s">\n"""
    templateListValue = """%(indent)s%(indent)s<value>%(value)s</value>\n"""
    templateListEnd = """%(indent)s</var>\n"""
    templateDictStart = """%(indent)s<var type="dict" name="%(name)s">\n"""
    templateDictValue = """%(indent)s%(indent)s<value name="%(name)s">%(value)s\
</value>\n"""
    templateDictEnd = """%(indent)s</var>\n"""
    templateDoc = "%(declaration)s%(rootStart)s%(vars)s%(rootEnd)s"
    varsXML = ""

    def addVar(self, name, value):
        """Add var to XML"""
        dictVar = {"indent":self.indent,
                   "name":name,
                   "value":self.serialize(value)}
        self.varsXML += self.templateVar %dictVar

    def addList(self, name, valueList):
        """Add var (valie - list) to XML"""
        dictVar = {"indent":self.indent,
                   "name":name}
        varXml = self.templateListStart %dictVar
        for value in valueList:
            dictValue = {"indent":self.indent,
                         "value":self.serialize(value)}
            varXml += self.templateListValue %dictValue
        varXml += self.templateListEnd %dictVar
        self.varsXML += varXml

    def addDict(self, name, valueDict):
        """Add var (valie - list) to XML"""
        dictVar = {"indent":self.indent,
                   "name":name}
        varXml = self.templateDictStart %dictVar
        for nameDict, valueDict in valueDict.items():
            dictValue = {"indent":self.indent,
                         "name":nameDict,
                         "value":self.serialize(valueDict)}
            varXml += self.templateDictValue %dictValue
        varXml += self.templateDictEnd %dictVar
        self.varsXML += varXml

    def serialize(self, value):
        """Serialization"""
        return str(value).replace("&","&amp;").replace("<","&lt;").replace(">",
                                  "&gt;")

    def toXML(self):
        dictXML = {"declaration":self.declaration,
                   "rootStart":self.rootElementStart,
                   "vars":self.varsXML,
                   "rootEnd":self.rootElementEnd}
        return self.templateDoc %dictXML
