/** @file
  PcdDatabase class.

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.build.pcd.action;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.tianocore.pcd.entity.DynamicTokenValue;
import org.tianocore.pcd.entity.Token;
import org.tianocore.pcd.exception.EntityException;

/**
    CStructTypeDeclaration

    This class is used to store the declaration string, such as
    "UINT32 PcdPlatformFlashBaseAddress", of
    each memember in the C structure, which is a standard C language
    feature used to implement a simple and efficient database for
    dynamic(ex) type PCD entry.
**/
class CStructTypeDeclaration {
    String key;
    int alignmentSize;
    String cCode;
    boolean initTable;

    public CStructTypeDeclaration (String key, int alignmentSize, String cCode, boolean initTable) {
        this.key = key;
        this.alignmentSize = alignmentSize;
        this.cCode = cCode;
        this.initTable = initTable;
    }
}

/**
    StringTable

    This class is used to store the String in a PCD database.

**/
class StringTable {
    class UnicodeString {
        //
        // In Schema, we define VariableName in DynamicPcdBuildDefinitions in FPD
        // file to be HexWordArrayType. For example, Unicode String L"Setup" is 
        // <VariableName>0x0053 0x0065 0x0074 0x0075 0x0070</VariableName>. 
        // We use raw to differentiate if the String is in form of L"Setup" (raw is false) or
        // in form of {0x0053, 0x0065, 0x0074, 0x0075, 0x0070}
        //
        // This str is the string that can be pasted directly into the C structure. 
        // For example, this str can be two forms:
        //      
        //      L"Setup",
        //      {0x0053, 0065, 0x0074, 0x0075, 0x0070, 0x0000}, //This is another form of L"Setup"
        //
        public String      str;
        //
        // This len includes the NULL character at the end of the String.
        //
        public int         len;
        
        public UnicodeString (String str, int len) {
            this.str = str;
            this.len = len;
        }
    }
    
    private ArrayList<UnicodeString>   al;
    private ArrayList<String>   alComments;
    private String              phase;
    int                         stringTableCharNum;

    public StringTable (String phase) {
        this.phase = phase;
        al = new ArrayList<UnicodeString>();
        alComments = new ArrayList<String>();
        stringTableCharNum = 0;
    }

    public String getSizeMacro () {
        return String.format(PcdDatabase.StringTableSizeMacro, phase, getSize());
    }

    private int getSize () {
        //
        // We have at least one Unicode Character in the table.
        //
        return stringTableCharNum == 0 ? 1 : stringTableCharNum;
    }

    public String getExistanceMacro () {
        return String.format(PcdDatabase.StringTableExistenceMacro, phase, (al.size() == 0)? "TRUE":"FALSE");
    }

    public void genCode (ArrayList<CStructTypeDeclaration> declaList, HashMap<String, String> instTable) {
        final String stringTable = "StringTable";
        final String tab         = "\t";
        final String newLine     = "\r\n";
        final String commaNewLine = ",\r\n";

        CStructTypeDeclaration decl;

        String cDeclCode = "";
        String cInstCode = "";

        //
        // If we have a empty StringTable
        //
        if (al.size() == 0) {
            cDeclCode += String.format("%-20s%s[1]; /* StringTable is empty */", "UINT16", stringTable) + newLine;
            decl = new CStructTypeDeclaration (
                                                stringTable,
                                                2,
                                                cDeclCode,
                                                true
                                        );
            declaList.add(decl);

            cInstCode = String.format("/* %s */", stringTable) + newLine + tab + "{ 0 }";
            instTable.put(stringTable, cInstCode);
        } else {

            //
            // If there is any String in the StringTable
            //
            for (int i = 0; i < al.size(); i++) {
                UnicodeString uStr = al.get(i);
                String stringTableName;

                if (i == 0) {
                    //
                    // StringTable is a well-known name in the PCD DXE driver
                    //
                    stringTableName = stringTable;

                } else {
                    stringTableName = String.format("%s_%d", stringTable, i);
                    cDeclCode += tab;
                }
                cDeclCode += String.format("%-20s%s[%d]; /* %s */", "UINT16",
                                           stringTableName, uStr.len,
                                           alComments.get(i))
                             + newLine;

                if (i == 0) {
                    cInstCode = "/* StringTable */" + newLine;
                }

                cInstCode += tab + String.format("%s /* %s */", uStr.str, alComments.get(i));
                if (i != al.size() - 1) {
                    cInstCode += commaNewLine;
                }
            }

            decl = new CStructTypeDeclaration (
                    stringTable,
                    2,
                    cDeclCode,
                    true
            );
            declaList.add(decl);

            instTable.put(stringTable, cInstCode);
        }
    }
    
    public int add (List inputStr, Token token) {
        String str;
        
        str = "{";
        
        for (int i = 0; i < inputStr.size(); i++) {
            str += " " + inputStr.get(i) + ",";
        }
        
        str +=  " 0x0000";
            
        str += "}";
        //
        // This is a raw Unicode String
        //
        return addToTable (str, inputStr.size() + 1, token);
    }

    public int add (String inputStr, Token token) {

        int len;
        String str = inputStr;

        //
        // The input can be two types:
        // "L\"Bootmode\"" or "Bootmode".
        // We drop the L\" and \" for the first type.
        if (str.startsWith("L\"") && str.endsWith("\"")) {
            //
            // Substract the character of "L", """, """.
            // and add in the NULL character. So it is 2.
            //
            len = str.length() - 2;
        } else {
            //
            // Include the NULL character.
            //
            len = str.length() + 1;
            str = "L\"" + str + "\"";
        }
        
        //
        // After processing, this is L"A String Sample" type of string.
        //
        return addToTable (str, len, token);
    }
    
    private int addToTable (String inputStr, int len, Token token) {
        int i;
        int pos;

        //
        // Check if StringTable has this String already.
        // If so, return the current pos.
        //
        for (i = 0, pos = 0; i < al.size(); i++) {
            UnicodeString s = al.get(i);;

            if (inputStr.equals(s.str)) {
                return pos;
            }
            pos += s.len;
        }

        i = stringTableCharNum;
        //
        // Include the NULL character at the end of String
        //
        stringTableCharNum += len;
        al.add(new UnicodeString(inputStr, len));
        alComments.add(token.getPrimaryKeyString());

        return i;
    }
}

/**
    SizeTable

    This class is used to store the Size information for
    POINTER TYPE PCD entry in a PCD database.

**/
class SizeTable {
    private ArrayList<ArrayList<Integer>>  al;
    private ArrayList<String>   alComments;
    private int                 len;
    private String              phase;

