#ifndef PROPERTIES_H_
#define PROPERTIES_H_
#include "Base.h"
#include "Matrix.h"
#include "Vector2.h"
namespace gameplay
{
/**
* Defines a utility for loading text files in the GamePlay "properties" files
* and reading primitive types and GamePlay math classes out of them.
*
* This class is used by ParticleEmitter, Animation and Materials to create objects
* of these types from text files.
*
* A properties file has very simple syntax and can contain only namespaces and
* name/value pairs (the properties of a namespace).
* The file can have any file extension a user specifies.
*
* Here's an example of a simple
* file that uses all the available features of the markup language:
@verbatim
// This is a comment.
// This property is in the default namespace:
integerProperty = 5
// This line defines a namespace of type "mynamespace" without an ID:
mynamespace
{
// This namespace can be retrieved by searching for its ID, "spriteTexture",
// or by its name "texture":
texture spriteTexture
{
fileName = sprite.png
width = 64
height = 64
}
// This property is in the "space" namespace:
booleanProperty = true
// It's legal to have a name without a value if you leave out the '=' character:
foo
// In fact, the '=' character is optional if you'd rather write:
bar 23
// But don't write this or you'll get an error:
// illegalProperty =
// Or this:
// = 15
// Properties objects let you retrieve values as various types.
floatProperty = 3.333
stringProperty = This is a string.
vector3Property = 1.0, 5.0, 3.55
colorProperty = 1.0, 0.4, 0.0, 1.0
}
@endverbatim
* Retrieving information out of a file like this could be done in two ways. If the
* available namespaces and name/value pairs are known in advance they can be queried by ID or name.
* For example, if the namespace "spriteTexture" and its properties are required then they can
* be retrieved with a call to getNamespace() followed by calls to getString() and getInt().
* A namespace is stored and retrieved as a Properties object.
* Reading the spriteTexture properties out of the file above in this way could be done with the following code:
@verbatim
// Create the top-level Properties object.
Properties* properties = Properties::create("example.properties");
// Retrieve the "spriteTexture" namespace.
Properties* spriteTexture = properties->getNamespace("spriteTexture");
// Get the values of known texture properties out of the namespace.
const char* fileName = spriteTexture->getString("fileName");
int width = spriteTexture->getInt("width");
int height = spriteTexture->getInt("height");
// Deleting the top-level Properties object will clean up all nested namespaces.
SAFE_DELETE(properties);
@endverbatim
* On the other hand, if the structure of the file is not known in advance its
* namespaces and name/value pairs can be retrieved one by one using the getNextNamespace()
* and getNextProperty() methods. The following method prints the contents of any properties file
* to the console:
@verbatim
void printProperties(Properties* properties)
{
// Print the name and ID of the current namespace.
const char* spacename = properties->getNamespace();
const char* id = properties->getId();
GP_WARN("Namespace: %s ID: %s\n{", spacename, id);
// Print all properties in this namespace.
const char* name = properties->getNextProperty();
const char* value = NULL;
while (name != NULL)
{
value = properties->getString(name);
GP_WARN("%s = %s", name, value);
name = properties->getNextProperty();
}
GP_WARN("}\n");
// Print the properties of every namespace within this one.
Properties* space = properties->getNextNamespace();
while (space != NULL)
{
printProperties(space);
space = properties->getNextNamespace();
}
}
@endverbatim
* Note that this method does not keep track of the namespace hierarchy, but could be
* modified to do so. Also note that nothing in a properties file indicates the type
* of a property. If the type is unknown, its string can be retrieved and interpreted
* as necessary.
*/
class Properties
{
friend class Game;
public:
/**
* Data types supported by the properties class.
*/
enum Type
{
NONE,
STRING,
NUMBER,
VECTOR2,
VECTOR3,
VECTOR4,
MATRIX
};
/**
* Creates a Properties runtime settings from the specified URL, where the URL is of
* the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
* (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
*
* @param url The URL to create the properties from.
* @script{create}
*/
static Properties* create(const char* url);
/**
* Destructor.
*/
~Properties();
/**
* Get the name of the next property.
*
* @param value Optional pointer to a const char* to store the value of the next property in.
*
* @return The name of the next property, or NULL if there are no properties remaining.
* @script{ignore}
*/
const char* getNextProperty(char** value = NULL);
/**
* Get the next namespace.
*/
Properties* getNextNamespace();
/**
* Rewind the getNextProperty() and getNextNamespace() iterators
* to the beginning of the file.
*/
void rewind();
/**
* Get a specific namespace by ID or name. This method will perform
* a depth-first search on all namespaces and inner namespaces within
* this Property.
*
* @param id The ID or name of the namespace to find.
* @param searchNames If true, namespace names are used in the search,
* instead of namespace IDs. By default this parameter is false
* and namespace IDs are searched.
*
* @return A properties object with the given ID or name.
*/
Properties* getNamespace(const char* id, bool searchNames = false) const;
/**
* Get the name of this Property's namespace.
*
* @return The name of this Property's namespace.
*/
const char* getNamespace() const;
/**
* Get the ID of this Property's namespace. The ID should be a unique identifier,
* but its uniqueness is not enforced.
*
* @return The ID of this Property's namespace.
*/
const char* getId() const;
/**
* Check if a property with the given name is specified in this Properties object.
*
* @param name The name of the property to query.
*
* @return True if the property exists, false otherwise.
*/
bool exists(const char* name) const;
/**
* Returns the type of a property.
*
* @param name The name of the property to interpret, or NULL to return the current property's type.
*
* @return The type of the property.
*/
Type getType(const char* name = NULL) const;
/**
* Get the value of the given property as a string. This can always be retrieved,
* whatever the intended type of the property.
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
*
* @return The value of the given property as a string, or the empty string if no property with that name exists.
*/
const char* getString(const char* name = NULL) const;
/**
* Interpret the value of the given property as a boolean.
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param defaultValue the default value to return if the specified property does not exist within the properties file.
*
* @return true if the property exists and its value is "true", otherwise false.
*/
bool getBool(const char* name = NULL, bool defaultValue = false) const;
/**
* Interpret the value of the given property as an integer.
* If the property does not exist, zero will be returned.
* If the property exists but could not be scanned, an error will be logged and zero will be returned.
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
*
* @return The value of the given property interpreted as an integer.
* Zero if the property does not exist or could not be scanned.
*/
int getInt(const char* name = NULL) const;
/**
* Interpret the value of the given property as a floating-point number.
* If the property does not exist, zero will be returned.
* If the property exists but could not be scanned, an error will be logged and zero will be returned.
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
*
* @return The value of the given property interpreted as a float.
* Zero if the property does not exist or could not be scanned.
*/
float getFloat(const char* name = NULL) const;
/**
* Interpret the value of the given property as a long integer.
* If the property does not exist, zero will be returned.
* If the property exists but could not be scanned, an error will be logged and zero will be returned.
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
*
* @return The value of the given property interpreted as a long.
* Zero if the property does not exist or could not be scanned.
*/
long getLong(const char* name = NULL) const;
/**
* Interpret the value of the given property as a Matrix.
* If the property does not exist, out will be set to the identity matrix.
* If the property exists but could not be scanned, an error will be logged and out will be set
* to the identity matrix.
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param out The matrix to set to this property's interpreted value.
*
* @return True on success, false if the property does not exist or could not be scanned.
*/
bool getMatrix(const char* name, Matrix* out) const;
/**
* Interpret the value of the given property as a Vector2.
* If the property does not exist, out will be set to Vector2(0.0f, 0.0f).
* If the property exists but could not be scanned, an error will be logged and out will be set
* to Vector2(0.0f, 0.0f).
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param out The vector to set to this property's interpreted value.
*
* @return True on success, false if the property does not exist or could not be scanned.
*/
bool getVector2(const char* name, Vector2* out) const;
/**
* Interpret the value of the given property as a Vector3.
* If the property does not exist, out will be set to Vector3(0.0f, 0.0f, 0.0f).
* If the property exists but could not be scanned, an error will be logged and out will be set
* to Vector3(0.0f, 0.0f, 0.0f).
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param out The vector to set to this property's interpreted value.
*
* @return True on success, false if the property does not exist or could not be scanned.
*/
bool getVector3(const char* name, Vector3* out) const;
/**
* Interpret the value of the given property as a Vector4.
* If the property does not exist, out will be set to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
* If the property exists but could not be scanned, an error will be logged and out will be set
* to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param out The vector to set to this property's interpreted value.
*
* @return True on success, false if the property does not exist or could not be scanned.
*/
bool getVector4(const char* name, Vector4* out) const;
/**
* Interpret the value of the given property as a Quaternion specified as an axis angle.
* If the property does not exist, out will be set to Quaternion().
* If the property exists but could not be scanned, an error will be logged and out will be set
* to Quaternion().
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param out The quaternion to set to this property's interpreted value.
*
* @return True on success, false if the property does not exist or could not be scanned.
*/
bool getQuaternionFromAxisAngle(const char* name, Quaternion* out) const;
/**
* Interpret the value of the given property as an RGB color in hex and write this color to a Vector3.
* E.g. 0xff0000 represents red and sets the vector to (1, 0, 0).
* If the property does not exist, out will be set to Vector3(0.0f, 0.0f, 0.0f).
* If the property exists but could not be scanned, an error will be logged and out will be set
* to Vector3(0.0f, 0.0f, 0.0f).
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param out The vector to set to this property's interpreted value.
*
* @return True on success, false if the property does not exist or could not be scanned.
*/
bool getColor(const char* name, Vector3* out) const;
/**
* Interpret the value of the given property as an RGBA color in hex and write this color to a Vector4.
* E.g. 0xff0000ff represents opaque red and sets the vector to (1, 0, 0, 1).
* If the property does not exist, out will be set to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
* If the property exists but could not be scanned, an error will be logged and out will be set
* to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
*
* @param name The name of the property to interpret, or NULL to return the current property's value.
* @param out The vector to set to this property's interpreted value.
*
* @return True on success, false if the property does not exist or could not be scanned.
*/
bool getColor(const char* name, Vector4* out) const;
private:
/**
* 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, const char* parentID = NULL);
void readProperties(FILE* file);
void skipWhiteSpace(FILE* file);
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);
// Clones the Properties object.
Properties* clone();
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;
std::vector<Properties*>::const_iterator _namespacesItr;
};
}
#endif