Only automatically convert json objects to other types when they are uninitialized
Originally committed to SVN as r6005.
This commit is contained in:
parent
a78417177a
commit
3097dc634e
6 changed files with 130 additions and 181 deletions
|
@ -14,32 +14,40 @@ Author: Terry Caton
|
|||
#include <cassert>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
using namespace json;
|
||||
|
||||
class CastVisitorBase : public Visitor, public ConstVisitor {
|
||||
void Visit(Array&) { }
|
||||
void Visit(Object&) { }
|
||||
void Visit(Number&) { }
|
||||
void Visit(String&) { }
|
||||
void Visit(Boolean&) { }
|
||||
void Visit(Null&) { is_null = true; }
|
||||
void Visit(Array const&) { }
|
||||
void Visit(Object const&) { }
|
||||
void Visit(Number const&) { }
|
||||
void Visit(String const&) { }
|
||||
void Visit(Boolean const&) { }
|
||||
void Visit(Null const&) { is_null = true; }
|
||||
public:
|
||||
bool is_null;
|
||||
CastVisitorBase() : is_null(false) { }
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct CastVisitor : public CastVisitorBase {
|
||||
T *element;
|
||||
CastVisitor() : element(0) { }
|
||||
void Visit(T& ele) { element = &ele; }
|
||||
};
|
||||
}
|
||||
|
||||
namespace json
|
||||
{
|
||||
|
||||
/////////////////////////
|
||||
// UnknownElement members
|
||||
class CastVisitor : public ConstVisitor
|
||||
{
|
||||
virtual void Visit(const Array&) { }
|
||||
virtual void Visit(const Object&) { }
|
||||
virtual void Visit(const Number&) { }
|
||||
virtual void Visit(const String&) { }
|
||||
virtual void Visit(const Boolean&) { }
|
||||
virtual void Visit(const Null&) { }
|
||||
};
|
||||
|
||||
template <typename ElementTypeT>
|
||||
class CastVisitor_T : public CastVisitor
|
||||
{
|
||||
public:
|
||||
const ElementTypeT *element;
|
||||
CastVisitor_T() : element(0) { }
|
||||
|
||||
// we don't know what this is, but it overrides one of the base's no-op functions
|
||||
void Visit(const ElementTypeT& element) { this->element = &element; }
|
||||
};
|
||||
|
||||
class UnknownElement::Imp
|
||||
{
|
||||
public:
|
||||
|
@ -57,15 +65,15 @@ template <typename ElementTypeT>
|
|||
class UnknownElement::Imp_T : public UnknownElement::Imp
|
||||
{
|
||||
public:
|
||||
Imp_T(const ElementTypeT& element) : m_Element(element) {}
|
||||
virtual Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
|
||||
Imp_T(const ElementTypeT& element) : m_Element(element) { }
|
||||
Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
|
||||
|
||||
virtual void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
|
||||
virtual void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
|
||||
void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
|
||||
void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
|
||||
|
||||
virtual bool Compare(const Imp& imp) const
|
||||
bool Compare(const Imp& imp) const
|
||||
{
|
||||
CastVisitor_T<ElementTypeT> castVisitor;
|
||||
CastVisitor<const ElementTypeT> castVisitor;
|
||||
imp.Accept(castVisitor);
|
||||
return castVisitor.element && m_Element == *castVisitor.element;
|
||||
}
|
||||
|
@ -88,31 +96,30 @@ UnknownElement::UnknownElement(const Null& null) : m_pImp( new Imp
|
|||
|
||||
UnknownElement::~UnknownElement() { delete m_pImp; }
|
||||
|
||||
UnknownElement::operator const Object& () const { return CastTo<Object>(); }
|
||||
UnknownElement::operator const Array& () const { return CastTo<Array>(); }
|
||||
UnknownElement::operator const Number& () const { return CastTo<Number>(); }
|
||||
UnknownElement::operator const Boolean& () const { return CastTo<Boolean>(); }
|
||||
UnknownElement::operator const String& () const { return CastTo<String>(); }
|
||||
UnknownElement::operator const Null& () const { return CastTo<Null>(); }
|
||||
UnknownElement::operator Object const&() const { return CastTo<Object>(); }
|
||||
UnknownElement::operator Array const&() const { return CastTo<Array>(); }
|
||||
UnknownElement::operator Number const&() const { return CastTo<Number>(); }
|
||||
UnknownElement::operator Boolean const&() const { return CastTo<Boolean>(); }
|
||||
UnknownElement::operator String const&() const { return CastTo<String>(); }
|
||||
UnknownElement::operator Null const&() const { return CastTo<Null>(); }
|
||||
|
||||
UnknownElement::operator Object& () { return ConvertTo<Object>(); }
|
||||
UnknownElement::operator Array& () { return ConvertTo<Array>(); }
|
||||
UnknownElement::operator Number& () { return ConvertTo<Number>(); }
|
||||
UnknownElement::operator Boolean& () { return ConvertTo<Boolean>(); }
|
||||
UnknownElement::operator String& () { return ConvertTo<String>(); }
|
||||
UnknownElement::operator Null& () { return ConvertTo<Null>(); }
|
||||
UnknownElement::operator Object&() { return CastTo<Object>(); }
|
||||
UnknownElement::operator Array&() { return CastTo<Array>(); }
|
||||
UnknownElement::operator Number&() { return CastTo<Number>(); }
|
||||
UnknownElement::operator Boolean&() { return CastTo<Boolean>(); }
|
||||
UnknownElement::operator String&() { return CastTo<String>(); }
|
||||
UnknownElement::operator Null&() { return CastTo<Null>(); }
|
||||
|
||||
UnknownElement& UnknownElement::operator = (const UnknownElement& unknown)
|
||||
UnknownElement& UnknownElement::operator =(const UnknownElement& unknown)
|
||||
{
|
||||
delete m_pImp;
|
||||
m_pImp = unknown.m_pImp->Clone();
|
||||
return *this;
|
||||
}
|
||||
|
||||
UnknownElement& UnknownElement::operator[] (const std::string& key)
|
||||
UnknownElement& UnknownElement::operator[](const std::string& key)
|
||||
{
|
||||
// the people want an object. make us one if we aren't already
|
||||
return ConvertTo<Object>()[key];
|
||||
return CastTo<Object>()[key];
|
||||
}
|
||||
|
||||
const UnknownElement& UnknownElement::operator[] (const std::string& key) const
|
||||
|
@ -125,23 +132,14 @@ const UnknownElement& UnknownElement::operator[] (const std::string& key) const
|
|||
return it->second;
|
||||
}
|
||||
|
||||
UnknownElement& UnknownElement::operator[] (size_t index)
|
||||
{
|
||||
// the people want an array. make us one if we aren't already
|
||||
return ConvertTo<Array>()[index];
|
||||
}
|
||||
|
||||
const UnknownElement& UnknownElement::operator[] (size_t index) const
|
||||
{
|
||||
// throws if we aren't an array
|
||||
return CastTo<Array>()[index];
|
||||
}
|
||||
UnknownElement& UnknownElement::operator[](size_t index) { return CastTo<Array>()[index]; }
|
||||
UnknownElement const& UnknownElement::operator[](size_t index) const { return CastTo<Array>()[index]; }
|
||||
|
||||
|
||||
template <typename ElementTypeT>
|
||||
const ElementTypeT& UnknownElement::CastTo() const
|
||||
ElementTypeT const& UnknownElement::CastTo() const
|
||||
{
|
||||
CastVisitor_T<ElementTypeT> castVisitor;
|
||||
CastVisitor<const ElementTypeT> castVisitor;
|
||||
m_pImp->Accept(castVisitor);
|
||||
if (!castVisitor.element)
|
||||
throw Exception("Bad cast");
|
||||
|
@ -149,17 +147,21 @@ const ElementTypeT& UnknownElement::CastTo() const
|
|||
}
|
||||
|
||||
template <typename ElementTypeT>
|
||||
ElementTypeT& UnknownElement::ConvertTo()
|
||||
ElementTypeT& UnknownElement::CastTo()
|
||||
{
|
||||
CastVisitor_T<ElementTypeT> castVisitor;
|
||||
CastVisitor<ElementTypeT> castVisitor;
|
||||
Accept(castVisitor);
|
||||
if (!castVisitor.element)
|
||||
{
|
||||
// we're not the right type. fix it & try again
|
||||
|
||||
// If this element is uninitialized, implicitly convert it to the desired type
|
||||
if (castVisitor.is_null) {
|
||||
*this = ElementTypeT();
|
||||
return *this;
|
||||
}
|
||||
|
||||
return *this;
|
||||
// Otherwise throw an exception
|
||||
if (!castVisitor.element)
|
||||
throw Exception("Bad cast");
|
||||
return *castVisitor.element;
|
||||
}
|
||||
|
||||
void UnknownElement::Accept(ConstVisitor& visitor) const { m_pImp->Accept(visitor); }
|
||||
|
|
|
@ -123,7 +123,7 @@ void Reader::Read_i(ElementTypeT& element, std::istream& istr)
|
|||
reader.Scan(tokens, inputStream);
|
||||
|
||||
TokenStream tokenStream(tokens);
|
||||
reader.Parse(element, tokenStream);
|
||||
element = reader.Parse(tokenStream);
|
||||
|
||||
if (!tokenStream.EOS())
|
||||
{
|
||||
|
@ -304,131 +304,79 @@ void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream)
|
|||
}
|
||||
|
||||
|
||||
void Reader::Parse(UnknownElement& element, Reader::TokenStream& tokenStream)
|
||||
UnknownElement Reader::Parse(Reader::TokenStream& tokenStream)
|
||||
{
|
||||
if (tokenStream.EOS()) {
|
||||
if (tokenStream.EOS())
|
||||
throw ParseException("Unexpected end of token stream", Location(), Location()); // nowhere to point to
|
||||
}
|
||||
|
||||
const Token& token = tokenStream.Peek();
|
||||
Token const& token = tokenStream.Peek();
|
||||
switch (token.nType) {
|
||||
case Token::TOKEN_OBJECT_BEGIN:
|
||||
{
|
||||
// implicit non-const cast will perform conversion for us (if necessary)
|
||||
Object& object = element;
|
||||
Parse(object, tokenStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::TOKEN_ARRAY_BEGIN:
|
||||
{
|
||||
Array& array = element;
|
||||
Parse(array, tokenStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::TOKEN_STRING:
|
||||
{
|
||||
String& string = element;
|
||||
Parse(string, tokenStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::TOKEN_NUMBER:
|
||||
{
|
||||
Number& number = element;
|
||||
Parse(number, tokenStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::TOKEN_BOOLEAN:
|
||||
{
|
||||
Boolean& boolean = element;
|
||||
Parse(boolean, tokenStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::TOKEN_NULL:
|
||||
{
|
||||
Null& null = element;
|
||||
Parse(null, tokenStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::TOKEN_OBJECT_BEGIN: return ParseObject(tokenStream);
|
||||
case Token::TOKEN_ARRAY_BEGIN: return ParseArray(tokenStream);
|
||||
case Token::TOKEN_STRING: return ParseString(tokenStream);
|
||||
case Token::TOKEN_NUMBER: return ParseNumber(tokenStream);
|
||||
case Token::TOKEN_BOOLEAN: return ParseBoolean(tokenStream);
|
||||
case Token::TOKEN_NULL: return ParseNull(tokenStream);
|
||||
default:
|
||||
{
|
||||
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Reader::Parse(Object& object, Reader::TokenStream& tokenStream)
|
||||
Object Reader::ParseObject(Reader::TokenStream& tokenStream)
|
||||
{
|
||||
MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
|
||||
|
||||
bool bContinue = (tokenStream.EOS() == false &&
|
||||
tokenStream.Peek().nType != Token::TOKEN_OBJECT_END);
|
||||
while (bContinue)
|
||||
Object object;
|
||||
|
||||
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
|
||||
{
|
||||
// first the member name. save the token in case we have to throw an exception
|
||||
const Token& tokenName = tokenStream.Peek();
|
||||
std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
|
||||
|
||||
if (object.count(name))
|
||||
{
|
||||
throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
|
||||
}
|
||||
|
||||
// ...then the key/value separator...
|
||||
MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
|
||||
|
||||
// ...then the value itself (can be anything).
|
||||
UnknownElement value;
|
||||
Parse(value, tokenStream);
|
||||
object[name] = Parse(tokenStream);
|
||||
|
||||
object[name] = value;
|
||||
|
||||
bContinue = (tokenStream.EOS() == false &&
|
||||
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
|
||||
if (bContinue)
|
||||
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
|
||||
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
|
||||
}
|
||||
|
||||
MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
void Reader::Parse(Array& array, Reader::TokenStream& tokenStream)
|
||||
Array Reader::ParseArray(Reader::TokenStream& tokenStream)
|
||||
{
|
||||
MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
|
||||
|
||||
bool bContinue = (tokenStream.EOS() == false &&
|
||||
tokenStream.Peek().nType != Token::TOKEN_ARRAY_END);
|
||||
while (bContinue)
|
||||
{
|
||||
// ...what's next? could be anything
|
||||
array.push_back(UnknownElement());
|
||||
UnknownElement& element = array.back();
|
||||
Parse(element, tokenStream);
|
||||
Array array;
|
||||
|
||||
bContinue = (tokenStream.EOS() == false &&
|
||||
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
|
||||
if (bContinue)
|
||||
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
|
||||
{
|
||||
array.push_back(Parse(tokenStream));
|
||||
|
||||
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
|
||||
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
|
||||
}
|
||||
|
||||
MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
void Reader::Parse(String& string, Reader::TokenStream& tokenStream)
|
||||
String Reader::ParseString(Reader::TokenStream& tokenStream)
|
||||
{
|
||||
string = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
|
||||
return MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
|
||||
}
|
||||
|
||||
|
||||
void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
|
||||
Number Reader::ParseNumber(Reader::TokenStream& tokenStream)
|
||||
{
|
||||
const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception
|
||||
const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
|
||||
|
@ -438,25 +386,21 @@ void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
|
|||
iStr >> dValue;
|
||||
|
||||
// did we consume all characters in the token?
|
||||
if (iStr.eof() == false)
|
||||
{
|
||||
if (!iStr.eof())
|
||||
throw ParseException("Unexpected character in NUMBER token: " + iStr.peek(), currentToken.locBegin, currentToken.locEnd);
|
||||
}
|
||||
|
||||
number = dValue;
|
||||
return dValue;
|
||||
}
|
||||
|
||||
|
||||
void Reader::Parse(Boolean& boolean, Reader::TokenStream& tokenStream)
|
||||
Boolean Reader::ParseBoolean(Reader::TokenStream& tokenStream)
|
||||
{
|
||||
const std::string& sValue = MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream);
|
||||
boolean = (sValue == "true");
|
||||
return MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream) == "true";
|
||||
}
|
||||
|
||||
|
||||
void Reader::Parse(Null&, Reader::TokenStream& tokenStream)
|
||||
Null Reader::ParseNull(Reader::TokenStream& tokenStream)
|
||||
{
|
||||
MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
|
||||
return Null();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ Hotkey::Hotkey(const std::string &file, const std::string &default_config)
|
|||
LOG_D("hotkey/init") << "Generating hotkeys.";
|
||||
|
||||
json::UnknownElement hotkey_root = agi::json_util::file(config_file, default_config);
|
||||
json::Object object = hotkey_root;
|
||||
json::Object const& object = hotkey_root;
|
||||
|
||||
for (json::Object::const_iterator index(object.begin()); index != object.end(); ++index)
|
||||
BuildHotkey(index->first, index->second);
|
||||
|
|
|
@ -93,6 +93,8 @@ LogSink::~LogSink() {
|
|||
array.push_back(entry);
|
||||
}
|
||||
|
||||
root["timeval"] = json::Object();
|
||||
|
||||
json::Array timeval_open;
|
||||
timeval_open.push_back(time_start.tv_sec);
|
||||
timeval_open.push_back(time_start.tv_usec);
|
||||
|
|
|
@ -79,24 +79,24 @@ public:
|
|||
UnknownElement& operator = (const UnknownElement& unknown);
|
||||
|
||||
// implicit cast to actual element type. throws on failure
|
||||
operator const Object& () const;
|
||||
operator const Array& () const;
|
||||
operator const Number& () const;
|
||||
operator const Boolean& () const;
|
||||
operator const String& () const;
|
||||
operator const Null& () const;
|
||||
|
||||
// implicit cast to actual element type. *converts* on failure, and always returns success
|
||||
operator Object& ();
|
||||
operator Array& ();
|
||||
operator Number& ();
|
||||
operator Boolean& ();
|
||||
operator String& ();
|
||||
operator Null& ();
|
||||
operator Object const&() const;
|
||||
operator Array const&() const;
|
||||
operator Number const&() const;
|
||||
operator Boolean const&() const;
|
||||
operator String const&() const;
|
||||
operator Null const&() const;
|
||||
operator Object&();
|
||||
operator Array&();
|
||||
operator Number&();
|
||||
operator Boolean&();
|
||||
operator String&();
|
||||
operator Null&();
|
||||
|
||||
// provides quick access to children when real element type is object
|
||||
UnknownElement& operator[] (const char *key) { return operator[](std::string(key)); }
|
||||
const UnknownElement& operator[] (const char *key) const { return operator[](std::string(key)); }
|
||||
template<int N>
|
||||
UnknownElement& operator[] (const char (&key)[N]) { return operator[](std::string(key)); }
|
||||
template<int N>
|
||||
const UnknownElement& operator[] (const char (&key)[N]) const { return operator[](std::string(key)); }
|
||||
UnknownElement& operator[] (const std::string& key);
|
||||
const UnknownElement& operator[] (const std::string& key) const;
|
||||
|
||||
|
@ -109,7 +109,8 @@ public:
|
|||
void Accept(Visitor& visitor);
|
||||
|
||||
// tests equality. first checks type, then value if possible
|
||||
bool operator == (const UnknownElement& element) const;
|
||||
bool operator ==(const UnknownElement& element) const;
|
||||
bool operator !=(const UnknownElement& element) const { return !(*this == element); }
|
||||
|
||||
private:
|
||||
class Imp;
|
||||
|
@ -118,10 +119,10 @@ private:
|
|||
class Imp_T;
|
||||
|
||||
template <typename ElementTypeT>
|
||||
const ElementTypeT& CastTo() const;
|
||||
ElementTypeT const& CastTo() const;
|
||||
|
||||
template <typename ElementTypeT>
|
||||
ElementTypeT& ConvertTo();
|
||||
ElementTypeT& CastTo();
|
||||
|
||||
Imp* m_pImp;
|
||||
};
|
||||
|
|
|
@ -110,13 +110,13 @@ private:
|
|||
void MatchExpectedString(const std::string& sExpected, InputStream& inputStream);
|
||||
|
||||
// parsing token sequence into element structure
|
||||
void Parse(UnknownElement& element, TokenStream& tokenStream);
|
||||
void Parse(Object& object, TokenStream& tokenStream);
|
||||
void Parse(Array& array, TokenStream& tokenStream);
|
||||
void Parse(String& string, TokenStream& tokenStream);
|
||||
void Parse(Number& number, TokenStream& tokenStream);
|
||||
void Parse(Boolean& boolean, TokenStream& tokenStream);
|
||||
void Parse(Null& null, TokenStream& tokenStream);
|
||||
UnknownElement Parse(TokenStream& tokenStream);
|
||||
Object ParseObject(TokenStream& tokenStream);
|
||||
Array ParseArray(TokenStream& tokenStream);
|
||||
String ParseString(TokenStream& tokenStream);
|
||||
Number ParseNumber(TokenStream& tokenStream);
|
||||
Boolean ParseBoolean(TokenStream& tokenStream);
|
||||
Null ParseNull(TokenStream& tokenStream);
|
||||
|
||||
const std::string& MatchExpectedToken(Token::Type nExpected, TokenStream& tokenStream);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue