Exemplo n.º 1
0
class Config (object) :

    def __init__(self, pid, gid=None) :
        '''Do the primary initialization for this class.'''

        self.pid                            = pid
        self.gid                            = gid
        self.user                           = UserConfig()
        self.userConfig                     = self.user.userConfig
        self.projHome                       = os.path.join(os.path.expanduser(self.userConfig['Resources']['projects']), self.pid)
        self.local                          = ProjLocal(pid, gid)
        self.tools                          = Tools()
        self.log                            = ProjLog(pid)
        # Create config placeholders
        self.projectConfig                  = None
        self.adjustmentConfig               = None
        self.layoutConfig                   = None
        self.illustrationConfig             = None
        self.fontConfig                     = None
        self.macroConfig                    = None

        # Log messages for this module
        self.errorCodes     = {
            '3100' : ['ERR', 'Macro package: [<<1>>] already exists in the project. Use force (-f) to reinstall.'],
            '3200' : ['ERR', 'Failed to install macro package: [<<1>>]'],
            '3300' : ['MSG', 'Install macro package: [<<1>>], Reinitialized [<<2>>]'],
            '3310' : ['ERR', 'Failed to copy [<<1>>] to folder [<<2>>].'],
            '3400' : ['MSG', 'Force set to True. Removed macro package configuration file: [<<1>>]'],
            '3500' : ['MSG', 'Removed macro package [<<1>>] folder and all files contained.'],
            '3600' : ['MSG', 'Updated macro package [<<1>>]'],
            '3650' : ['ERR', 'Failed to updated macro package [<<1>>]']
        }

        # Test for gid before trying to finish the init

#        import pdb; pdb.set_trace()

        if gid :
            if not self.projectConfig :
                self.getProjectConfig()
            # We need to skip over this if the group doesn't exist
            try :
                # Reinitialize local
                self.cType                      = self.projectConfig['Groups'][gid]['cType']
                self.Ctype                      = self.cType.capitalize()
                self.local                      = ProjLocal(pid, gid, self.cType)
            except :
                self.cType                      = None
                self.Ctype                      = None
        else :
            self.cType                      = None
            self.Ctype                      = None



###############################################################################
############################# Get Config Functions ############################
###############################################################################
####################### Error Code Block Series = 0500 ########################
###############################################################################


    def getProjectConfig (self) :
        '''Load/return the project configuation object.'''

#        import pdb; pdb.set_trace()

        self.projectConfig = self.tools.loadConfig(self.local.projectConfFile, self.local.projectConfXmlFile)


    def getAdjustmentConfig (self) :
        '''Load/return the adjustment configuation object.'''

        self.adjustmentConfig = self.tools.loadConfig(self.local.adjustmentConfFile, self.local.adjustmentConfXmlFile)


    def getLayoutConfig (self) :
        '''Load/return the layout configuation object.'''

        self.layoutConfig = self.tools.loadConfig(self.local.layoutConfFile, self.local.layoutConfXmlFile)


    def getIllustrationConfig (self) :
        '''Load/return the illustration configuation object.'''
        
        self.illustrationConfig = self.tools.loadConfig(self.local.illustrationConfFile, self.local.illustrationConfXmlFile)


    def getFontConfig (self) :
        '''Load/return the font configuation object.'''
        
        self.fontConfig = self.tools.loadConfig(self.local.fontConfFile, self.local.fontConfXmlFile)


    def getMacroConfig (self) :
        '''Load/return the macro configuration object.'''

        self.macroConfig = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile)


###############################################################################
############################ Manager Level Functions ##########################
###############################################################################
####################### Error Code Block Series = 1000 ########################
###############################################################################


    def makeNewprojectConf (self, local, pid, cVersion, pmid='book') : 
        '''Create a new project configuration file for a new project.'''

        self.projectConfig = ConfigObj(self.tools.getXMLSettings(os.path.join(local.rapumaConfigFolder, pmid + '.xml')), encoding='utf-8')
        # Insert intitial project settings
        self.projectConfig['ProjectInfo']['projectMediaIDCode']        = pmid
        self.projectConfig['ProjectInfo']['creatorID']                 = self.userConfig['System']['userID']
        self.projectConfig['ProjectInfo']['projectCreatorVersion']     = cVersion
        self.projectConfig['ProjectInfo']['projectCreateDate']         = self.tools.tStamp()
        self.projectConfig['ProjectInfo']['projectIDCode']             = pid
        self.projectConfig['Backup']['ownerID']                        = self.userConfig['System']['userID']
        self.projectConfig['Groups']                                   = {}
        # Even though there was no push, we need a time stamp to avoid confusion
        self.projectConfig['Backup']['lastCloudPush']                  = self.tools.fullFileTimeStamp()
        self.projectConfig.filename                                    = local.projectConfFile
        self.projectConfig.write()


###############################################################################
######################## Basic Config Handling Functions ######################
###############################################################################
####################### Error Code Block Series = 2000 ########################
###############################################################################


    def processSinglePlaceholder (self, ph, value) :
        '''Once we are sure we have a single placeholder (noting embedded) this
        will process it and replace it with the correct value.'''

        holderType = ph.split(':')[0]
        try :
            holderKey = ph.split(':')[1]
        except :
            holderKey = ''

        if self.hasPlaceHolder(value):
            value = self.processNestedPlaceholders(value, '')

        result = ph # If nothing matches below, default to returning placeholder unchanged
        if holderType == 'val' :
            result = value
        # A value that needs a measurement unit attached
        elif holderType == 'mu' :
            result = self.getMeasureUnit()
        # A value that is from a configObj
        elif holderKey and holderType == 'config' :
            result = self.getConfigValue(holderKey)
        # A value that is a path
        elif holderKey and holderType == 'path' :
            result = getattr(self.local, holderKey)
        # A value that is from a configObj
# FIXME: To work around a circular init problem between Config() and Macro()
# the macro package name (UsfmTex) has been hard coded. Not sure how to call
# another function in another class that relies on this class to work.
# This will break when the time comes that another macro family than UsfmTex
# is used to hold the functions needed to process values to be used
        elif holderKey and holderType == 'function' :