    public SizeTable (String phase) {
        al = new ArrayList<ArrayList<Integer>>();
        alComments = new ArrayList<String>();
        len = 0;
        this.phase = phase;
    }

    public String getSizeMacro () {
        return String.format(PcdDatabase.SizeTableSizeMacro, phase, getSize());
    }

    private int getSize() {
        return len == 0 ? 1 : len;
    }

    public void genCode (ArrayList<CStructTypeDeclaration> declaList, HashMap<String, String> instTable, String phase) {
        final String name = "SizeTable";

        CStructTypeDeclaration decl;
        String cCode;

        cCode = String.format(PcdDatabase.SizeTableDeclaration, phase);
        decl = new CStructTypeDeclaration (
                                            name,
                                            2,
                                            cCode,
                                            true
                                           );
        declaList.add(decl);


        cCode = PcdDatabase.genInstantiationStr(getInstantiation());
        instTable.put(name, cCode);
    }

    private ArrayList<String> getInstantiation () {
        final String comma   = ",";
        ArrayList<String> Output = new ArrayList<String>();

        Output.add("/* SizeTable */");
        Output.add("{");
        if (al.size() == 0) {
            Output.add("\t0");
        } else {
            for (int index = 0; index < al.size(); index++) {
                ArrayList<Integer> ial = al.get(index);

                String str = "\t";

                for (int index2 = 0; index2 < ial.size(); index2++) {
                    str += " " + ial.get(index2).toString();
                    if (index2 != ial.size() - 1) {
                        str += comma;
                    }
                }

                str += " /* " + alComments.get(index) + " */";

                if (index != (al.size() - 1)) {
                    str += comma;
                }

                Output.add(str);

            }
        }
        Output.add("}");

        return Output;
    }

    public void add (Token token) {

        //
        // We only have size information for POINTER type PCD entry.
        //
        if (token.datumType != Token.DATUM_TYPE.POINTER) {
            return;
        }

        ArrayList<Integer> ial = token.getPointerTypeSize();

        len+= ial.size();

        al.add(ial);
        alComments.add(token.getPrimaryKeyString());

        return;
    }

}

/**
    GuidTable

    This class is used to store the GUIDs in a PCD database.
**/
class GuidTable {
    private ArrayList<UUID> al;
    private ArrayList<String> alComments;
    private String          phase;
    private int             len;
    private int             bodyLineNum;

    public GuidTable (String phase) {
        this.phase = phase;
        al = new ArrayList<UUID>();
        alComments = new ArrayList<String>();
        len = 0;
        bodyLineNum = 0;
    }

    public String getSizeMacro () {
        return String.format(PcdDatabase.GuidTableSizeMacro, phase, getSize());
    }

    private int getSize () {
        return (al.size() == 0)? 1 : al.size();
    }

    public String getExistanceMacro () {
        return String.format(PcdDatabase.GuidTableExistenceMacro, phase, (al.size() == 0)? "TRUE":"FALSE");
    }

    public void genCode (ArrayList<CStructTypeDeclaration> declaList, HashMap<String, String> instTable, String phase) {
        final String name = "GuidTable";

        CStructTypeDeclaration decl;
        String cCode = "";

        cCode += String.format(PcdDatabase.GuidTableDeclaration, phase);
        decl = new CStructTypeDeclaration (
                                            name,
                                            4,
                                            cCode,
                                            true
                                           );
        declaList.add(decl);


        cCode = PcdDatabase.genInstantiationStr(getInstantiation());
        instTable.put(name, cCode);
    }

    private String getUuidCString (UUID uuid) {
        String[]  guidStrArray;

        guidStrArray =(uuid.toString()).split("-");

        return String.format("{0x%s, 0x%s, 0x%s, {0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s}}",
                                         guidStrArray[0],
                                         guidStrArray[1],
                                         guidStrArray[2],
                                        (guidStrArray[3].substring(0, 2)),
                                        (guidStrArray[3].substring(2, 4)),
                                        (guidStrArray[4].substring(0, 2)),
                                        (guidStrArray[4].substring(2, 4)),
                                        (guidStrArray[4].substring(4, 6)),
                                        (guidStrArray[4].substring(6, 8)),
                                        (guidStrArray[4].substring(8, 10)),
                                        (guidStrArray[4].substring(10, 12))
                                        );
    }

    private ArrayList<String> getInstantiation () {
        ArrayList<String> Output = new ArrayList<String>();

        Output.add("/* GuidTable */");
        Output.add("{");

        if (al.size() == 0) {
            Output.add("\t" + getUuidCString(new UUID(0, 0)));
        }

        for (int i = 0; i < al.size(); i++) {
            String str = "\t" + getUuidCString(al.get(i));

            str += "/* " + alComments.get(i) +  " */";
            if (i != (al.size() - 1)) {
                str += ",";
            }
            Output.add(str);
            bodyLineNum++;

        }
        Output.add("}");

        return Output;
    }

    public int add (UUID uuid, String name) {
        //
        // Check if GuidTable has this entry already.
        // If so, return the GuidTable index.
        //
        for (int i = 0; i < al.size(); i++) {
            if (al.get(i).compareTo(uuid) == 0) {
                return i;
            }
        }

        len++;
        al.add(uuid);
        alComments.add(name);

        //
        // Return the previous Table Index
        //
        return len - 1;
    }

}

/**
    SkuIdTable

    This class is used to store the SKU IDs in a PCD database.

**/
class SkuIdTable {
    private ArrayList<Integer[]> al;
    private ArrayList<String>    alComment;
    private String               phase;
    private int                  len;

    public SkuIdTable (String phase) {
        this.phase = phase;
        al = new ArrayList<Integer[]>();
        alComment = new ArrayList<String>();
        len = 0;
    }

    public String getSizeMacro () {
        return String.format(PcdDatabase.SkuIdTableSizeMacro, phase, getSize());
    }

    private int getSize () {
        return (len == 0)? 1 : len;
    }

    public String getExistanceMacro () {
        return String.format(PcdDatabase.SkuTableExistenceMacro, phase, (al.size() == 0)? "TRUE":"FALSE");
    }

    public void genCode (ArrayList<CStructTypeDeclaration> declaList, HashMap<String, String> instTable, String phase) {
        final String name = "SkuIdTable";

        CStructTypeDeclaration decl;
        String cCode = "";

        cCode += String.format(PcdDatabase.SkuIdTableDeclaration, phase);
        decl = new CStructTypeDeclaration (
                                            name,
                                            1,
                                            cCode,
                                            true
                                           );
        declaList.add(decl);


        cCode = PcdDatabase.genInstantiationStr(getInstantiation());
        instTable.put(name, cCode);

        //
        // SystemSkuId is in PEI phase PCD Database
        //
        if (phase.equalsIgnoreCase("PEI")) {
            decl = new CStructTypeDeclaration (
                                                "SystemSkuId",
                                                1,
                                                String.format("%-20sSystemSkuId;\r\n", "SKU_ID"),
                                                true
                                              );
            declaList.add(decl);

            instTable.put("SystemSkuId", "0");
        }

    }

    private ArrayList<String> getInstantiation () {
        ArrayList<String> Output = new ArrayList<String> ();

        Output.add("/* SkuIdTable */");
        Output.add("{");

        if (al.size() == 0) {
            Output.add("\t0");
        }

        for (int index = 0; index < al.size(); index++) {
            String str;

            str = "/* " + alComment.get(index) + "*/ ";
            str += "/* MaxSku */ ";


            Integer[] ia = al.get(index);

            str += "\t" + ia[0].toString() + ", ";
            for (int index2 = 1; index2 < ia.length; index2++) {
               str += ia[index2].toString();
               if (!((index2 == ia.length - 1) && (index == al.size() - 1))) {
                   str += ", ";
               }
            }

            Output.add(str);

        }

        Output.add("}");

        return Output;
    }

    public int add (Token token) {

        int index;
        int pos;

        //
        // Check if this SKU_ID Array is already in the table
        //
        pos = 0;
        for (Object o: al) {
            Integer [] s = (Integer[]) o;
            boolean different = false;
            if (s[0] == token.getSkuIdCount()) {
                for (index = 1; index < s.length; index++) {
                    if (s[index] != token.skuData.get(index-1).id) {
                        different = true;
                        break;
                    }
                }
            } else {
                different = true;
            }
            if (different) {
                pos += s[0] + 1;
            } else {
                return pos;
            }
        }

        Integer [] skuIds = new Integer[token.skuData.size() + 1];
        skuIds[0] = new Integer(token.skuData.size());
        for (index = 1; index < skuIds.length; index++) {
            skuIds[index] = new Integer(token.skuData.get(index - 1).id);
        }

        index = len;

        len += skuIds.length;
        al.add(skuIds);
        alComment.add(token.getPrimaryKeyString());

        return index;
    }

}

class LocalTokenNumberTable {
    private ArrayList<String>    al;
    private ArrayList<String>    alComment;
    private String               phase;
    private int                  len;

    public LocalTokenNumberTable (String phase) {
        this.phase = phase;
        al = new ArrayList<String>();
        alComment = new ArrayList<String>();

        len = 0;
    }

    public String getSizeMacro () {
    	return String.format(PcdDatabase.LocalTokenNumberTableSizeMacro, phase, getSize())
    			+ String.format(PcdDatabase.LocalTokenNumberSizeMacro, phase, al.size());
    }

    public int getSize () {
        return (al.size() == 0)? 1 : al.size();
    }

    public String getExistanceMacro () {
        return String.format(PcdDatabase.DatabaseExistenceMacro, phase, (al.size() == 0)? "TRUE":"FALSE");
    }

    public void genCode (ArrayList<CStructTypeDeclaration> declaList, HashMap<String, String> instTable, String phase) {
        final String name = "LocalTokenNumberTable";

        CStructTypeDeclaration decl;
        String cCode = "";

        cCode += String.format(PcdDatabase.LocalTokenNumberTableDeclaration, phase);
        decl = new CStructTypeDeclaration (
                                            name,
                                            4,
                                            cCode,
                                            true
                                           );
        declaList.add(decl);

        cCode = PcdDatabase.genInstantiationStr(getInstantiation());
        instTable.put(name, cCode);
    }

    private ArrayList<String> getInstantiation () {
        ArrayList<String> output = new ArrayList<String>();

        output.add("/* LocalTokenNumberTable */");
        output.add("{");

        if (al.size() == 0) {
            output.add("\t0");
        }

        for (int index = 0; index < al.size(); index++) {
            String str;

            str = "\t" + (String)al.get(index);

            str += " /* " + alComment.get(index) + " */ ";


            if (index != (al.size() - 1)) {
                str += ",";
            }

            output.add(str);

        }

        output.add("}");

        return output;
    }

    public int add (Token token) {
        int index = len;
        String str;

        len++;

        str =  String.format(PcdDatabase.offsetOfStrTemplate, phase, token.hasDefaultValue() ? "Init" : "Uninit", token.getPrimaryKeyString());

        if (token.isUnicodeStringType()) {
            str += " | PCD_TYPE_STRING";
        }

        if (token.isSkuEnable()) {
            str += " | PCD_TYPE_SKU_ENABLED";
        }

        if (token.getDefaultSku().type == DynamicTokenValue.VALUE_TYPE.HII_TYPE) {
            str += " | PCD_TYPE_HII";
        }

        if (token.getDefaultSku().type == DynamicTokenValue.VALUE_TYPE.VPD_TYPE) {
            str += " | PCD_TYPE_VPD";
        }

        switch (token.datumType) {
        case UINT8:
        case BOOLEAN:
            str += " | PCD_DATUM_TYPE_UINT8";
            break;
        case UINT16:
            str += " | PCD_DATUM_TYPE_UINT16";
            break;
        case UINT32:
            str += " | PCD_DATUM_TYPE_UINT32";
            break;
        case UINT64:
            str += " | PCD_DATUM_TYPE_UINT64";
            break;
        case POINTER:
            str += " | PCD_DATUM_TYPE_POINTER";
            break;
        }

        al.add(str);
        alComment.add(token.getPrimaryKeyString());

        return index;
    }
}

/**
    ExMapTable

    This class is used to store the table of mapping information
    between DynamicEX ID pair(Guid, TokenNumber) and
    the local token number assigned by PcdDatabase class.
**/
class ExMapTable {

    /**
        ExTriplet

        This class is used to store the mapping information
        between DynamicEX ID pair(Guid, TokenNumber) and
        the local token number assigned by PcdDatabase class.
    **/
    class ExTriplet {
        public Integer guidTableIdx;
        public Long exTokenNumber;
        public Long localTokenIdx;

        public ExTriplet (int guidTableIdx, long exTokenNumber, long localTokenIdx) {
            this.guidTableIdx = new Integer(guidTableIdx);
            this.exTokenNumber = new Long(exTokenNumber);
            this.localTokenIdx = new Long(localTokenIdx);
        }
    }

