CloverBootloader/rEFIt_UEFI/Settings/ConfigPlist
2021-05-08 16:24:10 +03:00
..
Config_ACPI_DSDT.cpp Fix bug in ConfigPlist when an unknown key happens is ACPI/DSDT/Fixes. 2021-05-04 13:03:40 +03:00
Config_ACPI_DSDT.h Fix bug in ConfigPlist when an unknown key happens is ACPI/DSDT/Fixes. 2021-05-04 13:03:40 +03:00
Config_ACPI_SSDT.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_ACPI.h Fix visual studio compilation. 2021-05-08 12:34:17 +03:00
Config_Boot.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_BootGraphics.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_CPU.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Devices_AddProperties.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Devices_Arbitrary.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Devices_Audio.h Cleaning and improvement of PlatformPOSIX. 2021-05-05 13:20:40 +03:00
Config_Devices_FakeID.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Devices_Properties.h DO NOT explicitly call default ctor with new because of clang bug. 2021-05-05 20:10:10 +03:00
Config_Devices_USB.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Devices.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Graphics.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_GUI.cpp New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_GUI.h Stop ignoring Mouse parameters. 2021-05-08 16:24:10 +03:00
Config_KernelAndKextPatches.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Quirks.cpp New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_Quirks.h Fix visual studio compilation. 2021-05-08 12:34:17 +03:00
Config_RtVariables.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
Config_SystemParameters.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
ConfigPlistAbstract.cpp New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
ConfigPlistAbstract.h New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
ConfigPlistClass.h Fix visual studio compilation. 2021-05-08 12:34:17 +03:00
README.md New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
SMBIOSPlist.cpp New parser XML. New ConfigManager. A lot of refactoring... 2021-04-28 21:30:34 +03:00
SMBIOSPlist.h Fix visual studio compilation. 2021-05-08 12:34:17 +03:00

How to use the xmlLite tools to read and validate plist

Creation of a dict

Example 1 :

class MyDictClass : public XmlDict
{
    using super = XmlDict;
  public:
    XmlBool aBool;
    XmlInt32 anInt32;

    XmlDictField m_fields[2] = {
        {"KeyNameForBool", aBool},
        {"KeyNameForInt32", anInt32},
    };
    virtual void getFields(XmlDictField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };
} MyDict;
  1. your class must heritate from XmlDict

  2. define an array to create a link between the xml key in the dict and the field.

  3. override method getFields() to return that array.

This can read this file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>KeyNameForBool</key>
    <true/>
    <key>KeyNameForInt32</key>
    <integer>13864</integer>
</dict>

NOTE : the top level dict can heritate from ConfigPlistAbstractClass instead. That'll give you parse() methods to parse from a buffer

Values

A dict is composed of key value pair. In the previous we used XmlBool and XmlInt32. In XmlLiteSimpleType.h there is few others :

  • XmlBool

  • XmlStrictBool

  • XmlString8

  • XmlStringW

  • XmlString8AllowEmpty

  • XmlString8Trimed

  • XmlData

  • XmlUInt{8, 16 32} and XmlInt {8, 16 32}

  • Of course, you can declare an XmlDict as a value in a containing dict. Example 2 :

class MyInsideDictClass : public XmlDict
{
    using super = XmlDict;
  public:
    XmlBool aBool;
    XmlInt32 anInt32;

    XmlDictField m_fields[2] = {
        {"KeyNameForBool", aBool},
        {"KeyNameForInt32", anInt32},
    };
    virtual void getFields(XmlDictField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };
};

class MyTopLevelDictClass : public ConfigPlistAbstractClass
{
    using super = XmlDict;
  public:
    MyInsideDictClass inside1;

    XmlDictField m_fields[1] = {
        {"KeyNameForInsideDict", inside1},
    };
    virtual void getFields(XmlDictField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };
} MyDict;