#            import pdb; pdb.set_trace()
            fnc = getattr(UsfmTex(self.layoutConfig), holderKey)
            result = fnc()
        # A value that is a special character (escaped character)
        elif holderKey and holderType == 'esc' :
            result = self.getEscapeCharacter(holderKey)
        # A value that is a font setting
        elif holderKey and holderType == 'font' :
            result = self.getFontSetting(holderKey)
        # A value that is a path separator character
        elif holderType == 'pathSep' :
            result = os.sep
        # A value that contains a system declared value
        # Note this only works if the value we are looking for has
        # been declared above in the module init
        elif holderType == 'self' :
#            if holderKey.find('.') >= 0 :
#                splitKey = holderKey.split('.')
#                if splitKey[0] == 'local' :
#                    result = getattr(self.local, splitKey[1])
#            else :
#                result = getattr(self, holderKey)
            result = getattr(self.local, holderKey)

        return result


    def getFontSetting (self, value) :
        '''Get a special font setting if there is one. Otherwise
        return null.'''

        # FIXME: This may need to be moved to Fonts, plus it might be a
        # little brittle. Using primaryFont for a default might be asking
        # for trouble
        result = ''
        if value == 'mapping' :
            useMapping      = self.fontConfig['GeneralSettings']['useMapping']
            primaryFont     = self.projectConfig['CompTypes'][self.cType.capitalize()]['fontName']
            if useMapping :
                result = ':mapping=' + os.path.join(self.local.projFontFolder, primaryFont, useMapping)
        elif value == 'renderer' :
            useRenderingSystem = self.fontConfig['GeneralSettings']['useRenderingSystem']
            if useRenderingSystem :
                result = '/' + useRenderingSystem
        elif value == 'language' :
            useLanguage = self.fontConfig['GeneralSettings']['useLanguage']
            if useLanguage :
                result = ':language=' + useLanguage
        elif value == 'feature' :
            useFeature = self.fontConfig['GeneralSettings']['useFeature']
            if useFeature :
                result = ':' + useFeature

        return result


    def getEscapeCharacter (self, value) :
        '''Return the character specified by the escape code.'''

        if value == 'lsBracket' :
            return '['
        elif value == 'rsBracket' :
            return ']'
        
        # Add more as needed...


    def processNestedPlaceholders (self, line, value = '') :
        '''Search a string (or line) for a type of Rapuma placeholder and
        insert the value. This is for building certain kinds of config values.'''

        result = []
        end_of_previous_segment = 0
        for (ph_start, ph_end) in self.getPlaceHolder(line) :
            unchanged_segment = line[end_of_previous_segment:ph_start]
            result.append(unchanged_segment)
            ph_text = line[ph_start+1:ph_end]
            replacement = self.processNestedPlaceholders(ph_text, value)
            result.append(unicode(replacement))
            end_of_previous_segment = ph_end+1  # Skip the closing bracket
        result.append(line[end_of_previous_segment:])
        resultline = "".join(result)
        result_text = self.processSinglePlaceholder(resultline, value)
        return result_text


    def hasPlaceHolder (self, line) :
        '''Return True if this line has a data place holder in it.'''

        # If things get more complicated we may need to beef this up a bit
        if line.find('[') > -1 and line.find(']') > -1 :
            return True


    def getPlaceHolder (self, line) :
        '''Return place holder type and a key if one exists from a TeX setting line.
        Pass over the line and return (yield) each placeholder found.'''

        nesting_level = 0
        remembered_idx = None
        for idx, ch in enumerate(line):
            if ch == '[':
                nesting_level += 1
                if remembered_idx is None:
                    remembered_idx = idx
            elif ch == ']':
                nesting_level -= 1
                if nesting_level <= 0:
                    found_idx = remembered_idx
                    remembered_idx = None
                    yield (found_idx, idx)


    def getConfigValue (self, val, default=None) :
        '''Return the value from a config function or just pass the
        value through, unchanged.'''

        keyparts = val.split('|')
        curval = getattr(self, keyparts[0], None)
        if curval is None: return default
        for key in keyparts[1:]:
            curval = curval.get(key, None)
            if curval is None: return default
        return curval


    def getMeasureUnit (self) :
        '''Return the value with the specified measurement unit attached.'''
        
        return self.layoutConfig['GeneralSettings']['measurementUnit']
