## @file DecPomAlignment.py
# This file contained the adapter for convert INF parser object to POM Object
#
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#

'''
DecPomAlignment
'''
from __future__ import print_function

##
# Import Modules
#
import os.path
from os import sep
import platform

import re
import edk2basetools.UPT.Logger.Log as Logger
from edk2basetools.UPT.Logger import StringTable as ST
from edk2basetools.UPT.Logger.ToolError import UPT_MUL_DEC_ERROR
from edk2basetools.UPT.Logger.ToolError import FORMAT_INVALID

from edk2basetools.UPT.Library.Parsing import NormPath
from edk2basetools.UPT.Library.DataType import ARCH_LIST
from edk2basetools.UPT.Library.DataType import TAB_GUIDS
from edk2basetools.UPT.Library.DataType import TAB_PROTOCOLS
from edk2basetools.UPT.Library.DataType import TAB_PPIS
from edk2basetools.UPT.Library.DataType import TAB_DEC_DEFINES_PACKAGE_NAME
from edk2basetools.UPT.Library.DataType import TAB_DEC_DEFINES_PACKAGE_GUID
from edk2basetools.UPT.Library.DataType import TAB_DEC_DEFINES_PACKAGE_VERSION
from edk2basetools.UPT.Library.DataType import TAB_DEC_DEFINES_DEC_SPECIFICATION
from edk2basetools.UPT.Library.DataType import TAB_DEC_DEFINES_PKG_UNI_FILE
from edk2basetools.UPT.Library.DataType import TAB_ARCH_COMMON
from edk2basetools.UPT.Library.DataType import TAB_INCLUDES
from edk2basetools.UPT.Library.DataType import TAB_LIBRARY_CLASSES
from edk2basetools.UPT.Library.DataType import TAB_PCDS
from edk2basetools.UPT.Library.DataType import TAB_PCDS_FIXED_AT_BUILD_NULL
from edk2basetools.UPT.Library.DataType import TAB_PCDS_PATCHABLE_IN_MODULE_NULL
from edk2basetools.UPT.Library.DataType import TAB_PCDS_FEATURE_FLAG_NULL
from edk2basetools.UPT.Library.DataType import TAB_PCDS_DYNAMIC_EX_NULL
from edk2basetools.UPT.Library.DataType import TAB_PCDS_DYNAMIC_NULL
from edk2basetools.UPT.Library.DataType import TAB_PTR_TYPE_PCD
from edk2basetools.UPT.Library.DataType import ITEM_UNDEFINED
from edk2basetools.UPT.Library.DataType import TAB_DEC_BINARY_ABSTRACT
from edk2basetools.UPT.Library.DataType import TAB_DEC_BINARY_DESCRIPTION
from edk2basetools.UPT.Library.DataType import TAB_LANGUAGE_EN_US
from edk2basetools.UPT.Library.DataType import TAB_BINARY_HEADER_IDENTIFIER
from edk2basetools.UPT.Library.DataType import TAB_BINARY_HEADER_USERID
from edk2basetools.UPT.Library.DataType import TAB_LANGUAGE_EN_X
from edk2basetools.UPT.Library.DataType import TAB_LANGUAGE_EN
from edk2basetools.UPT.Library.DataType import TAB_STR_TOKENCNAME
from edk2basetools.UPT.Library.DataType import TAB_STR_TOKENPROMPT
from edk2basetools.UPT.Library.DataType import TAB_STR_TOKENHELP
from edk2basetools.UPT.Library.DataType import TAB_STR_TOKENERR
from edk2basetools.UPT.Library.DataType import TAB_HEX_START
from edk2basetools.UPT.Library.DataType import TAB_SPLIT
import edk2basetools.UPT.Library.DataType as DT
from edk2basetools.UPT.Library.CommentParsing import ParseHeaderCommentSection
from edk2basetools.UPT.Library.CommentParsing import ParseGenericComment
from edk2basetools.UPT.Library.CommentParsing import ParseDecPcdGenericComment
from edk2basetools.UPT.Library.CommentParsing import ParseDecPcdTailComment
from edk2basetools.UPT.Library.Misc import GetFiles
from edk2basetools.UPT.Library.Misc import Sdict
from edk2basetools.UPT.Library.Misc import GetRelativePath
from edk2basetools.UPT.Library.Misc import PathClass
from edk2basetools.UPT.Library.Misc import ValidateUNIFilePath
from edk2basetools.UPT.Library.UniClassObject import UniFileClassObject
from edk2basetools.UPT.Library.UniClassObject import ConvertSpecialUnicodes
from edk2basetools.UPT.Library.UniClassObject import GetLanguageCode1766
from edk2basetools.UPT.Library.ParserValidate import IsValidPath
from edk2basetools.UPT.Parser.DecParser import Dec
from edk2basetools.UPT.Object.POM.PackageObject import PackageObject
from edk2basetools.UPT.Object.POM.CommonObject import UserExtensionObject
from edk2basetools.UPT.Object.POM.CommonObject import IncludeObject
from edk2basetools.UPT.Object.POM.CommonObject import GuidObject
from edk2basetools.UPT.Object.POM.CommonObject import ProtocolObject
from edk2basetools.UPT.Object.POM.CommonObject import PpiObject
from edk2basetools.UPT.Object.POM.CommonObject import LibraryClassObject
from edk2basetools.UPT.Object.POM.CommonObject import PcdObject
from edk2basetools.UPT.Object.POM.CommonObject import TextObject
from edk2basetools.UPT.Object.POM.CommonObject import MiscFileObject
from edk2basetools.UPT.Object.POM.CommonObject import FileObject


