summaryrefslogtreecommitdiff
path: root/plugins/Twitter/tinyjson.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Twitter/tinyjson.hpp')
-rw-r--r--plugins/Twitter/tinyjson.hpp586
1 files changed, 586 insertions, 0 deletions
diff --git a/plugins/Twitter/tinyjson.hpp b/plugins/Twitter/tinyjson.hpp
new file mode 100644
index 0000000000..19e1210d84
--- /dev/null
+++ b/plugins/Twitter/tinyjson.hpp
@@ -0,0 +1,586 @@
+/*
+ * TinyJson 1.3.0
+ * A Minimalistic JSON Reader Based On Boost.Spirit, Boost.Any, and Boost.Smart_Ptr.
+ *
+ * Copyright (c) 2008 Thomas Jansen (thomas@beef.de)
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ *
+ * See http://blog.beef.de/projects/tinyjson/ for documentation.
+ *
+ * (view source with tab-size = 3)
+ *
+ * 16 Mar 2009 - allow root of JSON to be array (Jim Porter)
+ * 29 Mar 2008 - use strict_real_p for number parsing, small cleanup (Thomas Jansen)
+ * 26 Mar 2008 - made json::grammar a template (Boris Schaeling)
+ * 20 Mar 2008 - optimized by using boost::shared_ptr (Thomas Jansen)
+ * 29 Jan 2008 - Small bugfixes (Thomas Jansen)
+ * 04 Jan 2008 - Released to the public (Thomas Jansen)
+ * 13 Nov 2007 - initial release (Thomas Jansen) *
+ *
+ * 29 Mar 2008
+ */
+
+
+#ifndef TINYJSON_HPP
+#define TINYJSON_HPP
+
+#include <boost/shared_ptr.hpp>
+#include <boost/any.hpp>
+#include <boost/spirit/core.hpp>
+#include <boost/spirit/utility/loops.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+#include <stack>
+#include <utility>
+#include <deque>
+#include <map>
+
+
+namespace json
+{
+ boost::spirit::int_parser<long long> const
+ longlong_p = boost::spirit::int_parser<long long>();
+
+ // ==========================================================================================================
+ // === U N I C O D E _ C O N V E R T ===
+ // ==========================================================================================================
+
+ template< typename Char >
+ struct unicodecvt
+ {
+ static std::basic_string< Char > convert(int iUnicode)
+ {
+ return std::basic_string< Char >(1, static_cast< Char >(iUnicode));
+ }
+ };
+
+
+ // ---[ TEMPLATE SPECIALIZATION FOR CHAR ]--------------------------------------------------------------------
+
+ template<>
+ struct unicodecvt< char >
+ {
+ static std::string convert(int iUnicode)
+ {
+ std::string strString;
+
+ if (iUnicode < 0x0080)
+ {
+ // character 0x0000 - 0x007f...
+
+ strString.push_back(0x00 | ((iUnicode & 0x007f) >> 0));
+ }
+ else if (iUnicode < 0x0800)
+ {
+ // character 0x0080 - 0x07ff...
+
+ strString.push_back(0xc0 | ((iUnicode & 0x07c0) >> 6));
+ strString.push_back(0x80 | ((iUnicode & 0x003f) >> 0));
+ }
+ else
+ {
+ // character 0x0800 - 0xffff...
+
+ strString.push_back(0xe0 | ((iUnicode & 0x00f000) >> 12));
+ strString.push_back(0x80 | ((iUnicode & 0x000fc0) >> 6));
+ strString.push_back(0x80 | ((iUnicode & 0x00003f) >> 0));
+ }
+
+ return strString;
+ }
+ };
+
+
+ // ==========================================================================================================
+ // === T H E J S O N G R A M M A R ===
+ // ==========================================================================================================
+
+ template< typename Char >
+ class grammar : public boost::spirit::grammar< grammar< Char > >
+ {
+ public:
+
+ // ---[ TYPEDEFINITIONS ]---------------------------------------------------------------------------------
+
+ typedef boost::shared_ptr< boost::any > variant; // pointer to a shared variant
+
+ typedef std::stack< variant > stack; // a stack of json variants
+ typedef std::pair< std::basic_string< Char >, variant > pair; // a pair as it appears in json
+
+ typedef std::deque< variant > array; // an array of json variants
+ typedef std::map< std::basic_string< Char >, variant > object; // an object with json pairs
+
+ protected:
+
+ // ---[ SEMANTIC ACTION: PUSH A STRING ON THE STACK (AND ENCODE AS UTF-8) ]-------------------------------
+
+ struct push_string
+ {
+ stack & m_stack;
+ push_string(stack & stack) : m_stack(stack) { }
+
+ template <typename Iterator>
+ void operator() (Iterator szStart, Iterator szEnd) const
+ {
+ // 1: skip the quotes...
+
+ ++szStart;
+ --szEnd;
+
+ // 2: traverse through the original string and check for escape codes..
+
+ std::basic_string< typename Iterator::value_type > strString;
+
+ while(szStart < szEnd)
+ {
+ // 2.1: if it's no escape code, just append to the resulting string...
+
+ if (*szStart != static_cast< typename Iterator::value_type >('\\'))
+ {
+ // 2.1.1: append the character...
+
+ strString.push_back(*szStart);
+ }
+ else
+ {
+ // 2.1.2: otherwise, check the escape code...
+
+ ++szStart;
+
+ switch(*szStart)
+ {
+ default:
+
+ strString.push_back(*szStart);
+ break;
+
+ case 'b':
+
+ strString.push_back(static_cast< typename Iterator::value_type >('\b'));
+ break;
+
+ case 'f':
+
+ strString.push_back(static_cast< typename Iterator::value_type >('\f'));
+ break;
+
+ case 'n':
+
+ strString.push_back(static_cast< typename Iterator::value_type >('\n'));
+ break;
+
+ case 'r':
+
+ strString.push_back(static_cast< typename Iterator::value_type >('\r'));
+ break;
+
+ case 't':
+
+ strString.push_back(static_cast< typename Iterator::value_type >('\t'));
+ break;
+
+ case 'u':
+ {
+ // 2.1.2.1: convert the following hex value into an int...
+
+ int iUnicode;
+ std::basic_istringstream< Char >(std::basic_string< typename Iterator::value_type >(&szStart[1], 4)) >> std::hex >> iUnicode;
+
+ szStart += 4;
+
+ // 2.1.2.2: append the unicode int...
+
+ strString.append(unicodecvt< typename Iterator::value_type >::convert(iUnicode));
+ }
+ }
+ }
+
+ // 2.2: go on with the next character...
+
+ ++szStart;
+ }
+
+ // 3: finally, push the string on the stack...
+
+ m_stack.push(variant(new boost::any(strString)));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: PUSH A REAL ON THE STACK ]-------------------------------------------------------
+
+ struct push_double
+ {
+ stack & m_stack;
+ push_double(stack & stack) : m_stack(stack) { }
+
+ void operator() (double dValue) const
+ {
+ m_stack.push(variant(new boost::any(dValue)));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: PUSH AN INT ON THE STACK ]-------------------------------------------------------
+
+ struct push_int
+ {
+ stack & m_stack;
+ push_int(stack & stack) : m_stack(stack) { }
+
+ void operator() (long long iValue) const
+ {
+ m_stack.push(variant(new boost::any(iValue)));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: PUSH A BOOLEAN ON THE STACK ]----------------------------------------------------
+
+ struct push_boolean
+ {
+ stack & m_stack;
+ push_boolean(stack & stack) : m_stack(stack) { }
+
+ template <typename Iterator>
+ void operator() (Iterator szStart, Iterator /* szEnd */ ) const
+ {
+ // 1: push a boolean that is "true" if the string starts with 't' and "false" otherwise...
+
+ m_stack.push(variant(new boost::any(*szStart == static_cast< typename Iterator::value_type >('t'))));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: PUSH A NULL VALUE ON THE STACK ]-------------------------------------------------
+
+ struct push_null
+ {
+ stack & m_stack;
+ push_null(stack & stack) : m_stack(stack) { }
+
+ template <typename Iterator>
+ void operator() (Iterator /* szStart */ , Iterator /* szEnd */ ) const
+ {
+ m_stack.push(variant(new boost::any()));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: CREATE A "JSON PAIR" ON THE STACK ]----------------------------------------------
+
+ struct create_pair
+ {
+ stack & m_stack;
+ create_pair(stack & stack) : m_stack(stack) { }
+
+ template <typename Iterator>
+ void operator() (Iterator /* szStart */, Iterator /* szEnd */ ) const
+ {
+ // 1: get the variant from the stack...
+
+ variant var = m_stack.top();
+ m_stack.pop();
+
+ // 2: get the name from the stack...
+
+ std::basic_string< typename Iterator::value_type > strName;
+
+ try
+ {
+ strName = boost::any_cast< std::basic_string< typename Iterator::value_type > >(*m_stack.top());
+ }
+ catch(boost::bad_any_cast &) { /* NOTHING */ }
+
+ m_stack.pop();
+
+ // 3: push a pair of both on the stack...
+
+ m_stack.push(variant(new boost::any(pair(strName, var))));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: BEGIN AN ARRAY ]-----------------------------------------------------------------
+
+ class array_delimiter { /* EMPTY CLASS */ };
+
+ struct begin_array
+ {
+ stack & m_stack;
+ begin_array(stack & stack) : m_stack(stack) { }
+
+ template <typename Iterator>
+ void operator() (Iterator /* cCharacter */) const
+ {
+ m_stack.push(variant(new boost::any(array_delimiter())));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: CREATE AN ARRAY FROM THE VALUES ON THE STACK ]-----------------------------------
+
+ struct end_array
+ {
+ stack & m_stack;
+ end_array(stack & stack) : m_stack(stack) { }
+
+ // - -[ functional operator ] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ template <typename Iterator>
+ void operator() (Iterator /* cCharacter */) const
+ {
+ // 1: create an array object and push everything in it, that's on the stack...
+
+ variant varArray(new boost::any(array()));
+
+ while(!m_stack.empty())
+ {
+ // 1.1: get the top most variant of the stack...
+
+ variant var = m_stack.top();
+ m_stack.pop();
+
+ // 1.2: is it the end of the array? if yes => break the loop...
+
+ if (boost::any_cast< array_delimiter >(var.get()) != NULL)
+ {
+ break;
+ }
+
+ // 1.3: otherwise, add to the array...
+
+ boost::any_cast< array >(varArray.get())->push_front(var);
+ }
+
+ // 2: finally, push the array at the end of the stack...
+
+ m_stack.push(varArray);
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: BEGIN AN OBJECT ]----------------------------------------------------------------
+
+ class object_delimiter { /* EMPTY CLASS */ };
+
+ struct begin_object
+ {
+ stack & m_stack;
+ begin_object(stack & stack) : m_stack(stack) { }
+
+ template <typename Iterator>
+ void operator() (Iterator /* cCharacter */) const
+ {
+ m_stack.push(variant(new boost::any(object_delimiter())));
+ }
+ };
+
+
+ // ---[ SEMANTIC ACTION: CREATE AN OBJECT FROM THE VALUES ON THE STACK ]----------------------------------
+
+ struct end_object
+ {
+ stack & m_stack;
+ end_object(stack & stack) : m_stack(stack) { }
+
+ template <typename Iterator>
+ void operator() (Iterator /* cCharacter */) const
+ {
+ // 1: create an array object and push everything in it, that's on the stack...
+
+ variant varObject(new boost::any(object()));
+
+ while(!m_stack.empty())
+ {
+ // 1.1: get the top most variant of the stack...
+
+ variant var = m_stack.top();
+ m_stack.pop();
+
+ // 1.2: is it the end of the array? if yes => break the loop...
+
+ if (boost::any_cast< object_delimiter >(var.get()) != NULL)
+ {
+ break;
+ }
+
+ // 1.3: if this is not a pair, we have a problem...
+
+ pair * pPair = boost::any_cast< pair >(var.get());
+ if (!pPair)
+ {
+ /* BIG PROBLEM!! */
+
+ continue;
+ }
+
+ // 1.4: set the child of this object...
+
+ boost::any_cast< object >(varObject.get())->insert(std::make_pair(pPair->first, pPair->second));
+ }
+
+ // 2: finally, push the array at the end of the stack...
+
+ m_stack.push(varObject);
+ }
+ };
+
+ public:
+
+ stack & m_stack;
+ grammar(stack & stack) : m_stack(stack) { }
+
+ // ---[ THE ACTUAL GRAMMAR DEFINITION ]-------------------------------------------------------------------
+
+ template <typename SCANNER>
+ class definition
+ {
+ boost::spirit::rule< SCANNER > m_start;
+ boost::spirit::rule< SCANNER > m_object;
+ boost::spirit::rule< SCANNER > m_array;
+ boost::spirit::rule< SCANNER > m_pair;
+ boost::spirit::rule< SCANNER > m_value;
+ boost::spirit::rule< SCANNER > m_string;
+ boost::spirit::rule< SCANNER > m_number;
+ boost::spirit::rule< SCANNER > m_boolean;
+ boost::spirit::rule< SCANNER > m_null;
+
+ public:
+
+ boost::spirit::rule< SCANNER > const & start() const { return m_start; }
+
+ // - -[ create the definition ] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ definition(grammar const & self)
+ {
+ using namespace boost::spirit;
+
+ // 0: JSON can either be an object or an array
+
+ m_start
+ = m_object
+ | m_array;
+
+ // 1: an object is an unordered set of pairs (seperated by commas)...
+
+ m_object
+ = ch_p('{') [ begin_object(self.m_stack) ] >>
+ !(m_pair >> *(ch_p(',') >> m_pair)) >>
+ ch_p('}') [ end_object (self.m_stack) ];
+
+ // 2: an array is an ordered collection of values (seperated by commas)...
+
+ m_array
+ = ch_p('[') [ begin_array(self.m_stack) ] >>
+ !(m_value >> *(ch_p(',') >> m_value)) >>
+ ch_p(']') [ end_array (self.m_stack) ];
+
+ // 3: a pair is given by a name and a value...
+
+ m_pair
+ = ( m_string >> ch_p(':') >> m_value )
+ [ create_pair(self.m_stack) ]
+ ;
+
+ // 4: a value can be a string in double quotes, a number, a boolean, an object or an array.
+
+ m_value
+ = m_string
+ | m_number
+ | m_object
+ | m_array
+ | m_boolean
+ | m_null
+ ;
+
+ // 5: a string is a collection of zero or more unicode characters, wrapped in double quotes...
+
+ m_string
+ = lexeme_d
+ [
+ ( ch_p('"') >> *(
+ ( (anychar_p - (ch_p('"') | ch_p('\\')))
+ | ch_p('\\') >>
+ ( ch_p('\"')
+ | ch_p('\\')
+ | ch_p('/')
+ | ch_p('b')
+ | ch_p('f')
+ | ch_p('n')
+ | ch_p('r')
+ | ch_p('t')
+ | (ch_p('u') >> repeat_p(4)[ xdigit_p ])
+ )
+ )) >> ch_p('"')
+ )
+ [ push_string(self.m_stack) ]
+ ]
+ ;
+
+ // 6: a number is very much like a C or java number...
+
+ m_number
+ = strict_real_p [ push_double(self.m_stack) ]
+ | longlong_p [ push_int (self.m_stack) ]
+ ;
+
+ // 7: a boolean can be "true" or "false"...
+
+ m_boolean
+ = ( str_p("true")
+ | str_p("false")
+ )
+ [ push_boolean(self.m_stack) ]
+ ;
+
+ // 8: finally, a value also can be a 'null', i.e. an empty item...
+
+ m_null
+ = str_p("null")
+ [ push_null(self.m_stack) ]
+ ;
+ }
+ };
+ };
+
+
+ // ==========================================================================================================
+ // === T H E F I N A L P A R S I N G R O U T I N E ===
+ // ==========================================================================================================
+
+ template <typename Iterator>
+ typename json::grammar< typename Iterator::value_type >::variant parse(Iterator const & szFirst, Iterator const & szEnd)
+ {
+ // 1: parse the input...
+
+ json::grammar< typename Iterator::value_type >::stack st;
+ json::grammar< typename Iterator::value_type > gr(st);
+
+ boost::spirit::parse_info<Iterator> pi = boost::spirit::parse(szFirst, szEnd, gr, boost::spirit::space_p);
+
+ // 2: skip any spaces at the end of the parsed section...
+
+ while((pi.stop != szEnd) && (*pi.stop == static_cast< typename Iterator::value_type >(' ')))
+ {
+ ++pi.stop;
+ }
+
+ // 3: if the input's end wasn't reached or if there is more than one object on the stack => cancel...
+
+ if ((pi.stop != szEnd) || (st.size() != 1))
+ {
+ return json::grammar< typename Iterator::value_type >::variant(new boost::any());
+ }
+
+ // 4: otherwise, return the result...
+
+ return st.top();
+ }
+};
+
+
+#endif // TINYJSON_HPP