Exemplo n.º 2
0
class Macro(object):
    def __init__(self, pid, cType, gid=None):
        """Do the primary initialization for this class."""

        self.pid = pid
        self.gid = gid
        self.cType = cType
        self.user = UserConfig()
        self.userConfig = self.user.userConfig
        self.projHome = os.path.join(os.path.expanduser(self.userConfig["Resources"]["projects"]), self.pid)
        self.local = ProjLocal(pid, gid, cType)

        #        import pdb; pdb.set_trace()

        self.proj_config = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig = self.proj_config.projectConfig
        self.layoutConfig = self.proj_config.layoutConfig
        self.tools = Tools()
        self.log = ProjLog(pid)
        # Create config placeholders
        self.layoutConfig = None
        self.illustrationConfig = None
        self.macroConfig = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile)

        # Log messages for this module
        self.errorCodes = {
            "3010": ["ERR", "No macro package is registered for the [<<1>>] component type."],
            "3020": ["ERR", "Cannot update! No macro package is registered for the [<<1>>] component type."],
            "3050": ["ERR", "Macro package file not found: [<<1>>]"],
            "3100": [
                "ERR",
                "Macro package: [<<1>>] already exists in the project. I am not allowed to copy over an existing package.",
            ],
            "3200": ["ERR", "Failed to install macro package: [<<1>>]"],
            "3300": ["MSG", "Installed macro package: [<<1>>], Reinitialized [<<2>>]"],
            "3310": ["ERR", "Failed to copy [<<1>>] to folder [<<2>>]."],
            "3400": ["MSG", "Removed macro package configuration settings for: [<<1>>] from the macro.conf file."],
            "3500": ["MSG", "Removed macro package [<<1>>] folder and all files contained."],
            "3600": ["MSG", "Updated component type [<<1>>] with macro package [<<2>>]"],
            "3650": ["ERR", "Failed to updated macro package [<<1>>]"],
        }

    ###############################################################################
    ###################### Macro Package Handling Functions #######################
    ###############################################################################
    ######################## Error Code Block Series = 3000 #######################
    ###############################################################################

    def createMacroFiles(self, macPackId):
        """Create all the necessary macro file names with their assigned paths."""

        self.projMacPackFolder = os.path.join(self.local.projMacPackFolder, macPackId)

        texFileIds = {
            "preStyTexExtFile": "preSty-ext.tex",
            "macSettingsFile": "settings.tex",
            "extTexFile": "extension.tex",
            "grpExtTexFile": self.gid + "-extension.tex",
            "": "",
            "": "",
            "": "",
            "": "",
            "": "",
        }
        styFileIds = {
            "glbExtStyFile": "extension.sty",
            "grpExtStyFile": self.gid + "-extension.sty",
            "": "",
            "": "",
            "": "",
            "": "",
            "": "",
        }

        # <file>
        # <name>TeX lccode Definition File</name>
        # <description>The TeX file that contains lccode definitions and is linked with the hypenation exclusions file.</description>
        # <fileID>lccodeTexFile</fileID>
        # <fileName>[self:gid]-lccode.tex</fileName>
        # <filePath>[self:projGidFolder]</filePath>
        # <depends></depends>
        # <relies></relies>
        # <note>This file is located in the component group folder to allow more segregated processing.</note>
        # </file>
        # <file>
        # <name>TeX Group Hyphenation Exclusions File</name>
        # <description>The file that contains the hypenation words exclusions list for the current group that TeX will use to render the text.</description>
        # <fileID>grpHyphExcTexFile</fileID>
        # <fileName>[self:gid]-hyphenation.tex</fileName>
        # <filePath>[self:projGidFolder]</filePath>
        # <depends></depends>
        # <relies></relies>
        # <note>This file is located in the component group folder to allow more segregated processing.</note>
        # </file>

    def getMacPackIdFromFileName(self, fileName):
        """Return the macPack ID based on the file name"""

        # File name less ext is the ID
        parts = len(fileName.split("."))
        return ".".join(fileName.split(".")[: parts - 1])

    def getMacPackIdFromSource(self, source):
        """Return the macPack ID based on the complete path and file name."""

        # Get the file name from the path
        fileName = self.tools.fName(source)
        # Return the ID
        return self.getMacPackIdFromFileName(fileName)

    def addMacPack(self, source):
        """Add a macro package to the project. It will not work if
        the same package is already present. Remove must be used
        to get rid of the existing one first."""

        #        import pdb; pdb.set_trace()

        macPackId = self.getMacPackIdFromSource(source)
        confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + ".xml")
        if not os.path.isfile(source):
            self.log.writeToLog(self.errorCodes["3050"], [source])

        # Do not add/install if there seems to be a macro package there already
        if self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] and os.path.exists(
            self.local.macroConfFile
        ):
            self.log.writeToLog(self.errorCodes["3100"], [macPackId])
            return False

        # Set the projectConf to the new/same package
        self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] = macPackId
        self.tools.writeConfFile(self.projectConfig)

        # If we got this far, install the a fresh copy of the macPack
        self.installMacPackOnly(source)
        # Move the style files and custom TeX files out of the macPack
        self.moveMacStyles(macPackId)
        self.moveMacTex(macPackId)

        # Create a fresh macro.conf file if it dosn't exist
        if not os.path.isfile(self.local.macroConfFile):
            self.macroConfig = self.tools.initNewConfig(self.local.macroConfFile, self.local.macroConfXmlFile)
        # Inject information from this particular macro package
        mInfo = self.tools.getXMLSettings(confXml)
        self.macroConfig["Macros"][macPackId] = mInfo.dict()

        # Save the settings now
        self.tools.writeConfFile(self.macroConfig)

        self.log.writeToLog(self.errorCodes["3300"], [macPackId, self.local.macroConfFileName])

        return True

    def moveMacStyles(self, macPackId):
        """Move the default macro package styles out of the freshly installed
        project macro package folder to the project Style folder."""

        #        import pdb; pdb.set_trace()

        # Collect the style files to copy
        for f in self.getMacStyExtFiles(macPackId):
            source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f))
            target = os.path.join(self.local.projStyleFolder, f)
            self.tools.makedirs(self.local.projStyleFolder)
            # Do not overwrite existing files unless force is used
            if not os.path.exists(target):
                shutil.copy(source, target)
                # Look for default and set to read-only
                defaultStyFile = os.path.join(self.local.projStyleFolder, macPackId + ".sty")
                if target == defaultStyFile:
                    self.tools.makeReadOnly(defaultStyFile)
            # Remove the source to avoid confusion
            if os.path.exists(target):
                os.remove(source)
            else:
                self.log.writeToLog(self.errorCodes["3310"], [source, self.local.projStyleFolder])

    def getMacStyExtFiles(self, macPackId):
        """Return a list of macro package style extention files."""

        sFiles = []
        macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId))
        for f in macPackFiles:
            if f.split(".")[1].lower() == "sty":
                sFiles.append(f)
        return sFiles

    def moveMacTex(self, macPackId):
        """Move the custom macro package TeX out of the freshly installed
        project macro package folder to the project TeX folder."""

        # Collect the TeX extention files to copy
        for f in self.getMacTexExtFiles(macPackId):
            source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f))
            target = os.path.join(self.local.projTexFolder, f)
            self.tools.makedirs(self.local.projTexFolder)
            # Do not overwrite existing files
            if not os.path.exists(target):
                shutil.copy(source, target)
            # Remove the source to avoid confusion
            if os.path.exists(target):
                os.remove(source)
            else:
                self.log.writeToLog(self.errorCodes["3310"], [source, self.local.projTexFolder])

    def getMacTexExtFiles(self, macPackId):
        """Return a list of macro package TeX extention files."""

        tFiles = []
        macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId))
        for f in macPackFiles:
            if f.find("ext.tex") > 0:
                tFiles.append(f)
        return tFiles

    def removeMacPack(self):
        """Remove the macro package from a component type"""

        #        import pdb; pdb.set_trace()

        if self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] != "":
            macPackId = self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"]
        else:
            self.log.writeToLog(self.errorCodes["3010"], [self.cType])
        # Aquire target to delete
        target = os.path.join(self.local.projMacroFolder, macPackId)

        # Remove the macPack settings from the config file if it is there
        try:
            del self.macroConfig["Macros"][macPackId]
            # Save the settings now
            self.tools.writeConfFile(self.macroConfig)
            self.log.writeToLog(self.errorCodes["3400"], [macPackId])
        except:
            pass

        # Now remove the macro folder (with all its contents)
        if os.path.exists(target):
            shutil.rmtree(target)
            self.log.writeToLog(self.errorCodes["3500"], [macPackId])

        # Remove any style files associated with the macro
        styTarget = os.path.join(self.local.projStyleFolder, macPackId + ".sty")
        self.tools.makeExecutable(styTarget)
        self.tools.removeFile(styTarget)

        # Remove the reference for this macro package from the component type
        # that uses it. Normally that would probably be just be one of them.
        self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] = ""
        self.tools.writeConfFile(self.projectConfig)

    def updateMacPack(self, source):
        """Update a macro package with the latest version but do not 
        touch the config file."""

        # Do not update if no macro package is registered in the projectConfig
        if not self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"]:
            self.log.writeToLog(self.errorCodes["3020"], [self.cType.capitalize()])
            return False

        macPackId = self.getMacPackIdFromSource(source)
        confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + ".xml")
        oldMacPackId = self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"]
        oldMacDir = os.path.join(self.local.projMacroFolder, oldMacPackId)
        newMacDir = os.path.join(self.local.projMacroFolder, macPackId)

        # Install the new macPack (but use the old settings)
        if self.installMacPackOnly(source):
            # The current macro system must have a "master" style file.
            # This is a version of the ParaText style file. Because
            # an update might include an update to the style file,
            # we need to copy that from the macro folder to the Style
            # folder. We will do that now. The name of the file should
            # be the macPackId + ".sty", in theory.
            srcStyleFile = os.path.join(self.local.projMacroFolder, macPackId, macPackId + ".sty")
            oldStyleFile = os.path.join(self.local.projStyleFolder, oldMacPackId + ".sty")
            newStyleFile = os.path.join(self.local.projStyleFolder, macPackId + ".sty")
            # Unlock the old one so it can be deleted.
            self.tools.makeExecutable(oldStyleFile)
            self.tools.removeFile(oldStyleFile)
            # Now copy in the new one.
            shutil.copy(srcStyleFile, newStyleFile)
            # The style file should never be edited by the user, relock it
            self.tools.makeReadOnly(newStyleFile)
            # Update exsisting extention file names. We never want to loose
            # any settings that the user may have added to the extention
            # files so we will rename the existing files to have the
            # new ID otherwise the system will not find them at render time.
            oldStyExtFile = os.path.join(self.local.projStyleFolder, oldMacPackId + "_ext.sty")
            oldTexExtFile = os.path.join(self.local.projTexFolder, oldMacPackId + "_ext.tex")
            oldTexPreStyExtFile = os.path.join(self.local.projTexFolder, oldMacPackId + "_preSty-ext.tex")
            newStyExtFile = os.path.join(self.local.projStyleFolder, macPackId + "_ext.sty")
            newTexExtFile = os.path.join(self.local.projTexFolder, macPackId + "_ext.tex")
            newTexPreStyExtFile = os.path.join(self.local.projTexFolder, macPackId + "_preSty-ext.tex")
            # By default, we protect any existing versions
            if os.path.exists(newStyExtFile):
                os.remove(oldStyExtFile)
            else:
                self.tools.renameFile(oldStyExtFile, newStyExtFile)
            if os.path.exists(newTexExtFile):
                os.remove(oldTexExtFile)
            else:
                self.tools.renameFile(oldTexExtFile, newTexExtFile)
            if os.path.exists(newTexPreStyExtFile):
                os.remove(oldTexPreStyExtFile)
            else:
                self.tools.renameFile(oldTexPreStyExtFile, newTexPreStyExtFile)
            # Remove un-needed sty and tex files from the newMacDir to
            # avoid confusion. The ext files never are updated because
            # they could contain custom project code that we don't want
            # to loose in an update.
            for f in self.getMacStyExtFiles(macPackId):
                source = os.path.join(newMacDir, f)
                if os.path.exists(source):
                    os.remove(source)
            for f in self.getMacTexExtFiles(macPackId):
                source = os.path.join(newMacDir, f)
                if os.path.exists(source):
                    os.remove(source)
            # Remove the old macPack folder
            shutil.rmtree(oldMacDir)
            # Merge new settings into old section (change name to new ID)
            # When updating, we are assuming the new macro is in the same
            # family as the old one. As such, settings should be almost
            # identical, but in case new settings are being added, we will
            # merge them in now.
            oldConfSettings = self.macroConfig["Macros"][oldMacPackId]
            newConfSettings = self.tools.getXMLSettings(confXml)
            # Now merge
            newConfSettings.merge(oldConfSettings)
            # Inject the new section
            #            self.tools.buildConfSection(self.macroConfig, macPackId)
            self.macroConfig["Macros"][macPackId] = newConfSettings.dict()
            # Delete the old section
            del self.macroConfig["Macros"][oldMacPackId]
            # Save the changes
            self.tools.writeConfFile(self.macroConfig)

            # Assuming everything went well we will change the macPackID on the cType
            self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] = macPackId
            self.tools.writeConfFile(self.projectConfig)
            # Report success
            self.log.writeToLog(self.errorCodes["3600"], [self.cType.capitalize(), macPackId])
            return True
        # But if the install fails everything stays the same and we report
        else:
            self.log.writeToLog(self.errorCodes["3650"], [macPackId])
            return False

    def installMacPackOnly(self, source):
        """Install the new macro package but only that."""

        #        import pdb; pdb.set_trace()

        if self.tools.pkgExtract(source, self.local.projMacroFolder, self.local.macroConfXmlFile):
            return True
        else:
            self.log.writeToLog(self.errorCodes["3200"], [self.tools.fName(source)])
            return False