## DecPomAlignment
#
# Inherited from PackageObject
#
class DecPomAlignment(PackageObject):
    def __init__(self, Filename, WorkspaceDir = None, CheckMulDec = False):
        PackageObject.__init__(self)
        self.UserExtensions = ''
        self.WorkspaceDir = WorkspaceDir
        self.SupArchList = ARCH_LIST
        self.CheckMulDec = CheckMulDec
        self.DecParser = None
        self.UniFileClassObject = None
        self.PcdDefaultValueDict = {}

        #
        # Load Dec file
        #
        self.LoadDecFile(Filename)

        #
        # Transfer to Package Object if IsToPackage is True
        #
        self.DecToPackage()

    ## Load Dec file
    #
    # Load the file if it exists
    #
    # @param Filename:  Input value for filename of Dec file
    #
    def LoadDecFile(self, Filename):
        #
        # Insert a record for file
        #
        Filename = NormPath(Filename)
        (Path, Name) = os.path.split(Filename)
        self.SetFullPath(Filename)
        self.SetRelaPath(Path)
        self.SetFileName(Name)
        self.SetPackagePath(GetRelativePath(Path, self.WorkspaceDir))
        self.SetCombinePath(GetRelativePath(Filename, self.WorkspaceDir))

        self.DecParser = Dec(Filename)

    ## Transfer to Package Object
    #
    # Transfer all contents of a Dec file to a standard Package Object
    #
    def DecToPackage(self):
        #
        # Init global information for the file
        #
        ContainerFile = self.GetFullPath()

        #
        # Generate Package Header
        #
        self.GenPackageHeader(ContainerFile)

        #
        # Generate Includes
        #
        self.GenIncludes(ContainerFile)

        #
        # Generate Guids
        #
        self.GenGuidProtocolPpis(TAB_GUIDS, ContainerFile)

        #
        # Generate Protocols
        #
        self.GenGuidProtocolPpis(TAB_PROTOCOLS, ContainerFile)

        #
        # Generate Ppis
        #
        self.GenGuidProtocolPpis(TAB_PPIS, ContainerFile)

        #
        # Generate LibraryClasses
        #
        self.GenLibraryClasses(ContainerFile)

        #
        # Generate Pcds
        #
        self.GenPcds(ContainerFile)

        #
        # Generate Module File list, will be used later on to generate
        # distribution
        #
        self.GenModuleFileList(ContainerFile)

        #
        # Generate user extensions
        #
        self.GenUserExtensions()

    ## Generate user extension
    #
    #
    def GenUserExtensions(self):
        UEObj = self.DecParser.GetUserExtensionSectionObject()
        UEList = UEObj.GetAllUserExtensions()
        for Item in UEList:
            if not Item.UserString:
                continue
            UserExtension = UserExtensionObject()
            UserId = Item.UserId
            if UserId.startswith('"') and UserId.endswith('"'):
                UserId = UserId[1:-1]
            UserExtension.SetUserID(UserId)
            Identifier = Item.IdString
            if Identifier.startswith('"') and Identifier.endswith('"'):
                Identifier = Identifier[1:-1]
            #
            # Generate miscellaneous files of DEC file
            #
            if UserId == 'TianoCore' and Identifier == 'ExtraFiles':
                self.GenMiscFiles(Item.UserString)
            UserExtension.SetIdentifier(Identifier)
            UserExtension.SetStatement(Item.UserString)
            UserExtension.SetSupArchList(
                Item.ArchAndModuleType
            )
            self.SetUserExtensionList(
                self.GetUserExtensionList() + [UserExtension]
            )

        # Add Private sections to UserExtension
        if self.DecParser.GetPrivateSections():
            PrivateUserExtension = UserExtensionObject()
            PrivateUserExtension.SetStatement(self.DecParser.GetPrivateSections())
            PrivateUserExtension.SetIdentifier(DT.TAB_PRIVATE)
            PrivateUserExtension.SetUserID(DT.TAB_INTEL)
            self.SetUserExtensionList(self.GetUserExtensionList() + [PrivateUserExtension])

    ## Generate miscellaneous files on DEC file
    #
    #
    def GenMiscFiles(self, Content):
        MiscFileObj = MiscFileObject()
        for Line in Content.splitlines():
            FileName = ''
            if '#' in Line:
                FileName = Line[:Line.find('#')]
            else:
                FileName = Line
            if FileName:
                if IsValidPath(FileName, self.GetRelaPath()):
                    FileObj = FileObject()
                    FileObj.SetURI(FileName)
                    MiscFileObj.SetFileList(MiscFileObj.GetFileList()+[FileObj])
                else:
                    Logger.Error("InfParser",
                                 FORMAT_INVALID,
                                 ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID%(Line),
                                 File=self.GetFileName(),
                                 ExtraData=Line)
        self.SetMiscFileList(self.GetMiscFileList()+[MiscFileObj])

    ## Generate Package Header
    #
    # Gen Package Header of Dec as <Key> = <Value>
    #
    # @param ContainerFile: The Dec file full path
    #
    def GenPackageHeader(self, ContainerFile):
        Logger.Debug(2, "Generate PackageHeader ...")
        DefinesDict = {}

        #
        # Update all defines item in database
        #
        DefObj = self.DecParser.GetDefineSectionObject()
        for Item in DefObj.GetDefines():
            #
            # put items into Dict except for PackageName, Guid, Version, DEC_SPECIFICATION
            #
            SkipItemList = [TAB_DEC_DEFINES_PACKAGE_NAME, \
                TAB_DEC_DEFINES_PACKAGE_GUID, TAB_DEC_DEFINES_PACKAGE_VERSION, \
                TAB_DEC_DEFINES_DEC_SPECIFICATION, TAB_DEC_DEFINES_PKG_UNI_FILE]
            if Item.Key in SkipItemList:
                continue
            DefinesDict['%s = %s' % (Item.Key, Item.Value)] = TAB_ARCH_COMMON

        self.SetBaseName(DefObj.GetPackageName())
        self.SetVersion(DefObj.GetPackageVersion())
