/** @file
   PlatformPcdPreprocessAction class.

   The abstract parent class PlatformPcdPreprocessAction, This class is to collect platform's
   pcd build information from fpd file.
   This class will be extended by building tools and wizard tools.

Copyright (c) 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/
package org.tianocore.pcd.action;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.tianocore.DynamicPcdBuildDefinitionsDocument.DynamicPcdBuildDefinitions;
import org.tianocore.PcdBuildDefinitionDocument.PcdBuildDefinition;
import org.tianocore.pcd.entity.*;
import org.tianocore.pcd.entity.Token;
import org.tianocore.pcd.entity.MemoryDatabaseManager;
import org.tianocore.pcd.exception.PlatformPcdPreprocessException;

/**
   The abstract parent class PlatformPcdPreprocessAction, This class is to collect platform's
   pcd build information from fpd file.
   This class will be extended by building tools and wizard tools.

**/
public abstract class PlatformPcdPreprocessAction {
    ///
    /// PCD memory database
    ///
    private MemoryDatabaseManager pcdDbManager;

    ///
    /// Errors string when perform preprocess 
    /// 
    private String                errorString;

    ///
    /// the count of errors when perform preprocess
    /// 
    private int                   errorCount;

    /**
       Default contructor function  
    **/
    public PlatformPcdPreprocessAction() {
        pcdDbManager = null;
        errorString  = null;
	errorCount   = 0;
    }

    /**
       Set parameter pcdDbManager

       @param pcdDbManager
    **/
    public void setPcdDbManager(MemoryDatabaseManager pcdDbManager) {
        this.pcdDbManager = pcdDbManager;
    }

    /**
       Get parameter pcdDbManager

       @return MemoryDatabaseManager
    **/
    public MemoryDatabaseManager getPcdDbManager() {
        return pcdDbManager;
    }

    /**
       Abstract function: retrieve module information from FPD file.

       In building environement, this function will be implementated by FpdParserTask.

       @return List<ModulePcdInfoFromFpd>        the component array.
       @throws PlatformPcdPreprocessException    get all modules in <ModuleSA> in FPD file.

    **/
    public abstract List<ModulePcdInfoFromFpd> getComponentsFromFpd()
                                               throws PlatformPcdPreprocessException;

    /**
       Abstract function to get GUID string from SPD file.

       In building evnironment, this function will be implementated by GlobaData.

       @param guidCName the CName of GUID

       @return String        Guid information from SPD file.
       @throws PlatformPcdPreprocessException
                            Fail to get Guid information from SPD file.
    **/
    public abstract String getGuidInfoFromSpd(String guidCName) throws PlatformPcdPreprocessException;

    /**
       Abstract function: Verification the PCD data.

       In different environment, such as building environment and wizard environment,
       it has different implementation according to optimization.

       @param cName         The token name
       @param moduleName    The module who use this PCD token
       @param datum         The PCD's datum
       @param datumType     The PCD's datum type
       @param maxDatumSize  The max size for PCD's Datum.

       @return String       exception strings.

    **/
    public abstract String verifyDatum(String cName, String moduleName, String datum,
                                       Token.DATUM_TYPE  datumType, int maxDatumSize);

    /**
       Abstract function: Get dynamic information for a token

       @param token
       @param moduleName

       @return DynamicPcdBuildDefinitions.PcdBuildData
    **/
    public abstract DynamicPcdBuildDefinitions.PcdBuildData
                                            getDynamicInfoFromFpd(Token     token,
                                                                  String    moduleName)
                                            throws PlatformPcdPreprocessException;

    /**
       Abstract function: Get all dynamic PCD information from FPD file.

       @return List<DynamicPcdBuildDefinitions.PcdBuildData>    All DYNAMIC PCD list in <DynamicPcdBuildDefinitions> in FPD file.
       @throws PlatformPcdPreprocessBuildException              Failure to get dynamic information list.

    **/
    public abstract List<DynamicPcdBuildDefinitions.PcdBuildData>
                                            getAllDynamicPcdInfoFromFpd()
                                            throws PlatformPcdPreprocessException;

    /**
       Return the error string after preprocess 

       @return String error string
    **/
    public String getErrorString() {
        return errorString;
    }