    private ArrayList<ExTriplet> al;
    private Map<ExTriplet, String> alComment;
    private String               phase;
    private int                  len;
    private int                   bodyLineNum;

    public ExMapTable (String phase) {
        this.phase = phase;
        al = new ArrayList<ExTriplet>();
        alComment = new HashMap<ExTriplet, String>();
        bodyLineNum = 0;
        len = 0;
    }

    public String getSizeMacro () {
        return String.format(PcdDatabase.ExMapTableSizeMacro, phase, getTableLen())
             + String.format(PcdDatabase.ExTokenNumber, phase, al.size());
    }

    public String getExistanceMacro () {
        return String.format(PcdDatabase.ExMapTableExistenceMacro, phase, (al.size() == 0)? "TRUE":"FALSE");
    }

    public void genCode (ArrayList<CStructTypeDeclaration> declaList, HashMap<String, String> instTable, String phase) {
        final String exMapTableName = "ExMapTable";

        sortTable();

        CStructTypeDeclaration decl;
        String cCode = "";

        cCode += String.format(PcdDatabase.ExMapTableDeclaration, phase);
        decl = new CStructTypeDeclaration (
                                            exMapTableName,
                                            4,
                                            cCode,
                                            true
                                           );
        declaList.add(decl);


        cCode = PcdDatabase.genInstantiationStr(getInstantiation());
        instTable.put(exMapTableName, cCode);
    }

    private ArrayList<String> getInstantiation () {
        ArrayList<String> Output = new ArrayList<String>();

        Output.add("/* ExMapTable */");
        Output.add("{");
        if (al.size() == 0) {
            Output.add("\t{0, 0, 0}");
        }

        int index;
        for (index = 0; index < al.size(); index++) {
            String str;

            ExTriplet e = (ExTriplet)al.get(index);

            str = "\t" + "{ " + String.format("0x%08X", e.exTokenNumber) + ", ";
            str += e.localTokenIdx.toString() + ", ";
            str += e.guidTableIdx.toString();

            str += "}" + " /* " + alComment.get(e) + " */" ;

            if (index != al.size() - 1) {
                str += ",";
            }

            Output.add(str);
            bodyLineNum++;

        }

        Output.add("}");

        return Output;
    }

    public int add (int localTokenIdx, long exTokenNum, int guidTableIdx, String name) {
        int index = len;

        len++;
        ExTriplet et = new ExTriplet(guidTableIdx, exTokenNum, localTokenIdx);

        al.add(et);
        alComment.put(et, name);

        return index;
    }

    private int getTableLen () {
        return al.size() == 0 ? 1 : al.size();
    }

    //
    // To simplify the algorithm for GetNextToken and GetNextTokenSpace in
    // PCD PEIM/Driver, we need to sort the ExMapTable according to the
    // following order:
    // 1) ExGuid
    // 2) ExTokenNumber
    //
    class ExTripletComp implements Comparator<ExTriplet> {
        public int compare (ExTriplet a, ExTriplet b) {
            if (a.guidTableIdx == b.guidTableIdx ) {
                //
                // exTokenNumber is long, we can't use simple substraction.
                //
                if (a.exTokenNumber > b.exTokenNumber) {
                    return 1;
                } else if (a.exTokenNumber == b.exTokenNumber) {
                    return 0;
                } else {
                    return -1;
                }
            }

            return a.guidTableIdx - b.guidTableIdx;
        }
    }

    private void sortTable () {
        java.util.Comparator<ExTriplet> comparator = new ExTripletComp();
        java.util.Collections.sort(al, comparator);
    }
}

/**
    PcdDatabase

    This class is used to generate C code for Autogen.h and Autogen.c of
    a PCD service DXE driver and PCD service PEIM.
**/
public class PcdDatabase {

    private final static int    SkuHeadAlignmentSize             = 4;
    private final String        newLine                         = "\r\n";
    private final String        commaNewLine                    = ",\r\n";
    private final String        tab                             = "\t";
    public final static String ExMapTableDeclaration            = "DYNAMICEX_MAPPING   ExMapTable[%s_EXMAPPING_TABLE_SIZE];\r\n";
    public final static String GuidTableDeclaration             = "EFI_GUID            GuidTable[%s_GUID_TABLE_SIZE];\r\n";
    public final static String LocalTokenNumberTableDeclaration = "UINT32              LocalTokenNumberTable[%s_LOCAL_TOKEN_NUMBER_TABLE_SIZE];\r\n";
    public final static String StringTableDeclaration           = "UINT16              StringTable[%s_STRING_TABLE_SIZE];\r\n";
    public final static String SizeTableDeclaration             = "SIZE_INFO           SizeTable[%s_SIZE_TABLE_SIZE];\r\n";
    public final static String SkuIdTableDeclaration            = "UINT8               SkuIdTable[%s_SKUID_TABLE_SIZE];\r\n";


    public final static String ExMapTableSizeMacro              = "#define %s_EXMAPPING_TABLE_SIZE  %d\r\n";
    public final static String ExTokenNumber                    = "#define %s_EX_TOKEN_NUMBER       %d\r\n";
    public final static String GuidTableSizeMacro               = "#define %s_GUID_TABLE_SIZE         %d\r\n";
    public final static String LocalTokenNumberTableSizeMacro   = "#define %s_LOCAL_TOKEN_NUMBER_TABLE_SIZE            %d\r\n";
    public final static String LocalTokenNumberSizeMacro   		= "#define %s_LOCAL_TOKEN_NUMBER            %d\r\n";
    public final static String SizeTableSizeMacro               = "#define %s_SIZE_TABLE_SIZE            %d\r\n";
    public final static String StringTableSizeMacro             = "#define %s_STRING_TABLE_SIZE       %d\r\n";
    public final static String SkuIdTableSizeMacro              = "#define %s_SKUID_TABLE_SIZE        %d\r\n";


    public final static String ExMapTableExistenceMacro         = "#define %s_EXMAP_TABLE_EMPTY    %s\r\n";
    public final static String GuidTableExistenceMacro          = "#define %s_GUID_TABLE_EMPTY     %s\r\n";
    public final static String DatabaseExistenceMacro           = "#define %s_DATABASE_EMPTY       %s\r\n";
    public final static String StringTableExistenceMacro        = "#define %s_STRING_TABLE_EMPTY   %s\r\n";
    public final static String SkuTableExistenceMacro           = "#define %s_SKUID_TABLE_EMPTY    %s\r\n";

    public final static String offsetOfSkuHeadStrTemplate       = "offsetof(%s_PCD_DATABASE, %s.%s_SkuDataTable)";
    public final static String offsetOfVariableEnabledDefault   = "offsetof(%s_PCD_DATABASE, %s.%s_VariableDefault_%d)";
    public final static String offsetOfStrTemplate              = "offsetof(%s_PCD_DATABASE, %s.%s)";

    private final static String  skuDataTableTemplate           = "SkuDataTable";


    private StringTable stringTable;
    private GuidTable   guidTable;
    private LocalTokenNumberTable localTokenNumberTable;
    private SkuIdTable  skuIdTable;
    private SizeTable   sizeTable;
    private ExMapTable  exMapTable;

    private ArrayList<Token> alTokens;
    private String phase;
    private int assignedTokenNumber;

    //
    // Use two class global variable to store
    // temperary
    //
    private String      privateGlobalName;
    private String      privateGlobalCCode;
    //
    // After Major changes done to the PCD
    // database generation class PcdDatabase
    // Please increment the version and please
    // also update the version number in PCD
    // service PEIM and DXE driver accordingly.
    //
    private final int version = 2;

    private String hString;
    private String cString;

    /**
        Constructor for PcdDatabase class.

        <p>We have two PCD dynamic(ex) database for the Framework implementation. One
        for PEI phase and the other for DXE phase.  </p>

        @param alTokens A ArrayList of Dynamic(EX) PCD entry.
        @param exePhase The phase to generate PCD database for: valid input
                        is "PEI" or "DXE".
        @param startLen The starting Local Token Number for the PCD database. For
                        PEI phase, the starting Local Token Number starts from 0.
                        For DXE phase, the starting Local Token Number starts
                        from the total number of PCD entry of PEI phase.
        @return void
    **/
    public PcdDatabase (ArrayList<Token> alTokens, String exePhase, int startLen) {
       phase = exePhase;

       stringTable = new StringTable(phase);
       guidTable = new GuidTable(phase);
       localTokenNumberTable = new LocalTokenNumberTable(phase);
       skuIdTable = new SkuIdTable(phase);
       sizeTable = new SizeTable(phase);
       exMapTable = new ExMapTable(phase);

       //
       // Local token number 0 is reserved for INVALID_TOKEN_NUMBER.
       // So we will increment 1 for the startLen passed from the
       // constructor.
       //
       assignedTokenNumber = startLen + 1;
       this.alTokens = alTokens;
    }

    private void getNonExAndExTokens (ArrayList<Token> alTokens, List<Token> nexTokens, List<Token> exTokens) {
        for (int i = 0; i < alTokens.size(); i++) {
            Token t = (Token)alTokens.get(i);
            if (t.isDynamicEx()) {
                exTokens.add(t);
            } else {
                nexTokens.add(t);
            }
        }

        return;
    }

    private int getDataTypeAlignmentSize (Token token) {
        switch (token.datumType) {
        case UINT8:
            return 1;
        case UINT16:
            return 2;
        case UINT32:
            return 4;
        case UINT64:
            return 8;
        case POINTER:
            return 1;
        case BOOLEAN:
            return 1;
        default:
            return 1;
        }
    }

    private int getHiiPtrTypeAlignmentSize(Token token) {
        switch (token.datumType) {
        case UINT8:
            return 1;
        case UINT16:
            return 2;
        case UINT32:
            return 4;
        case UINT64:
            return 8;
        case POINTER:
            if (token.isHiiEnable()) {
                if (token.isHiiDefaultValueUnicodeStringType()) {
                    return 2;
                }
            }
            return 1;
        case BOOLEAN:
            return 1;
        default:
            return 1;
        }
    }

    private int getAlignmentSize (Token token) {
        if (token.getDefaultSku().type == DynamicTokenValue.VALUE_TYPE.HII_TYPE) {
            return 2;
        }

        if (token.getDefaultSku().type == DynamicTokenValue.VALUE_TYPE.VPD_TYPE) {
            return 4;
        }

        if (token.isUnicodeStringType()) {
            return 2;
        }

        return getDataTypeAlignmentSize(token);
     }

    public String getCString () {
        return cString;
    }

    public String getHString () {
        return hString;
    }

    private void genCodeWorker(Token t,
            ArrayList<CStructTypeDeclaration> declaList,
            HashMap<String, String> instTable, String phase)
            throws EntityException {

        CStructTypeDeclaration decl;

        //
        // Insert SKU_HEAD if isSkuEnable is true
        //
        if (t.isSkuEnable()) {
            int tableIdx;
            tableIdx = skuIdTable.add(t);
            decl = new CStructTypeDeclaration(t.getPrimaryKeyString(),
                    SkuHeadAlignmentSize, getSkuEnabledTypeDeclaration(t), true);
            declaList.add(decl);
            instTable.put(t.getPrimaryKeyString(),
                    getSkuEnabledTypeInstantiaion(t, tableIdx));
        }

        //
        // Insert PCD_ENTRY declaration and instantiation
        //
        getCDeclarationString(t);

        decl = new CStructTypeDeclaration(privateGlobalName,
                getAlignmentSize(t), privateGlobalCCode, t.hasDefaultValue());
        declaList.add(decl);

        if (t.hasDefaultValue()) {
            instTable.put(privateGlobalName,
                          getTypeInstantiation(t, declaList, instTable, phase)
                          );
        }

    }

    private void ProcessTokens (List<Token> tokens,
                                   ArrayList<CStructTypeDeclaration> cStructDeclList,
                                   HashMap<String, String> cStructInstTable,
                                   String phase
                                   )
    throws EntityException {

        for (int idx = 0; idx < tokens.size(); idx++) {
            Token t = tokens.get(idx);

            genCodeWorker (t, cStructDeclList, cStructInstTable, phase);

            sizeTable.add(t);
            localTokenNumberTable.add(t);
            t.tokenNumber = assignedTokenNumber++;

            //
            // Add a mapping if this dynamic PCD entry is a EX type
            //
            if (t.isDynamicEx()) {
                exMapTable.add((int)t.tokenNumber,
                                t.dynamicExTokenNumber,
                                guidTable.add(translateSchemaStringToUUID(t.tokenSpaceName), t.getPrimaryKeyString()),
                                t.getPrimaryKeyString()
                                );
            }
        }

    }