#        self.SetName(DefObj.GetPackageName() + ' Version ' + \
#                     DefObj.GetPackageVersion())
        self.SetName(os.path.splitext(self.GetFileName())[0])
        self.SetGuid(DefObj.GetPackageGuid())
        if DefObj.GetPackageUniFile():
            ValidateUNIFilePath(DefObj.GetPackageUniFile())
            self.UniFileClassObject = \
            UniFileClassObject([PathClass(os.path.join(DefObj.GetPackagePath(), DefObj.GetPackageUniFile()))])
        else:
            self.UniFileClassObject = None

        if DefinesDict:
            UserExtension = UserExtensionObject()
            UserExtension.SetDefinesDict(DefinesDict)
            UserExtension.SetIdentifier('DefineModifiers')
            UserExtension.SetUserID('EDK2')
            self.SetUserExtensionList(
                self.GetUserExtensionList() + [UserExtension]
            )

        #
        # Get File header information
        #
        if self.UniFileClassObject:
            Lang = TAB_LANGUAGE_EN_X
        else:
            Lang = TAB_LANGUAGE_EN_US
        Abstract, Description, Copyright, License = \
            ParseHeaderCommentSection(self.DecParser.GetHeadComment(),
                                      ContainerFile)
        if Abstract:
            self.SetAbstract((Lang, Abstract))
        if Description:
            self.SetDescription((Lang, Description))
        if Copyright:
            self.SetCopyright(('', Copyright))
        if License:
            self.SetLicense(('', License))

        #
        # Get Binary header information
        #
        if self.DecParser.BinaryHeadComment:
            Abstract, Description, Copyright, License = \
                ParseHeaderCommentSection(self.DecParser.BinaryHeadComment,
                                      ContainerFile, True)

            if not Abstract  or not Description or not Copyright or not License:
                Logger.Error('MkPkg',
                             FORMAT_INVALID,
                             ST.ERR_INVALID_BINARYHEADER_FORMAT,
                             ContainerFile)
            else:
                self.SetBinaryHeaderAbstract((Lang, Abstract))
                self.SetBinaryHeaderDescription((Lang, Description))
                self.SetBinaryHeaderCopyright(('', Copyright))
                self.SetBinaryHeaderLicense(('', License))

        BinaryAbstractList = []
        BinaryDescriptionList = []

        #Get Binary header from UNI file
        # Initialize the UniStrDict dictionary, top keys are language codes
        UniStrDict = {}
        if self.UniFileClassObject:
            UniStrDict = self.UniFileClassObject.OrderedStringList
            for Lang in UniStrDict:
                for StringDefClassObject in UniStrDict[Lang]:
                    Lang = GetLanguageCode1766(Lang)
                    if StringDefClassObject.StringName == TAB_DEC_BINARY_ABSTRACT:
                        if (Lang, ConvertSpecialUnicodes(StringDefClassObject.StringValue)) \
                        not in self.GetBinaryHeaderAbstract():
                            BinaryAbstractList.append((Lang, ConvertSpecialUnicodes(StringDefClassObject.StringValue)))
                    if StringDefClassObject.StringName == TAB_DEC_BINARY_DESCRIPTION:
                        if (Lang, ConvertSpecialUnicodes(StringDefClassObject.StringValue)) \
                        not in self.GetBinaryHeaderDescription():
                            BinaryDescriptionList.append((Lang,
                                                          ConvertSpecialUnicodes(StringDefClassObject.StringValue)))
        #Combine Binary header from DEC file and UNI file
        BinaryAbstractList = self.GetBinaryHeaderAbstract() + BinaryAbstractList
        BinaryDescriptionList = self.GetBinaryHeaderDescription() + BinaryDescriptionList
        BinaryCopyrightList = self.GetBinaryHeaderCopyright()
        BinaryLicenseList = self.GetBinaryHeaderLicense()
        #Generate the UserExtensionObject for TianoCore."BinaryHeader"
        if BinaryAbstractList or BinaryDescriptionList or BinaryCopyrightList or BinaryLicenseList:
            BinaryUserExtension = UserExtensionObject()
            BinaryUserExtension.SetBinaryAbstract(BinaryAbstractList)
            BinaryUserExtension.SetBinaryDescription(BinaryDescriptionList)
            BinaryUserExtension.SetBinaryCopyright(BinaryCopyrightList)
            BinaryUserExtension.SetBinaryLicense(BinaryLicenseList)
            BinaryUserExtension.SetIdentifier(TAB_BINARY_HEADER_IDENTIFIER)
            BinaryUserExtension.SetUserID(TAB_BINARY_HEADER_USERID)
            self.SetUserExtensionList(self.GetUserExtensionList() + [BinaryUserExtension])


    ## GenIncludes
    #
    # Gen Includes of Dec
    #
    # @param ContainerFile: The Dec file full path
    #
    def GenIncludes(self, ContainerFile):
        if ContainerFile:
            pass
        Logger.Debug(2, "Generate %s ..." % TAB_INCLUDES)
        IncludesDict = Sdict()

        IncObj = self.DecParser.GetIncludeSectionObject()
        for Item in IncObj.GetAllIncludes():
            IncludePath = os.path.normpath(Item.File)
            if platform.system() != 'Windows' and platform.system() != 'Microsoft':
                IncludePath = IncludePath.replace('\\', '/')
            if IncludePath in IncludesDict:
                if Item.GetArchList() == [TAB_ARCH_COMMON] or IncludesDict[IncludePath] == [TAB_ARCH_COMMON]:
                    IncludesDict[IncludePath] = [TAB_ARCH_COMMON]
                else:
                    IncludesDict[IncludePath] = IncludesDict[IncludePath] + Item.GetArchList()
            else:
                IncludesDict[IncludePath] = Item.GetArchList()

        #
        # get the  standardIncludeFileList(industry), packageIncludeFileList
        # (others) for PackageObject
        #
        PackagePath = os.path.split(self.GetFullPath())[0]
        IncludePathList = \
            sorted([os.path.normpath(Path) + sep for Path in IncludesDict.keys()])

        #
        # get a non-overlap set of include path, IncludePathList should be
        # sorted, and path should be end with path separator '\'
        #
        NonOverLapList = []
        for Path1 in IncludePathList:
            for Path2 in NonOverLapList:
                if Path1.startswith(Path2):
                    break
            else:
                NonOverLapList.append(Path1)
        #
        # revert the list so the longest path shown first in list, also need
        # to remove the extra path separator '\'
        # as this list is used to search the supported Arch info
        #
        for IndexN in range (0, len(IncludePathList)):
            IncludePathList[IndexN] = os.path.normpath(IncludePathList[IndexN])
        IncludePathList.sort()
        IncludePathList.reverse()
        #
        # save the include path list for later usage
        #
        self.SetIncludePathList(IncludePathList)
        StandardIncludeFileList = []
        PackageIncludeFileList = []

        IncludeFileList = []
        for Path in NonOverLapList:
            FileList = GetFiles(os.path.join(PackagePath, Path), ['CVS', '.svn'], False)
            IncludeFileList += [os.path.normpath(os.path.join(Path, File)) for File in FileList]
        for Includefile in IncludeFileList:
            ExtName = os.path.splitext(Includefile)[1]
            if ExtName.upper() == '.DEC' and self.CheckMulDec:
                Logger.Error('MkPkg',
                             UPT_MUL_DEC_ERROR,
                             ST.ERR_MUL_DEC_ERROR%(os.path.dirname(ContainerFile),
                                                   os.path.basename(ContainerFile),
                                                   Includefile))

            FileCombinePath = os.path.dirname(Includefile)
            Include = IncludeObject()
            for Path in IncludePathList:
                if FileCombinePath.startswith(Path):
                    SupArchList = IncludesDict[Path]
                    break
            Include.SetFilePath(Includefile)
            Include.SetSupArchList(SupArchList)
            if Includefile.find('IndustryStandard') != -1:
                StandardIncludeFileList.append(Include)
            else:
                PackageIncludeFileList.append(Include)

        self.SetStandardIncludeFileList(StandardIncludeFileList)

        #
        # put include path into the PackageIncludeFileList
        #
        PackagePathList = []
        IncObj = self.DecParser.GetIncludeSectionObject()
        for Item in IncObj.GetAllIncludes():
            IncludePath = Item.File
            Include = IncludeObject()
            Include.SetFilePath(IncludePath)
            Include.SetSupArchList(Item.GetArchList())
            PackagePathList.append(Include)
        self.SetPackageIncludeFileList(PackagePathList + PackageIncludeFileList)

    ## GenPpis
    #
    # Gen Ppis of Dec
    # <CName>=<GuidValue>
    #
    # @param ContainerFile: The Dec file full path
    #
    def GenGuidProtocolPpis(self, Type, ContainerFile):
        if ContainerFile:
            pass
        Logger.Debug(2, "Generate %s ..." % Type)

        Obj = None
        Factory = None
        if Type == TAB_GUIDS:
            Obj = self.DecParser.GetGuidSectionObject()
            def CreateGuidObject():
                Object = GuidObject()
                Object.SetGuidTypeList([])
                Object.SetUsage(None)
                Object.SetName(None)
                return Object
            Factory = CreateGuidObject
        elif Type == TAB_PROTOCOLS:
            Obj = self.DecParser.GetProtocolSectionObject()

            def CreateProtocolObject():
                return ProtocolObject()
            Factory = CreateProtocolObject
        elif Type == TAB_PPIS:
            Obj = self.DecParser.GetPpiSectionObject()

            def CreatePpiObject():
                return PpiObject()
            Factory = CreatePpiObject
        else:
            #
            # Should not be here
            #
            return

        DeclarationsList = []

        #
        # Go through each arch
        #
        for Item in Obj.GetGuidStyleAllItems():
            Name = Item.GuidCName
            Value = Item.GuidString
            HelpTxt = ParseGenericComment(Item.GetHeadComment() + \
                                          Item.GetTailComment())

            ListObject = Factory()
            ListObject.SetCName(Name)
            ListObject.SetGuid(Value)
            ListObject.SetSupArchList(Item.GetArchList())
            if HelpTxt:
                if self.UniFileClassObject:
                    HelpTxt.SetLang(TAB_LANGUAGE_EN_X)
                ListObject.SetHelpTextList([HelpTxt])

            DeclarationsList.append(ListObject)

        #
        #GuidTypeList is abstracted from help
        #
        if Type == TAB_GUIDS:
            self.SetGuidList(self.GetGuidList() + DeclarationsList)
        elif Type == TAB_PROTOCOLS:
            self.SetProtocolList(self.GetProtocolList() + DeclarationsList)
        elif Type == TAB_PPIS:
            self.SetPpiList(self.GetPpiList() + DeclarationsList)

    ## GenLibraryClasses
    #
    # Gen LibraryClasses of Dec
    # <CName>=<GuidValue>
    #
    # @param ContainerFile: The Dec file full path
    #
    def GenLibraryClasses(self, ContainerFile):
        if ContainerFile:
            pass
        Logger.Debug(2, "Generate %s ..." % TAB_LIBRARY_CLASSES)
        LibraryClassDeclarations = []

        LibObj = self.DecParser.GetLibraryClassSectionObject()
        for Item in LibObj.GetAllLibraryclasses():
            LibraryClass = LibraryClassObject()
            LibraryClass.SetLibraryClass(Item.Libraryclass)
            LibraryClass.SetSupArchList(Item.GetArchList())
            LibraryClass.SetIncludeHeader(Item.File)
            HelpTxt = ParseGenericComment(Item.GetHeadComment() + \
                                          Item.GetTailComment(), None, '@libraryclass')
            if HelpTxt:
                if self.UniFileClassObject:
                    HelpTxt.SetLang(TAB_LANGUAGE_EN_X)
                LibraryClass.SetHelpTextList([HelpTxt])
            LibraryClassDeclarations.append(LibraryClass)

        self.SetLibraryClassList(self.GetLibraryClassList() + \
                                 LibraryClassDeclarations)

    ## GenPcds
    #
    # Gen Pcds of Dec
    # <TokenSpcCName>.<TokenCName>|<Value>|<DatumType>|<Token>
    #
    # @param ContainerFile: The Dec file full path
    #
    def GenPcds(self, ContainerFile):
        Logger.Debug(2, "Generate %s ..." % TAB_PCDS)
        PcdObj = self.DecParser.GetPcdSectionObject()
        #
        # Get all Pcds
        #
        PcdDeclarations = []
        IterList = [
            (TAB_PCDS_FIXED_AT_BUILD_NULL,      'FixedPcd'),
            (TAB_PCDS_PATCHABLE_IN_MODULE_NULL, 'PatchPcd'),
            (TAB_PCDS_FEATURE_FLAG_NULL,        'FeaturePcd'),
            (TAB_PCDS_DYNAMIC_EX_NULL,          'PcdEx'),
            (TAB_PCDS_DYNAMIC_NULL,             'Pcd')]

        PromptStrList = []
        HelpStrList = []
        PcdErrStrList = []
        # Initialize UniStrDict dictionary, top keys are language codes
        UniStrDict = {}
        StrList = []

        Language = ''
        if self.UniFileClassObject:
            Language = TAB_LANGUAGE_EN_X
        else:
            Language = TAB_LANGUAGE_EN_US

        if self.UniFileClassObject:
            UniStrDict = self.UniFileClassObject.OrderedStringList
            for Lang in UniStrDict:
                for StringDefClassObject in UniStrDict[Lang]:
                    StrList = StringDefClassObject.StringName.split('_')
                    # StringName format is STR_<TOKENSPACECNAME>_<PCDCNAME>_PROMPT
                    if len(StrList) == 4 and StrList[0] == TAB_STR_TOKENCNAME and StrList[3] == TAB_STR_TOKENPROMPT:
                        PromptStrList.append((GetLanguageCode1766(Lang), StringDefClassObject.StringName, \
                                              StringDefClassObject.StringValue))
                    # StringName format is STR_<TOKENSPACECNAME>_<PCDCNAME>_HELP
                    if len(StrList) == 4 and StrList[0] == TAB_STR_TOKENCNAME and StrList[3] == TAB_STR_TOKENHELP:
                        HelpStrList.append((GetLanguageCode1766(Lang), StringDefClassObject.StringName, \
                                            StringDefClassObject.StringValue))
                    # StringName format is STR_<TOKENSPACECNAME>_ERR_##
                    if len(StrList) == 4 and StrList[0] == TAB_STR_TOKENCNAME and StrList[2] == TAB_STR_TOKENERR:
                        PcdErrStrList.append((GetLanguageCode1766(Lang), StringDefClassObject.StringName, \
                                              StringDefClassObject.StringValue))
        #
        # For each PCD type
        #
        for PcdType, Type in IterList:
            #
            # Go through all archs
            #
            # for Arch in self.SupArchList + [TAB_ARCH_COMMON]:
            #
            for Item in PcdObj.GetPcdsByType(PcdType.upper()):
                PcdDeclaration = GenPcdDeclaration(
                        ContainerFile,
                        (Item.TokenSpaceGuidCName, Item.TokenCName,
                        Item.DefaultValue, Item.DatumType, Item.TokenValue,
                        Type, Item.GetHeadComment(), Item.GetTailComment(), ''),
                        Language,
                        self.DecParser.GetDefineSectionMacro()
                        )
                PcdDeclaration.SetSupArchList(Item.GetArchListOfType(PcdType))

                #
                # Get PCD error message from PCD error comment section in DEC file
                #
                for PcdErr in PcdDeclaration.GetPcdErrorsList():
                    if (PcdDeclaration.GetTokenSpaceGuidCName(), PcdErr.GetErrorNumber()) \
                        in self.DecParser.PcdErrorCommentDict:
                        Key = (PcdDeclaration.GetTokenSpaceGuidCName(), PcdErr.GetErrorNumber())
                        PcdErr.SetErrorMessageList(PcdErr.GetErrorMessageList() + \
                                                      [(Language, self.DecParser.PcdErrorCommentDict[Key])])

                for Index in range(0, len(PromptStrList)):
                    StrNameList = PromptStrList[Index][1].split('_')
                    if StrNameList[1].lower() == Item.TokenSpaceGuidCName.lower() and \
                    StrNameList[2].lower() == Item.TokenCName.lower():
                        TxtObj = TextObject()
                        TxtObj.SetLang(PromptStrList[Index][0])
                        TxtObj.SetString(PromptStrList[Index][2])
                        for Prompt in PcdDeclaration.GetPromptList():
                            if Prompt.GetLang() == TxtObj.GetLang() and \
                                Prompt.GetString() == TxtObj.GetString():
                                break
                        else:
                            PcdDeclaration.SetPromptList(PcdDeclaration.GetPromptList() + [TxtObj])

                for Index in range(0, len(HelpStrList)):
                    StrNameList = HelpStrList[Index][1].split('_')
                    if StrNameList[1].lower() == Item.TokenSpaceGuidCName.lower() and \
                    StrNameList[2].lower() == Item.TokenCName.lower():
                        TxtObj = TextObject()
                        TxtObj.SetLang(HelpStrList[Index][0])
                        TxtObj.SetString(HelpStrList[Index][2])
                        for HelpStrObj in PcdDeclaration.GetHelpTextList():
                            if HelpStrObj.GetLang() == TxtObj.GetLang() and \
                                HelpStrObj.GetString() == TxtObj.GetString():
                                break
                        else:
                            PcdDeclaration.SetHelpTextList(PcdDeclaration.GetHelpTextList() + [TxtObj])

                #
                # Get PCD error message from UNI file
                #
                for Index in range(0, len(PcdErrStrList)):
                    StrNameList = PcdErrStrList[Index][1].split('_')
                    if StrNameList[1].lower() == Item.TokenSpaceGuidCName.lower() and \
                        StrNameList[2].lower() == TAB_STR_TOKENERR.lower():
                        for PcdErr in PcdDeclaration.GetPcdErrorsList():
                            if PcdErr.GetErrorNumber().lower() == (TAB_HEX_START + StrNameList[3]).lower() and \
                                (PcdErrStrList[Index][0], PcdErrStrList[Index][2]) not in PcdErr.GetErrorMessageList():
                                PcdErr.SetErrorMessageList(PcdErr.GetErrorMessageList() + \
                                                            [(PcdErrStrList[Index][0], PcdErrStrList[Index][2])])

                #
                # Check to prevent missing error message if a Pcd has the error code.
                #
                for PcdErr in PcdDeclaration.GetPcdErrorsList():
                    if PcdErr.GetErrorNumber().strip():
                        if not PcdErr.GetErrorMessageList():
                            Logger.Error('UPT',
                                         FORMAT_INVALID,
                                         ST.ERR_DECPARSE_PCD_UNMATCHED_ERRORCODE % PcdErr.GetErrorNumber(),
                                         ContainerFile,
                                         PcdErr.GetLineNum(),
                                         PcdErr.GetFileLine())

                PcdDeclarations.append(PcdDeclaration)
        self.SetPcdList(self.GetPcdList() + PcdDeclarations)
        self.CheckPcdValue()

    ##
    # Get error message via language
    # @param ErrorMessageList: Error message tuple list the language and its message
    # @param Lang: the language of setting
    # @return: the error message described in the related UNI file
    def GetEnErrorMessage(self, ErrorMessageList):
        if self.FullPath:
            pass
        Lang = TAB_LANGUAGE_EN_US
        for (Language, Message) in ErrorMessageList:
            if Language == Lang:
                return Message
        for (Language, Message) in ErrorMessageList:
            if Language.find(TAB_LANGUAGE_EN) >= 0:
                return Message
        else:
            try:
                return ErrorMessageList[0][1]
            except IndexError:
                return ''
        return ''

    ##
    # Replace the strings for Python eval function.
    # @param ReplaceValue: The string that needs to be replaced.
    # @return: The string was replaced, then eval function is always making out it.
    def ReplaceForEval(self, ReplaceValue, IsRange=False, IsExpr=False):
        if self.FullPath:
            pass
        #
        # deal with "NOT EQ", "NOT LT", "NOT GT", "NOT LE", "NOT GE", "NOT NOT"
        #
        NOTNOT_Pattern = '[\t\s]*NOT[\t\s]+NOT[\t\s]*'
        NOTGE_Pattern = '[\t\s]*NOT[\t\s]+GE[\t\s]*'
        NOTLE_Pattern = '[\t\s]*NOT[\t\s]+LE[\t\s]*'
        NOTGT_Pattern = '[\t\s]*NOT[\t\s]+GT[\t\s]*'
        NOTLT_Pattern = '[\t\s]*NOT[\t\s]+LT[\t\s]*'
        NOTEQ_Pattern = '[\t\s]*NOT[\t\s]+EQ[\t\s]*'
        ReplaceValue = re.compile(NOTNOT_Pattern).sub('', ReplaceValue)
        ReplaceValue = re.compile(NOTLT_Pattern).sub('x >= ', ReplaceValue)
        ReplaceValue = re.compile(NOTGT_Pattern).sub('x <= ', ReplaceValue)
        ReplaceValue = re.compile(NOTLE_Pattern).sub('x > ', ReplaceValue)
        ReplaceValue = re.compile(NOTGE_Pattern).sub('x < ', ReplaceValue)
        ReplaceValue = re.compile(NOTEQ_Pattern).sub('x != ', ReplaceValue)

        if IsRange:
            ReplaceValue = ReplaceValue.replace('EQ', 'x ==')
            ReplaceValue = ReplaceValue.replace('LT', 'x <')
            ReplaceValue = ReplaceValue.replace('LE', 'x <=')
            ReplaceValue = ReplaceValue.replace('GT', 'x >')
            ReplaceValue = ReplaceValue.replace('GE', 'x >=')
            ReplaceValue = ReplaceValue.replace('XOR', 'x ^')
        elif IsExpr:
            ReplaceValue = ReplaceValue.replace('EQ', '==')
            ReplaceValue = ReplaceValue.replace('NE', '!=')
            ReplaceValue = ReplaceValue.replace('LT', '<')
            ReplaceValue = ReplaceValue.replace('LE', '<=')
            ReplaceValue = ReplaceValue.replace('GT', '>')
            ReplaceValue = ReplaceValue.replace('GE', '>=')
            ReplaceValue = ReplaceValue.replace('XOR', '^')

        ReplaceValue = ReplaceValue.replace('AND', 'and')
        ReplaceValue = ReplaceValue.replace('&&', ' and ')
        ReplaceValue = ReplaceValue.replace('xor', '^')
        ReplaceValue = ReplaceValue.replace('OR', 'or')
        ReplaceValue = ReplaceValue.replace('||', ' or ')
        ReplaceValue = ReplaceValue.replace('NOT', 'not')
        if ReplaceValue.find('!') >= 0 and ReplaceValue[ReplaceValue.index('!') + 1] != '=':
            ReplaceValue = ReplaceValue.replace('!', ' not ')
        if '.' in ReplaceValue:
            Pattern = '[a-zA-Z0-9]{1,}\.[a-zA-Z0-9]{1,}'
            MatchedList = re.findall(Pattern, ReplaceValue)
            for MatchedItem in MatchedList:
                if MatchedItem not in self.PcdDefaultValueDict:
                    Logger.Error("Dec File Parser", FORMAT_INVALID, Message=ST.ERR_DECPARSE_PCD_NODEFINED % MatchedItem,
                                     File=self.FullPath)

                ReplaceValue = ReplaceValue.replace(MatchedItem, self.PcdDefaultValueDict[MatchedItem])

        return ReplaceValue

    ##
    # Check pcd's default value according to the pcd's description
    #
    def CheckPcdValue(self):
        for Pcd in self.GetPcdList():
            self.PcdDefaultValueDict[TAB_SPLIT.join((Pcd.GetTokenSpaceGuidCName(), Pcd.GetCName())).strip()] = \
            Pcd.GetDefaultValue()

        for Pcd in self.GetPcdList():
            ValidationExpressions = []
            PcdGuidName = TAB_SPLIT.join((Pcd.GetTokenSpaceGuidCName(), Pcd.GetCName()))
            Valids = Pcd.GetPcdErrorsList()
            for Valid in Valids:
                Expression = Valid.GetExpression()
                if Expression:
                    #
                    # Delete the 'L' prefix of a quoted string, this operation is for eval()
                    #
                    QUOTED_PATTERN = '[\t\s]*L?"[^"]*"'
                    QuotedMatchedObj = re.search(QUOTED_PATTERN, Expression)
                    if QuotedMatchedObj:
                        MatchedStr = QuotedMatchedObj.group().strip()
                        if MatchedStr.startswith('L'):
                            Expression = Expression.replace(MatchedStr, MatchedStr[1:].strip())

                    Expression = self.ReplaceForEval(Expression, IsExpr=True)
                    Expression = Expression.replace(PcdGuidName, 'x')
                    Message = self.GetEnErrorMessage(Valid.GetErrorMessageList())
                    ValidationExpressions.append((Expression, Message))

                ValidList = Valid.GetValidValue()
                if ValidList:
                    ValidValue = 'x in %s' % [eval(v) for v in ValidList.split(' ') if v]
                    Message = self.GetEnErrorMessage(Valid.GetErrorMessageList())
                    ValidationExpressions.append((ValidValue, Message))

                ValidValueRange = Valid.GetValidValueRange()
                if ValidValueRange:
                    ValidValueRange = self.ReplaceForEval(ValidValueRange, IsRange=True)
                    if ValidValueRange.find('-') >= 0:
                        ValidValueRange = ValidValueRange.replace('-', '<= x <=')
                    elif not ValidValueRange.startswith('x ') and not ValidValueRange.startswith('not ') \
                        and not ValidValueRange.startswith('not(') and not ValidValueRange.startswith('('):
                        ValidValueRange = 'x %s' % ValidValueRange
                    Message = self.GetEnErrorMessage(Valid.GetErrorMessageList())
                    ValidationExpressions.append((ValidValueRange, Message))

            DefaultValue = self.PcdDefaultValueDict[PcdGuidName.strip()]
            #
            # Delete the 'L' prefix of a quoted string, this operation is for eval()
            #
            QUOTED_PATTERN = '[\t\s]*L?"[^"]*"'
            QuotedMatchedObj = re.search(QUOTED_PATTERN, DefaultValue)
            if QuotedMatchedObj:
                MatchedStr = QuotedMatchedObj.group().strip()
                if MatchedStr.startswith('L'):
                    DefaultValue = DefaultValue.replace(MatchedStr, MatchedStr[1:].strip())

            try:
                DefaultValue = eval(DefaultValue.replace('TRUE', 'True').replace('true', 'True')
                                        .replace('FALSE', 'False').replace('false', 'False'))
            except BaseException:
                pass

            for (Expression, Msg) in ValidationExpressions:
                try:
                    if not eval(Expression, {'x':DefaultValue}):
                        Logger.Error("Dec File Parser", FORMAT_INVALID, ExtraData='%s, value = %s' %\
                                     (PcdGuidName, DefaultValue), Message=Msg, File=self.FullPath)
                except TypeError:
                    Logger.Error("Dec File Parser", FORMAT_INVALID, ExtraData=PcdGuidName, \
                                    Message=Msg, File=self.FullPath)

    ## GenModuleFileList
    #
    def GenModuleFileList(self, ContainerFile):
        ModuleFileList = []
        ContainerFileName = os.path.basename(ContainerFile)
        ContainerFilePath = os.path.dirname(ContainerFile)
        for Item in GetFiles(ContainerFilePath,
                        ['CVS', '.svn'] + self.GetIncludePathList(), False):
            ExtName = os.path.splitext(Item)[1]
            if ExtName.lower() == '.inf':
                ModuleFileList.append(Item)
            elif ExtName.upper() == '.DEC' and self.CheckMulDec:
                if Item == ContainerFileName:
                    continue
                Logger.Error('MkPkg',
                             UPT_MUL_DEC_ERROR,
                             ST.ERR_MUL_DEC_ERROR%(ContainerFilePath,
                                                   ContainerFileName,
                                                   Item))

        self.SetModuleFileList(ModuleFileList)

    ## Show detailed information of Package
    #
    # Print all members and their values of Package class
    #
    def ShowPackage(self):
        print('\nName =', self.GetName())
        print('\nBaseName =', self.GetBaseName())
        print('\nVersion =', self.GetVersion())
        print('\nGuid =', self.GetGuid())

        print('\nStandardIncludes = %d ' \
            % len(self.GetStandardIncludeFileList()), end=' ')
        for Item in self.GetStandardIncludeFileList():
            print(Item.GetFilePath(), '  ', Item.GetSupArchList())
        print('\nPackageIncludes = %d \n' \
            % len(self.GetPackageIncludeFileList()), end=' ')
        for Item in self.GetPackageIncludeFileList():
            print(Item.GetFilePath(), '  ', Item.GetSupArchList())

        print('\nGuids =', self.GetGuidList())
        for Item in self.GetGuidList():
            print(Item.GetCName(), Item.GetGuid(), Item.GetSupArchList())
        print('\nProtocols =', self.GetProtocolList())
        for Item in self.GetProtocolList():
            print(Item.GetCName(), Item.GetGuid(), Item.GetSupArchList())
        print('\nPpis =', self.GetPpiList())
        for Item in self.GetPpiList():
            print(Item.GetCName(), Item.GetGuid(), Item.GetSupArchList())
        print('\nLibraryClasses =', self.GetLibraryClassList())
        for Item in self.GetLibraryClassList():
            print(Item.GetLibraryClass(), Item.GetRecommendedInstance(), \
            Item.GetSupArchList())
        print('\nPcds =', self.GetPcdList())
        for Item in self.GetPcdList():
            print('CName=', Item.GetCName(), 'TokenSpaceGuidCName=', \
                Item.GetTokenSpaceGuidCName(), \
                'DefaultValue=', Item.GetDefaultValue(), \
                'ValidUsage=', Item.GetValidUsage(), \
                'SupArchList', Item.GetSupArchList(), \
                'Token=', Item.GetToken(), 'DatumType=', Item.GetDatumType())

        for Item in self.GetMiscFileList():
            print(Item.GetName())
            for FileObjectItem in Item.GetFileList():
                print(FileObjectItem.GetURI())
        print('****************\n')