Exemplo n.º 3
0
class Macro (object) :

    def __init__(self, pid, cType, gid=None) :
        '''Do the primary initialization for this class.'''

        self.pid                            = pid
        self.gid                            = gid
        self.cType                          = cType
        self.user                           = UserConfig()
        self.userConfig                     = self.user.userConfig
        self.projHome                       = os.path.join(os.path.expanduser(os.environ['RAPUMA_PROJECTS']), self.pid)
        self.local                          = ProjLocal(pid, gid, cType)


#        import pdb; pdb.set_trace()


        self.proj_config                    = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig                  = self.proj_config.projectConfig
        self.layoutConfig                   = self.proj_config.layoutConfig
        self.tools                          = Tools()
        self.log                            = ProjLog(pid)
        # Create config placeholders
        self.layoutConfig                   = None
        self.illustrationConfig             = None
        self.macroConfig                    = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile)

        # Log messages for this module
        self.errorCodes     = {
            '3010' : ['ERR', 'No macro package is registered for the [<<1>>] component type.'],
            '3020' : ['ERR', 'Cannot update! No macro package is registered for the [<<1>>] component type.'],
            '3050' : ['ERR', 'Macro package file not found: [<<1>>]'],
            '3100' : ['ERR', 'Macro package: [<<1>>] already exists in the project. I am not allowed to copy over an existing package.'],
            '3200' : ['ERR', 'Failed to install macro package: [<<1>>]'],
            '3300' : ['MSG', 'Installed macro package: [<<1>>], Reinitialized [<<2>>]'],
            '3310' : ['ERR', 'Failed to copy [<<1>>] to folder [<<2>>].'],
            '3400' : ['MSG', 'Removed macro package configuration settings for: [<<1>>] from the macro.conf file.'],
            '3500' : ['MSG', 'Removed macro package [<<1>>] folder and all files contained.'],
            '3600' : ['MSG', 'Updated component type [<<1>>] with macro package [<<2>>]'],
            '3650' : ['ERR', 'Failed to updated macro package [<<1>>]']
        }

