mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
492 lines
18 KiB
C++
492 lines
18 KiB
C++
|
|
#pragma once
|
|
|
|
#include "reflection/member.h"
|
|
#include "reflection/enumconverter.h"
|
|
#include "reflection/type.h"
|
|
#include "v8xml/xmlelement.h"
|
|
#include "V8Xml/Reference.h" // TODO: Reflection namespace should not know about V8Tree
|
|
#include "boost/cast.hpp"
|
|
|
|
namespace RBX
|
|
{
|
|
namespace Reflection
|
|
{
|
|
class ConstProperty;
|
|
class Property;
|
|
|
|
typedef enum {
|
|
READONLY,
|
|
READWRITE
|
|
} Mutability;
|
|
|
|
// Base that describes a Property
|
|
class RBXBaseClass PropertyDescriptor : public MemberDescriptor
|
|
{
|
|
private:
|
|
unsigned bIsPublic : 1;
|
|
unsigned bIsEditable : 1;
|
|
unsigned bCanReplicate : 1;
|
|
unsigned bCanXmlRead : 1;
|
|
unsigned bCanXmlWrite : 1;
|
|
unsigned bIsScriptable : 1;
|
|
unsigned bAlwaysClone : 1;
|
|
|
|
public:
|
|
typedef ConstProperty ConstMember;
|
|
typedef Property Member;
|
|
|
|
public:
|
|
// Note: isPublic == PropertyUI shown, and Can BaseScript against. ToDO: possibly split?
|
|
|
|
enum Functionality {
|
|
STANDARD = 1 + 2 + 4 + 8 + 16, // isPublic, canReplicate, canXmlRead , canXmlWrite, isScriptable
|
|
NO_XML_WRITE = 1 + 2 + 4 + 0 + 16, // isPublic, canReplicate, canXmlRead , , isScriptable
|
|
UI = 1 + 0 +(4)+ 0 + 16, // isPublic, (canXmlRead), isScriptable //Remove canXmlRead from UI
|
|
SCRIPTING = 1 + 2 + 0 + 0 + 16, // isPublic, canReplicate, isScriptable
|
|
STREAMING = 0 + 2 + 4 + 8 + 0, // canReplicate, canXmlRead , canXmlWrite,
|
|
CLUSTER = 0 + 0 + 4 + 8 + 0, // canXmlRead , canXmlWrite,
|
|
LEGACY = 0 + 0 + 4 + 0 + 0, // canXmlRead ,
|
|
REPLICATE_ONLY = 0 + 2 + 0 + 0 + 0, // canReplicate
|
|
LEGACY_SCRIPTING = 0 + 0 + 4 + 0 + 16, // canXmlRead , isScriptable
|
|
HIDDEN_SCRIPTING = 0 + 0 + 0 + 0 + 16, // isScriptable
|
|
PUBLIC_SERIALIZED = 1 + 0 + 4 + 8 + 0, // isPublic, canXmlRead , canXmlWrite,
|
|
REPLICATE_CLONE = 0 + 2 + 0 + 0 + 0 + 32, // canReplicate alwaysClone
|
|
STANDARD_NO_REPLICATE = 1 + 0 + 4 + 8 + 16, // isPublic, canXmlRead , canXmlWrite, isScriptable
|
|
STANDARD_NO_SCRIPTING = 1 + 2 + 4 + 8 + 0, // isPublic, canReplicate, canXmlRead , canXmlWrite
|
|
PUBLIC_REPLICATE = 1 + 2 + 0 + 0 + 0, // isPublic, canReplicate
|
|
};
|
|
|
|
struct Attributes : public Descriptor::Attributes
|
|
{
|
|
Functionality flags;
|
|
|
|
Attributes():flags(STANDARD) {}
|
|
Attributes(Functionality flags):flags(flags) {}
|
|
static Attributes deprecated(const MemberDescriptor& preferred, Functionality flags = UI);
|
|
static Attributes deprecated(Functionality flags = UI);
|
|
};
|
|
|
|
const Type& type;
|
|
const bool bIsEnum;
|
|
|
|
protected:
|
|
PropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, Attributes attributes, Security::Permissions security, bool isEnum = false);
|
|
|
|
inline void checkFlags()
|
|
{
|
|
if (isWriteOnly())
|
|
{
|
|
bCanXmlWrite = 0;
|
|
bCanReplicate = 0;
|
|
}
|
|
if (isReadOnly())
|
|
{
|
|
bCanXmlRead = 0;
|
|
bCanReplicate = 0;
|
|
}
|
|
}
|
|
public:
|
|
inline bool isPublic() const { return bIsPublic != 0; }
|
|
inline bool isScriptable() const { return bIsScriptable != 0; }
|
|
|
|
void setEditable(bool editable) { bIsEditable = editable ? 1 : 0; }
|
|
inline bool isEditable() const { return bIsEditable != 0; }
|
|
|
|
virtual bool isReadOnly() const = 0;
|
|
virtual bool isWriteOnly() const = 0;
|
|
inline bool canXmlRead() const
|
|
{
|
|
RBXASSERT(bCanXmlRead == 0 || !isReadOnly());
|
|
return bCanXmlRead != 0;
|
|
}
|
|
inline bool canXmlWrite() const
|
|
{
|
|
RBXASSERT(bCanXmlWrite == 0 || !isWriteOnly());
|
|
return bCanXmlWrite != 0;
|
|
}
|
|
inline bool canReplicate() const
|
|
{
|
|
RBXASSERT(bCanReplicate == 0 || (!isReadOnly() && !isWriteOnly()));
|
|
return bCanReplicate != 0;
|
|
}
|
|
inline bool alwaysClone() const
|
|
{
|
|
return bAlwaysClone != 0;
|
|
}
|
|
|
|
bool operator==(const PropertyDescriptor& other) const {
|
|
return this == &other;
|
|
}
|
|
bool operator!=(const PropertyDescriptor& other) const {
|
|
return this != &other;
|
|
}
|
|
|
|
virtual bool equalValues(const DescribedBase* a, const DescribedBase* b) const = 0;
|
|
|
|
virtual void getVariant(const DescribedBase* instance, Variant& value) const = 0;
|
|
virtual void setVariant(DescribedBase* instance, const Variant& value) const = 0;
|
|
virtual void copyValue(const DescribedBase* source, DescribedBase* destination) const = 0;
|
|
|
|
virtual int getDataSize(const DescribedBase* instance) const = 0;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// String conversion interface
|
|
public:
|
|
virtual bool hasStringValue() const = 0;
|
|
virtual std::string getStringValue(const DescribedBase* instance) const {
|
|
if (hasStringValue())
|
|
debugAssertM(false, "you must implement getStringValue");
|
|
else
|
|
debugAssertM(false, "don't call getStringValue when hasStringValue()==false");
|
|
return "";
|
|
}
|
|
virtual bool setStringValue(DescribedBase* instance, const std::string& text) const {
|
|
if (hasStringValue())
|
|
debugAssertM(false, "you must implement setStringValue");
|
|
else
|
|
debugAssertM(false, "don't call setStringValue when hasStringValue()==false");
|
|
return false;
|
|
}
|
|
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Xml streaming interface
|
|
public:
|
|
XmlElement* write(const DescribedBase* instance, bool ignoreWriteProtection = false) const;
|
|
virtual void read(DescribedBase* instance, const XmlElement* element, RBX::IReferenceBinder& binder) const;
|
|
private:
|
|
virtual void writeValue(const DescribedBase* instance, XmlElement* element) const = 0;
|
|
virtual void readValue(DescribedBase* instance, const XmlElement* element, RBX::IReferenceBinder& binder) const = 0;
|
|
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/
|
|
|
|
};
|
|
|
|
|
|
template <typename V>
|
|
class TypedPropertyDescriptor : public PropertyDescriptor
|
|
{
|
|
private:
|
|
typedef PropertyDescriptor Super;
|
|
|
|
public:
|
|
class RBXInterface GetSet
|
|
{
|
|
public:
|
|
virtual bool isReadOnly() const = 0;
|
|
virtual bool isWriteOnly() const = 0;
|
|
virtual V getValue(const DescribedBase* object) const = 0;
|
|
virtual void setValue(DescribedBase* object, const V& value) const = 0;
|
|
};
|
|
protected:
|
|
std::auto_ptr<GetSet> getset;
|
|
TypedPropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, std::auto_ptr<GetSet> getset, Attributes flags, Security::Permissions security)
|
|
:PropertyDescriptor(classDescriptor, type, name, category, flags, security),getset(getset)
|
|
{
|
|
if (this->getset.get())
|
|
this->checkFlags();
|
|
}
|
|
TypedPropertyDescriptor(ClassDescriptor& classDescriptor, const char* name, const char* category, std::auto_ptr<GetSet> getset, Attributes flags, Security::Permissions security)
|
|
:PropertyDescriptor(classDescriptor, Type::singleton<V>(), name, category, flags, security),getset(getset)
|
|
{
|
|
if (this->getset.get())
|
|
this->checkFlags();
|
|
}
|
|
public:
|
|
/*implement*/ V get(const DescribedBase* instance) const
|
|
{
|
|
return getset->getValue(instance);
|
|
}
|
|
/*implement*/ void set(DescribedBase* instance, const V& value) const
|
|
{
|
|
getset->setValue(instance, value);
|
|
}
|
|
|
|
/*implement*/ void getVariant(const DescribedBase* instance, Variant& value) const
|
|
{
|
|
value = getset->getValue(instance);
|
|
}
|
|
/*implement*/ void setVariant(DescribedBase* instance, const Variant& value) const
|
|
{
|
|
// TODO: This might be inefficient. How is the value stored in getset???
|
|
getset->setValue(instance, value.get<V>());
|
|
}
|
|
/*implement*/ void copyValue(const DescribedBase* source, DescribedBase* destination) const
|
|
{
|
|
set(destination, get(source));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Variant interface
|
|
public:
|
|
virtual bool isReadOnly() const {
|
|
return getset->isReadOnly();
|
|
}
|
|
|
|
virtual bool isWriteOnly() const {
|
|
return getset->isWriteOnly();
|
|
}
|
|
|
|
V getValue(const DescribedBase* object) const {
|
|
return getset->getValue(object);
|
|
}
|
|
|
|
void setValue(DescribedBase* object, const V& value) const {
|
|
getset->setValue(object, value);
|
|
}
|
|
|
|
/*implement*/ bool equalValues(const DescribedBase* a, const DescribedBase* b) const {
|
|
return getValue(a) == getValue(b);
|
|
}
|
|
|
|
virtual int getDataSize(const DescribedBase* instance) const;
|
|
|
|
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/
|
|
|
|
virtual bool hasStringValue() const;
|
|
virtual std::string getStringValue(const DescribedBase* instance) const;
|
|
virtual bool setStringValue(DescribedBase* instance, const std::string& text) const;
|
|
private:
|
|
virtual void readValue(DescribedBase* instance, const XmlElement* element, IReferenceBinder& binder) const;
|
|
virtual void writeValue(const DescribedBase* instance, XmlElement* element) const;
|
|
};
|
|
|
|
// A light-weight convenience class that associates a PropertyDescriptor
|
|
// with a described object to create a "Property"
|
|
class ConstProperty
|
|
{
|
|
protected:
|
|
const PropertyDescriptor* descriptor;
|
|
const DescribedBase* instance;
|
|
public:
|
|
inline ConstProperty():descriptor(0),instance(0) {}
|
|
inline ConstProperty(const PropertyDescriptor& descriptor, const DescribedBase* instance)
|
|
:descriptor(&descriptor),instance(instance)
|
|
{
|
|
RBXASSERT(!instance || descriptor.isMemberOf(instance));
|
|
}
|
|
|
|
inline ConstProperty(const ConstProperty& other)
|
|
:descriptor(other.descriptor),instance(other.instance)
|
|
{}
|
|
|
|
inline const DescribedBase* getInstance() const { return instance; }
|
|
inline const PropertyDescriptor& getDescriptor() const { return *descriptor; }
|
|
|
|
inline ConstProperty& operator =(const ConstProperty& other)
|
|
{
|
|
this->descriptor = other.descriptor;
|
|
this->instance = other.instance;
|
|
return *this;
|
|
}
|
|
|
|
inline bool operator ==(const ConstProperty& other) const
|
|
{
|
|
return (this->descriptor == other.descriptor) && (this->instance == other.instance);
|
|
}
|
|
|
|
inline const RBX::Name& getName() const {
|
|
return descriptor->name;
|
|
}
|
|
|
|
template<typename V>
|
|
inline bool isValueType() const
|
|
{
|
|
return descriptor->type==Type::singleton<V>();
|
|
}
|
|
template<typename V>
|
|
inline V getValue() const
|
|
{
|
|
RBXASSERT(isValueType<V>());
|
|
return static_cast<const TypedPropertyDescriptor<V>*>(descriptor)->getValue(instance);
|
|
}
|
|
|
|
inline bool hasStringValue() const
|
|
{
|
|
return descriptor->hasStringValue();
|
|
}
|
|
inline std::string getStringValue() const
|
|
{
|
|
return descriptor->getStringValue(instance);
|
|
}
|
|
|
|
inline XmlElement* write() const
|
|
{
|
|
return descriptor->write(instance);
|
|
}
|
|
};
|
|
|
|
|
|
// A light-weight convenience class that associates a PropertyDescriptor
|
|
// with a described object to create a "Property"
|
|
class Property : public ConstProperty
|
|
{
|
|
public:
|
|
inline Property(const PropertyDescriptor& descriptor, DescribedBase* instance)
|
|
:ConstProperty(descriptor, instance)
|
|
{}
|
|
inline Property(const Property& other)
|
|
:ConstProperty(*other.descriptor, other.instance)
|
|
{}
|
|
inline Property& operator =(const Property& other)
|
|
{
|
|
this->descriptor = other.descriptor;
|
|
this->instance = other.instance;
|
|
return *this;
|
|
}
|
|
inline bool operator ==(const Property& other) const
|
|
{
|
|
return this->descriptor==other.descriptor && this->instance==other.instance;
|
|
}
|
|
|
|
inline bool operator !=(const Property& other) const
|
|
{
|
|
return this->descriptor!=other.descriptor || this->instance!=other.instance;
|
|
}
|
|
|
|
DescribedBase* getInstance() const { return const_cast<DescribedBase*>(instance); }
|
|
|
|
template<typename V>
|
|
inline void setValue(const V& value)
|
|
{
|
|
RBXASSERT(isValueType<V>());
|
|
static_cast<const TypedPropertyDescriptor<V>*>(descriptor)->setValue(const_cast<DescribedBase*>(instance), value);
|
|
}
|
|
|
|
inline bool setStringValue(const std::string& text)
|
|
{
|
|
return descriptor->setStringValue(const_cast<DescribedBase*>(instance), text);
|
|
}
|
|
|
|
inline void read(const XmlElement* element, RBX::IReferenceBinder& binder)
|
|
{
|
|
descriptor->read(const_cast<DescribedBase*>(instance), element, binder);
|
|
}
|
|
|
|
};
|
|
|
|
std::size_t hash_value(const ConstProperty& prop);
|
|
|
|
// Interface
|
|
// maps enums to an index (Used by UIs like a property grid)
|
|
class RBXInterface EnumPropertyDescriptor : public PropertyDescriptor {
|
|
public:
|
|
const EnumDescriptor& enumDescriptor;
|
|
virtual size_t getIndexValue(const DescribedBase* instance) const = 0;
|
|
virtual bool setIndexValue(DescribedBase* instance, size_t value) const = 0; // throws an exception if value is illegal
|
|
virtual int getEnumValue(const DescribedBase* instance) const = 0;
|
|
virtual bool setEnumValue(DescribedBase* instance, int index) const = 0;
|
|
virtual const EnumDescriptor::Item* getEnumItem(const DescribedBase* instance) const = 0;
|
|
bool setEnumItem(DescribedBase* instance, const EnumDescriptor::Item& item) const {
|
|
if (item.owner!=enumDescriptor)
|
|
return false;
|
|
return setEnumValue(instance, item.value);
|
|
}
|
|
virtual int getDataSize(const DescribedBase* instance) const
|
|
{ return sizeof(int); }
|
|
protected:
|
|
EnumPropertyDescriptor(ClassDescriptor& classDescriptor, const EnumDescriptor& enumDescriptor, const char* name, const char* category, Attributes flags = STANDARD, Security::Permissions security=Security::None)
|
|
:PropertyDescriptor(classDescriptor, enumDescriptor, name, category, flags, security, true)
|
|
,enumDescriptor(enumDescriptor)
|
|
{}
|
|
};
|
|
|
|
class RBXBaseClass RefPropertyDescriptor : public PropertyDescriptor {
|
|
|
|
private:
|
|
typedef PropertyDescriptor Super;
|
|
|
|
public:
|
|
virtual DescribedBase* getRefValue(const DescribedBase* instance) const = 0;
|
|
virtual void setRefValue(DescribedBase* instance, DescribedBase* value) const = 0;
|
|
virtual void setRefValueUnsafe(DescribedBase* instance, DescribedBase* value) const = 0;
|
|
|
|
RefPropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, Attributes flags = STANDARD, Security::Permissions security=Security::None)
|
|
:PropertyDescriptor(classDescriptor, type, name, category, flags, security)
|
|
{}
|
|
|
|
virtual int getDataSize(const DescribedBase* instance) const
|
|
{ return 0; }
|
|
|
|
bool hasStringValue() const {
|
|
return false;
|
|
}
|
|
std::string getStringValue(const DescribedBase* instance) const{
|
|
return Super::getStringValue(instance);
|
|
}
|
|
bool setStringValue(DescribedBase* instance, const std::string& text) const {
|
|
return Super::setStringValue(instance, text);
|
|
}
|
|
|
|
|
|
static bool isRefPropertyDescriptor(const Reflection::Type& type)
|
|
{
|
|
static const RBX::Name& name = RBX::Name::lookup("Object");
|
|
return (type.name == name);
|
|
}
|
|
|
|
static bool isRefPropertyDescriptor(const PropertyDescriptor& descriptor)
|
|
{
|
|
// See RefType in reflection.h
|
|
bool result = isRefPropertyDescriptor(descriptor.type);
|
|
RBXASSERT(result == (0 != dynamic_cast<const Reflection::RefPropertyDescriptor*>(&descriptor)));
|
|
return result;
|
|
}
|
|
};
|
|
|
|
// A very useful class for binding Instance members to PropertyDescriptors
|
|
template<typename V, Mutability mutability = READWRITE>
|
|
class BoundProp : public Reflection::TypedPropertyDescriptor<V>
|
|
{
|
|
template<class Class>
|
|
class BoundPropGetSet : public TypedPropertyDescriptor<V>::GetSet
|
|
{
|
|
BoundProp& desc;
|
|
V Class::*member;
|
|
typedef void (Class::*ChangedMember)(const Reflection::PropertyDescriptor&);
|
|
ChangedMember changed;
|
|
public:
|
|
BoundPropGetSet(BoundProp& desc, V Class::*member, ChangedMember changed):desc(desc),member(member),changed(changed) {}
|
|
virtual bool isReadOnly() const {
|
|
return mutability == READONLY;
|
|
}
|
|
virtual bool isWriteOnly() const {
|
|
return false;
|
|
}
|
|
virtual V getValue(const Reflection::DescribedBase* object) const {
|
|
const Class* c = static_cast<const Class*>(object);
|
|
return c->*member;
|
|
}
|
|
virtual void setValue(Reflection::DescribedBase* object, const V& value) const {
|
|
if (mutability == READONLY)
|
|
throw std::runtime_error("can't set value");
|
|
|
|
Class* c = static_cast<Class*>(object);
|
|
if (c->*member != value)
|
|
{
|
|
c->*member = value;
|
|
if (changed)
|
|
(c->*changed)(desc);
|
|
c->raisePropertyChanged(desc);
|
|
}
|
|
}
|
|
};
|
|
public:
|
|
template<class Class>
|
|
BoundProp(const char* name, const char* category, V Class::*member, void (Class::*changed)(const Reflection::PropertyDescriptor&), typename PropertyDescriptor::Attributes flags = PropertyDescriptor::STANDARD, Security::Permissions security = Security::None)
|
|
:Reflection::TypedPropertyDescriptor<V>(Class::classDescriptor(), name, category, std::auto_ptr<typename TypedPropertyDescriptor<V>::GetSet>(), flags, security)
|
|
{
|
|
this->getset.reset(new BoundPropGetSet<Class>(*this, member, changed));
|
|
this->checkFlags();
|
|
}
|
|
template<class Class>
|
|
BoundProp(const char* name, const char* category, V Class::*member, typename PropertyDescriptor::Attributes flags = PropertyDescriptor::STANDARD, Security::Permissions security = Security::None)
|
|
:Reflection::TypedPropertyDescriptor<V>(Class::classDescriptor(), name, category, std::auto_ptr<typename TypedPropertyDescriptor<V>::GetSet>(), flags, security)
|
|
{
|
|
this->getset.reset(new BoundPropGetSet<Class>(*this, member, NULL));
|
|
this->checkFlags();
|
|
}
|
|
};
|
|
}
|
|
}
|