    public void putError(String error) {
        if (errorString == null) {
            errorString = "### ERROR[" + errorCount + "] ###\r\n" + error + "\r\n";
        } else {
            errorString += "### ERROR[" + errorCount + "] ###\r\n" + error + "\r\n";
        }

        errorCount++;
    }

    /**
      Collect all PCD information from FPD file into PCD memory database.

    **/
    public void initPcdMemoryDbWithPlatformInfo()
        throws PlatformPcdPreprocessException {
        int                                 index;
        int                                 pcdIndex;
        List<PcdBuildDefinition.PcdData>    pcdBuildDataArray = new ArrayList<PcdBuildDefinition.PcdData>();
        PcdBuildDefinition.PcdData          pcdBuildData;
        Token                               token             = null;
        List<ModulePcdInfoFromFpd>          modules;
        String                              primaryKey;
        String                              exceptionString;
        UsageInstance                       usageInstance;
        Token.PCD_TYPE                      pcdType           = Token.PCD_TYPE.UNKNOWN;
        Token.DATUM_TYPE                    datumType         = Token.DATUM_TYPE.UNKNOWN;
        long                                tokenNumber;
        String                              moduleName;
        String                              datum;
        int                                 maxDatumSize;
        String                              tokenSpaceStrRet;
        ModulePcdInfoFromFpd                curModule;

        //
        // ----------------------------------------------
        // 1), Get all <ModuleSA> from FPD file.
        // ----------------------------------------------
        //
        modules = getComponentsFromFpd();

        if (modules == null) {
            throw new PlatformPcdPreprocessException(
                "No modules found in the FPD file.\nPlease check whether there are elements in <FrameworkModules> in the FPD file!");
        }

        //
        // -------------------------------------------------------------------
        // 2), Loop all modules to process <PcdBuildDeclarations> for each module.
        // -------------------------------------------------------------------
        //
        for (index = 0; index < modules.size(); index++) {
            curModule = modules.get(index);

            //
    	    // It is legal for a module does not contains ANY pcd build definitions.
    	    //
    	    if (curModule.pcdBuildDefinition == null) {
                continue;
    	    }

            pcdBuildDataArray = curModule.pcdBuildDefinition.getPcdDataList();
            moduleName        = curModule.usageId.moduleName;

            //
            // ----------------------------------------------------------------------
            // 2.1), Loop all Pcd entry for a module and add it into memory database.
            // ----------------------------------------------------------------------
            //
            for (pcdIndex = 0; pcdIndex < pcdBuildDataArray.size(); pcdIndex++) {
                pcdBuildData = pcdBuildDataArray.get(pcdIndex);

                tokenSpaceStrRet = getGuidInfoFromSpd(pcdBuildData.getTokenSpaceGuidCName());

                if (tokenSpaceStrRet == null) {
                    putError("Failed to get Token Space Guid for token " + pcdBuildData.getCName() +
                             " from any SPD file. You must have an <GuidDeclaration> for this Token Space Guid!");
                    //
                    // Do not break preprocess, continues to analysis.
                    // All errors will be summary to be shown.
                    // 
                    continue;
                }

                primaryKey   = Token.getPrimaryKeyString(pcdBuildData.getCName(), tokenSpaceStrRet);
                pcdType      = Token.getPcdTypeFromString(pcdBuildData.getItemType().toString());
                datumType    = Token.getdatumTypeFromString(pcdBuildData.getDatumType().toString());
                tokenNumber  = Long.decode(pcdBuildData.getToken().toString());
                if (pcdBuildData.getValue() != null) {
                    datum = pcdBuildData.getValue().toString();
                } else {
                    datum = null;
                }
                maxDatumSize = pcdBuildData.getMaxDatumSize();

                if ((pcdType    == Token.PCD_TYPE.FEATURE_FLAG) &&
                    (datumType  != Token.DATUM_TYPE.BOOLEAN)){
                    exceptionString = String.format("In FPD file, for PCD %s in module %s, the PCD type is FEATURE_FLAG but "+
                                                    "datum type for this PCD entry is not BOOLEAN!",
                                                    pcdBuildData.getCName(),
                                                    moduleName);
                    putError(exceptionString);
                    //
                    // Do not break preprocess, continues to analysis.
                    // All errors will be summary to be shown.
                    // 
                    continue;
                }

                //
                // -------------------------------------------------------------------------------------------
                // 2.1.1), Do some necessary checking work for FixedAtBuild, FeatureFlag and PatchableInModule
                // -------------------------------------------------------------------------------------------
                //
                if (!Token.isDynamic(pcdType)) {
                     //
                     // Value is required.
                     //
                     if (datum == null) {
                         exceptionString = String.format("In the FPD file, there is no value for PCD entry %s in module %s!",
                                                         pcdBuildData.getCName(),
                                                         moduleName);
                         putError(exceptionString);
                         //
                         // Do not break preprocess, continues to analysis.
                         // All errors will be summary to be shown.
                         // 
                         continue;
                     }

                     //
                     // Check whether the datum size is matched datum type.
                     //
                     if ((exceptionString = verifyDatum(pcdBuildData.getCName(),
                                                        moduleName,
                                                        datum,
                                                        datumType,
                                                        maxDatumSize)) != null) {
                         putError(exceptionString);
                         //
                         // Do not break preprocess, continues to analysis.
                         // All errors will be summary to be shown.
                         // 
                         continue;
                     }
                }

                //
                // ---------------------------------------------------------------------------------
                // 2.1.2), Create token or update token information for current anaylized PCD data.
                // ---------------------------------------------------------------------------------
                //
                if (pcdDbManager.isTokenInDatabase(primaryKey)) {
                    //
                    // If the token is already exist in database, do some necessary checking
                    // and add a usage instance into this token in database
                    //
                    token = pcdDbManager.getTokenByKey(primaryKey);

                    //
                    // checking for DatumType, DatumType should be unique for one PCD used in different
                    // modules.
                    //
                    if (token.datumType != datumType) {
                        exceptionString = String.format("In the FPD file, the datum type of the PCD entry %s is %s, which is different from %s which was previously defined!",
                                                        pcdBuildData.getCName(),
                                                        pcdBuildData.getDatumType().toString(),
                                                        Token.getStringOfdatumType(token.datumType));
                        putError(exceptionString);
                        //
                        // Do not break preprocess, continues to analysis.
                        // All errors will be summary to be shown.
                        // 
                        continue;
                    }

                    //
                    // Check token number is valid
                    //
                    if (tokenNumber != token.tokenNumber) {
                        exceptionString = String.format("In the FPD file, the token number of PCD entry %s in module %s is different from the same PCD entry in other modules!",
                                                        pcdBuildData.getCName(),
                                                        moduleName);
                        putError(exceptionString);
                        //
                        // Do not break preprocess, continues to analysis.
                        // All errors will be summary to be shown.
                        // 
                        continue;
                    }

                    //
                    // For same PCD used in different modules, the PCD type should all be dynamic or non-dynamic.
                    //
                    if (token.isDynamicPCD != Token.isDynamic(pcdType)) {
                        exceptionString = String.format("In the FPD file, for PCD entry %s in module %s, you defined dynamic or non-dynamic PCD type which"+
                                                        " is different from other module's definition.",
                                                        token.cName,
                                                        moduleName);
                        putError(exceptionString);
                        //
                        // Do not break preprocess, continues to analysis.
                        // All errors will be summary to be shown.
                        // 
                        continue;
                    }

                    if (token.isDynamicPCD) {
                        if ((maxDatumSize != 0) &&
                            (maxDatumSize != token.datumSize)){
                            exceptionString = String.format("In the FPD file, for dynamic PCD %s in module %s, the max datum size is %d which "+
                                                            "is different than <MaxDatumSize> %d defined in <DynamicPcdBuildDefinitions>!",
                                                            token.cName,
                                                            moduleName,
                                                            maxDatumSize,
                                                            token.datumSize);
                            putError(exceptionString);
                            //
                            // Do not break preprocess, continues to analysis.
                            // All errors will be summary to be shown.
                            // 
                            continue;
                        }
                    }

                } else {
                    //
                    // If the token is not in database, create a new token instance and add
                    // a usage instance into this token in database.
                    //
                    tokenSpaceStrRet = getGuidInfoFromSpd(pcdBuildData.getTokenSpaceGuidCName());

                    if (tokenSpaceStrRet == null) {
                        putError("Failed to get the Token Space Guid for token" + token.cName +
                                 " from any SPD file. You must have a <GuidDeclaration> for this Token Space Guid!");
                        //
                        // Do not break preprocess, continues to analysis.
                        // All errors will be summary to be shown.
                        // 
                        continue;
                    }

                    token = new Token(pcdBuildData.getCName(), tokenSpaceStrRet);

                    token.datumType     = datumType;
                    token.tokenNumber   = tokenNumber;
                    token.isDynamicPCD  = Token.isDynamic(pcdType);
                    token.datumSize     = maxDatumSize;

                    if (token.isDynamicPCD) {
                        //
                        // For Dynamic and Dynamic Ex type, need find the dynamic information
                        // in <DynamicPcdBuildDefinition> section in FPD file.
                        //
                        if (null == updateDynamicInformation(moduleName,
                                                             token,
                                                             datum,
                                                             maxDatumSize)) {
                            continue;
                        }
                    }

                    pcdDbManager.addTokenToDatabase(primaryKey, token);
                }

                //
                // -----------------------------------------------------------------------------------
                // 2.1.3), Add the PcdType in current module into this Pcd token's supported PCD type.
                // -----------------------------------------------------------------------------------
                //
                token.updateSupportPcdType(pcdType);

                //
                // ------------------------------------------------
                // 2.1.4), Create an usage instance for this token.
                // ------------------------------------------------
                //
                usageInstance = new UsageInstance(token,
                                                  curModule.usageId,
                                                  pcdType,
                                                  datum,
                                                  maxDatumSize);
                if (!token.addUsageInstance(usageInstance)) {
                    putError(String.format("PCD %s for module %s(%s) already exists in the database.\nPlease check all PCD build entries "+
                                           "in the %s module's <ModuleSA> section to make sure there are no duplicated definitions in the FPD file!",
                                           token.cName,
                                           curModule.usageId.moduleGuid,
                                           moduleName,
                                           moduleName));
                    continue;
                }
            }
        }

        //
        // ------------------------------------------------
        // 3), Add unreference dynamic_Ex pcd token into Pcd database.
        // ------------------------------------------------
        //
        List<Token> tokenArray = getUnreferencedDynamicPcd();
        if (tokenArray != null) {
            for (index = 0; index < tokenArray.size(); index++) {
                pcdDbManager.addTokenToDatabase(tokenArray.get(index).getPrimaryKeyString(),
                                             tokenArray.get(index));
            }
        }
    }

