ソースを参照

Merge pull request #146 from blackberry-gaming/properties

Properties inheritance support.
Steve Grenier 14 年 前
コミット
6f7ca0db49
2 ファイル変更177 行追加15 行削除
  1. 164 11
      gameplay/src/Properties.cpp
  2. 13 4
      gameplay/src/Properties.h

+ 164 - 11
gameplay/src/Properties.cpp

@@ -6,22 +6,44 @@
 namespace gameplay
 {
 
+Properties::Properties()
+{
+}
+
+Properties::Properties(const Properties& copy)
+{
+    _namespace = copy._namespace;
+    _id = copy._id;
+    _parentID = copy._parentID;
+    _properties = copy._properties;
+    
+    _namespaces = std::vector<Properties*>();
+    std::vector<Properties*>::const_iterator it;
+    for (it = copy._namespaces.begin(); it < copy._namespaces.end(); it++)
+    {
+        _namespaces.push_back(new Properties(**it));
+    }
+    rewind();
+}
+
 Properties::Properties(FILE* file)
 {
     readProperties(file);
-    _propertiesItr = _properties.end();
-    _namespacesItr = _namespaces.end();
+    rewind();
 }
 
-Properties::Properties(FILE* file, const char* name, const char* id) : _namespace(name)
+Properties::Properties(FILE* file, const char* name, const char* id, const char* parentID) : _namespace(name)
 {
     if (id)
     {
         _id = id;
     }
+    if (parentID)
+    {
+        _parentID = parentID;
+    }
     readProperties(file);
-    _propertiesItr = _properties.end();
-    _namespacesItr = _namespaces.end();
+    rewind();
 }
 
 Properties* Properties::create(const char* filePath)
@@ -36,6 +58,8 @@ Properties* Properties::create(const char* filePath)
 
     Properties* properties = new Properties(file);
 
+    properties->resolveInheritance();
+
     fclose(file);
 
     return properties;
@@ -47,7 +71,9 @@ void Properties::readProperties(FILE* file)
     int c;
     char* name;
     char* value;
+    char* parentID;
     char* rc;
+    char* rcc;
 
     while (true)
     {
@@ -104,11 +130,16 @@ void Properties::readProperties(FILE* file)
             }
             else
             {
+                parentID = NULL;
+
                 // This line might begin or end a namespace,
                 // or it might be a key/value pair without '='.
 
                 // Check for '{' on same line.
                 rc = strchr(line, '{');
+
+                // Check for inheritance: ':'
+                rcc = strchr(line, ':');
             
                 // Get the name of the namespace.
                 name = strtok(line, " \t\n{");
@@ -124,12 +155,18 @@ void Properties::readProperties(FILE* file)
                 }
 
                 // Get its ID if it has one.
-                value = strtok(NULL, "{");
+                value = strtok(NULL, ":{");
                 value = trimWhiteSpace(value);
+                if (rcc != NULL)
+                {
+                    parentID = strtok(NULL, "{");
+                    parentID = trimWhiteSpace(parentID);
+                }
+
                 if (value != NULL && value[0] == '{')
                 {
                     // New namespace without an ID.
-                    Properties* space = new Properties(file, name, NULL);
+                    Properties* space = new Properties(file, name, NULL, parentID);
                     _namespaces.push_back(space);
                 }
                 else
@@ -138,7 +175,7 @@ void Properties::readProperties(FILE* file)
                     if (rc != NULL)
                     {
                         // Create new namespace.
-                        Properties* space = new Properties(file, name, value);
+                        Properties* space = new Properties(file, name, value, parentID);
                         _namespaces.push_back(space);
                     }
                     else
@@ -149,7 +186,7 @@ void Properties::readProperties(FILE* file)
                         if (c == '{')
                         {
                             // Create new namespace.
-                            Properties* space = new Properties(file, name, value);
+                            Properties* space = new Properties(file, name, value, parentID);
                             _namespaces.push_back(space);
                         }
                         else
@@ -228,7 +265,123 @@ char* Properties::trimWhiteSpace(char *str)
     return str;
 }
 
