diff --git a/DesktopEditor/doctrenderer/json/json.cpp b/DesktopEditor/doctrenderer/json/json.cpp index 82e03cd9db..e70d630197 100644 --- a/DesktopEditor/doctrenderer/json/json.cpp +++ b/DesktopEditor/doctrenderer/json/json.cpp @@ -4,122 +4,141 @@ namespace NSJSON { - CValueContainer::CValueContainer() : m_type(vtUndefined) + CTypedValue::CTypedValue() : m_type(vtUndefined) { } - CValueContainer::CValueContainer(ValueType type, IBaseValue* pValue) : m_value(pValue), m_type(type) + CTypedValueContainer::CTypedValueContainer() : m_typedValue(new CTypedValue()), m_isReference(false) { } - CValueContainer CValueContainer::DeepCopy(const CValueContainer& other) + CTypedValueContainer::CTypedValueContainer(const CTypedValueContainer& other) : CTypedValueContainer() { - CValueContainer ret; - ValueType type = other.m_type; - ret.m_type = type; - switch (type) + *this = other; + } + + CTypedValueContainer& CTypedValueContainer::operator=(const CTypedValueContainer& other) + { + if (other.m_isReference) { - case vtPrimitive: - ret.m_value = std::make_shared(*static_cast(other.m_value.get())); - break; - case vtArray: - ret.m_value = std::make_shared(*static_cast(other.m_value.get())); - break; - case vtTypedArray: - ret.m_value = std::make_shared(*static_cast(other.m_value.get())); - break; - case vtObject: - ret.m_value = std::make_shared(*static_cast(other.m_value.get())); - break; - default: - // vtUndefined of vtNull - break; + // set pointer to the other's typed value + m_typedValue = other.m_typedValue; } - return ret; + else + { + // copy typed value without changing pointer to it + *m_typedValue = *other.m_typedValue; + } + return *this; } - CValue::CValue() : m_internal(new CValueContainer()) + CValue::CValue() : m_internal(new CTypedValueContainer()) { } + CValue::CValue(const CValue& other) : m_internal(new CTypedValueContainer(*other.m_internal)) + { + } + + CValue::~CValue() + { + delete m_internal; + } + + CValue& CValue::operator=(const CValue& other) + { + *m_internal = *other.m_internal; + return *this; + } + bool CValue::IsUndefined() const { - return m_internal->m_type == CValueContainer::vtUndefined; + return m_internal->m_typedValue->m_type == CTypedValue::vtUndefined; } bool CValue::IsNull() const { - return m_internal->m_type == CValueContainer::vtNull; + return m_internal->m_typedValue->m_type == CTypedValue::vtNull; } bool CValue::IsBool() const { - return (m_internal->m_type == CValueContainer::vtPrimitive && - static_cast(m_internal->m_value.get())->isBool()); + return (m_internal->m_typedValue->m_type == CTypedValue::vtPrimitive && + static_cast(m_internal->m_typedValue->m_value.get())->isBool()); } bool CValue::IsInt() const { - return (m_internal->m_type == CValueContainer::vtPrimitive && - static_cast(m_internal->m_value.get())->isInt()); + return (m_internal->m_typedValue->m_type == CTypedValue::vtPrimitive && + static_cast(m_internal->m_typedValue->m_value.get())->isInt()); } bool CValue::IsDouble() const { - return (m_internal->m_type == CValueContainer::vtPrimitive && - static_cast(m_internal->m_value.get())->isDouble()); + return (m_internal->m_typedValue->m_type == CTypedValue::vtPrimitive && + static_cast(m_internal->m_typedValue->m_value.get())->isDouble()); } bool CValue::IsStringA() const { - return (m_internal->m_type == CValueContainer::vtPrimitive && - static_cast(m_internal->m_value.get())->isStringA()); + return (m_internal->m_typedValue->m_type == CTypedValue::vtPrimitive && + static_cast(m_internal->m_typedValue->m_value.get())->isStringA()); } bool CValue::IsStringW() const { - return (m_internal->m_type == CValueContainer::vtPrimitive && - static_cast(m_internal->m_value.get())->isStringW()); + return (m_internal->m_typedValue->m_type == CTypedValue::vtPrimitive && + static_cast(m_internal->m_typedValue->m_value.get())->isStringW()); } bool CValue::IsArray() const { - return m_internal->m_type == CValueContainer::vtArray; + return m_internal->m_typedValue->m_type == CTypedValue::vtArray; } bool CValue::IsTypedArray() const { - return m_internal->m_type == CValueContainer::vtTypedArray; + return m_internal->m_typedValue->m_type == CTypedValue::vtTypedArray; } bool CValue::IsObject() const { - return m_internal->m_type == CValueContainer::vtObject; + return m_internal->m_typedValue->m_type == CTypedValue::vtObject; } bool CValue::ToBool() const { - return static_cast(m_internal->m_value.get())->toBool(); + if (m_internal->m_typedValue->m_type != CTypedValue::vtPrimitive) + return false; + return static_cast(m_internal->m_typedValue->m_value.get())->toBool(); } int CValue::ToInt() const { - return static_cast(m_internal->m_value.get())->toInt(); + if (m_internal->m_typedValue->m_type != CTypedValue::vtPrimitive) + return 0; + return static_cast(m_internal->m_typedValue->m_value.get())->toInt(); } double CValue::ToDouble() const { - return static_cast(m_internal->m_value.get())->toDouble(); + if (m_internal->m_typedValue->m_type != CTypedValue::vtPrimitive) + return 0.0; + return static_cast(m_internal->m_typedValue->m_value.get())->toDouble(); } std::string CValue::ToStringA() const { - return static_cast(m_internal->m_value.get())->toStringA(); + if (m_internal->m_typedValue->m_type != CTypedValue::vtPrimitive) + return ""; + return static_cast(m_internal->m_typedValue->m_value.get())->toStringA(); } std::wstring CValue::ToStringW() const { - return static_cast(m_internal->m_value.get())->toStringW(); + if (m_internal->m_typedValue->m_type != CTypedValue::vtPrimitive) + return L""; + return static_cast(m_internal->m_typedValue->m_value.get())->toStringW(); } CValue::operator bool() const @@ -147,108 +166,70 @@ namespace NSJSON return ToStringW(); } - CValue::CValue(bool value) : m_internal(new CValueContainer(CValueContainer::vtPrimitive, new CPrimitive(value))) + CValue::CValue(bool value) : CValue() { + m_internal->m_typedValue->m_value = std::make_shared(value); + m_internal->m_typedValue->m_type = CTypedValue::vtPrimitive; } - CValue::CValue(int value) : m_internal(new CValueContainer(CValueContainer::vtPrimitive, new CPrimitive(value))) + CValue::CValue(int value) : CValue() { + m_internal->m_typedValue->m_value = std::make_shared(value); + m_internal->m_typedValue->m_type = CTypedValue::vtPrimitive; } - CValue::CValue(double value) : m_internal(new CValueContainer(CValueContainer::vtPrimitive, new CPrimitive(value))) + CValue::CValue(double value) : CValue() { + m_internal->m_typedValue->m_value = std::make_shared(value); + m_internal->m_typedValue->m_type = CTypedValue::vtPrimitive; } - CValue::CValue(const char* value) : m_internal(new CValueContainer(CValueContainer::vtPrimitive, new CPrimitive(std::string(value)))) + CValue::CValue(const char* value) : CValue() { + m_internal->m_typedValue->m_value = std::make_shared(std::string(value)); + m_internal->m_typedValue->m_type = CTypedValue::vtPrimitive; } - CValue::CValue(const std::string& value) : m_internal(new CValueContainer(CValueContainer::vtPrimitive, new CPrimitive(value))) + CValue::CValue(const std::string& value) : CValue() { + m_internal->m_typedValue->m_value = std::make_shared(value); + m_internal->m_typedValue->m_type = CTypedValue::vtPrimitive; } - CValue::CValue(const wchar_t* value) : m_internal(new CValueContainer(CValueContainer::vtPrimitive, new CPrimitive(std::wstring(value)))) + CValue::CValue(const wchar_t* value) : CValue() { + m_internal->m_typedValue->m_value = std::make_shared(std::wstring(value)); + m_internal->m_typedValue->m_type = CTypedValue::vtPrimitive; } - CValue::CValue(const std::wstring& value) : m_internal(new CValueContainer(CValueContainer::vtPrimitive, new CPrimitive(value))) + CValue::CValue(const std::wstring& value) : CValue() { - } - - // Helper function to reduce code duplication for further assignment operators - template - static inline void setPrimitive(const std::shared_ptr& value, T primitive) - { - if (value->m_type != CValueContainer::vtPrimitive) - { - value->m_type = CValueContainer::vtPrimitive; - value->m_value = std::make_shared(primitive); - } - else - { - static_cast(value->m_value.get())->set(primitive); - } - } - - CValue& CValue::operator=(bool value) - { - setPrimitive(m_internal, value); - return *this; - } - - CValue& CValue::operator=(int value) - { - setPrimitive(m_internal, value); - return *this; - } - - CValue& CValue::operator=(double value) - { - setPrimitive(m_internal, value); - return *this; - } - - CValue& CValue::operator=(const char* value) - { - setPrimitive(m_internal, value); - return *this; - } - - CValue& CValue::operator=(const std::string& value) - { - setPrimitive(m_internal, value); - return *this; - } - - CValue& CValue::operator=(const wchar_t* value) - { - setPrimitive(m_internal, value); - return *this; - } - - CValue& CValue::operator=(const std::wstring& value) - { - setPrimitive(m_internal, value); - return *this; + m_internal->m_typedValue->m_value = std::make_shared(value); + m_internal->m_typedValue->m_type = CTypedValue::vtPrimitive; } int CValue::GetCount() const { - if (m_internal->m_type != CValueContainer::vtArray) + if (m_internal->m_typedValue->m_type != CTypedValue::vtArray) return 0; - return static_cast(m_internal->m_value.get())->getCount(); + return static_cast(m_internal->m_typedValue->m_value.get())->getCount(); } const CValue CValue::Get(int index) const { - if (m_internal->m_type != CValueContainer::vtArray) + if (m_internal->m_typedValue->m_type != CTypedValue::vtArray) return CValue(); - return static_cast(m_internal->m_value.get())->get(index); + // don't set reference mode, because this is a const method + return static_cast(m_internal->m_typedValue->m_value.get())->get(index); } CValue CValue::Get(int index) { - return static_cast(*this).Get(index); + if (m_internal->m_typedValue->m_type != CTypedValue::vtArray) + return CValue(); + CValue& value = static_cast(m_internal->m_typedValue->m_value.get())->get(index); + value.m_internal->m_isReference = true; + return value; } const CValue CValue::operator[](int index) const @@ -258,25 +239,30 @@ namespace NSJSON CValue CValue::operator[](int index) { - return Get(index); + CValue ret = Get(index); + ret.m_internal->m_isReference = true; + return ret; } - CValue::CValue(std::initializer_list elements) : m_internal(new CValueContainer(CValueContainer::vtArray, new CArray(elements))) + CValue::CValue(std::initializer_list elements) : CValue() { + m_internal->m_typedValue->m_value = std::make_shared(elements); + m_internal->m_typedValue->m_type = CTypedValue::vtArray; } CValue CValue::CreateArray(int count) { CValue ret; - ret.m_internal = std::make_shared(CValueContainer::vtArray, new CArray(count)); + ret.m_internal->m_typedValue->m_value = std::make_shared(count); + ret.m_internal->m_typedValue->m_type = CTypedValue::vtArray; return ret; } const BYTE* CValue::GetData() const { - if (m_internal->m_type != CValueContainer::vtTypedArray) + if (m_internal->m_typedValue->m_type != CTypedValue::vtTypedArray) return nullptr; - return static_cast(m_internal->m_value.get())->getData(); + return static_cast(m_internal->m_typedValue->m_value.get())->getData(); } BYTE* CValue::GetData() @@ -303,19 +289,19 @@ namespace NSJSON const CValue CValue::Get(const char* name) const { - if (m_internal->m_type != CValueContainer::vtObject) + if (m_internal->m_typedValue->m_type != CTypedValue::vtObject) return CValue(); - return static_cast(m_internal->m_value.get())->get(name); + // don't set reference mode, because this is a const method + return static_cast(m_internal->m_typedValue->m_value.get())->get(name); } CValue CValue::Get(const char* name) { - return static_cast(*this).Get(name); - } - - CValue CValue::operator[](const char* name) - { - return Get(name); + if (m_internal->m_typedValue->m_type != CTypedValue::vtObject) + return CValue(); + CValue& value = static_cast(m_internal->m_typedValue->m_value.get())->get(name); + value.m_internal->m_isReference = true; + return value; } const CValue CValue::operator[](const char* name) const @@ -323,10 +309,25 @@ namespace NSJSON return Get(name); } + CValue CValue::operator[](const char* name) + { + CValue ret = Get(name); + ret.m_internal->m_isReference = true; + return ret; + } + + std::vector CValue::GetPropertyNames() const + { + if (m_internal->m_typedValue->m_type != CTypedValue::vtObject) + return {}; + return static_cast(m_internal->m_typedValue->m_value.get())->getPropertyNames(); + } + CValue CValue::CreateObject() { CValue ret; - ret.m_internal = std::make_shared(CValueContainer::vtObject, new CObject()); + ret.m_internal->m_typedValue->m_value = std::make_shared(); + ret.m_internal->m_typedValue->m_type = CTypedValue::vtObject; return ret; } @@ -338,7 +339,7 @@ namespace NSJSON CValue CValue::CreateNull() { CValue ret; - ret.m_internal->m_type = CValueContainer::vtNull; + ret.m_internal->m_typedValue->m_type = CTypedValue::vtNull; return ret; } } diff --git a/DesktopEditor/doctrenderer/json/json.h b/DesktopEditor/doctrenderer/json/json.h index f4793fbee6..47c79b7274 100644 --- a/DesktopEditor/doctrenderer/json/json.h +++ b/DesktopEditor/doctrenderer/json/json.h @@ -1,9 +1,9 @@ #ifndef JSON_H_ #define JSON_H_ -#include #include -#include +#include +#include #ifdef JSBASE_NO_USE_DYNAMIC_LIBRARY #define JSON_DECL @@ -19,14 +19,18 @@ namespace NSJSON { typedef unsigned char BYTE; - - class CValueContainer; + + class CTypedValueContainer; // Main class for storing values. // This class provide interface to work with each type of values. class JSON_DECL CValue { public: CValue(); + CValue(const CValue& other); + ~CValue(); + + CValue& operator=(const CValue& other); public: // TYPE CHECKS @@ -114,15 +118,6 @@ namespace NSJSON CValue(const wchar_t* value); CValue(const std::wstring& value); - // Assigns primitive - CValue& operator=(bool value); - CValue& operator=(int value); - CValue& operator=(double value); - CValue& operator=(const char* value); - CValue& operator=(const std::string& value); - CValue& operator=(const wchar_t* value); - CValue& operator=(const std::wstring& value); - // FUNCTIONS FOR WORKING WITH ARRAYS /** * Gets lengths of the array/typed array. @@ -185,7 +180,7 @@ namespace NSJSON /** * Gets a property of this object. * @param name The name of the property. - * @returns the value of the object's property. If current value is not an object returns undefined value. + * @returns the value of the object's property. If current value is not an object, returns undefined value. */ const CValue Get(const char* name) const; CValue Get(const char* name); @@ -194,6 +189,12 @@ namespace NSJSON const CValue operator[](const char* name) const; CValue operator[](const char* name); + /** + * Retrieves all property names from current object. + * @returns a vector containing the names of the properties of this object as strings. If current value is not an object, returns an empty vector. + */ + std::vector GetPropertyNames() const; + /** * Creates and returns empty object. */ @@ -210,7 +211,7 @@ namespace NSJSON static CValue CreateNull(); private: - std::shared_ptr m_internal; + CTypedValueContainer* m_internal; }; } diff --git a/DesktopEditor/doctrenderer/json/json_p.h b/DesktopEditor/doctrenderer/json/json_p.h index 9f3be1ac0e..aaa8fc040a 100644 --- a/DesktopEditor/doctrenderer/json/json_p.h +++ b/DesktopEditor/doctrenderer/json/json_p.h @@ -6,7 +6,8 @@ namespace NSJSON { class IBaseValue; - class CValueContainer + // Wrapper around IBaseValue with specific value type + class CTypedValue { public: enum ValueType @@ -20,15 +21,32 @@ namespace NSJSON }; public: - CValueContainer(); - CValueContainer(ValueType type, IBaseValue* pValue); - - static CValueContainer DeepCopy(const CValueContainer& other); + CTypedValue(); public: std::shared_ptr m_value; ValueType m_type; }; + + // Container for CTypedValue which is also allows to treat typed value as reference + class CTypedValueContainer + { + public: + CTypedValueContainer(); + CTypedValueContainer(const CTypedValueContainer& other); + + CTypedValueContainer& operator=(const CTypedValueContainer& other); + + public: + std::shared_ptr m_typedValue; + /* We need some sort of "reference mode" for values, returned from arrays (with CValue::Get(int)) + * and from objects (with CValue::Get(const char*)), because they should not be simply the COPY of that values, but + * instead they should be some sort of REFERENCES to original values, that are stored in array or object. + * That is because we want to be able to change them. + * m_isReference is TRUE for that type of values. For all the rest it is FALSE. + */ + bool m_isReference; + }; } #endif // JSON_PRIVATE_H_ diff --git a/DesktopEditor/doctrenderer/json/json_values.cpp b/DesktopEditor/doctrenderer/json/json_values.cpp index 70414e0444..acb92da57c 100644 --- a/DesktopEditor/doctrenderer/json/json_values.cpp +++ b/DesktopEditor/doctrenderer/json/json_values.cpp @@ -216,10 +216,10 @@ namespace NSJSON int CArray::getCount() const { - return static_cast(m_values.size()); + return (int)m_values.size(); } - CValue CArray::get(int index) + CValue& CArray::get(int index) { return m_values[index]; } @@ -251,8 +251,19 @@ namespace NSJSON { } - CValue CObject::get(const std::string& name) + CValue& CObject::get(const std::string& name) { return m_values[name]; } + + std::vector CObject::getPropertyNames() + { + std::vector ret; + for (const storage_t::value_type& entry : m_values) + { + if (!entry.second.IsUndefined()) + ret.push_back(entry.first); + } + return ret; + } } diff --git a/DesktopEditor/doctrenderer/json/json_values.h b/DesktopEditor/doctrenderer/json/json_values.h index 43c5ec7618..d9d5a58ba8 100644 --- a/DesktopEditor/doctrenderer/json/json_values.h +++ b/DesktopEditor/doctrenderer/json/json_values.h @@ -87,7 +87,7 @@ namespace NSJSON public: int getCount() const; - CValue get(int index); + CValue& get(int index); private: std::vector m_values; @@ -118,7 +118,8 @@ namespace NSJSON ~CObject(); public: - CValue get(const std::string& name); + CValue& get(const std::string& name); + std::vector getPropertyNames(); private: storage_t m_values; diff --git a/DesktopEditor/doctrenderer/json/serialization.h b/DesktopEditor/doctrenderer/json/serialization.h index 10c4c5f721..27f0d133f3 100644 --- a/DesktopEditor/doctrenderer/json/serialization.h +++ b/DesktopEditor/doctrenderer/json/serialization.h @@ -9,65 +9,57 @@ namespace NSJSON { - static JSSmart toJS(const CValue& pValue) + static JSSmart toJS(const CValue& value) { - IBaseValue::ValueType type = pValue->m_type; - if (type == IBaseValue::vtUndefined) + if (value.IsUndefined()) return NSJSBase::CJSContext::createUndefined(); - if (type == IBaseValue::vtNull) + if (value.IsNull()) return NSJSBase::CJSContext::createNull(); JSSmart ret; - if (type == IBaseValue::vtObject) + if (value.IsObject()) { - const CObject* pObject = static_cast(pValue); JSSmart jsObj = NSJSBase::CJSContext::createObject(); - for (const auto& entry : pObject->m_values) + std::vector properties = value.GetPropertyNames(); + for (const std::string& name : properties) { - JSSmart jsValue = toJS(entry.second); - jsObj->set(entry.first.c_str(), jsValue); + JSSmart jsValue = toJS(value[name.c_str()]); + jsObj->set(name.c_str(), jsValue); } ret = jsObj->toValue(); } - else if (type == IBaseValue::vtArray) + else if (value.IsArray()) { - const CArray* pArray = static_cast(pValue); - JSSmart jsArr = NSJSBase::CJSContext::createArray(0); - for (const IBaseValue* pArrValue : pArray->m_values) + const int len = value.GetCount(); + JSSmart jsArr = NSJSBase::CJSContext::createArray(len); + for (int i = 0; i < len; i++) { - jsArr->add(toJS(pArrValue).GetPointer()); + jsArr->set(i, toJS(value[i]).GetPointer()); } ret = jsArr->toValue(); } - else if (type == IBaseValue::vtTypedArray) + else if (value.IsTypedArray()) { - const CTypedArray* pTypedArray = static_cast(pValue); - JSSmart jsTypedArr = NSJSBase::CJSContext::createUint8Array(pTypedArray->m_data, pTypedArray->m_len); + // TODO: + /* + JSSmart jsTypedArr = NSJSBase::CJSContext::createUint8Array(value.GetData(), value.GetCount()); ret = jsTypedArr->toValue(); + */ + ret = NSJSBase::CJSContext::createUndefined(); } else { - // primitive type - const CPrimitive* pPrimitiveValue = static_cast(pValue); - switch (type) { - case IBaseValue::vtBoolean: - ret = NSJSBase::CJSContext::createBool(pPrimitiveValue->m_bool); - break; - case IBaseValue::vtInteger: - ret = NSJSBase::CJSContext::createInt(pPrimitiveValue->m_int); - break; - case IBaseValue::vtDouble: - ret = NSJSBase::CJSContext::createDouble(pPrimitiveValue->m_double); - break; - case IBaseValue::vtStringA: - ret = NSJSBase::CJSContext::createString(pPrimitiveValue->m_string); - break; - case IBaseValue::vtStringW: - ret = NSJSBase::CJSContext::createString(pPrimitiveValue->m_wstring); - break; - default: - break; - } + // primitive types + if (value.IsBool()) + ret = NSJSBase::CJSContext::createBool((bool)value); + else if (value.IsInt()) + ret = NSJSBase::CJSContext::createInt((int)value); + else if (value.IsDouble()) + ret = NSJSBase::CJSContext::createDouble((double)value); + else if (value.IsStringA()) + ret = NSJSBase::CJSContext::createString((std::string)value); + else + ret = NSJSBase::CJSContext::createString((std::wstring)value); } return ret; @@ -75,6 +67,7 @@ namespace NSJSON static CValue fromJS(JSSmart jsValue) { + /* if (jsValue->isUndefined()) return new CPrimitive(); @@ -138,8 +131,9 @@ namespace NSJSON // TODO: how do we know whether string consist of char or wchar_t? Can we convert all strings to std::wstring? } // else ret is nullptr + */ - return ret; + return CValue(); } } diff --git a/DesktopEditor/doctrenderer/test/json/main.cpp b/DesktopEditor/doctrenderer/test/json/main.cpp index edda7aa998..8027f47e0a 100644 --- a/DesktopEditor/doctrenderer/test/json/main.cpp +++ b/DesktopEditor/doctrenderer/test/json/main.cpp @@ -1,7 +1,6 @@ //#include "gtest/gtest.h" #include "js_internal/js_base.h" -//#include "json/serialization.h" -#include "json/json.h" +#include "json/serialization.h" #include #include @@ -40,7 +39,7 @@ int main() JSSmart pContext = new CJSContext(); CJSContextScope scope(pContext); - // top object with text parameters + // top object with some text parameters CValue textPr = CValue::CreateObject(); CValue colorRGB = CValue::CreateObject(); @@ -53,6 +52,10 @@ int main() colorRGBA["alpha"] = 80; textPr["size"] = 4.2; + // CValue behaves as reference, when it was initialized with value returned from Get() (or operator[]) of objects & arrays. + CValue name = textPr["name"]; + // from now on, name - is a reference to the property "name" of textPr. Changing it will affect this object property: + name = "Foo"; textPr["color"] = colorRGBA; textPr["font"] = CValue::CreateObject(); textPr["font"]["fontName"] = L"Times New Roman"; @@ -81,7 +84,6 @@ int main() */ // convert to JS - /* JSSmart jsObj = toJS(textPr)->toObject(); JSSmart global = pContext->GetGlobal(); global->set("textPr", jsObj); @@ -90,7 +92,6 @@ int main() { std::cout << ret->toStringA() << std::endl; } - */ return 0; }