    /**
       Update dynamic information for PCD entry.

       Dynamic information is retrieved from <PcdDynamicBuildDeclarations> in
       FPD file.

       @param moduleName        The name of the module who use this PCD
       @param token             The token instance
       @param datum             The <datum> in module's PCD information
       @param maxDatumSize      The <maxDatumSize> in module's PCD information

       @return Token
     */
    private Token updateDynamicInformation(String   moduleName,
                                           Token    token,
                                           String   datum,
                                           int      maxDatumSize)
        throws PlatformPcdPreprocessException {
        int                 index           = 0;
        int                 offset;
        String              exceptionString = null;
        SkuInstance         skuInstance     = null;
        String              temp;
        boolean             hasSkuId0       = false;
        long                tokenNumber     = 0;
        String              hiiDefaultValue = null;
        String              variableGuidString = null;

        List<DynamicPcdBuildDefinitions.PcdBuildData.SkuInfo>   skuInfoList = null;
        DynamicPcdBuildDefinitions.PcdBuildData                 dynamicInfo = null;

        dynamicInfo = getDynamicInfoFromFpd(token, moduleName);
        if (dynamicInfo == null) {
            exceptionString = String.format("In the FPD file, the Dynamic PCD %s is used by module %s.\n" +
                                            "However, there is no dynamic information in the <DynamicPcdBuildDefinitions> " +
                                            "section of the FPD file.  This section is required!",
                                            token.cName,
                                            moduleName);
            putError(exceptionString);
            return null;
        }

        token.datumSize = dynamicInfo.getMaxDatumSize();

        exceptionString = verifyDatum(token.cName,
                                      moduleName,
                                      null,
                                      token.datumType,
                                      token.datumSize);
        if (exceptionString != null) {
            throw new PlatformPcdPreprocessException(exceptionString);
        }

        if ((maxDatumSize != 0) &&
            (maxDatumSize != token.datumSize)) {
            exceptionString = String.format("In the FPD file, for dynamic PCD %s, the datum size in module %s is %d, but "+
                                            "the datum size in <DynamicPcdBuildDefinitions> is %d, they do not match!",
                                            token.cName,
                                            moduleName,
                                            maxDatumSize,
                                            dynamicInfo.getMaxDatumSize());
            putError(exceptionString);
            return null;
        }
        tokenNumber = Long.decode(dynamicInfo.getToken().toString());
        if (tokenNumber != token.tokenNumber) {
            exceptionString = String.format("In the FPD file, for dynamic PCD %s, the token number in module %s is 0x%x, but "+
                                            "in the <DynamicPcdBuildDefinictions> section, the token number is 0x%x, they do not match!",
                                            token.cName,
                                            moduleName,
                                            token.tokenNumber,
                                            tokenNumber);
            putError(exceptionString);
            return null;
        }

        token.dynamicExTokenNumber = tokenNumber;

        skuInfoList = dynamicInfo.getSkuInfoList();

        //
        // Loop all sku data
        //
        for (index = 0; index < skuInfoList.size(); index++) {
            skuInstance = new SkuInstance();
            //
            // Although SkuId in schema is BigInteger, but in fact, sku id is 32 bit value.
            //
            temp = skuInfoList.get(index).getSkuId().toString();
            skuInstance.id = Integer.decode(temp);
            if (skuInstance.id == 0) {
                hasSkuId0 = true;
            }
            //
            // Judge whether is DefaultGroup at first, because most case is DefautlGroup.
            //
            if (skuInfoList.get(index).getValue() != null) {
                skuInstance.value.setValue(skuInfoList.get(index).getValue().toString());
                if ((exceptionString = verifyDatum(token.cName,
                                                   null,
                                                   skuInfoList.get(index).getValue().toString(),
                                                   token.datumType,
                                                   token.datumSize)) != null) {
                    putError(exceptionString);
                    return null;
                }

                token.skuData.add(skuInstance);

                continue;
            }

            //
            // Judge whether is HII group case.
            //
            if (skuInfoList.get(index).getVariableName() != null) {
                exceptionString = null;
                if (skuInfoList.get(index).getVariableGuid() == null) {
                    exceptionString = String.format("In the FPD file, for dynamic PCD %s in <DynamicPcdBuildDefinitions> section in FPD "+
                                                    "file, use of HII was defined, but there is no <VariableGuid> defined for SKU %d data!",
                                                    token.cName,
                                                    index);
                    putError(exceptionString);
                    return null;
                }

                if (skuInfoList.get(index).getVariableOffset() == null) {
                    exceptionString = String.format("In the FPD file, for dynamic PCD %s in <DynamicPcdBuildDefinitions> section in FPD "+
                                                    "file, use of HII was defined, but there is no <VariableOffset> defined for SKU %d data!",
                                                    token.cName,
                                                    index);
                    putError(exceptionString);
                    return null;
                }

                if (skuInfoList.get(index).getHiiDefaultValue() == null) {
                    exceptionString = String.format("In the FPD file, for dynamic PCD %s in <DynamicPcdBuildDefinitions> section in FPD "+
                                                    "file, use of HII was defined, but there is no <HiiDefaultValue> defined for SKU %d data!",
                                                    token.cName,
                                                    index);
                    putError(exceptionString);
                    return null;
                }

                if (skuInfoList.get(index).getHiiDefaultValue() != null) {
                    hiiDefaultValue = skuInfoList.get(index).getHiiDefaultValue().toString();
                } else {
                    hiiDefaultValue = null;
                }

                if ((exceptionString = verifyDatum(token.cName,
                                                   null,
                                                   hiiDefaultValue,
                                                   token.datumType,
                                                   token.datumSize)) != null) {
                    throw new PlatformPcdPreprocessException(exceptionString);
                }

                offset = Integer.decode(skuInfoList.get(index).getVariableOffset());
                if (offset > 0xFFFF) {
                    putError(String.format("In the FPD file, for dynamic PCD %s, the variable offset defined in SKU %d data "+
                                           "exceeds 64K, which is not allowed!",
                                           token.cName,
                                           index));
                    return null;
                }

                //
                // Get variable guid string according to the name of guid which will be mapped into a GUID in SPD file.
                //
                variableGuidString = getGuidInfoFromSpd(skuInfoList.get(index).getVariableGuid().toString());
                if (variableGuidString == null) {
                    putError(String.format("In the FPD file, for dynamic PCD %s, the variable guid: %s cannot be found in any SPD file!",
                                           token.cName,
                                           skuInfoList.get(index).getVariableGuid().toString()));
                    return null;
                }
                String variableStr = skuInfoList.get(index).getVariableName();
                Pattern pattern = Pattern.compile("0x([a-fA-F0-9]){4}");
                Matcher matcher = pattern.matcher(variableStr);
                List<String> varNameList = new ArrayList<String>();
                while (matcher.find()){
                    String str = variableStr.substring(matcher.start(),matcher.end());
                    varNameList.add(str);
                }

                skuInstance.value.setHiiData(varNameList,
                                             translateSchemaStringToUUID(variableGuidString),
                                             skuInfoList.get(index).getVariableOffset(),
                                             skuInfoList.get(index).getHiiDefaultValue().toString());
                token.skuData.add(skuInstance);
                continue;
            }

            if (skuInfoList.get(index).getVpdOffset() != null) {
                skuInstance.value.setVpdData(skuInfoList.get(index).getVpdOffset());
                token.skuData.add(skuInstance);
                continue;
            }

            exceptionString = String.format("In the FPD file, for dynamic PCD %s, the dynamic info must "+
                                            "be one of: 'DefaultGroup', 'HIIGroup', 'VpdGroup'.",
                                            token.cName);
            putError(exceptionString);
            return null;
        }

        if (!hasSkuId0) {
            exceptionString = String.format("In the FPD file, for dynamic PCD %s in <DynamicPcdBuildDefinitions>, there is "+
                                            "no SKU ID = 0 data, which is required for every dynamic PCD",
                                            token.cName);
            putError(exceptionString);
            return null;
        }

        return token;
    }