###############################################################################
###################### Macro Package Handling Functions #######################
###############################################################################
######################## Error Code Block Series = 3000 #######################
###############################################################################

    def createMacroFiles (self, macPackId) :
        '''Create all the necessary macro file names with their assigned paths.'''

        self.projMacPackFolder = os.path.join(self.local.projMacPackFolder, macPackId)

        texFileIds = {'preStyTexExtFile':'preSty-ext.tex', 'macSettingsFile':'settings.tex', 
                        'extTexFile':'extension.tex', 'grpExtTexFile': self.gid + '-extension.tex', 
                        '':'', '':'', '':'', '':'', '':'', }
        styFileIds = {'glbExtStyFile':'extension.sty', 'grpExtStyFile': self.gid + '-extension.sty', 
                        '':'', '':'', '':'', '':'', '':''}


    def getMacPackIdFromFileName (self, fileName) :
        '''Return the macPack ID based on the file name'''

        # File name less ext is the ID
        parts = len(fileName.split('.'))
        return '.'.join(fileName.split('.')[:parts-1])
    
    
    def getMacPackIdFromSource (self, source) :
        '''Return the macPack ID based on the complete path and file name.'''

        # Get the file name from the path
        fileName = self.tools.fName(source)
        # Return the ID
        return self.getMacPackIdFromFileName(fileName)


    def addMacPack (self, source) :
        '''Add a macro package to the project. It will not work if
        the same package is already present. Remove must be used
        to get rid of the existing one first.'''

#        import pdb; pdb.set_trace()

        macPackId = self.getMacPackIdFromSource(source)
        confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.xml')
        if not os.path.isfile(source) :
            self.log.writeToLog(self.errorCodes['3050'], [source])

        # Do not add/install if there seems to be a macro package there already
        if self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] and os.path.exists(self.local.macroConfFile) :
            self.log.writeToLog(self.errorCodes['3100'], [macPackId])
            return False

        # Set the projectConf to the new/same package
        self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = macPackId
        self.tools.writeConfFile(self.projectConfig)

        # If we got this far, install the a fresh copy of the macPack
        self.installMacPackOnly(source)
        # Move the style files and custom TeX files out of the macPack
        self.moveMacStyles(macPackId)
        self.moveMacTex(macPackId)

        # Create a fresh macro.conf file if it dosn't exist
        if not os.path.isfile(self.local.macroConfFile) :
            self.macroConfig = self.tools.initNewConfig(self.local.macroConfFile, self.local.macroConfXmlFile)
        # Inject information from this particular macro package
        mInfo = self.tools.getXMLSettings(confXml)
        self.macroConfig['Macros'][macPackId] = mInfo.dict()

        # Save the settings now
        self.tools.writeConfFile(self.macroConfig)

        self.log.writeToLog(self.errorCodes['3300'], [macPackId, self.local.macroConfFileName])
        
        return True


    def moveMacStyles (self, macPackId) :
        '''Move the default macro package styles out of the freshly installed
        project macro package folder to the project Style folder.'''


#        import pdb; pdb.set_trace()

        # Collect the style files to copy
        for f in self.getMacStyExtFiles(macPackId) :
            source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f))
            target = os.path.join(self.local.projStyleFolder, f)
            self.tools.makedirs(self.local.projStyleFolder)
            # Do not overwrite existing files unless force is used
            if not os.path.exists(target) :
                shutil.copy(source, target)
                # Look for default and set to read-only
                defaultStyFile = os.path.join(self.local.projStyleFolder, macPackId + '.sty')
                if target == defaultStyFile :
                    self.tools.makeReadOnly(defaultStyFile)
            # Remove the source to avoid confusion
            if os.path.exists(target) :
                os.remove(source)
            else :
                self.log.writeToLog(self.errorCodes['3310'], [source,self.local.projStyleFolder])


    def getMacStyExtFiles (self, macPackId) :
        '''Return a list of macro package style extention files.'''

        sFiles = []
        macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId))
        for f in macPackFiles :
            if f.split('.')[1].lower() == 'sty' :
                sFiles.append(f)
        return sFiles


    def moveMacTex (self, macPackId) :
        '''Move the custom macro package TeX out of the freshly installed
        project macro package folder to the project TeX folder.'''

        # Collect the TeX extention files to copy
        for f in self.getMacTexExtFiles(macPackId) :
            source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f))
            target = os.path.join(self.local.projTexFolder, f)
            self.tools.makedirs(self.local.projTexFolder)
            # Do not overwrite existing files
            if not os.path.exists(target) :
                shutil.copy(source, target)
            # Remove the source to avoid confusion
            if os.path.exists(target) :
                os.remove(source)
            else :
                self.log.writeToLog(self.errorCodes['3310'], [source,self.local.projTexFolder])


    def getMacTexExtFiles (self, macPackId) :
        '''Return a list of macro package TeX extention files.'''

        tFiles = []
        macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId))
        for f in macPackFiles :
            if f.find('ext.tex') > 0 :
                tFiles.append(f)
        return tFiles


    def removeMacPack (self) :
        '''Remove the macro package from a component type'''