## GenPcdDeclaration
#
# @param ContainerFile:   File name of the DEC file
# @param PcdInfo:         Pcd information, of format (TokenGuidCName,
#                         TokenName, Value, DatumType, Token, Type,
#                         GenericComment, TailComment, Arch)
# @param Language: The language of HelpText, Prompt
#
def GenPcdDeclaration(ContainerFile, PcdInfo, Language, MacroReplaceDict):
    HelpStr = ''
    PromptStr = ''
    TailHelpStr = ''
    TokenGuidCName, TokenName, Value, DatumType, Token, Type, \
        GenericComment, TailComment, Arch = PcdInfo
    Pcd = PcdObject()
    Pcd.SetCName(TokenName)
    Pcd.SetToken(Token)
    Pcd.SetTokenSpaceGuidCName(TokenGuidCName)
    Pcd.SetDatumType(DatumType)
    Pcd.SetDefaultValue(Value)
    Pcd.SetValidUsage(Type)
    #
    #  MaxDatumSize is required field for 'VOID*' PCD
    #
    if DatumType == TAB_PTR_TYPE_PCD:
        Pcd.SetMaxDatumSize(ITEM_UNDEFINED)

    SupArchList = [Arch]
    Pcd.SetSupArchList(SupArchList)

    if GenericComment:
        HelpStr, PcdErrList, PromptStr = ParseDecPcdGenericComment(GenericComment,
                                                                   ContainerFile,
                                                                   TokenGuidCName,
                                                                   TokenName,
                                                                   MacroReplaceDict)
        if PcdErrList:
            Pcd.SetPcdErrorsList(PcdErrList)

    if TailComment:
        SupModuleList, TailHelpStr = ParseDecPcdTailComment(TailComment,
                                                        ContainerFile)
        if SupModuleList:
            Pcd.SetSupModuleList(SupModuleList)

    if HelpStr and (not HelpStr.endswith('\n')) and TailHelpStr:
        HelpStr += '\n'
    HelpStr += TailHelpStr
    if HelpStr:
        HelpTxtObj = TextObject()
        HelpTxtObj.SetLang(Language)
        HelpTxtObj.SetString(HelpStr)
        Pcd.SetHelpTextList([HelpTxtObj])
    if PromptStr:
        TxtObj = TextObject()
        TxtObj.SetLang(Language)
        TxtObj.SetString(PromptStr)
        Pcd.SetPromptList([TxtObj])

    return Pcd