    /**
       Get all dynamic PCD defined in <DynamicPcdBuildDefinitions> which unreferenced by
       any <ModuleSA> in FPD file.

       @return List<Token>  Return PCD token
    **/
    private List<Token> getUnreferencedDynamicPcd () throws PlatformPcdPreprocessException {
        List<Token>                                   tokenArray                 = new ArrayList<Token>();
        Token                                         token                      = null;
        List<DynamicPcdBuildDefinitions.PcdBuildData> dynamicPcdBuildDataArray   = null;
        DynamicPcdBuildDefinitions.PcdBuildData       pcdBuildData               = null;
        List<DynamicPcdBuildDefinitions.PcdBuildData.SkuInfo>   skuInfoList      = null;
        Token.PCD_TYPE                                pcdType;
        SkuInstance                                   skuInstance                = null;
        String  primaryKey = null;
        boolean hasSkuId0  = false;
        int     index, offset, index2;
        String  temp;
        String  exceptionString;
        String  hiiDefaultValue;
        String  tokenSpaceStrRet;
        String  variableGuidString;

        dynamicPcdBuildDataArray = getAllDynamicPcdInfoFromFpd();
        if (dynamicPcdBuildDataArray == null) {
            return null;
        }

        for (index2 = 0; index2 < dynamicPcdBuildDataArray.size(); index2++) {
            pcdBuildData = dynamicPcdBuildDataArray.get(index2);
            tokenSpaceStrRet = getGuidInfoFromSpd(pcdBuildData.getTokenSpaceGuidCName());

            if (tokenSpaceStrRet == null) {
                putError("Failed to get the Token Space Guid for token" + pcdBuildData.getCName());
                continue;
            }

            primaryKey = Token.getPrimaryKeyString(pcdBuildData.getCName(),
                                                   tokenSpaceStrRet);

            if (pcdDbManager.isTokenInDatabase(primaryKey)) {
                continue;
            }

            pcdType = Token.getPcdTypeFromString(pcdBuildData.getItemType().toString());
            if (pcdType != Token.PCD_TYPE.DYNAMIC_EX) {
                putError(String.format("In the FPD file, it not allowed to define DYNAMIC PCD %s that is not used by any module",
                                       pcdBuildData.getCName()));
                continue;
            }

            //
            // Create new token for unreference dynamic PCD token
            //
            token           = new Token(pcdBuildData.getCName(), tokenSpaceStrRet);
            token.datumSize = pcdBuildData.getMaxDatumSize();


            token.datumType     = Token.getdatumTypeFromString(pcdBuildData.getDatumType().toString());
            token.tokenNumber   = Long.decode(pcdBuildData.getToken().toString());
            token.dynamicExTokenNumber = token.tokenNumber;
            token.isDynamicPCD  = true;
            token.updateSupportPcdType(pcdType);

            exceptionString = verifyDatum(token.cName,
                                          null,
                                          null,
                                          token.datumType,
                                          token.datumSize);
            if (exceptionString != null) {
                putError(exceptionString);
                continue;
            }

            skuInfoList = pcdBuildData.getSkuInfoList();

            //
            // Loop all sku data
            //
            for (index = 0; index < skuInfoList.size(); index++) {
                skuInstance = new SkuInstance();
                //
                // Although SkuId in schema is BigInteger, but in fact, sku id is 32 bit value.
                //
                temp = skuInfoList.get(index).getSkuId().toString();
                skuInstance.id = Integer.decode(temp);
                if (skuInstance.id == 0) {
                    hasSkuId0 = true;
                }
                //
                // Judge whether is DefaultGroup at first, because most case is DefautlGroup.
                //
                if (skuInfoList.get(index).getValue() != null) {
                    skuInstance.value.setValue(skuInfoList.get(index).getValue().toString());
                    if ((exceptionString = verifyDatum(token.cName,
                                                       null,
                                                       skuInfoList.get(index).getValue().toString(),
                                                       token.datumType,
                                                       token.datumSize)) != null) {
                        putError(exceptionString);
                        continue;
                    }

                    token.skuData.add(skuInstance);

                    continue;
                }

                //
                // Judge whether is HII group case.
                //
                if (skuInfoList.get(index).getVariableName() != null) {
                    exceptionString = null;
                    if (skuInfoList.get(index).getVariableGuid() == null) {
                        exceptionString = String.format("In the FPD file, for dynamic PCD %s in the <DynamicPcdBuildDefinitions> section of the FPD "+
                                                        "file, use of HII is defined, but there is no <VariableGuid> defined for SKU %d data!",
                                                        token.cName,
                                                        index);
                        putError(exceptionString);
                        continue;
                    }

                    if (skuInfoList.get(index).getVariableOffset() == null) {
                        exceptionString = String.format("In the FPD file, for dynamic PCD %s in the <DynamicPcdBuildDefinitions> section of the FPD "+
                                                        "file, use of HII is defined, but there is no <VariableOffset> defined for SKU %d data!",
                                                        token.cName,
                                                        index);
                        putError(exceptionString);
                        continue;
                    }

                    if (skuInfoList.get(index).getHiiDefaultValue() == null) {
                        exceptionString = String.format("In the FPD file, for dynamic PCD %s in the <DynamicPcdBuildDefinitions> section of the FPD "+
                                                        "file, use of HII is defined, but there is no <HiiDefaultValue> defined for SKU %d data!",
                                                        token.cName,
                                                        index);
                        putError(exceptionString);
                        continue;
                    }

                    if (skuInfoList.get(index).getHiiDefaultValue() != null) {
                        hiiDefaultValue = skuInfoList.get(index).getHiiDefaultValue().toString();
                    } else {
                        hiiDefaultValue = null;
                    }

                    if ((exceptionString = verifyDatum(token.cName,
                                                       null,
                                                       hiiDefaultValue,
                                                       token.datumType,
                                                       token.datumSize)) != null) {
                        putError(exceptionString);
                        continue;
                    }

                    offset = Integer.decode(skuInfoList.get(index).getVariableOffset());
                    if (offset > 0xFFFF) {
                        exceptionString = String.format("In the FPD file, for dynamic PCD %s, the variable offset defined in SKU %d data "+
                                          "exceeds 64K, which is not allowed!",
                                          token.cName,
                                          index);
                        putError(exceptionString);
                        continue;
                    }

                    //
                    // Get variable guid string according to the name of guid which will be mapped into a GUID in SPD file.
                    //
                    variableGuidString = getGuidInfoFromSpd(skuInfoList.get(index).getVariableGuid().toString());
                    if (variableGuidString == null) {
                        exceptionString = String.format("In the FPD file, for dynamic PCD %s, the variable guid %s cannot be found in any SPD file!",
                                                        token.cName,
                                                        skuInfoList.get(index).getVariableGuid().toString());
                        putError(exceptionString);
                        continue;
                    }
                    String variableStr = skuInfoList.get(index).getVariableName();
                    Pattern pattern = Pattern.compile("0x([a-fA-F0-9]){4}");
                    Matcher matcher = pattern.matcher(variableStr);
                    List<String> varNameList = new ArrayList<String>();
                    while (matcher.find()){
                        String str = variableStr.substring(matcher.start(),matcher.end());
                        varNameList.add(str);
                    }

                    skuInstance.value.setHiiData(varNameList,
                                                 translateSchemaStringToUUID(variableGuidString),
                                                 skuInfoList.get(index).getVariableOffset(),
                                                 skuInfoList.get(index).getHiiDefaultValue().toString());
                    token.skuData.add(skuInstance);
                    continue;
                }

                if (skuInfoList.get(index).getVpdOffset() != null) {
                    skuInstance.value.setVpdData(skuInfoList.get(index).getVpdOffset());
                    token.skuData.add(skuInstance);
                    continue;
                }

                exceptionString = String.format("In the FPD file, for dynamic PCD %s, the dynamic info must "+
                                                "be one of 'DefaultGroup', 'HIIGroup', 'VpdGroup'.",
                                                token.cName);
                putError(exceptionString);
            }

            if (!hasSkuId0) {
                exceptionString = String.format("In the FPD file, for dynamic PCD %s in <DynamicPcdBuildDefinitions>, there is "+
                                                "no SKU ID = 0 data, which is required for every dynamic PCD",
                                                token.cName);
                putError(exceptionString);
                continue;
            }

            tokenArray.add(token);
        }

        return tokenArray;
    }