#        import pdb; pdb.set_trace()

        if self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] != '' :
            macPackId = self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage']
        else :
            self.log.writeToLog(self.errorCodes['3010'], [self.cType])
        # Aquire target to delete
        target = os.path.join(self.local.projMacroFolder, macPackId)

        # Remove the macPack settings from the config file if it is there
        try :
            del self.macroConfig['Macros'][macPackId]
            # Save the settings now
            self.tools.writeConfFile(self.macroConfig)
            self.log.writeToLog(self.errorCodes['3400'], [macPackId])
        except :
            pass

        # Now remove the macro folder (with all its contents)
        if os.path.exists(target) :
            shutil.rmtree(target)
            self.log.writeToLog(self.errorCodes['3500'], [macPackId])

        # Remove any style files associated with the macro
        styTarget = os.path.join(self.local.projStyleFolder, macPackId + '.sty')
        self.tools.makeExecutable(styTarget)
        self.tools.removeFile(styTarget)

        # Remove the reference for this macro package from the component type
        # that uses it. Normally that would probably be just be one of them.
        self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = ''
        self.tools.writeConfFile(self.projectConfig)


    def updateMacPack (self, source) :
        '''Update a macro package with the latest version but do not 
        touch the config file.'''


        # Do not update if no macro package is registered in the projectConfig
        if not self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] :
            self.log.writeToLog(self.errorCodes['3020'], [self.cType.capitalize()])
            return False

        macPackId = self.getMacPackIdFromSource(source)
        confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.xml')
        oldMacPackId = self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage']
        oldMacDir = os.path.join(self.local.projMacroFolder, oldMacPackId)
        newMacDir = os.path.join(self.local.projMacroFolder, macPackId)

        # Install the new macPack (but use the old settings)
        if self.installMacPackOnly(source) :
            # The current macro system must have a "master" style file.
            # This is a version of the ParaText style file. Because
            # an update might include an update to the style file,
            # we need to copy that from the macro folder to the Style
            # folder. We will do that now. The name of the file should
            # be the macPackId + ".sty", in theory.
            srcStyleFile = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.sty')
            oldStyleFile = os.path.join(self.local.projStyleFolder, oldMacPackId + '.sty')
            newStyleFile = os.path.join(self.local.projStyleFolder, macPackId + '.sty')
            # Unlock the old one so it can be deleted.
            self.tools.makeExecutable(oldStyleFile)
            self.tools.removeFile(oldStyleFile)
            # Now copy in the new one.
            shutil.copy(srcStyleFile, newStyleFile)
            # The style file should never be edited by the user, relock it
            self.tools.makeReadOnly(newStyleFile)
            # Update exsisting extention file names. We never want to loose 
            # any settings that the user may have added to the extention
            # files so we will rename the existing files to have the
            # new ID otherwise the system will not find them at render time.
            oldStyExtFile           = os.path.join(self.local.projStyleFolder, oldMacPackId + '_ext.sty')
            oldTexExtFile           = os.path.join(self.local.projTexFolder, oldMacPackId + '_ext.tex')
            oldTexPreStyExtFile     = os.path.join(self.local.projTexFolder, oldMacPackId + '_preSty-ext.tex')
            newStyExtFile           = os.path.join(self.local.projStyleFolder, macPackId + '_ext.sty')
            newTexExtFile           = os.path.join(self.local.projTexFolder, macPackId + '_ext.tex')
            newTexPreStyExtFile     = os.path.join(self.local.projTexFolder, macPackId + '_preSty-ext.tex')
            # By default, we protect any existing versions
            if os.path.exists(newStyExtFile) :
                os.remove(oldStyExtFile)
            else :
                self.tools.renameFile(oldStyExtFile, newStyExtFile)
            if os.path.exists(newTexExtFile) :
                os.remove(oldTexExtFile)
            else :
                self.tools.renameFile(oldTexExtFile, newTexExtFile)
            if os.path.exists(newTexPreStyExtFile) :
                os.remove(oldTexPreStyExtFile)
            else :
                self.tools.renameFile(oldTexPreStyExtFile, newTexPreStyExtFile)
            # Remove un-needed sty and tex files from the newMacDir to
            # avoid confusion. The ext files never are updated because
            # they could contain custom project code that we don't want
            # to loose in an update.
            for f in self.getMacStyExtFiles(macPackId) :
                source = os.path.join(newMacDir, f)
                if os.path.exists(source) :
                    os.remove(source)
            for f in self.getMacTexExtFiles(macPackId) :
                source = os.path.join(newMacDir, f)
                if os.path.exists(source) :
                    os.remove(source)
            # Remove the old macPack folder
            shutil.rmtree(oldMacDir)
            # Merge new settings into old section (change name to new ID)
            # When updating, we are assuming the new macro is in the same
            # family as the old one. As such, settings should be almost
            # identical, but in case new settings are being added, we will
            # merge them in now.
            oldConfSettings = self.macroConfig['Macros'][oldMacPackId]
            newConfSettings = self.tools.getXMLSettings(confXml)
            # Now merge
            newConfSettings.merge(oldConfSettings)
            # Inject the new section
#            self.tools.buildConfSection(self.macroConfig, macPackId)
            self.macroConfig['Macros'][macPackId] = newConfSettings.dict()
            # Delete the old section
            del self.macroConfig['Macros'][oldMacPackId]
            # Save the changes
            self.tools.writeConfFile(self.macroConfig)

            # Assuming everything went well we will change the macPackID on the cType
            self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = macPackId
            self.tools.writeConfFile(self.projectConfig)
            # Report success
            self.log.writeToLog(self.errorCodes['3600'], [self.cType.capitalize(), macPackId])
            return True
        # But if the install fails everything stays the same and we report
        else :
            self.log.writeToLog(self.errorCodes['3650'], [macPackId])
            return False


    def installMacPackOnly (self, source) :
        '''Install the new macro package but only that.'''

#        import pdb; pdb.set_trace()

        if self.tools.pkgExtract(source, self.local.projMacroFolder, self.local.macroConfXmlFile) :
            return True
        else :
            self.log.writeToLog(self.errorCodes['3200'], [self.tools.fName(source)])
            return False
Exemplo n.º 4
0
class ProjLocal (object) :

#    def __init__(self, pid, gid = None, cType = 'usfm', mType = 'book', macPack = 'usfmTex') :

