Browse Source

Update comments. Generalize XMLParser. Data model and variables refactoring.

Michael Ragazzon 5 years ago
parent
commit
951a54a600

+ 3 - 0
CMake/FileList.cmake

@@ -113,6 +113,8 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Core.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataController.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataModel.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataTypeRegister.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataTypes.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataVariable.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataView.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Debug.h
@@ -203,6 +205,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/DataControllerDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataModel.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataParser.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/DataTypeRegister.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataVariable.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataView.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataViewDefault.cpp

+ 3 - 0
Include/RmlUi/Core.h

@@ -39,8 +39,11 @@
 #include "Core/ComputedValues.h"
 #include "Core/Context.h"
 #include "Core/ContextInstancer.h"
+#include "Core/DataTypes.h"
 #include "Core/DataController.h"
 #include "Core/DataModel.h"
+#include "Core/DataTypeRegister.h"
+#include "Core/DataVariable.h"
 #include "Core/DataView.h"
 #include "Core/Decorator.h"
 #include "Core/DecoratorInstancer.h"

+ 14 - 25
Include/RmlUi/Core/DataModel.h

@@ -32,24 +32,24 @@
 #include "Header.h"
 #include "Types.h"
 #include "Traits.h"
-#include "Variant.h"
-#include "DataView.h"
+#include "DataTypes.h"
+#include "DataTypeRegister.h"
 #include "DataController.h"