    /**
       Translate the schema string to UUID instance.

       In schema, the string of UUID is defined as following two types string:
        1) GuidArrayType: pattern = 0x[a-fA-F0-9]{1,8},( )*0x[a-fA-F0-9]{1,4},(
        )*0x[a-fA-F0-9]{1,4}(,( )*\{)?(,?( )*0x[a-fA-F0-9]{1,2}){8}( )*(\})?

        2) GuidNamingConvention: pattern =
        [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}

       This function will convert string and create uuid instance.

       @param uuidString    UUID string in XML file

       @return UUID         UUID instance
    **/
    private UUID translateSchemaStringToUUID(String uuidString)
        throws PlatformPcdPreprocessException {
        String      temp;
        String[]    splitStringArray;
        int         index;
        int         chIndex;
        int         chLen;

        if (uuidString == null) {
            return null;
        }

        if (uuidString.length() == 0) {
            return null;
        }

        if (uuidString.equals("0") ||
            uuidString.equalsIgnoreCase("0x0")) {
            return new UUID(0, 0);
        }

        uuidString = uuidString.replaceAll("\\{", "");
        uuidString = uuidString.replaceAll("\\}", "");

        //
        // If the UUID schema string is GuidArrayType type then need translate
        // to GuidNamingConvention type at first.
        //
        if ((uuidString.charAt(0) == '0') && ((uuidString.charAt(1) == 'x') || (uuidString.charAt(1) == 'X'))) {
            splitStringArray = uuidString.split("," );
            if (splitStringArray.length != 11) {
                throw new PlatformPcdPreprocessException ("Wrong format for GUID string: " + uuidString);
            }

            //
            // Remove blank space from these string and remove header string "0x"
            //
            for (index = 0; index < 11; index++) {
                splitStringArray[index] = splitStringArray[index].trim();
                splitStringArray[index] = splitStringArray[index].substring(2, splitStringArray[index].length());
            }

            //
            // Add heading '0' to normalize the string length
            //
            for (index = 3; index < 11; index++) {
                chLen = splitStringArray[index].length();
                for (chIndex = 0; chIndex < 2 - chLen; chIndex++) {
                    splitStringArray[index] = "0" + splitStringArray[index];
                }
            }

            //
            // construct the final GuidNamingConvention string
            //
            temp = String.format("%s-%s-%s-%s%s-%s%s%s%s%s%s",
                                 splitStringArray[0],
                                 splitStringArray[1],
                                 splitStringArray[2],
                                 splitStringArray[3],
                                 splitStringArray[4],
                                 splitStringArray[5],
                                 splitStringArray[6],
                                 splitStringArray[7],
                                 splitStringArray[8],
                                 splitStringArray[9],
                                 splitStringArray[10]);
            uuidString = temp;
        }

        return UUID.fromString(uuidString);
    }
}