    public void genCode () throws EntityException {

        ArrayList<CStructTypeDeclaration> cStructDeclList = new ArrayList<CStructTypeDeclaration>();
        HashMap<String, String> cStructInstTable = new HashMap<String, String>();

        List<Token> nexTokens = new ArrayList<Token> ();
        List<Token> exTokens = new ArrayList<Token> ();

        getNonExAndExTokens (alTokens, nexTokens, exTokens);

        //
        // We have to process Non-Ex type PCD entry first. The reason is
        // that our optimization assumes that the Token Number of Non-Ex
        // PCD entry start from 1 (for PEI phase) and grows continously upwards.
        //
        // EX type token number starts from the last Non-EX PCD entry and
        // grows continously upwards.
        //
        ProcessTokens (nexTokens, cStructDeclList, cStructInstTable, phase);
        ProcessTokens (exTokens, cStructDeclList, cStructInstTable, phase);

        stringTable.genCode(cStructDeclList, cStructInstTable);
        skuIdTable.genCode(cStructDeclList, cStructInstTable, phase);
        exMapTable.genCode(cStructDeclList, cStructInstTable, phase);
        localTokenNumberTable.genCode(cStructDeclList, cStructInstTable, phase);
        sizeTable.genCode(cStructDeclList, cStructInstTable, phase);
        guidTable.genCode(cStructDeclList, cStructInstTable, phase);

        hString = genCMacroCode ();

        HashMap <String, String> result;

        result = genCStructCode(cStructDeclList,
                cStructInstTable,
                phase
                );

        hString += result.get("initDeclStr");
        hString += result.get("uninitDeclStr");

        hString += String.format("#define PCD_%s_SERVICE_DRIVER_AUTOGEN_VERSION         %d", phase, version);

        cString = newLine + newLine + result.get("initInstStr");

    }

    private String genCMacroCode () {
        String macroStr   = "";

        //
        // Generate size info Macro for all Tables
        //
        macroStr += guidTable.getSizeMacro();
        macroStr += stringTable.getSizeMacro();
        macroStr += skuIdTable.getSizeMacro();
        macroStr += localTokenNumberTable.getSizeMacro();
        macroStr += exMapTable.getSizeMacro();
        macroStr += sizeTable.getSizeMacro();

        //
        // Generate existance info Macro for all Tables
        //
        macroStr += guidTable.getExistanceMacro();
        macroStr += stringTable.getExistanceMacro();
        macroStr += skuIdTable.getExistanceMacro();
        macroStr += localTokenNumberTable.getExistanceMacro();
        macroStr += exMapTable.getExistanceMacro();

        macroStr += newLine;

        return macroStr;
    }

    private HashMap <String, String> genCStructCode(
                                            ArrayList<CStructTypeDeclaration> declaList,
                                            HashMap<String, String> instTable,
                                            String phase
                                            ) {

        int i;
        HashMap <String, String> result = new HashMap<String, String>();
        HashMap <Integer, ArrayList<String>>    alignmentInitDecl = new HashMap<Integer, ArrayList<String>>();
        HashMap <Integer, ArrayList<String>>    alignmentUninitDecl = new HashMap<Integer, ArrayList<String>>();
        HashMap <Integer, ArrayList<String>>    alignmentInitInst = new HashMap<Integer, ArrayList<String>>();

        //
        // Initialize the storage for each alignment
        //
        for (i = 8; i > 0; i>>=1) {
            alignmentInitDecl.put(new Integer(i), new ArrayList<String>());
            alignmentInitInst.put(new Integer(i), new ArrayList<String>());
            alignmentUninitDecl.put(new Integer(i), new ArrayList<String>());
        }

        String initDeclStr   = "typedef struct {" + newLine;
        String initInstStr   = String.format("%s_PCD_DATABASE_INIT g%sPcdDbInit = { ", phase.toUpperCase(), phase.toUpperCase()) + newLine;
        String uninitDeclStr = "typedef struct {" + newLine;

        //
        // Sort all C declaration and instantiation base on Alignment Size
        //
        for (Object d : declaList) {
            CStructTypeDeclaration decl = (CStructTypeDeclaration) d;

            if (decl.initTable) {
                alignmentInitDecl.get(new Integer(decl.alignmentSize)).add(decl.cCode);
                alignmentInitInst.get(new Integer(decl.alignmentSize)).add(instTable.get(decl.key));
            } else {
                alignmentUninitDecl.get(new Integer(decl.alignmentSize)).add(decl.cCode);
            }
        }

        //
        // Generate code for every alignment size
        //
        boolean uinitDatabaseEmpty = true;
        for (int align = 8; align > 0; align >>= 1) {
            ArrayList<String> declaListBasedOnAlignment = alignmentInitDecl.get(new Integer(align));
            ArrayList<String> instListBasedOnAlignment = alignmentInitInst.get(new Integer(align));
            for (i = 0; i < declaListBasedOnAlignment.size(); i++) {
                initDeclStr += tab + declaListBasedOnAlignment.get(i);
                initInstStr += tab + instListBasedOnAlignment.get(i);

                //
                // We made a assumption that both PEI_PCD_DATABASE and DXE_PCD_DATABASE
                // has a least one data memember with alignment size of 1. So we can
                // remove the last "," in the C structure instantiation string. Luckily,
                // this is true as both data structure has SKUID_TABLE anyway.
                //
                if ((align == 1) && (i == declaListBasedOnAlignment.size() - 1)) {
                    initInstStr += newLine;
                } else {
                    initInstStr += commaNewLine;
                }
            }

            declaListBasedOnAlignment = alignmentUninitDecl.get(new Integer(align));

            if (declaListBasedOnAlignment.size() != 0) {
                uinitDatabaseEmpty = false;
            }

            for (Object d : declaListBasedOnAlignment) {
                String s = (String)d;
                uninitDeclStr += tab + s;
            }
        }

        if (uinitDatabaseEmpty) {
            uninitDeclStr += tab + String.format("%-20sdummy; /* PCD_DATABASE_UNINIT is emptry */\r\n", "UINT8");
        }

        initDeclStr += String.format("} %s_PCD_DATABASE_INIT;", phase) + newLine + newLine;
        initInstStr += "};" + newLine;
        uninitDeclStr += String.format("} %s_PCD_DATABASE_UNINIT;", phase) + newLine + newLine;

        result.put("initDeclStr", initDeclStr);
        result.put("initInstStr", initInstStr);
        result.put("uninitDeclStr", uninitDeclStr);

        return result;
    }

    public static String genInstantiationStr (ArrayList<String> alStr) {
        String str = "";
        for (int i = 0; i< alStr.size(); i++) {
            if (i != 0) {
                str += "\t";
            }
            str += alStr.get(i);
            if (i != alStr.size() - 1) {
                str += "\r\n";
            }
        }

        return str;
    }

    private String getSkuEnabledTypeDeclaration (Token token) {
        return String.format("%-20s%s;\r\n", "SKU_HEAD", token.getPrimaryKeyString());
    }

