mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
fahhh
This commit is contained in:
@@ -0,0 +1,491 @@
|
||||
|
||||
#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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user