/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-12 Miranda IM, 2012-13 Miranda NG project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "..\commonheaders.h" #include #include "internalJSONNode.h" #include "NumberToString.h" //So that I can convert numbers into strings #include "JSONNode.h" //To fill in the foreward declaration #include "JSONWorker.h" //For fetching and parsing and such /* The point of these constants is for faster assigning, if I were to constantly assign to a string literal, there would be lots of copies, but since strings are copy on write, this assignment is much faster */ static const json_string CONST_TRUE(JSON_TEXT("true")); static const json_string CONST_FALSE(JSON_TEXT("false")); static const json_string CONST_NULL(JSON_TEXT("null")); #ifdef JSON_UNIT_TEST void internalJSONNode::incinternalAllocCount(void){ JSONNode::incinternalAllocCount(); } void internalJSONNode::decinternalAllocCount(void){ JSONNode::decinternalAllocCount(); } #endif internalJSONNode::internalJSONNode(const internalJSONNode & orig) : _type(orig._type), _name(orig._name), _name_encoded(orig._name_encoded), Children(), _string(orig._string), _string_encoded(orig._string_encoded), _value(orig._value) initializeRefCount(1) initializeFetch(orig.fetched) initializeMutex(0) initializeComment(orig._comment) initializeValid(orig.isValid){ incinternalAllocCount(); #ifdef JSON_MUTEX_CALLBACKS _set_mutex(orig.mylock, false); #endif if (!orig.Children.empty()) { Children.reserve(orig.Children.size()); json_foreach(orig.Children, myrunner){ Children.push_back(JSONNode::newJSONNode((*myrunner) -> duplicate())); } } } #ifdef JSON_PREPARSE #define SetFetchedFalseOrDo(code) code #else #define SetFetchedFalseOrDo(code) SetFetched(false) #endif #ifdef JSON_VALIDATE #define NOTVALID false #else #define NOTVALID #endif //this one is specialized because the root can only be array or node internalJSONNode::internalJSONNode(const json_string & unparsed) : _type(), _name(),_name_encoded(false), _string(unparsed), _string_encoded(), _value(), Children() initializeMutex(0) initializeRefCount(1) initializeFetch(false) initializeComment(0) initializeValid(0){ incinternalAllocCount(); switch (unparsed[0]) { case JSON_TEXT('{'): //node _type = JSON_NODE; #ifdef JSON_PREPARSE FetchNode(); #endif break; case JSON_TEXT('['): //array _type = JSON_ARRAY; #ifdef JSON_PREPARSE FetchArray(); #endif break; default: JSON_FAIL_SAFE(JSON_TEXT("root not starting with either { or ["), Nullify(NOTVALID);); break; } } internalJSONNode::internalJSONNode(const json_string & name_t, const json_string & value_t) : _type(), _name_encoded(), _name(JSONWorker::FixString(name_t, NAME_ENCODED)), _string(), _string_encoded(), _value(), Children() initializeMutex(0) initializeRefCount(1) initializeFetch(0) initializeComment(0) initializeValid(0){ incinternalAllocCount(); if (value_t.empty()) { _type = JSON_NULL; #ifdef JSON_VALIDATE isValid = true; #endif SetFetched(true); return; } _string = value_t; const json_char firstchar = value_t[0]; #if defined JSON_DEBUG || defined JSON_SAFE const json_char lastchar = value_t[value_t.length() - 1]; #endif switch (firstchar){ case JSON_TEXT('\"'): //a json_string literal, still escaped and with leading and trailing quotes JSON_ASSERT_SAFE(lastchar == JSON_TEXT('\"'), JSON_TEXT("Unterminated quote"), Nullify(NOTVALID); return;); _type = JSON_STRING; SetFetchedFalseOrDo(FetchString()); break; case JSON_TEXT('{'): //a child node, or set of children JSON_ASSERT_SAFE(lastchar == JSON_TEXT('}'), JSON_TEXT("Missing }"), Nullify(NOTVALID); return;); _type = JSON_NODE; SetFetchedFalseOrDo(FetchNode()); break; case JSON_TEXT('['): //an array JSON_ASSERT_SAFE(lastchar == JSON_TEXT(']'), JSON_TEXT("Missing ]"), Nullify(NOTVALID); return;); _type = JSON_ARRAY; SetFetchedFalseOrDo(FetchArray()); break; case JSON_TEXT('t'): JSON_ASSERT_SAFE(value_t == JSON_TEXT("true"), json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); _value._bool = true; _type = JSON_BOOL; SetFetched(true); break; case JSON_TEXT('f'): JSON_ASSERT_SAFE(value_t == JSON_TEXT("false"), json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); _value._bool = false; _type = JSON_BOOL; SetFetched(true); break; case JSON_TEXT('n'): JSON_ASSERT_SAFE(value_t == JSON_TEXT("null"), json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); _type = JSON_NULL; SetFetched(true); #ifdef JSON_VALIDATE isValid = true; #endif break; default: JSON_ASSERT_SAFE(value_t.find_first_not_of(JSON_TEXT("0123456789.e+-")) == json_string::npos, json_string(json_string(JSON_TEXT("unknown JSON literal: ")) + value_t).c_str(), Nullify(NOTVALID); return;); _type = JSON_NUMBER; SetFetchedFalseOrDo(FetchNumber()); break; } } internalJSONNode::~internalJSONNode(void){ decinternalAllocCount(); #ifdef JSON_MUTEX_CALLBACKS _unset_mutex(); #endif //DO NOT delete the children! It automatically gets removed } void internalJSONNode::FetchString(void) const { JSON_ASSERT_SAFE(!_string.empty(), JSON_TEXT("JSON json_string type is empty?"), Nullify(NOTVALID); return;); JSON_ASSERT_SAFE(_string[0] == JSON_TEXT('\"'), JSON_TEXT("JSON json_string type doesn't start with a quotation?"), Nullify(NOTVALID); return;); JSON_ASSERT_SAFE(_string[_string.length() - 1] == JSON_TEXT('\"'), JSON_TEXT("JSON json_string type doesn't end with a quotation?"), Nullify(NOTVALID); return;); _string = JSONWorker::FixString(_string.substr(1, _string.length() - 2), STRING_ENCODED); } void internalJSONNode::FetchNode(void) const { JSON_ASSERT_SAFE(!_string.empty(), JSON_TEXT("JSON node type is empty?"), Nullify(NOTVALID); return;); JSON_ASSERT_SAFE(_string[0] == JSON_TEXT('{'), JSON_TEXT("JSON node type doesn't start with a bracket?"), Nullify(NOTVALID); return;); JSON_ASSERT_SAFE(_string[_string.length() - 1] == JSON_TEXT('}'), JSON_TEXT("JSON node type doesn't end with a bracket?"), Nullify(NOTVALID); return;); JSONWorker::DoNode(this, _string); clearString(_string); } void internalJSONNode::FetchArray(void) const { JSON_ASSERT_SAFE(!_string.empty(), JSON_TEXT("JSON node type is empty?"), Nullify(NOTVALID); return;); JSON_ASSERT_SAFE(_string[0] == JSON_TEXT('['), JSON_TEXT("JSON node type doesn't start with a square bracket?"), Nullify(NOTVALID); return;); JSON_ASSERT_SAFE(_string[_string.length() - 1] == JSON_TEXT(']'), JSON_TEXT("JSON node type doesn't end with a square bracket?"), Nullify(NOTVALID); return;); JSONWorker::DoArray(this, _string); clearString(_string); } void internalJSONNode::FetchNumber(void) const { #ifdef JSON_UNICODE { const size_t len = _string.length(); json_auto temp(len + 1); wcstombs(temp.ptr, _string.c_str(), len); temp.ptr[len] = '\0'; _value._number = (json_number)atof(temp.ptr); } #else _value._number = (json_number)atof(_string.c_str()); #endif } #ifndef JSON_PREPARSE void internalJSONNode::Fetch(void) const { if (fetched) return; switch (type()) { case JSON_STRING: FetchString(); break; case JSON_NODE: FetchNode(); break; case JSON_ARRAY: FetchArray(); break; case JSON_NUMBER: FetchNumber(); break; #if defined JSON_DEBUG || defined JSON_SAFE default: JSON_FAIL(JSON_TEXT("Fetching an unknown type")); Nullify(NOTVALID); #endif } fetched = true; } #endif void internalJSONNode::Set(const json_string & val){ _type = JSON_STRING; _string = val; _string_encoded = true; SetFetched(true); } #ifdef JSON_LIBRARY void internalJSONNode::Set(long val){ _type = JSON_NUMBER; _value._number = (json_number)val; _string = NumberToString::_itoa(val, sizeof(long)); SetFetched(true); } void internalJSONNode::Set(json_number val){ _type = JSON_NUMBER; _value._number = val; _string = NumberToString::_ftoa(val); SetFetched(true); } #else #define SET(converter, type)\ void internalJSONNode::Set(type val){\ _type = JSON_NUMBER;\ _value._number = (json_number)val;\ _string = NumberToString::converter(val, sizeof(type));\ SetFetched(true);\ } #define SET_INTEGER(type) SET(_itoa, type) SET(_uitoa, unsigned type) #define SET_FLOAT(type) \ void internalJSONNode::Set(type val){\ _type = JSON_NUMBER;\ _value._number = (json_number)val;\ _string = NumberToString::_ftoa(val);\ SetFetched(true);\ } SET_INTEGER(char) SET_INTEGER(short) SET_INTEGER(int) SET_INTEGER(long) #ifndef JSON_ISO_STRICT SET_INTEGER(long long) #endif SET_FLOAT(float) SET_FLOAT(double) #endif void internalJSONNode::Set(bool val){ _type = JSON_BOOL; _value._bool = val; _string = val ? CONST_TRUE : CONST_FALSE; SetFetched(true); } bool internalJSONNode::IsEqualTo(const internalJSONNode * val) const { #ifdef JSON_REF_COUNT if (this == val) return true; //reference counting the same internal object, so they must be equal #endif if (type() != val -> type()) return false; //aren't even same type if (_name != val -> _name) return false; //names aren't the same if (type() == JSON_NULL) return true; //both null, can't be different #ifndef JSON_PREPARSE Fetch(); val -> Fetch(); #endif switch (type()) { case JSON_STRING: return val -> _string == _string; case JSON_NUMBER: return NumberToString::areEqual(val -> _value._number, _value._number); case JSON_BOOL: return val -> _value._bool == _value._bool; }; JSON_ASSERT(type() == JSON_NODE || type() == JSON_ARRAY, JSON_TEXT("Checking for equality, not sure what type")); if (Children.size() != val -> Children.size()) return false; //if they arne't he same size then they certainly aren't equal //make sure each children is the same JSONNode ** valrunner = val -> Children.begin(); json_foreach(Children, myrunner){ JSON_ASSERT(*myrunner, JSON_TEXT("a null pointer within the children")); JSON_ASSERT(*valrunner, JSON_TEXT("a null pointer within the children")); JSON_ASSERT(valrunner != val -> Children.end(), JSON_TEXT("at the end of other one's children, but they're the same size?")); if (**myrunner != **valrunner) return false; ++valrunner; } return true; } #ifdef JSON_VALIDATE void internalJSONNode::Nullify(bool validation) const { isValid = validation; #else void internalJSONNode::Nullify(void) const { #endif _type = JSON_NULL; _string = CONST_NULL; SetFetched(true); } #ifdef JSON_MUTEX_CALLBACKS #define JSON_MUTEX_COPY ,mylock #else #define JSON_MUTEX_COPY #endif #ifdef JSON_LIBRARY void internalJSONNode::push_back(JSONNode *node){ #else void internalJSONNode::push_back(const JSONNode & node){ #endif JSON_ASSERT(type() == JSON_ARRAY || type() == JSON_NODE, JSON_TEXT("pushing back to something that is not an array or object")); #ifdef JSON_LIBRARY #ifdef JSON_MUTEX_CALLBACKS if (mylock) node -> set_mutex(mylock); #endif Children.push_back(node); #else Children.push_back(JSONNode::newJSONNode(node JSON_MUTEX_COPY)); #endif } void internalJSONNode::push_front(const JSONNode & node){ JSON_ASSERT(type() == JSON_ARRAY || type() == JSON_NODE, JSON_TEXT("pushing front to something that is not an array or object")); Children.push_front(JSONNode::newJSONNode(node JSON_MUTEX_COPY)); } JSONNode * internalJSONNode::pop_back(json_index_t pos){ JSONNode * result = Children[pos]; JSONNode ** temp = Children.begin() + pos; Children.erase(temp); return result; } JSONNode * internalJSONNode::pop_back(const json_string & name_t){ if (JSONNode ** res = at(name_t)) { JSONNode * result = *res; Children.erase(res); return result; } return 0; } #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS JSONNode * internalJSONNode::pop_back_nocase(const json_string & name_t){ if (JSONNode ** res = at_nocase(name_t)) { JSONNode * result = *res; Children.erase(res); return result; } return 0; } #endif JSONNode ** internalJSONNode::at(const json_string & name_t){ Fetch(); json_foreach(Children, myrunner){ JSON_ASSERT(*myrunner, JSON_TEXT("a null pointer within the children")); if ((*myrunner) -> name() == name_t) return myrunner; } return 0; } #ifdef JSON_CASE_INSENSITIVE_FUNCTIONS bool internalJSONNode::AreEqualNoCase(const json_char * ch_one, const json_char * ch_two){ while (*ch_one){ //only need to check one, if the other one terminates early, the check will cause it to fail const json_char c_one = *ch_one; const json_char c_two = *ch_two; if (c_one != c_two){ if ((c_two > 64) && (c_two < 91)) { //A - Z if (c_one != (json_char)(c_two + 32)) return false; } else if ((c_two > 96) && (c_two < 123)) { //a - z if (c_one != (json_char)(c_two - 32)) return false; } else { //not a letter, so return false return false; } } ++ch_one; ++ch_two; } return *ch_two == '\0'; //this one has to be null terminated too, or else json_string two is longer, hence, not equal } JSONNode ** internalJSONNode::at_nocase(const json_string & name_t){ Fetch(); json_foreach(Children, myrunner){ JSON_ASSERT(*myrunner, JSON_TEXT("a null pointer within the children")); if (AreEqualNoCase((*myrunner) -> name().c_str(), name_t.c_str())) return myrunner; } return 0; } #endif #ifndef JSON_PREPARSE void internalJSONNode::preparse(void){ Fetch(); json_foreach(Children, myrunner){ (*myrunner) -> preparse(); } } #endif #ifdef JSON_VALIDATE bool internalJSONNode::validate(void){ json_foreach(Children, myrunner){ if ((*myrunner) -> type() != JSON_NULL){ #ifndef JSON_PREPARSE (*myrunner) -> internal -> Fetch(); #endif if ((*myrunner) -> type() == JSON_NULL) return false; } else if (!((*myrunner) -> internal -> isValid)) { JSON_FAIL(_name + JSON_TEXT(" is null and not valid")); return false; } } json_foreach(Children, runner){ if (!((*runner) -> internal -> validate())) return false; } return true; } #endif #ifdef JSON_DEBUG #ifndef JSON_LIBRARY JSONNode internalJSONNode::Dump(size_t & totalbytes) const { JSONNode dumpage(JSON_NODE); dumpage.set_name(JSON_TEXT("internalJSONNode")); dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("this"), (long)this))); size_t memory = sizeof(internalJSONNode); memory += _name.capacity() * sizeof(json_char); memory += _string.capacity() * sizeof(json_char); memory += Children.capacity() * sizeof(JSONNode*); #ifdef JSON_COMMENTS memory += _comment.capacity() * sizeof(json_char); #endif totalbytes += memory; dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("bytes used"), memory))); #ifdef JSON_REF_COUNT dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("refcount"), refcount))); #endif #ifdef JSON_MUTEX_CALLBACKS dumpage.push_back(JSON_NEW(DumpMutex())); #endif #define DUMPCASE(ty)\ case ty:\ dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_type"), JSON_TEXT(#ty))));\ break; switch(type()) { DUMPCASE(JSON_NULL) DUMPCASE(JSON_STRING) DUMPCASE(JSON_NUMBER) DUMPCASE(JSON_BOOL) DUMPCASE(JSON_ARRAY) DUMPCASE(JSON_NODE) default: dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_type"), JSON_TEXT("Unknown")))); } JSONNode str(JSON_NODE); str.set_name(JSON_TEXT("_name")); str.push_back(JSON_NEW(JSONNode(json_string(JSON_TEXT("value")), _name))); str.push_back(JSON_NEW(JSONNode(JSON_TEXT("length"), _name.length()))); str.push_back(JSON_NEW(JSONNode(JSON_TEXT("capactiy"), _name.capacity()))); #ifdef JSON_LESS_MEMORY dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_name_encoded"), _type & 0x10))); #else dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_name_encoded"), _name_encoded))); #endif dumpage.push_back(JSON_NEW(str)); dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("_string_encoded"), _string_encoded))); str.clear(); str.set_name(JSON_TEXT("_string")); str.push_back(JSON_NEW(JSONNode(json_string(JSON_TEXT("value")), _string))); str.push_back(JSON_NEW(JSONNode(JSON_TEXT("length"), _string.length()))); str.push_back(JSON_NEW(JSONNode(JSON_TEXT("capactiy"), _string.capacity()))); dumpage.push_back(JSON_NEW(str)); JSONNode unio(JSON_NODE); unio.set_name(JSON_TEXT("_value")); unio.push_back(JSON_NEW(JSONNode(JSON_TEXT("_bool"), _value._bool))); unio.push_back(JSON_NEW(JSONNode(JSON_TEXT("_number"), _value._number))); dumpage.push_back(JSON_NEW(unio)); #ifndef JSON_PREPARSE dumpage.push_back(JSON_NEW(JSONNode(JSON_TEXT("fetched"), fetched))); #endif #ifdef JSON_COMMENTS str.clear(); str.set_name(JSON_TEXT("_comment")); str.push_back(JSON_NEW(JSONNode(JSON_TEXT("value"), _comment))); str.push_back(JSON_NEW(JSONNode(JSON_TEXT("length"), _comment.length()))); str.push_back(JSON_NEW(JSONNode(JSON_TEXT("capactiy"), _comment.capacity()))); dumpage.push_back(JSON_NEW(str)); #endif JSONNode arra(JSON_NODE); arra.set_name(JSON_TEXT("Children")); arra.push_back(JSON_NEW(JSONNode(JSON_TEXT("size"), Children.size()))); arra.push_back(JSON_NEW(JSONNode(JSON_TEXT("capacity"), Children.capacity()))); JSONNode chil(JSON_ARRAY); chil.set_name(JSON_TEXT("array")); json_foreach(Children, it){ chil.push_back(JSON_NEW((*it) -> dump(totalbytes))); } arra.push_back(JSON_NEW(chil)); dumpage.push_back(JSON_NEW(arra)); return dumpage; } #endif #endif