    private String getSkuEnabledTypeInstantiaion (Token token, int SkuTableIdx) {

        String offsetof = String.format(PcdDatabase.offsetOfSkuHeadStrTemplate, phase, token.hasDefaultValue()? "Init" : "Uninit", token.getPrimaryKeyString());
        return String.format("{ %s, %d } /* SKU_ENABLED: %s */", offsetof, SkuTableIdx, token.getPrimaryKeyString());
    }

    private String getDataTypeInstantiationForVariableDefault (Token token, String cName, int skuId) {
        return String.format("%s /* %s */", token.skuData.get(skuId).value.hiiDefaultValue, cName);
    }

    private String getCType (Token t)
        throws EntityException {

        if (t.isHiiEnable()) {
            return "VARIABLE_HEAD";
        }

        if (t.isVpdEnable()) {
            return "VPD_HEAD";
        }

        if (t.isUnicodeStringType()) {
            return "STRING_HEAD";
        }

        switch (t.datumType) {
        case UINT64:
            return "UINT64";
        case UINT32:
            return "UINT32";
        case UINT16:
            return "UINT16";
        case UINT8:
            return "UINT8";
        case BOOLEAN:
            return "BOOLEAN";
        case POINTER:
            return "UINT8";
        default:
            throw new EntityException("Unknown DatumType in getDataTypeCDeclaration");
        }
    }

    //
    // privateGlobalName and privateGlobalCCode is used to pass output to caller of getCDeclarationString
    //
    private void getCDeclarationString(Token t)
        throws EntityException {

        if (t.isSkuEnable()) {
            privateGlobalName = String.format("%s_%s", t.getPrimaryKeyString(), skuDataTableTemplate);
        } else {
            privateGlobalName = t.getPrimaryKeyString();
        }

        String type = getCType(t);
        if ((t.datumType == Token.DATUM_TYPE.POINTER) && (!t.isHiiEnable()) && (!t.isUnicodeStringType())) {
            int bufferSize;
            if (t.isASCIIStringType()) {
                //
                // Build tool will add a NULL string at the end of the ASCII string
                //
                bufferSize = t.datumSize + 1;
            } else {
                bufferSize = t.datumSize;
            }
            privateGlobalCCode = String.format("%-20s%s[%d][%d];\r\n", type, privateGlobalName, t.getSkuIdCount(), bufferSize);
        } else {
            privateGlobalCCode = String.format("%-20s%s[%d];\r\n", type, privateGlobalName, t.getSkuIdCount());
        }
    }

    private String getDataTypeDeclarationForVariableDefault (Token token, String cName, int skuId)
        throws EntityException {

        String typeStr;

        if (token.datumType == Token.DATUM_TYPE.UINT8) {
            typeStr = "UINT8";
        } else if (token.datumType == Token.DATUM_TYPE.UINT16) {
            typeStr = "UINT16";
        } else if (token.datumType == Token.DATUM_TYPE.UINT32) {
            typeStr = "UINT32";
        } else if (token.datumType == Token.DATUM_TYPE.UINT64) {
            typeStr = "UINT64";
        } else if (token.datumType == Token.DATUM_TYPE.BOOLEAN) {
            typeStr = "BOOLEAN";
        } else if (token.datumType == Token.DATUM_TYPE.POINTER) {
            int size;
            if (token.isHiiDefaultValueUnicodeStringType()) {
                typeStr = "UINT16";
                //
                // Include the NULL charactor
                //
                size = token.datumSize / 2 + 1;
            } else {
                typeStr = "UINT8";
                if (token.isHiiDefaultValueASCIIStringType()) {
                    //
                    // Include the NULL charactor
                    //
                    size = token.datumSize + 1;
                } else {
                    size = token.datumSize;
                }
            }
            return String.format("%-20s%s[%d];\r\n", typeStr, cName, size);
        } else {
            throw new EntityException("Unknown DATUM_TYPE type in when generating code for VARIABLE_ENABLED PCD entry");
        }

        return String.format("%-20s%s;\r\n", typeStr, cName);
    }

    private String getTypeInstantiation (Token t, ArrayList<CStructTypeDeclaration> declaList, HashMap<String, String> instTable, String phase) throws EntityException {

        int     i;

        String s;
        s = String.format("/* %s */", t.getPrimaryKeyString()) + newLine;
        s += tab + "{" + newLine;

        for (i = 0; i < t.skuData.size(); i++) {
            if (t.isUnicodeStringType()) {
                s += tab + tab + String.format(" %d ", stringTable.add(t.skuData.get(i).value.value, t));
            } else if (t.isHiiEnable()) {
                /* VPD_HEAD definition
                   typedef struct {
                      UINT16  GuidTableIndex;   // Offset in Guid Table in units of GUID.
                      UINT16  StringIndex;      // Offset in String Table in units of UINT16.
                      UINT16  Offset;           // Offset in Variable
                      UINT16  DefaultValueOffset; // Offset of the Default Value
                    } VARIABLE_HEAD  ;
                 */
                String variableDefaultName = String.format("%s_VariableDefault_%d", t.getPrimaryKeyString(), i);

                s += tab + tab + String.format("{ %d, %d, %s, %s }", guidTable.add(t.skuData.get(i).value.variableGuid, t.getPrimaryKeyString()),
                                                          stringTable.add(t.skuData.get(i).value.getStringOfVariableName(), t),
                                                          t.skuData.get(i).value.variableOffset,
                                                          String.format("offsetof(%s_PCD_DATABASE, Init.%s)", phase, variableDefaultName)
                                                          );
                //
                // We need to support the default value, so we add the declaration and
                // the instantiation for the default value.
                //
                CStructTypeDeclaration decl = new CStructTypeDeclaration (variableDefaultName,
                                                        getHiiPtrTypeAlignmentSize(t),
                                                        getDataTypeDeclarationForVariableDefault(t, variableDefaultName, i),
                                                        true
                                                        );
                declaList.add(decl);
                instTable.put(variableDefaultName, getDataTypeInstantiationForVariableDefault (t, variableDefaultName, i));
            } else if (t.isVpdEnable()) {
                    /* typedef  struct {
                        UINT32  Offset;
                      } VPD_HEAD;
                    */
                s += tab + tab + String.format("{ %s }", t.skuData.get(i).value.vpdOffset);
            } else {
                if (t.isByteStreamType()) {
                    //
                    // Byte stream type input has their own "{" "}", so we won't help to insert.
                    //
                    s += tab + tab + String.format(" %s ", t.skuData.get(i).value.value);
                } else {
                    s += tab + tab + String.format("{ %s }", t.skuData.get(i).value.value);
                }
            }

            if (i != t.skuData.size() - 1) {
                s += commaNewLine;
            } else {
                s += newLine;
            }

        }

        s += tab + "}";

        return s;
    }