-#include "DataVariable.h"
+#include "DataView.h"
 
 namespace Rml {
 namespace Core {
 
-class Element;
 
 class RMLUICORE_API DataModel : NonCopyMoveable {
 public:
 	DataModel(const TransformFuncRegister* transform_register = nullptr) : transform_register(transform_register) {}
 
-	void AddView(DataViewPtr view) { views.Add(std::move(view)); }
-	void AddController(DataControllerPtr controller) { controllers.Add(std::move(controller)); }
+	void AddView(DataViewPtr view);
+	void AddController(DataControllerPtr controller);
 
-	bool BindVariable(const String& name, Variable variable);
+	bool BindVariable(const String& name, DataVariable variable);
+	bool BindFunc(const String& name, DataGetFunc get_func, DataSetFunc set_func);
 
 	bool BindEventCallback(const String& name, DataEventFunc event_func);
 
@@ -57,21 +57,10 @@ public:
 	bool EraseAliases(Element* element);
 
 	DataAddress ResolveAddress(const String& address_str, Element* element) const;
-
-	Variable GetVariable(const DataAddress& address) const;
-
 	const DataEventFunc* GetEventCallback(const String& name);
 
-	template<typename T>
-	bool GetValue(const DataAddress& address, T& out_value) const {
-		Variant variant;
-		Variable variable = GetVariable(address);
-		return variable && variable.Get(variant) && variant.GetInto<T>(out_value);
-	}
-	bool GetValue(const DataAddress& address, Variant& out_value) const {
-		Variable variable = GetVariable(address);
-		return variable && variable.Get(out_value);
-	}
+	DataVariable GetVariable(const DataAddress& address) const;
+	bool GetVariableInto(const DataAddress& address, Variant& out_value) const;
 
 	void DirtyVariable(const String& variable_name);
 	bool IsVariableDirty(const String& variable_name) const;
@@ -86,9 +75,10 @@ private:
 	DataViews views;
 	DataControllers controllers;
 
-	UnorderedMap<String, Variable> variables;
+	UnorderedMap<String, DataVariable> variables;
 	DirtyVariables dirty_variables;
 
+	UnorderedMap<String, UniquePtr<FuncDefinition>> functions;
 	UnorderedMap<String, DataEventFunc> event_callbacks;
 
 	using ScopedAliases = UnorderedMap<Element*, SmallUnorderedMap<String, DataAddress>>;
@@ -140,13 +130,12 @@ public:
 	// @note For non-scalar types make sure they first have been registered with the appropriate 'Register...()' functions.
 	template<typename T> bool Bind(const String& name, T* ptr) {
 		RMLUI_ASSERTMSG(ptr, "Invalid pointer to data variable");
-		return model->BindVariable(name, Variable(type_register->GetOrAddScalar<T>(), ptr));
+		return model->BindVariable(name, DataVariable(type_register->GetOrAddScalar<T>(), ptr));
 	}
 
 	// Bind a get/set function pair.
 	bool BindFunc(const String& name, DataGetFunc get_func, DataSetFunc set_func = {}) {
-		VariableDefinition* func_definition = type_register->RegisterFunc(std::move(get_func), std::move(set_func));
-		return model->BindVariable(name, Variable(func_definition, nullptr));
+		return model->BindFunc(name, std::move(get_func), std::move(set_func));
 	}
 
 	// Bind an event callback.
@@ -170,7 +159,7 @@ public:
 	// Register an array type.
 	// @note The type applies to every data model associated with the current Context.
 	// @note If 'Container::value_type' represents a non-scalar type, that type must already have been registered with the appropriate 'Register...()' functions.
-	// @note Container requires the following functions implemented: size() and operator[]. This is satisfied by std::vector and std::array.
+	// @note Container requires the following functions to be implemented: size() and begin(). This is satisfied by several containers such as std::vector and std::array.
 	template<typename Container>
 	bool RegisterArray() {
 		return type_register->RegisterArray<Container>();

+ 209 - 0
Include/RmlUi/Core/DataTypeRegister.h

@@ -0,0 +1,209 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREDATATYPEREGISTER_H
+#define RMLUICOREDATATYPEREGISTER_H
+
+#include "Header.h"
+#include "Types.h"
+#include "Traits.h"
+#include "Variant.h"
+#include "DataTypes.h"
+#include "DataVariable.h"
+
+
+namespace Rml {
+namespace Core {
+
+
+template<typename T>
+struct is_valid_data_scalar {
+	static constexpr bool value = std::is_arithmetic<T>::value
+		|| std::is_same<typename std::remove_cv<T>::type, String>::value;
+};
+
+
+template<typename Object>
+class StructHandle {
+public:
+	StructHandle(DataTypeRegister* type_register, StructDefinition* struct_definition) : type_register(type_register), struct_definition(struct_definition) {}
+	
+	template <typename MemberType>
+	StructHandle<Object>& AddMember(const String& name, MemberType Object::* member_ptr);
+
+	StructHandle<Object>& AddMemberFunc(const String& name, MemberGetFunc<Object> get_func, MemberSetFunc<Object> set_func = nullptr);
+
+	explicit operator bool() const {
+		return type_register && struct_definition;
+	}
+
+private:
+	DataTypeRegister* type_register;
+	StructDefinition* struct_definition;
+};
+
+
+class RMLUICORE_API TransformFuncRegister {
+public:
+	void Register(const String& name, DataTransformFunc transform_func);
+
+	bool Call(const String& name, Variant& inout_result, const VariantList& arguments) const;
+
+private:
+	UnorderedMap<String, DataTransformFunc> transform_functions;
+};
+
+
+
+class RMLUICORE_API DataTypeRegister : NonCopyMoveable {
+public:
+	DataTypeRegister();
+	~DataTypeRegister();
+
+	template<typename T>
+	StructHandle<T> RegisterStruct()
+	{
+		static_assert(std::is_class<T>::value, "Type must be a struct or class type.");
+		FamilyId id = Family<T>::Id();
+
+		auto struct_variable = std::make_unique<StructDefinition>();
+		StructDefinition* struct_variable_raw = struct_variable.get();
+
+		bool inserted = type_register.emplace(id, std::move(struct_variable)).second;
+		if (!inserted)
+		{
+			RMLUI_ERRORMSG("Type already declared");
+			return StructHandle<T>(nullptr, nullptr);
+		}
+		
+		return StructHandle<T>(this, struct_variable_raw);
+	}
+
+	template<typename Container>
+	bool RegisterArray()
+	{
+		using value_type = typename Container::value_type;
+		VariableDefinition* value_variable = GetOrAddScalar<value_type>();
+		RMLUI_ASSERTMSG(value_variable, "Underlying value type of array has not been registered.");
+		if (!value_variable)
+			return false;
+
+		FamilyId container_id = Family<Container>::Id();
+
+		auto array_variable = std::make_unique<ArrayDefinition<Container>>(value_variable);
+		ArrayDefinition<Container>* array_variable_raw = array_variable.get();
+
+		bool inserted = type_register.emplace(container_id, std::move(array_variable)).second;
+		if (!inserted)
+		{
+			RMLUI_ERRORMSG("Array type already declared.");
+			return false;
+		}
+
+		return true;
+	}
+
+	template<typename T>
+	VariableDefinition* RegisterMemberFunc(MemberGetFunc<T> get_func, MemberSetFunc<T> set_func)
+	{
+		FamilyId id = Family<MemberGetFunc<T>>::Id();
+
+		auto result = type_register.emplace(id, nullptr);
+		auto& it = result.first;
+		bool inserted = result.second;
+
+		if (inserted)
+			it->second = std::make_unique<MemberFuncDefinition<T>>(get_func, set_func);
+
+		return it->second.get();
+	}
+
+	template<typename T, typename std::enable_if<is_valid_data_scalar<T>::value, int>::type = 0>
+	VariableDefinition* GetOrAddScalar()
+	{
+		FamilyId id = Family<T>::Id();
+
+		auto result = type_register.emplace(id, nullptr);
+		bool inserted = result.second;
+		UniquePtr<VariableDefinition>& definition = result.first->second;
+
+		if (inserted)
+			definition = std::make_unique<ScalarDefinition<T>>();
+
+		return definition.get();
+	}
+
+	template<typename T, typename std::enable_if<!is_valid_data_scalar<T>::value, int>::type = 0>
+	VariableDefinition* GetOrAddScalar()
+	{
+		return Get<T>();
+	}
+
+	template<typename T>
+	VariableDefinition* Get()
+	{
+		FamilyId id = Family<T>::Id();
+		auto it = type_register.find(id);
+		if (it == type_register.end())
+		{
+			RMLUI_ERRORMSG("Desired data type T not registered with the type register, please use the 'Register...()' functions before binding values, adding members, or registering arrays of non-scalar types.")
+			return nullptr;
+		}
+
+		return it->second.get();
+	}
+
+	TransformFuncRegister* GetTransformFuncRegister() {
+		return &transform_register;
+	}
+
+private:
+	UnorderedMap<FamilyId, UniquePtr<VariableDefinition>> type_register;
+
+	TransformFuncRegister transform_register;
+
+};
+
+template<typename Object>
+template<typename MemberType>
+inline StructHandle<Object>& StructHandle<Object>::AddMember(const String& name, MemberType Object::* member_ptr) {
+	VariableDefinition* member_type = type_register->GetOrAddScalar<MemberType>();
+	struct_definition->AddMember(name, std::make_unique<StructMemberDefault<Object, MemberType>>(member_type, member_ptr));
+	return *this;
+}
+template<typename Object>
+inline StructHandle<Object>& StructHandle<Object>::AddMemberFunc(const String& name, MemberGetFunc<Object> get_func, MemberSetFunc<Object> set_func) {
+	VariableDefinition* definition = type_register->RegisterMemberFunc<Object>(get_func, set_func);
+	struct_definition->AddMember(name, std::make_unique<StructMemberFunc>(definition));
+	return *this;
+}
+
+}
+}
+
+#endif

+ 68 - 0
Include/RmlUi/Core/DataTypes.h

@@ -0,0 +1,68 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREDATADEFINITIONS_H
+#define RMLUICOREDATADEFINITIONS_H
+
+#include "Header.h"
+#include "Types.h"
+#include "Traits.h"
+#include "Variant.h"
+#include <functional>
+
+namespace Rml {
+namespace Core {
+
+class VariableDefinition;
+class DataTypeRegister;
+class TransformFuncRegister;
+class DataModelHandle;
+class DataVariable;
+
+using DataGetFunc = std::function<void(Variant&)>;
+using DataSetFunc = std::function<void(const Variant&)>;
+using DataTransformFunc = std::function<bool(Variant&, const VariantList&)>;
+using DataEventFunc = std::function<void(DataModelHandle, Event&, const VariantList&)>;
+
+template<typename T> using MemberGetFunc = void(T::*)(Variant&);
+template<typename T> using MemberSetFunc = void(T::*)(const Variant&);
+
+using DirtyVariables = SmallUnorderedSet<String>;
+
+struct DataAddressEntry {
+	DataAddressEntry(String name) : name(name), index(-1) { }
+	DataAddressEntry(int index) : index(index) { }
+	String name;
+	int index;
+};
+using DataAddress = std::vector<DataAddressEntry>;
+
+}
+}
+
+#endif

+ 47 - 274
Include/RmlUi/Core/DataVariable.h

@@ -33,89 +33,52 @@
 #include "Types.h"
 #include "Traits.h"
 #include "Variant.h"
-#include <functional>
+#include "DataTypes.h"
+#include <iterator>
 
 namespace Rml {
 namespace Core {
 
-class Variable;
-class DataTypeRegister;
-class DataModelHandle;
-class Event;
 
-template<typename T>
-struct is_valid_scalar {
-	static constexpr bool value = std::is_arithmetic<T>::value 
-		|| std::is_same<typename std::remove_cv<T>::type, String>::value;
-};
+enum class DataVariableType { Scalar, Array, Struct, Function, MemberFunction };
 
-enum class VariableType { Scalar, Array, Struct, Function, MemberFunction };
 
-enum class DataFunctionHandle : int {};
-
-using DataGetFunc = std::function<void(Variant&)>;
-using DataSetFunc = std::function<void(const Variant&)>;
-using DataTransformFunc = std::function<bool(Variant&, const VariantList&)>;
-using DataEventFunc = std::function<void(DataModelHandle, Event&, const VariantList&)>;
+class RMLUICORE_API DataVariable {
+public:
+	DataVariable() {}
+	DataVariable(VariableDefinition* definition, void* ptr) : definition(definition), ptr(ptr) {}
 
-template<typename T> using MemberGetFunc = void(T::*)(Variant&);
-template<typename T> using MemberSetFunc = void(T::*)(const Variant&);
+	explicit operator bool() const { return definition; }
 
+	bool Get(Variant& variant);
+	bool Set(const Variant& variant);
+	int Size();
+	DataVariable Child(const DataAddressEntry& address);
+	DataVariableType Type();
 
-struct AddressEntry {
-	AddressEntry(String name) : name(name), index(-1) { }
-	AddressEntry(int index) : index(index) { }
-	String name;
-	int index;
+private:
+	VariableDefinition* definition = nullptr;
+	void* ptr = nullptr;
 };
-using DataAddress = std::vector<AddressEntry>;
+
 
 
 class RMLUICORE_API VariableDefinition {
 public:
 	virtual ~VariableDefinition() = default;
-	VariableType Type() const { return type; }
+	DataVariableType Type() const { return type; }
 
 	virtual bool Get(void* ptr, Variant& variant);
 	virtual bool Set(void* ptr, const Variant& variant);
+
 	virtual int Size(void* ptr);
-	virtual Variable GetChild(void* ptr, const AddressEntry& address);
+	virtual DataVariable Child(void* ptr, const DataAddressEntry& address);
 
 protected:
-	VariableDefinition(VariableType type) : type(type) {}
-
-private:
-	VariableType type;
-};
-
-
-
-class Variable {
-public:
-	Variable() {}
-	Variable(VariableDefinition* definition, void* ptr) : definition(definition), ptr(ptr) {}
-
-	explicit operator bool() const { return definition && (ptr || Type() == VariableType::Function); }
-
-	inline bool Get(Variant& variant) {
-		return definition->Get(ptr, variant);
-	}
-	inline bool Set(const Variant& variant) {
-		return definition->Set(ptr, variant);
-	}
-	inline int Size() {
-		return definition->Size(ptr);
-	}
-	inline Variable GetChild(const AddressEntry& address) {
-		return definition->GetChild(ptr, address);
-	}
-	inline VariableType Type() const {
-		return definition->Type();
-	}
+	VariableDefinition(DataVariableType type) : type(type) {}
 
 private:
-	VariableDefinition* definition = nullptr;
-	void* ptr = nullptr;
+	DataVariableType type;
 };
 
 
@@ -123,7 +86,7 @@ private:
 template<typename T>
 class ScalarDefinition final : public VariableDefinition {
 public:
-	ScalarDefinition() : VariableDefinition(VariableType::Scalar) {}
+	ScalarDefinition() : VariableDefinition(DataVariableType::Scalar) {}
 
 	bool Get(void* ptr, Variant& variant) override
 	{
@@ -140,7 +103,7 @@ public:
 class FuncDefinition final : public VariableDefinition {
 public:
 
-	FuncDefinition(DataGetFunc get, DataSetFunc set) : VariableDefinition(VariableType::Function), get(std::move(get)), set(std::move(set)) {}
+	FuncDefinition(DataGetFunc get, DataSetFunc set) : VariableDefinition(DataVariableType::Function), get(std::move(get)), set(std::move(set)) {}
 
 	bool Get(void* ptr, Variant& variant) override
 	{
@@ -165,14 +128,16 @@ private:
 template<typename Container>
 class ArrayDefinition final : public VariableDefinition {
 public:
-	ArrayDefinition(VariableDefinition* underlying_definition) : VariableDefinition(VariableType::Array), underlying_definition(underlying_definition) {}
+	ArrayDefinition(VariableDefinition* underlying_definition) : VariableDefinition(DataVariableType::Array), underlying_definition(underlying_definition) {}
 
 	int Size(void* ptr) override {
 		return int(static_cast<Container*>(ptr)->size());
 	}
 
+	DataVariable MakeSizeVariable(int container_size);
+
 protected:
-	Variable GetChild(void* void_ptr, const AddressEntry& address) override
+	DataVariable Child(void* void_ptr, const DataAddressEntry& address) override
 	{
 		Container* ptr = static_cast<Container*>(void_ptr);
 		const int index = address.index;
@@ -181,14 +146,17 @@ protected:
 		if (index < 0 || index >= container_size)
 		{
 			if (address.name == "size")
-				return Variable(DataTypeRegister::GetArraySizeDefinition(), reinterpret_cast<void*>(static_cast<intptr_t>(container_size)));
+				return MakeSizeVariable(container_size);
 
 			Log::Message(Log::LT_WARNING, "Data array index out of bounds.");
-			return Variable();
+			return DataVariable();
 		}
 
-		void* next_ptr = &((*ptr)[index]);
-		return Variable(underlying_definition, next_ptr);
+		auto it = ptr->begin();
+		std::advance(it, index);
+
+		void* next_ptr = &(*it);
+		return DataVariable(underlying_definition, next_ptr);
 	}
 
 private:
@@ -201,7 +169,7 @@ public:
 	StructMember(VariableDefinition* definition) : definition(definition) {}
 	virtual ~StructMember() = default;
 
-	VariableDefinition* GetVariable() const { return definition; }
+	VariableDefinition* GetDefinition() const { return definition; }
 
 	virtual void* GetPointer(void* base_ptr) = 0;
 
@@ -233,29 +201,29 @@ public:
 
 class StructDefinition final : public VariableDefinition {
 public:
-	StructDefinition() : VariableDefinition(VariableType::Struct)
+	StructDefinition() : VariableDefinition(DataVariableType::Struct)
 	{}
 
-	Variable GetChild(void* ptr, const AddressEntry& address) override
+	DataVariable Child(void* ptr, const DataAddressEntry& address) override
 	{
 		const String& name = address.name;
 		if (name.empty())
 		{
 			Log::Message(Log::LT_WARNING, "Expected a struct member name but none given.");
-			return Variable();
+			return DataVariable();
 		}
 
 		auto it = members.find(name);
 		if (it == members.end())
 		{
 			Log::Message(Log::LT_WARNING, "Member %s not found in data struct.", name.c_str());
-			return Variable();
+			return DataVariable();
 		}
 
 		void* next_ptr = it->second->GetPointer(ptr);
-		VariableDefinition* next_variable = it->second->GetVariable();
+		VariableDefinition* next_definition = it->second->GetDefinition();
 
-		return Variable(next_variable, next_ptr);
+		return DataVariable(next_definition, next_ptr);
 	}
 
 	void AddMember(const String& name, UniquePtr<StructMember> member)
@@ -274,7 +242,7 @@ private:
 template<typename T>
 class MemberFuncDefinition final : public VariableDefinition {
 public:
-	MemberFuncDefinition(MemberGetFunc<T> get, MemberSetFunc<T> set) : VariableDefinition(VariableType::MemberFunction), get(get), set(set) {}
+	MemberFuncDefinition(MemberGetFunc<T> get, MemberSetFunc<T> set) : VariableDefinition(DataVariableType::MemberFunction), get(get), set(set) {}
 
 	bool Get(void* ptr, Variant& variant) override
 	{
@@ -296,206 +264,11 @@ private:
 };
 
 
-class DataTypeRegister;
-
-template<typename Object>
-class StructHandle {
-public:
-	StructHandle(DataTypeRegister* type_register, StructDefinition* struct_definition) : type_register(type_register), struct_definition(struct_definition) {}
-	
-	template <typename MemberType>
-	StructHandle<Object>& AddMember(const String& name, MemberType Object::* member_ptr);
-
-	StructHandle<Object>& AddMemberFunc(const String& name, MemberGetFunc<Object> get_func, MemberSetFunc<Object> set_func = nullptr);
-
-	explicit operator bool() const {
-		return type_register && struct_definition;
-	}
-
-private:
-	DataTypeRegister* type_register;
-	StructDefinition* struct_definition;
-};
-
-
-
-
-
-class TransformFuncRegister {
-public:
-	void Register(const String& name, DataTransformFunc transform_func)
-	{
-		RMLUI_ASSERT(transform_func);
-		bool inserted = transform_functions.emplace(name, std::move(transform_func)).second;
-		if (!inserted)
-		{
-			Log::Message(Log::LT_ERROR, "Transform function '%s' already exists.", name.c_str());
-			RMLUI_ERROR;
-		}
-	}
-
-	bool Call(const String& name, Variant& inout_result, const VariantList& arguments) const
-	{
-		auto it = transform_functions.find(name);
-		if (it == transform_functions.end())
-			return false;
-
-		const DataTransformFunc& transform_func = it->second;
-		RMLUI_ASSERT(transform_func);
-
-		return transform_func(inout_result, arguments);
-	}
-
-private:
-	UnorderedMap<String, DataTransformFunc> transform_functions;
-};
-
-
-
-class DataTypeRegister : NonCopyMoveable {
-public:
-	DataTypeRegister();
-	~DataTypeRegister();
-
-	template<typename T>
-	StructHandle<T> RegisterStruct()
-	{
-		static_assert(std::is_class<T>::value, "Type must be a struct or class type.");
-		FamilyId id = Family<T>::Id();
+RMLUICORE_API VariableDefinition* GetArraySizeDefinition();
 
-		auto struct_variable = std::make_unique<StructDefinition>();
-		StructDefinition* struct_variable_raw = struct_variable.get();
-
-		bool inserted = type_register.emplace(id, std::move(struct_variable)).second;
-		if (!inserted)
-		{
-			RMLUI_ERRORMSG("Type already declared");
-			return StructHandle<T>(nullptr, nullptr);
-		}
-		
-		return StructHandle<T>(this, struct_variable_raw);
-	}
-
-	template<typename Container>
-	bool RegisterArray()
-	{
-		using value_type = typename Container::value_type;
-		VariableDefinition* value_variable = GetOrAddScalar<value_type>();
-		RMLUI_ASSERTMSG(value_variable, "Underlying value type of array has not been registered.");
-		if (!value_variable)
-			return false;
-
-		FamilyId container_id = Family<Container>::Id();
-
-		auto array_variable = std::make_unique<ArrayDefinition<Container>>(value_variable);
-		ArrayDefinition<Container>* array_variable_raw = array_variable.get();
-
-		bool inserted = type_register.emplace(container_id, std::move(array_variable)).second;
-		if (!inserted)
-		{
-			RMLUI_ERRORMSG("Array type already declared.");
-			return false;
-		}
-
-		return true;
-	}
-
-
-	VariableDefinition* RegisterFunc(DataGetFunc get_func, DataSetFunc set_func)
-	{
-		static DataFunctionHandle current_handle = DataFunctionHandle(0);
-		current_handle = DataFunctionHandle(int(current_handle) + 1);
-
-		auto result = functions.emplace(current_handle, nullptr);
-		auto& it = result.first;
-		bool inserted = result.second;
-		RMLUI_ASSERT(inserted);
-		(void)inserted;
-
-		it->second = std::make_unique<FuncDefinition>(std::move(get_func), std::move(set_func));
-
-		return it->second.get();
-	}
-
-	template<typename T>
-	VariableDefinition* RegisterMemberFunc(MemberGetFunc<T> get_func, MemberSetFunc<T> set_func)
-	{
-		FamilyId id = Family<MemberGetFunc<T>>::Id();
-
-		auto result = type_register.emplace(id, nullptr);
-		auto& it = result.first;
-		bool inserted = result.second;
-
-		if (inserted)
-			it->second = std::make_unique<MemberFuncDefinition<T>>(get_func, set_func);
-
-		return it->second.get();
-	}
-
-	template<typename T, typename std::enable_if<is_valid_scalar<T>::value, int>::type = 0>
-	VariableDefinition* GetOrAddScalar()
-	{
-		FamilyId id = Family<T>::Id();
-
-		auto result = type_register.emplace(id, nullptr);
-		bool inserted = result.second;
-		UniquePtr<VariableDefinition>& definition = result.first->second;
-
-		if (inserted)
-			definition = std::make_unique<ScalarDefinition<T>>();
-
-		return definition.get();
-	}
-
-	template<typename T, typename std::enable_if<!is_valid_scalar<T>::value, int>::type = 0>
-	VariableDefinition* GetOrAddScalar()
-	{
-		return Get<T>();
-	}
-
-	template<typename T>
-	VariableDefinition* Get()
-	{
-		FamilyId id = Family<T>::Id();
-		auto it = type_register.find(id);
-		if (it == type_register.end())
-		{
-			RMLUI_ERRORMSG("Desired data type T not registered with the type register, please use the 'Register...()' functions before binding values, adding members, or registering arrays of non-scalar types.")
-			return nullptr;
-		}
-
-		return it->second.get();
-	}
-
-	TransformFuncRegister* GetTransformFuncRegister() {
-		return &transform_register;
-	}
-
-	static VariableDefinition* GetArraySizeDefinition();
-
-private:
-	UnorderedMap<DataFunctionHandle, UniquePtr<FuncDefinition>> functions;
-
-	UnorderedMap<FamilyId, UniquePtr<VariableDefinition>> type_register;
-
-	TransformFuncRegister transform_register;
-
-};
-
-
-
-template<typename Object>
-template<typename MemberType>
-inline StructHandle<Object>& StructHandle<Object>::AddMember(const String& name, MemberType Object::* member_ptr) {
-	VariableDefinition* member_type = type_register->GetOrAddScalar<MemberType>();
-	struct_definition->AddMember(name, std::make_unique<StructMemberDefault<Object, MemberType>>(member_type, member_ptr));
-	return *this;
-}
-template<typename Object>
-inline StructHandle<Object>& StructHandle<Object>::AddMemberFunc(const String& name, MemberGetFunc<Object> get_func, MemberSetFunc<Object> set_func) {
-	VariableDefinition* definition = type_register->RegisterMemberFunc<Object>(get_func, set_func);
-	struct_definition->AddMember(name, std::make_unique<StructMemberFunc>(definition));
-	return *this;
+template<typename Container>
+inline DataVariable ArrayDefinition<Container>::MakeSizeVariable(int container_size) {
+	return DataVariable(GetArraySizeDefinition(), reinterpret_cast<void*>(static_cast<intptr_t>(container_size)));
 }
 
 }

+ 1 - 0
Include/RmlUi/Core/DataView.h

@@ -32,6 +32,7 @@
 #include "Header.h"
 #include "Types.h"
 #include "Traits.h"
+#include "DataTypes.h"
 #include <unordered_map>
 
 namespace Rml {

+ 20 - 3
Include/RmlUi/Core/Factory.h

@@ -175,13 +175,30 @@ public:
 	/// @return The instanced event listener.
 	static EventListener* InstanceEventListener(const String& value, Element* element);
 
-	// TODO documentation
-	static void RegisterDataViewInstancer(DataViewInstancer* instancer, const String& name, bool is_structural_view = false);
-	static void RegisterDataControllerInstancer(DataControllerInstancer* instancer, const String& name);
+	/// Register an instancer for data views.
+	/// Structural views start a special XML parsing procedure when encountering a declaration of the view. Instead of instancing
+	/// children elements, the raw inner XML/RML contents are submitted to the initializing procedure of the view.
+	/// @param[in] instancer  The instancer to be called.
+	/// @param[in] type_name  The type name of the view, determines the element attribute that is used to initialize it.
+	/// @param[in] is_structural_view  Set true if the view should be parsed as a structural view.
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown.
+	static void RegisterDataViewInstancer(DataViewInstancer* instancer, const String& type_name, bool is_structural_view = false);
+
+	/// Register an instancer for data controllers.
+	/// @param[in] instancer  The instancer to be called.
+	/// @param[in] type_name  The type name of the controller, determines the element attribute that is used to initialize it.
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown.
+	static void RegisterDataControllerInstancer(DataControllerInstancer* instancer, const String& type_name);
 
+	/// Instance the data view with the given type name.
 	static DataViewPtr InstanceDataView(const String& type_name, Element* element, bool is_structural_view);
+
+	/// Instance the data controller with the given type name.
 	static DataControllerPtr InstanceDataController(const String& type_name, Element* element);
 
+	/// Returns the list of element attribute names with an associated structural data view instancer.
+	static const StringList& GetStructuralDataViewAttributeNames();
+
 private:
 	Factory();
 	~Factory();

+ 0 - 1
Include/RmlUi/Core/Types.h

@@ -191,7 +191,6 @@ class DataView;
 using DataViewPtr = UniqueReleaserPtr<DataView>;
 class DataController;
 using DataControllerPtr = UniqueReleaserPtr<DataController>;
-using DirtyVariables = SmallUnorderedSet<String>;
 
 }
 }

+ 1 - 1
Source/Core/DataControllerDefault.cpp

@@ -96,7 +96,7 @@ void DataControllerValue::SetValue(const Variant& value)
 	if (!model)
 		return;
 
-	if (Variable variable = model->GetVariable(address))
+	if (DataVariable variable = model->GetVariable(address))
 	{
 		variable.Set(value);
 		model->DirtyVariable(address.front().name);

+ 41 - 10
Source/Core/DataModel.cpp

@@ -100,7 +100,15 @@ static const char* LegalVariableName(const String& name)
 	return nullptr;
 }
 
-bool DataModel::BindVariable(const String& name, Variable variable)
+void DataModel::AddView(DataViewPtr view) {
+	views.Add(std::move(view));
+}
+
+void DataModel::AddController(DataControllerPtr controller) {
+	controllers.Add(std::move(controller));
+}
+
+bool DataModel::BindVariable(const String& name, DataVariable variable)
 {
 	const char* name_error_str = LegalVariableName(name);
 	if (name_error_str)
@@ -125,6 +133,22 @@ bool DataModel::BindVariable(const String& name, Variable variable)
 	return true;
 }
 
+bool DataModel::BindFunc(const String& name, DataGetFunc get_func, DataSetFunc set_func)
+{
+	auto result = functions.emplace(name, nullptr);
+	auto& it = result.first;
+	bool inserted = result.second;
+	if (!inserted)
+	{
+		Log::Message(Log::LT_ERROR, "Data get/set function with name %s already exists in model", name.c_str());
+		return false;
+	}
+	auto& func_definition_ptr = it->second;
+	func_definition_ptr = std::make_unique<FuncDefinition>(std::move(get_func), std::move(set_func));
+
+	return BindVariable(name, DataVariable(func_definition_ptr.get(), nullptr));
+}
+
 bool DataModel::BindEventCallback(const String& name, DataEventFunc event_func)
 {
 	const char* name_error_str = LegalVariableName(name);
@@ -224,22 +248,22 @@ DataAddress DataModel::ResolveAddress(const String& address_str, Element* elemen
 	return DataAddress();
 }
 
-Variable DataModel::GetVariable(const DataAddress& address) const
+DataVariable DataModel::GetVariable(const DataAddress& address) const
 {
 	if (address.empty() || address.front().name.empty())
-		return Variable();
+		return DataVariable();
 
 	auto it = variables.find(address.front().name);
 	if (it == variables.end())
-		return Variable();
+		return DataVariable();
 
-	Variable variable = it->second;
+	DataVariable variable = it->second;
 
 	for (int i = 1; i < (int)address.size() && variable; i++)
 	{
-		variable = variable.GetChild(address[i]);
+		variable = variable.Child(address[i]);
 		if (!variable)
-			return Variable();
+			return DataVariable();
 	}
 
 	return variable;
@@ -257,14 +281,21 @@ const DataEventFunc* DataModel::GetEventCallback(const String& name)
 	return &it->second;
 }
 
+bool DataModel::GetVariableInto(const DataAddress& address, Variant& out_value) const {
+	DataVariable variable = GetVariable(address);
+	return variable && variable.Get(out_value);
+}
+
 void DataModel::DirtyVariable(const String& variable_name)
 {
+	RMLUI_ASSERTMSG(LegalVariableName(variable_name) == nullptr, "Illegal variable name provided. Only top-level variables can be dirtied.");
 	RMLUI_ASSERTMSG(variables.count(variable_name) == 1, "Variable name not found among added variables.");
 	dirty_variables.emplace(variable_name);
 }
 
 bool DataModel::IsVariableDirty(const String& variable_name) const
 {
+	RMLUI_ASSERTMSG(LegalVariableName(variable_name) == nullptr, "Illegal variable name provided. Only top-level variables can be dirtied.");
 	return dirty_variables.count(variable_name) == 1;
 }
 
@@ -352,9 +383,9 @@ static struct TestDataVariables {
 			{
 				DataAddress address = ParseAddress(str_address);
 
-				String result;
-				if(model.GetValue<String>(address, result))
-					results.push_back(result);
+				Variant result;
+				if(model.GetVariableInto(address, result))
+					results.push_back(result.Get<String>());
 			}
 
 			RMLUI_ASSERT(results == expected_results);

+ 3 - 3
Source/Core/DataParser.cpp

@@ -1048,7 +1048,7 @@ DataExpressionInterface::DataExpressionInterface(DataModel* data_model, Element*
 DataAddress DataExpressionInterface::ParseAddress(const String& address_str) const
 {
 	if (address_str.size() >= 4 && address_str[0] == 'e' && address_str[1] == 'v' && address_str[2] == '.')
-		return DataAddress{ AddressEntry("ev"), AddressEntry(address_str.substr(3)) };
+		return DataAddress{ DataAddressEntry("ev"), DataAddressEntry(address_str.substr(3)) };
 
 	return data_model ? data_model->ResolveAddress(address_str, element) : DataAddress();
 }
@@ -1064,7 +1064,7 @@ Variant DataExpressionInterface::GetValue(const DataAddress& address) const
 	}
 	else if (data_model)
 	{
-		data_model->GetValue(address, result);
+		data_model->GetVariableInto(address, result);
 	}
 	return result;
 }
@@ -1074,7 +1074,7 @@ bool DataExpressionInterface::SetValue(const DataAddress& address, const Variant
 	bool result = false;
 	if (data_model && !address.empty())
 	{
-		if (Variable variable = data_model->GetVariable(address))
+		if (DataVariable variable = data_model->GetVariable(address))
 			result = variable.Set(value);
 
 		if (result)

+ 1 - 1
Source/Core/DataParser.h

@@ -42,7 +42,7 @@ struct InstructionData;
 using Program = std::vector<InstructionData>;
 using AddressList = std::vector<DataAddress>;
 
-class DataExpressionInterface {
+class RMLUICORE_API DataExpressionInterface {
 public:
     DataExpressionInterface() = default;
     DataExpressionInterface(DataModel* data_model, Element* element, Event* event = nullptr);

+ 84 - 0
Source/Core/DataTypeRegister.cpp

@@ -0,0 +1,84 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "precompiled.h"
+#include "../../Include/RmlUi/Core/DataTypeRegister.h"
+
+namespace Rml {
+namespace Core {
+
+
+DataTypeRegister::DataTypeRegister()
+{
+    // Add default transform functions.
+
+	transform_register.Register("to_lower", [](Variant& variant, const VariantList& arguments) -> bool {
+		String value;
+		if (!variant.GetInto(value))
+			return false;
+		variant = StringUtilities::ToLower(value);
+		return true;
+	});
+
+	transform_register.Register("to_upper", [](Variant& variant, const VariantList& arguments) -> bool {
+		String value;
+		if (!variant.GetInto(value))
+			return false;
+		variant = StringUtilities::ToUpper(value);
+		return true;
+	});
+}
+
+DataTypeRegister::~DataTypeRegister()
+{}
+
+void TransformFuncRegister::Register(const String& name, DataTransformFunc transform_func)
+{
+    RMLUI_ASSERT(transform_func);
+    bool inserted = transform_functions.emplace(name, std::move(transform_func)).second;
+    if (!inserted)
+    {
+        Log::Message(Log::LT_ERROR, "Transform function '%s' already exists.", name.c_str());
+        RMLUI_ERROR;
+    }
+}
+
+bool TransformFuncRegister::Call(const String& name, Variant& inout_result, const VariantList& arguments) const
+{
+    auto it = transform_functions.find(name);
+    if (it == transform_functions.end())
+        return false;
+
+    const DataTransformFunc& transform_func = it->second;
+    RMLUI_ASSERT(transform_func);
+
+    return transform_func(inout_result, arguments);
+}
+
+}
+}

+ 27 - 33
Source/Core/DataVariable.cpp

@@ -32,6 +32,27 @@
 namespace Rml {
 namespace Core {
 
+bool DataVariable::Get(Variant& variant) {
+    return definition->Get(ptr, variant);
+}
+
+bool DataVariable::Set(const Variant& variant) {
+    return definition->Set(ptr, variant);
+}
+
+int DataVariable::Size() {
+    return definition->Size(ptr);
+}
+
+DataVariable DataVariable::Child(const DataAddressEntry& address) {
+    return definition->Child(ptr, address);
+}
+
+DataVariableType DataVariable::Type() {
+    return definition->Type();
+}
+
+
 bool VariableDefinition::Get(void* ptr, Variant& variant) {
     Log::Message(Log::LT_WARNING, "Values can only be retrieved from scalar data types.");
     return false;
@@ -44,41 +65,14 @@ int VariableDefinition::Size(void* ptr) {
     Log::Message(Log::LT_WARNING, "Tried to get the size from a non-array data type.");
     return 0;
 }
-Variable VariableDefinition::GetChild(void* ptr, const AddressEntry& address) {
+DataVariable VariableDefinition::Child(void* ptr, const DataAddressEntry& address) {
     Log::Message(Log::LT_WARNING, "Tried to get the child of a scalar type.");
-    return Variable();
-}
-
-
-
-DataTypeRegister::DataTypeRegister()
-{
-    // Add default transform functions.
-
-	transform_register.Register("to_lower", [](Variant& variant, const VariantList& arguments) -> bool {
-		String value;
-		if (!variant.GetInto(value))
-			return false;
-		variant = StringUtilities::ToLower(value);
-		return true;
-	});
-
-	transform_register.Register("to_upper", [](Variant& variant, const VariantList& arguments) -> bool {
-		String value;
-		if (!variant.GetInto(value))
-			return false;
-		variant = StringUtilities::ToUpper(value);
-		return true;
-	});
+    return DataVariable();
 }
 
-DataTypeRegister::~DataTypeRegister()
-{}
-
-
 class ArraySizeDefinition final : public VariableDefinition {
 public:
-    ArraySizeDefinition() : VariableDefinition(VariableType::Scalar) {}
+    ArraySizeDefinition() : VariableDefinition(DataVariableType::Scalar) {}
 
     bool Get(void* ptr, Variant& variant) override
     {
@@ -87,10 +81,10 @@ public:
     }
 };
 
-VariableDefinition* DataTypeRegister::GetArraySizeDefinition()
+VariableDefinition* GetArraySizeDefinition()
 {
-	static ArraySizeDefinition size_definition;
-	return &size_definition;
+    static ArraySizeDefinition size_definition;
+    return &size_definition;
 }
 
 }

+ 2 - 2
Source/Core/DataViewDefault.cpp

@@ -384,7 +384,7 @@ bool DataViewFor::Initialize(DataModel& model, Element* element, const String& i
 
 bool DataViewFor::Update(DataModel& model)
 {
-	Variable variable = model.GetVariable(container_address);
+	DataVariable variable = model.GetVariable(container_address);
 	if (!variable)
 		return false;
 
@@ -402,7 +402,7 @@ bool DataViewFor::Update(DataModel& model)
 			DataAddress iterator_address;
 			iterator_address.reserve(container_address.size() + 1);
 			iterator_address = container_address;
-			iterator_address.push_back(AddressEntry(i));
+			iterator_address.push_back(DataAddressEntry(i));
 
 			model.InsertAlias(new_element_ptr.get(), iterator_name, std::move(iterator_address));
 

+ 8 - 3
Source/Core/Factory.cpp

@@ -86,7 +86,7 @@ using StructuralDataViewInstancerMap = SmallUnorderedMap< String, DataViewInstan
 static StructuralDataViewInstancerMap structural_data_view_instancers;
 
 // Structural data view names.
-static StringList structural_data_view_names;
+static StringList structural_data_view_attribute_names;
 
 // The context instancer.
 static ContextInstancer* context_instancer = nullptr;;
@@ -224,7 +224,7 @@ void Factory::Shutdown()
 
 	data_view_instancers.clear();
 	structural_data_view_instancers.clear();
-	structural_data_view_names.clear();
+	structural_data_view_attribute_names.clear();
 
 	context_instancer = nullptr;
 
@@ -554,7 +554,7 @@ void Factory::RegisterDataViewInstancer(DataViewInstancer* instancer, const Stri
 	{
 		inserted = structural_data_view_instancers.emplace(name, instancer).second;
 		if (inserted)
-			structural_data_view_names.push_back(name);
+			structural_data_view_attribute_names.push_back(String("data-") + name);
 	}
 	else
 	{
@@ -599,5 +599,10 @@ DataControllerPtr Factory::InstanceDataController(const String& type_name, Eleme
 	return DataControllerPtr();
 }
 
+const StringList& Factory::GetStructuralDataViewAttributeNames()
+{
+	return structural_data_view_attribute_names;
+}
+
 }
 }

+ 0 - 2
Source/Core/XMLNodeHandlerDefault.cpp

@@ -73,8 +73,6 @@ bool XMLNodeHandlerDefault::ElementEnd(XMLParser* RMLUI_UNUSED_PARAMETER(parser)
 	RMLUI_UNUSED(parser);
 	RMLUI_UNUSED(name);
 
-	// TODO: Perhaps it's better to submit views from here...
-
 	return true;
 }
 

+ 4 - 1
Source/Core/XMLParser.cpp

@@ -34,6 +34,7 @@
 #include "../../Include/RmlUi/Core/XMLNodeHandler.h"
 #include "../../Include/RmlUi/Core/URL.h"
 #include "../../Include/RmlUi/Core/XMLParser.h"
+#include "../../Include/RmlUi/Core/Factory.h"
 
 namespace Rml {
 namespace Core {
@@ -45,7 +46,9 @@ static SharedPtr<XMLNodeHandler> default_node_handler;
 XMLParser::XMLParser(Element* root)
 {
 	RegisterCDATATag("script");
-	RegisterInnerXMLAttribute("data-for");
+
+	for (const String& name : Factory::GetStructuralDataViewAttributeNames())
+		RegisterInnerXMLAttribute(name);
 
 	// Add the first frame.
 	ParseFrame frame;