-const char* Properties::getNextProperty(const char** value)
+void Properties::resolveInheritance(const char* id)
+{
+    // Namespaces can be defined like so:
+    // "name id : parentID { }"
+    // This method merges data from the parent namespace into the child.
+
+    // Get a top-level namespace.
+    Properties* derived;
+    if (id)
+    {
+        derived = getNamespace(id);
+    }
+    else
+    {
+        derived = getNextNamespace();
+    }
+    while (derived)
+    {
+        // If the namespace has a parent ID, find the parent.
+        if (!derived->_parentID.empty())
+        {
+            Properties* parent = getNamespace(derived->_parentID.c_str());
+            if (parent)
+            {
+                resolveInheritance(parent->getId());
+
+                // Copy the child.
+                Properties* overrides = new Properties(*derived);
+
+                // Delete the child's data.
+                unsigned int count = derived->_namespaces.size();
+                for (unsigned int i = 0; i < count; i++)
+                {
+                    SAFE_DELETE(derived->_namespaces[i]);
+                }
+
+                // Copy data from the parent into the child.
+                derived->_properties = parent->_properties;
+                derived->_namespaces = std::vector<Properties*>();
+                std::vector<Properties*>::const_iterator itt;
+                for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); itt++)
+                {
+                    derived->_namespaces.push_back(new Properties(**itt));
+                }
+                derived->rewind();
+
+                // Take the original copy of the child and override the data copied from the parent.
+                derived->mergeWith(overrides);
+
+                // Delete the child copy.
+                SAFE_DELETE(overrides);
+            }
+        }
+
+        // Resolve inheritance within this namespace.
+        derived->resolveInheritance();
+
+        // Get the next top-level namespace and check again.
+        if (!id)
+        {
+            derived = getNextNamespace();
+        }
+        else
+        {
+            derived = NULL;
+        }
+    }
+}
+
+void Properties::mergeWith(Properties* overrides)
+{
+    // Overwrite or add each property found in child.
+    char* value = new char[255];
+    overrides->rewind();
+    const char* name = overrides->getNextProperty(&value);
+    while (name)
+    {
+        this->_properties[name] = value;
+        name = overrides->getNextProperty(&value);
+    }
+    SAFE_DELETE(value);
+    this->_propertiesItr = this->_properties.end();
+
+    // Merge all common nested namespaces, add new ones.
+    Properties* overridesNamespace = overrides->getNextNamespace();
+    while (overridesNamespace)
+    {
+        bool merged = false;
+
+        rewind();
+        Properties* derivedNamespace = getNextNamespace();
+        while (derivedNamespace)
+        {
+            if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 &&
+                strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0)
+            {   
+                derivedNamespace->mergeWith(overridesNamespace);
+                merged = true;
+            }
+
+            derivedNamespace = getNextNamespace();
+        }
+
+        if (!merged)
+        {
+            // Add this new namespace.
+            Properties* newNamespace = new Properties(*overridesNamespace);
+
+            this->_namespaces.push_back(newNamespace);
+            this->_namespacesItr = this->_namespaces.end();
+        }
+
+        overridesNamespace = overrides->getNextNamespace();
+    }
+}
+
+const char* Properties::getNextProperty(char** value)
 {
     if (_propertiesItr == _properties.end())
     {
@@ -248,7 +401,7 @@ const char* Properties::getNextProperty(const char** value)
         {
             if (value)
             {
-                *value = _propertiesItr->second.c_str();
+                strcpy(*value, _propertiesItr->second.c_str());
             }
             return name.c_str();
         }

+ 13 - 4
gameplay/src/Properties.h

@@ -97,7 +97,7 @@ void printProperties(Properties* properties)
 {
     // Print the name and ID of the current namespace.
     const char* spacename = properties->getNamespace();
-    const char* id = properties->getID();
+    const char* id = properties->getId();
     WARN_VARG("Namespace: %s  ID: %s\n{", spacename, id);
  
     // Print all properties in this namespace.
@@ -163,7 +163,7 @@ public:
      * 
      * @return The name of the next property, or NULL if there are no properties remaining.
      */
-    const char* getNextProperty(const char** value = NULL);
+    const char* getNextProperty(char** value = NULL);
 
     /**
      * Get the next namespace.
@@ -371,14 +371,16 @@ public:
 private:
     
     /**
-     * Constructor.
+     * Constructors.
      */
+    Properties();
     Properties(FILE* file);
+    Properties(const Properties& copy);
 
     /**
      * Constructor. Read from the beginning of namespace specified
      */
-    Properties(FILE* file, const char* name, const char* id = NULL);
+    Properties(FILE* file, const char* name, const char* id = NULL, const char* parentID = NULL);
 
     void readProperties(FILE* file);
 
@@ -386,8 +388,15 @@ private:
 
     char* trimWhiteSpace(char* str);
 
+    // Called after create(); copies info from parents into derived namespaces.
+    void resolveInheritance(const char* id = NULL);
+
+    // Called by resolveInheritance().
+    void mergeWith(Properties* overrides);
+
     std::string _namespace;
     std::string _id;
+    std::string _parentID;
     std::map<std::string, std::string> _properties;
     std::map<std::string, std::string>::const_iterator _propertiesItr;
     std::vector<Properties*> _namespaces;