This can read this file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>KeyNameForInsideDict</key>
    <dict>
      <key>KeyNameForBool</key>
      <true/>
      <key>KeyNameForInt32</key>
      <integer>13864</integer>
  </dict>
</dict>
  • Arrays

Validation

Validation of a single value

To validate and reject bad input, override the method validate. Imagine we have an integer value that is a count between 0 and 15. Example 3 :

class CountClass : public XmlInt64
{
  using super = XmlInt64;
  virtual bool validate(XmlLiteParser* xmlLiteParser, const XString8& xmlPath, const XmlParserPosition& keyPos, bool generateErrors) override {
    if ( !super::validate(xmlLiteParser, xmlPath, keyPos, generateErrors) ) return false;
    if ( value() < -2 ) {
        xmlLiteParser->addWarning(generateErrors, S8Printf("Count cannot be negative. It must a number between -2 and 18 inclusive at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
        return false;
    }
    if ( value() > 18 ) {
        xmlLiteParser->addWarning(generateErrors, S8Printf("Count cannot > 18. It must a number between -2 and 18 inclusive at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
        return false;
    }
    return true;
  }
} Count = CountClass();

Returning false will put back the value to an undefined state.

In your dict, declare a member CountClass Count. If the value is out of range, Count will be undefined.

Validation of interdependent values

To check for consistency of values in a dict, you override the same validate method for your dict.

Imagine you have a dict containing a type (1 or 2) and a subType, but type 1 cannot have subtype. Type 2 may have subtype, and if there is, it must be 11 or 12. Example 4 :

class MyDictClass : public XmlDict
{
    using super = XmlDict;
  public:
    MyXmlType type;       // this is a subclass of XmlUInt8 that check that type is 1 or 2
    MyXmlSubType subType; // this is a subclass of XmlUInt8 that check that subtype is 11 or 12
    XmlString8 name;      // as many other field that there is in this dict

    XmlDictField m_fields[2] = {
        {"Type", type},
        {"SubType", subType},
    };
    virtual void getFields(XmlDictField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };

    virtual bool validate(XmlLiteParser* xmlLiteParser, const XString8& xmlPath, const XmlParserPosition& keyPos, bool generateErrors) override {
      if ( !super::validate(xmlLiteParser, xmlPath, keyPos, generateErrors) ) return false;
      if ( !type.isDefined() ) {
          xmlLiteParser->addWarning(generateErrors, S8Printf("Type must befined at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
          return false;
      }
      if ( type.value() == 1 ) {
        if ( subType.isDefined() ) {
            xmlLiteParser->addWarning(generateErrors, S8Printf("Type 1 cannot have a subtype at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
            return false;
        }
      }else if ( type.value() == 2 ) {
        // nothing to do because subtype is optional, and if it exists, weknow that the value is correct because of th validation in MyXmlSubType
      }else{
        panic("There is a bug in MyXmlType::validate() !");
      }
      return true;
    }
};

Returning false will put back the dict to an undefined state. Which means, in that case, the whole dict to be undefined as it wasn't at all in the XML file.

NOTE : it's possible to do the single field validation at dict level. The previous example could written as example 5 :

class MyDictClass : public XmlDict
{
    using super = XmlDict;
  public:
    XmlUInt8 type; // no validation except that the value is an unsigned 8 bits int 
    XmlUInt8 subType; // no validation except that the value is an unsigned 8 bits int 
    XmlString8 name;      // as many other field that there is in this dict

    XmlDictField m_fields[2] = {
        {"Type", type},
        {"SubType", subType},
    };
    virtual void getFields(XmlDictField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };

    virtual bool validate(XmlLiteParser* xmlLiteParser, const XString8& xmlPath, const XmlParserPosition& keyPos, bool generateErrors) override {
      if ( !super::validate(xmlLiteParser, xmlPath, keyPos, generateErrors) ) return false;
      if ( !type.isDefined() ) {
          xmlLiteParser->addWarning(generateErrors, S8Printf("Type must befined at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
          return false;
      }
      if ( type.value() == 1 ) {
        if ( subType.isDefined() ) {
            xmlLiteParser->addWarning(generateErrors, S8Printf("Type 1 cannot have a subtype at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
            return false;
        }
      }else if ( type.value() == 2 ) {
        if ( subType.isDefined() ) {
            if ( type.value() != 11 and type.value() != 12 )
            xmlLiteParser->addWarning(generateErrors, S8Printf("SubType must be 11 or 12 at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
            return false;
        }else{
          // subtype is optional, so it's ok.
        }
      }else{
        xmlLiteParser->addWarning(generateErrors, S8Printf("Type must be 1 or 2 at '%s:%d'", xmlPath.c_str(), keyPos.getLine()));
        // Let's think that we want to ignore this value but we syill want to keep the dict as the other field still has meaning.
        type.reset(); // we only reset this field. We don't return false because that'll undefine the whole dict
        subtype.reset(); // SubType means nothing without a Type.
      }
      return true;
    }
};

Arrays

To create an array, declare an XmlArray<[any class that inherite from XmlAbstractType]> member. Example 6 :

class MyDictClass : public XmlDict
{
    using super = XmlDict;
  public:
    XmlArray<XmlBool> aBoolArray;

    XmlDictField m_fields[2] = {
        {"KeyNameForBoolArray", aBoolArray},
    };
    virtual void getFields(XmlDictField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };
};

This can read this file :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>KeyNameForBoolArray</key>
    <array>
      <true/>
      <false/>
      <true/>
      ... {as many as ther is}
  </dict>
</dict>

Of course, you can create an XmlArray of a dict you created.

Repeating dict

Until now, examples shown a dict that has a predefined set of keys. But you can have a dict that contains repeating key/value pair. The key could be any string.

For that, you have a template class XmlRepeatingDict. To create a dict that can contains a sequence of key and int32. Example 7 :

class MyDictClass : public ConfigPlistAbstractClass
{
    using super = XmlDict;
  public:
    XmlRepeatingDict<XmlAddKey<XmlKey, XmlInt32>> keyIntPairs;

    XmlDictField m_fields[1] = {
        {"KeyNameForKeyIntPairs", keyIntPairs},
    };
    virtual void getFields(XmlDictField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };
} MyDict;

Because a dict has to be a sequence of key and value, please note the use of the class XmlAddKey<XmlKey, XmlInt32>> that "adds" a key to a XmlInt32. You can replace XmlInt32 by any type the inherite from XmlAbstractType. That includes your own dicts and arrays.

The example above will parse a file like :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>KeyNameForKeyIntPairs</key>
    <dict>
        <key>a key</key>
        <integer>1</integer>
        <key>another key</key>
        <integer>2</integer>
        <key>third key</key>
        <integer>3</integer>
    </dict>
</dict>

After the dict is parsed, you can access values this way :

XObjArray<XmlAddKey<XmlKey, XmlInt32>> array = MyDict.keyIntPairs.valueArray();
XString8 keyOne = array[1].key(); // == "another key"
int32_t valueOne = array[1].value(); // == 2

Union

Sometimes, a value can be, for example, a bool or a string. In that case it's easy to declare. Just subclass XmlUnion :

class XmlBoolOrString : public XmlUnion
{
  using super = XmlUnion;
public:
  XmlBool xmlBool = XmlBool();
  XmlString8 xmlString8 = XmlString8();
  virtual const char* getDescription() override { return "bool or string"; };
  XmlUnionField m_fields[2] = { xmlBool, xmlString8 };
  virtual void getFields(XmlUnionField** fields, size_t* nb) override { *fields = m_fields; *nb = sizeof(m_fields)/sizeof(m_fields[0]); };
};

In this example, the value will be tried to be parsed as a bool. If it doesn't work, the next possibility is tried. Here it's xmlString8. You can create union with any kind of field you create, including dict and arrays.