    public static String getPcdDatabaseCommonDefinitions () {

		String retStr;

		retStr 	= "//\r\n";
		retStr += "// The following definition will be generated by build tool\r\n";
		retStr += "//\r\n";
		retStr += "\r\n";
		retStr += "//\r\n";
		retStr += "// Common definitions\r\n";
		retStr += "//\r\n";
		retStr += "typedef UINT8 SKU_ID;\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_TYPE_SHIFT        28\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_TYPE_DATA         (0x0 << PCD_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_TYPE_HII    	    (0x8 << PCD_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_TYPE_VPD    	    (0x4 << PCD_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_TYPE_SKU_ENABLED 	(0x2 << PCD_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_TYPE_STRING       (0x1 << PCD_TYPE_SHIFT)\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_TYPE_ALL_SET      (PCD_TYPE_DATA | PCD_TYPE_HII | PCD_TYPE_VPD | PCD_TYPE_SKU_ENABLED | PCD_TYPE_STRING)\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_DATUM_TYPE_SHIFT  24\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_DATUM_TYPE_POINTER        (0x0 << PCD_DATUM_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_DATUM_TYPE_UINT8          (0x1 << PCD_DATUM_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_DATUM_TYPE_UINT16    	    (0x2 << PCD_DATUM_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_DATUM_TYPE_UINT32    	    (0x4 << PCD_DATUM_TYPE_SHIFT)\r\n";
		retStr += "#define PCD_DATUM_TYPE_UINT64        	(0x8 << PCD_DATUM_TYPE_SHIFT)\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_DATUM_TYPE_ALL_SET    (PCD_DATUM_TYPE_POINTER | \\\r\n";
		retStr += "                                    PCD_DATUM_TYPE_UINT8  | \\\r\n";
		retStr += "                                    PCD_DATUM_TYPE_UINT16 | \\\r\n";
		retStr += "                                    PCD_DATUM_TYPE_UINT32 | \\\r\n";
		retStr += "                                    PCD_DATUM_TYPE_UINT64)\r\n";
		retStr += "\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_DATABASE_OFFSET_MASK (~(PCD_TYPE_ALL_SET | PCD_DATUM_TYPE_ALL_SET))\r\n";
		retStr += "\r\n";
		retStr += "typedef struct  {\r\n";
		retStr += "  UINT32                ExTokenNumber;\r\n";
		retStr += "  UINT16                LocalTokenNumber;   // PCD Number of this particular platform build\r\n";
		retStr += "  UINT16                ExGuidIndex;        // Index of GuidTable\r\n";
		retStr += "} DYNAMICEX_MAPPING;\r\n";
		retStr += "\r\n";
		retStr += "\r\n";
		retStr += "typedef struct {\r\n";
		retStr += "  UINT32  SkuDataStartOffset; //We have to use offsetof MACRO as we don't know padding done by compiler\r\n";
		retStr += "  UINT32  SkuIdTableOffset;   //Offset from the PCD_DB\r\n";
		retStr += "} SKU_HEAD;\r\n";
		retStr += "\r\n";
		retStr += "\r\n";
		retStr += "typedef struct {\r\n";
		retStr += "  UINT16  GuidTableIndex;     // Offset in Guid Table in units of GUID.\r\n";
		retStr += "  UINT16  StringIndex;        // Offset in String Table in units of UINT16.\r\n";
		retStr += "  UINT16  Offset;             // Offset in Variable\r\n";
		retStr += "  UINT16  DefaultValueOffset; // Offset of the Default Value\r\n";
		retStr += "} VARIABLE_HEAD  ;\r\n";
		retStr += "\r\n";
		retStr += "\r\n";
		retStr += "typedef  struct {\r\n";
		retStr += "  UINT32  Offset;\r\n";
		retStr += "} VPD_HEAD;\r\n";
		retStr += "\r\n";
		retStr += "typedef UINT16 STRING_HEAD;\r\n";
		retStr += "\r\n";
		retStr += "typedef UINT16 SIZE_INFO;\r\n";
		retStr += "\r\n";
		retStr += "#define offsetof(s,m)                 (UINT32) (UINTN) &(((s *)0)->m)\r\n";
		retStr += "\r\n";
		retStr += "\r\n";
		retStr += "\r\n";
		
		return retStr;
    }

    public static String getPcdDxeDatabaseDefinitions ()
        throws EntityException {

        String retStr = "";
		
		retStr += "\r\n";
		retStr += "typedef struct {\r\n";
		retStr += "  DXE_PCD_DATABASE_INIT Init;\r\n";
		retStr += "  DXE_PCD_DATABASE_UNINIT Uninit;\r\n";
		retStr += "} DXE_PCD_DATABASE;\r\n";
		retStr += "\r\n";
		retStr += "\r\n";
		retStr += "typedef struct {\r\n";
		retStr += "  PEI_PCD_DATABASE PeiDb;\r\n";
		retStr += "  DXE_PCD_DATABASE DxeDb;\r\n";
		retStr += "} PCD_DATABASE;\r\n";
		retStr += "\r\n";
		retStr += "#define DXE_NEX_TOKEN_NUMBER (DXE_LOCAL_TOKEN_NUMBER - DXE_EX_TOKEN_NUMBER)\r\n";
		retStr += "\r\n";
		retStr += "#define PCD_TOTAL_TOKEN_NUMBER (PEI_LOCAL_TOKEN_NUMBER + DXE_LOCAL_TOKEN_NUMBER)\r\n";
		retStr += "\r\n";
		retStr += "\r\n";

        return retStr;
    }

    public static String getPcdPeiDatabaseDefinitions ()
        throws EntityException {

		String retStr = "";
		
		retStr += "\r\n";
		retStr += "typedef struct {\r\n";
		retStr += "  PEI_PCD_DATABASE_INIT Init;\r\n";
		retStr += "  PEI_PCD_DATABASE_UNINIT Uninit;\r\n";
		retStr += "} PEI_PCD_DATABASE;\r\n";
		retStr += "\r\n";
		retStr += "#define PEI_NEX_TOKEN_NUMBER (PEI_LOCAL_TOKEN_NUMBER - PEI_EX_TOKEN_NUMBER)\r\n";
		retStr += "\r\n";

        return retStr;
    }

    /**
       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 EntityException {
        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 EntityException ("[FPD file error] 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);
    }
}