# FIXME: Media type can be found in the general settings of an existing project, change to None
# cType is found in a group and not needed to start a project, remove from init

    def __init__(self, pid, gid=None, cType=None, mType='book') :
        '''Intitate a class object which contains all the project file folder locations.
        The files and folders are processed by state. If the system is in a state where
        certain parent files or folders do not exist, the child file or folder will be
        set to None. This should only cause problems when certain proecess are attempted
        that should not be given a particular state. For example, a render process should
        fail if a group/component was never added.'''

#        import pdb; pdb.set_trace()

        self.tools              = Tools()
        self.pid                = pid
        self.gid                = gid
        self.rapumaHome         = os.environ.get('RAPUMA_BASE')
        self.userHome           = os.environ.get('RAPUMA_USER')
        self.osPlatform         = platform.architecture()[0][:2]
        self.user               = UserConfig()
        self.userConfig         = self.user.userConfig
        self.userResource       = os.path.join(site.USER_BASE, 'share', 'rapuma')
        self.projHome           = os.path.join(os.path.expanduser(os.environ['RAPUMA_PROJECTS']), self.pid)
        self.projFolders        = []
        self.localDict          = None
        self.macPackId          = None
        self.mType              = mType
        self.cType              = cType

# FIXME: The if statement to follow is a sad and desperate attempt to
# set the macPackId so that file paths will turn out in processes
# that rely on this module. This is really bad but right now I cannot
# think of another way to make this happen. I appologize for the future
# grief this is going to cause me or anyone else who tries to figure
# this situation out.
        if self.cType :
            pcf = os.path.join(self.projHome, 'Config', 'project.conf')
            pcfx = os.path.join(self.rapumaHome, 'config', mType + '.xml')
            projectConfig = self.tools.loadConfig(pcf, pcfx)
            # Having come this far, it is still possible that we cannot
            # set the macPackId because the component type doesn't even
            # need a macro package, PDF, for example. For that reason
            # we will use "try" to check passively.
            try :
                self.macPackId = projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage']
            except :
                pass

        debug                   = self.userConfig['System']['debugging']
        debugOutput             = os.path.join(self.userResource, 'debug', 'local_path.log')
        if debug and not os.path.exists(os.path.join(self.userResource, 'debug')) :
            os.makedirs(os.path.join(self.userResource, 'debug'))

        # Bring in all the Rapuma default project location settings
        rapumaXMLDefaults = os.path.join(self.rapumaHome, 'config', 'proj_local.xml')
        if os.path.exists(rapumaXMLDefaults) :
            self.localDict = self.tools.xmlFileToDict(rapumaXMLDefaults)
        else :
            raise IOError, "Can't open " + rapumaXMLDefaults

        # Create the user resources dir under .local/share
        if not os.path.isdir(self.userResource) :
            os.makedirs(self.userResource)

        # Troll through the localDict and create the file and folder defs we need
        if debug :
            debugObj = codecs.open(debugOutput, "w", encoding='utf_8')
        for sections in self.localDict['root']['section'] :
            for section in sections :
                secItems = sections[section]
                if type(secItems) is list :
                    for item in secItems :
                        if item.has_key('folderID') :
                            # First make a folder name placeholder and set 
                            # to None if it doesn't exist already
                            try :
                                getattr(self, str(item['folderID']))
                            except :
                                setattr(self, item['folderID'], None)
                            # Next, if the 'relies' exists, set the value
                            val = self.processNestedPlaceholders(item['folderPath'])
                            # Check for relative path and fix it if needed
                            if len(val) > 0 :
                                if val[0] == '~' :
                                    val = os.path.expanduser(val)
                            if item['relies'] :
                                if getattr(self, item['relies']) :
                                    setattr(self, item['folderID'], val)
                                    self.projFolders.append(val)
                                    if debug :
                                        debugObj.write(item['folderID'] + ' = ' + val + '\n')
                            else :
                                setattr(self, item['folderID'], val)
                                if debug :
                                    debugObj.write(item['folderID'] + ' = ' + val + '\n')
                        elif item.has_key('fileID') :
                            # First make a file name placeholder and set 
                            # to None if it doesn't exist already
                            try :
                                getattr(self, str(item['fileID']))
                                getattr(self, str(item['fileID'] + 'Name'))
                            except :
                                setattr(self, item['fileID'], None)
                                setattr(self, item['fileID'] + 'Name', None)
                            # Get the two values we need
                            valName = self.processNestedPlaceholders(item['fileName'])
                            valPath = self.processNestedPlaceholders(item['filePath'])
                            if item['relies'] :
                                if getattr(self, item['relies']) :
                                    setattr(self, item['fileID'] + 'Name', valName)
                                    setattr(self, item['fileID'], os.path.join(valPath, valName))
                                    if debug :
                                        debugObj.write(item['fileID'] + 'Name = ' + valName + '\n')
                                        debugObj.write(item['fileID'] + ' = ' + getattr(self, item['fileID']) + '\n')
                            else :
                                setattr(self, item['fileID'] + 'Name', valName)
                                setattr(self, item['fileID'], os.path.join(valPath, valName))
                                if debug :
                                    debugObj.write(item['fileID'] + 'Name = ' + valName + '\n')
                                    debugObj.write(item['fileID'] + ' = ' + getattr(self, item['fileID']) + '\n')

        # Add configuation file names
        configFiles = ['adjustment', 'font', 'illustration', 'layout', 'macro', 'project']
        # Create placeholders for basic conf file names
        for cf in configFiles :
            setattr(self, cf + 'ConfFile', None)
        # Add the real settings if we can
        if self.projHome :
            for cf in configFiles :
                # Set the config path/file value
                setattr(self, cf + 'ConfFileName', cf + '.conf')
                if debug :
                    debugObj.write(cf + 'ConfFileName' + ' = ' + getattr(self, cf + 'ConfFileName', cf + '.conf') + '\n')
                setattr(self, cf + 'ConfFile', os.path.join(self.projConfFolder, cf + '.conf'))
                if debug :
                    debugObj.write(cf + 'ConfFile' + ' = ' + getattr(self, cf + 'ConfFile', cf + '.conf') + '\n')
                # Set the xml config file name (project is according to media type)
                if cf == 'project' :
                    setattr(self, cf + 'ConfXmlFileName', self.mType + '.xml')
                    if debug :
                        debugObj.write(cf + 'ConfXmlFileName' + ' = ' + getattr(self, cf + 'ConfXmlFileName', self.mType + '.xml') + '\n')
                elif cf == 'layout' :
                    setattr(self, cf + 'ConfXmlFileName', self.mType + '_layout.xml')
                    if debug :
                        debugObj.write(cf + 'ConfXmlFileName' + ' = ' + getattr(self, cf + 'ConfXmlFileName', self.mType + '_layout.xml') + '\n')
                else :
                    setattr(self, cf + 'ConfXmlFileName', cf + '.xml')
                    if debug :
                        debugObj.write(cf + 'ConfXmlFileName' + ' = ' + getattr(self, cf + 'ConfXmlFileName', cf + '.xml') + '\n')
                # Set the full path/file value
                setattr(self, cf + 'ConfXmlFile', os.path.join(self.rapumaConfigFolder, getattr(self, cf + 'ConfXmlFileName')))
                if debug :
                    debugObj.write(cf + 'ConfXmlFileName' + ' = ' + getattr(self, cf + 'ConfXmlFile', os.path.join(self.rapumaConfigFolder, getattr(self, cf + 'ConfXmlFileName'))) + '\n')

        # Add macPack files via a specific macPack XML configuation file
        if self.macPackId and os.path.exists(self.macPackConfXmlFile) :
            macPackDict = self.tools.xmlFileToDict(self.macPackConfXmlFile)
        #if self.macPackId and os.path.exists(self.macroConfXmlFile) :
            #macPackDict = self.tools.xmlFileToDict(self.macroConfXmlFile)
            macPackFilesDict = {}
            for sections in macPackDict['root']['section'] :
                if sections['sectionID'] == 'Files' :
                    for section in sections :
                        secItem = sections[section]
                        if type(secItem) is list :
                            for f in secItem :
                                macPackFilesDict[f['moduleID']] = self.processNestedPlaceholders(f['fileName'])
            for f in macPackFilesDict.keys() :
                setattr(self, f, macPackFilesDict[f])
                if debug :
                    debugObj.write(f + ' = ' + getattr(self, f) + '\n')

        # Close the debug file if we need to
        if debug :
            debugObj.close()

        # Set Rapuma User config file name (already defined in user_config)
        self.userConfFileName           = self.user.userConfFileName
        self.userConfFile               = self.user.userConfFile
        # Add log file names
        if self.projHome :
            self.projLogFileName        = 'rapuma.log'
            self.projLogFile            = os.path.join(self.projHome, self.projLogFileName)
            self.projErrorLogFileName   = 'error.log'
            self.projErrorLogFile       = os.path.join(self.projHome, self.projErrorLogFileName)
        # Other info vals to set
        self.lockExt                    = '.lck'

        # Do some cleanup like getting rid of the last sessions error log file.
        try :
            if os.path.isfile(self.projErrorLogFile) :
                os.remove(self.projErrorLogFile)
        except :
            pass


###############################################################################
############################### Local Functions ###############################
###############################################################################

    def processNestedPlaceholders (self, line) :
        '''Search a string (or line) for a type of Rapuma placeholder and
        insert the value.'''

        result = []
        end_of_previous_segment = 0
        for (ph_start, ph_end) in self.getPlaceHolder(line) :
            unchanged_segment = line[end_of_previous_segment:ph_start]
            result.append(unchanged_segment)
            ph_text = line[ph_start+1:ph_end]
            replacement = self.processNestedPlaceholders(ph_text)
            result.append(unicode(replacement))
            end_of_previous_segment = ph_end+1  # Skip the closing bracket
        result.append(line[end_of_previous_segment:])
        resultline = "".join(result)
        result_text = self.processSinglePlaceholder(resultline)
        return result_text


    def processSinglePlaceholder (self, ph) :
        '''Once we are sure we have a single placeholder (noting embedded) this
        will process it and replace it with the correct value.'''

#        import pdb; pdb.set_trace()

        holderType = ph.split(':')[0]
        try :
            holderKey = ph.split(':')[1]
        except :
            holderKey = ''

        result = ph # If nothing matches below, default to returning placeholder unchanged

        # The following placeholders are supported
        if holderType == 'pathSep' :
            result = os.sep
        elif holderType == 'self' :
            result = getattr(self, holderKey)
        elif holderType == 'config' :
            result = self.getConfigValue(holderKey)

        return result


    def hasPlaceHolder (self, line) :
        '''Return True if this line has a data place holder in it.'''

        # If things get more complicated we may need to beef this up a bit
        if line.find('[') > -1 and line.find(']') > -1 :
            return True


    def getConfigValue (self, val, default=None) :
        '''Return the value from a config function or return default value if key not found.'''

        keyparts = val.split('|')
        curval = getattr(self, keyparts[0], None)
        if curval is None: return default
        for key in keyparts[1:]:
            curval = curval.get(key, None)
            if curval is None: return default
        return curval



    def getPlaceHolder (self, line) :
        '''Return place holder type and a key if one exists from a TeX setting line.
        Pass over the line and return (yield) each placeholder found.'''

        nesting_level = 0
        remembered_idx = None
        for idx, ch in enumerate(line):
            if ch == '[':
                nesting_level += 1
                if remembered_idx is None:
                    remembered_idx = idx
            elif ch == ']':
                nesting_level -= 1
                if nesting_level <= 0:
                    found_idx = remembered_idx
                    remembered_idx = None
                    yield (found_idx, idx)


    def getComponentFiles (self, gid, cid, cType) :
        '''Return a dictionary that contains files and paths for a set
        of component files. The path/names are only theoretical and
        need to be tested by the calling function. However, in the case
        of the source name, it will be a null item if the file does not
        exist.'''

#        import pdb; pdb.set_trace()

        files               = {}
        compFolder          = os.path.join(self.projComponentFolder, cid)
        # Handle USFM text special
        if cType == 'usfm' :
            # These are predictable
            fileHandle          = cid + '_base'
            files['working']    = os.path.join(compFolder, fileHandle + '.' +  cType)
            files['backup']     = files['working'] + '.cv1'
            # Source file may not exist so we will try to find it
            try :
                # Since the source name is not predictable we will look for a
                # file with a .source extention and assume that is it. If not
                # found we leave the source item empty
                fileList = os.listdir(compFolder)
                for f in fileList :
                    if f.find('.source') > 0 :
                        files['source'] = os.path.join(compFolder, f)
            except :
                files['source'] = None
        else :
            files['working']    = os.path.join(compFolder, cid + '.' +  cType)
            files['backup']     = files['working'] + '.bak'
        
        # Return the dictionary
        return files