Files
azerothcore-wotlk-pbot/deps/g3dlite/source/Any.cpp
Yehonal f06f32849f Directory Structure [step 1]: moving files
working on #672

NOTE: This commit can't be compiled!!
2017-10-12 20:00:52 +02:00

1351 lines
30 KiB
C++

/**
@file Any.cpp
@author Morgan McGuire
@author Shawn Yarbrough
@created 2006-06-11
@edited 2010-07-24
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#include "G3D/Any.h"
#include "G3D/TextOutput.h"
#include "G3D/TextInput.h"
#include "G3D/stringutils.h"
#include "G3D/fileutils.h"
#include "G3D/FileSystem.h"
#include <deque>
#include <iostream>
namespace G3D {
std::string Any::resolveStringAsFilename() const {
verifyType(STRING);
std::string f = FileSystem::resolve(string(), sourceDirectory());
if (FileSystem::exists(f)) {
return f;
} else {
const std::string& s = System::findDataFile(string(), false);
if (s.empty()) {
return string();
} else {
return s;
}
}
}
bool Any::nameBeginsWith(const std::string& s) const {
return nameBeginsWith(s.c_str());
}
bool Any::nameEquals(const std::string& s) const {
// If std::string has a fast hash compare, use it first
return (name() == s) || nameEquals(s.c_str());
}
inline static char toLower(char c) {
return ((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 'a') : c;
}
bool Any::nameBeginsWith(const char* s) const {
verifyType(Any::ARRAY, Any::TABLE);
const char* n = name().c_str();
// Walk through character-by-character
while ((*s != '\0') && (*n != '\0')) {
if (toLower(*s) != toLower(*n)) {
// Mismatch
return false;
}
++s; ++n;
}
// Make sure s ran out no later than n
return (*s == '\0');
}
bool Any::nameEquals(const char* s) const {
verifyType(Any::ARRAY, Any::TABLE);
#ifdef G3D_WIN32
return stricmp(name().c_str(), s) == 0;
#else
return strcasecmp(name().c_str(), s) == 0;
#endif
}
void Any::beforeRead() const {
if (isPlaceholder()) {
// Tried to read from a placeholder--throw an exception as if
// the original operator[] had failed.
KeyNotFound e;
alwaysAssertM(m_data, "Corrupt placeholder");
e.filename = m_data->source.filename;
e.line = m_data->source.line;
e.character = m_data->source.character;
e.key = m_placeholderName;
e.message =
"This exception may have been thrown later than "
"the actual operator[] invocation.";
throw e;
}
}
Any::Data* Any::Data::create(const Data* d) {
Data* p = create(d->type);
p->comment = d->comment;
p->name = d->name;
switch (d->type) {
case NONE:
case BOOLEAN:
case NUMBER:
// No clone needed
break;
case STRING:
*(p->value.s) = *(d->value.s);
break;
case ARRAY:
*(p->value.a) = *(d->value.a);
break;
case TABLE:
*(p->value.t) = *(d->value.t);
// Note that placeholders may be copied; that is ok--they are still
// just placeholders.
break;
}
return p;
}
Any::Data* Any::Data::create(Any::Type t) {
size_t s = sizeof(Data);
switch (t) {
case NONE:
case BOOLEAN:
case NUMBER:
// No extra space needed
break;
case STRING:
s += sizeof(std::string);
break;
case ARRAY:
s += sizeof(AnyArray);
break;
case TABLE:
s += sizeof(AnyTable);
break;
}
// Allocate the data object
Data* p = new (MemoryManager::create()->alloc(s)) Data(t);
// Create the (empyt) value object at the end of the Data object
switch (t) {
case NONE:
case BOOLEAN:
case NUMBER:
// No value
break;
case STRING:
p->value.s = new (p + 1) std::string();
break;
case ARRAY:
p->value.a = new (p + 1) AnyArray();
break;
case TABLE:
p->value.t = new (p + 1) AnyTable();
break;
}
return p;
}
void Any::Data::destroy(Data* d) {
if (d != NULL) {
d->~Data();
MemoryManager::create()->free(d);
}
}
Any::Data::~Data() {
debugAssertM(referenceCount.value() <= 0, "Deleted while still referenced.");
// Destruct but do not deallocate children
switch (type) {
case STRING:
debugAssert(value.s != NULL);
value.s->~basic_string();
break;
case ARRAY:
debugAssert(value.a != NULL);
value.a->~Array();
break;
case TABLE:
debugAssert(value.t != NULL);
value.t->~Table();
break;
default:
// All other types should have a NULL value pointer (i.e., they were used just for name and comment fields)
debugAssertM(value.s == NULL, "Corrupt Any::Data::Value");
}
value.s = NULL;
}
//////////////////////////////////////////////////////////////
bool Any::containsKey(const std::string& x) const {
beforeRead();
verifyType(TABLE);
Any* a = m_data->value.t->getPointer(x);
// Don't return true for placeholder objects
return (a != NULL) && (! a->isPlaceholder());
}
void Any::dropReference() {
if (m_data && m_data->referenceCount.decrement() <= 0) {
// This was the last reference to the shared data
Data::destroy(m_data);
}
m_data = NULL;
}
void Any::ensureMutable() {
if (m_data && (m_data->referenceCount.value() >= 1)) {
// Copy the data. We must do this before dropping the reference
// to avoid a race condition
Data* d = Data::create(m_data);
dropReference();
m_data = d;
}
}
Any::Any() : m_type(NONE), m_data(NULL) {
}
Any::Any(TextInput& t) : m_type(NONE), m_data(NULL) {
deserialize(t);
}
Any::Any(const Any& x) : m_type(NONE), m_data(NULL) {
x.beforeRead();
*this = x;
}
Any::Any(double x) : m_type(NUMBER), m_simpleValue(x), m_data(NULL) {
}
#ifdef G3D_32BIT
Any::Any(int64 x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
}
#endif // G3D_32BIT
Any::Any(long x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
}
Any::Any(int x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
}
Any::Any(short x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
}
Any::Any(bool x) : m_type(BOOLEAN), m_simpleValue(x), m_data(NULL) {
}
Any::Any(const std::string& s) : m_type(STRING), m_data(Data::create(STRING)) {
*(m_data->value.s) = s;
}
Any::Any(const char* s) : m_type(STRING), m_data(NULL) {
if (s == NULL) {
m_type = NONE;
} else {
ensureData();
*(m_data->value.s) = s;
}
}
Any::Any(Type t, const std::string& name) : m_type(t), m_data(NULL) {
alwaysAssertM(t == ARRAY || t == TABLE, "Can only create ARRAY or TABLE from Type enum.");
ensureData();
if (name != "") {
m_data->name = name;
}
}
Any::~Any() {
dropReference();
}
void Any::beforeWrite() {
if (isPlaceholder()) {
// This is no longer a placeholder
m_placeholderName = "";
}
}
Any& Any::operator=(const Any& x) {
x.beforeRead();
if (this == &x) {
return *this;
}
beforeWrite();
dropReference();
m_type = x.m_type;
m_simpleValue = x.m_simpleValue;
if (x.m_data != NULL) {
x.m_data->referenceCount.increment();
m_data = x.m_data;
}
return *this;
}
Any& Any::operator=(double x) {
*this = Any(x);
return *this;
}
Any& Any::operator=(int x) {
return (*this = Any(x));
}
Any& Any::operator=(bool x) {
*this = Any(x);
return *this;
}
Any& Any::operator=(const std::string& x) {
*this = Any(x);
return *this;
}
Any& Any::operator=(const char* x) {
*this = Any(x);
return *this;
}
Any& Any::operator=(Type t) {
switch (t) {
case NONE:
*this = Any();
break;
case TABLE:
case ARRAY:
*this = Any(t);
break;
default:
alwaysAssertM(false, "Can only assign NONE, TABLE, or ARRAY Type enum.");
}
return *this;
}
Any::Type Any::type() const {
beforeRead();
return m_type;
}
const std::string& Any::comment() const {
beforeRead();
static const std::string blank;
if (m_data != NULL) {
return m_data->comment;
} else {
return blank;
}
}
void Any::setComment(const std::string& c) {
beforeRead();
ensureData();
m_data->comment = c;
}
bool Any::isNone() const {
beforeRead();
return (m_type == NONE);
}
double Any::number() const {
beforeRead();
verifyType(NUMBER);
return m_simpleValue.n;
}
const std::string& Any::string() const {
beforeRead();
verifyType(STRING);
return *(m_data->value.s);
}
bool Any::boolean() const {
beforeRead();
verifyType(BOOLEAN);
return m_simpleValue.b;
}
const std::string& Any::name() const {
beforeRead();
static const std::string blank;
if (m_data != NULL) {
return m_data->name;
} else {
return blank;
}
}
void Any::setName(const std::string& n) {
beforeRead();
ensureData();
m_data->name = n;
}
int Any::size() const {
beforeRead();
verifyType(ARRAY, TABLE);
switch (m_type) {
case TABLE:
return m_data->value.t->size();
case ARRAY:
return m_data->value.a->size();
default:;
return 0;
} // switch (m_type)
}
int Any::length() const {
beforeRead();
return size();
}
void Any::resize(int n) {
beforeRead();
alwaysAssertM(n >= 0, "Cannot resize less than 0.");
verifyType(ARRAY);
m_data->value.a->resize(n);
}
void Any::clear() {
beforeRead();
verifyType(ARRAY, TABLE);
switch (m_type) {
case ARRAY:
m_data->value.a->clear();
break;
case TABLE:
m_data->value.t->clear();
break;
default:;
}
}
const Any& Any::operator[](int i) const {
beforeRead();
verifyType(ARRAY);
debugAssert(m_data != NULL);
Array<Any>& array = *(m_data->value.a);
return array[i];
}
Any& Any::next() {
beforeRead();
verifyType(ARRAY);
int n = size();
resize(n + 1);
return (*this)[n];
}
Any& Any::operator[](int i) {
beforeRead();
verifyType(ARRAY);
debugAssert(m_data != NULL);
Array<Any>& array = *(m_data->value.a);
return array[i];
}
const Array<Any>& Any::array() const {
beforeRead();
verifyType(ARRAY);
debugAssert(m_data != NULL);
return *(m_data->value.a);
}
void Any::append(const Any& x0) {
beforeRead();
verifyType(ARRAY);
debugAssert(m_data != NULL);
m_data->value.a->append(x0);
}
void Any::append(const Any& x0, const Any& x1) {
beforeRead();
append(x0);
append(x1);
}
void Any::append(const Any& x0, const Any& x1, const Any& x2) {
beforeRead();
append(x0);
append(x1);
append(x2);
}
void Any::append(const Any& x0, const Any& x1, const Any& x2, const Any& x3) {
beforeRead();
append(x0);
append(x1);
append(x2);
append(x3);
}
const Table<std::string, Any>& Any::table() const {
beforeRead();
verifyType(TABLE);
debugAssert(m_data != NULL);
return *(m_data->value.t);
}
const Any& Any::operator[](const std::string& x) const {
beforeRead();
verifyType(TABLE);
debugAssert(m_data != NULL);
const Table<std::string, Any>& table = *(m_data->value.t);
Any* value = table.getPointer(x);
if (value == NULL) {
KeyNotFound e;
if (m_data) {
e.filename = m_data->source.filename;
e.line = m_data->source.line;
e.character = m_data->source.character;
}
e.key = x;
throw e;
}
return *value;
}
Any& Any::operator[](const std::string& key) {
beforeRead();
verifyType(TABLE);
bool created = false;
Any& value = m_data->value.t->getCreate(key, created);
if (created) {
// The entry was created by this method; do not allow it to be
// read before it is written.
value.m_placeholderName = key;
// Write source data for the value
value.ensureData();
value.m_data->source = source();
}
return value;
}
void Any::set(const std::string& k, const Any& v) {
beforeRead();
v.beforeRead();
verifyType(TABLE);
debugAssert(m_data != NULL);
Table<std::string, Any>& table = *(m_data->value.t);
table.set(k, v);
}
const Any& Any::get(const std::string& x, const Any& defaultVal) const {
beforeRead();
defaultVal.beforeRead();
try {
return operator[](x);
} catch(KeyNotFound) {
return defaultVal;
}
}
bool Any::operator==(const Any& x) const {
beforeRead();
x.beforeRead();
if (m_type != x.m_type) {
return false;
}
switch (m_type) {
case NONE:
return true;
case BOOLEAN:
return (m_simpleValue.b == x.m_simpleValue.b);
case NUMBER:
return (m_simpleValue.n == x.m_simpleValue.n);
case STRING:
debugAssert(m_data != NULL);
return (*(m_data->value.s) == *(x.m_data->value.s));
case TABLE: {
if (size() != x.size()) {
return false;
}
debugAssert(m_data != NULL);
if (m_data->name != x.m_data->name) {
return false;
}
Table<std::string, Any>& cmptable = *( m_data->value.t);
Table<std::string, Any>& xcmptable = *(x.m_data->value.t);
for (Table<std::string,Any>::Iterator it1 = cmptable.begin(), it2 = xcmptable.begin();
it1 != cmptable.end() && it2 != xcmptable.end();
++it1, ++it2) {
if (*it1 != *it2) {
return false;
}
}
return true;
}
case ARRAY: {
if (size() != x.size()) {
return false;
}
debugAssert(m_data != NULL);
if (m_data->name != x.m_data->name) {
return false;
}
Array<Any>& cmparray = *( m_data->value.a);
Array<Any>& xcmparray = *(x.m_data->value.a);
for (int ii = 0; ii < size(); ++ii) {
if (cmparray[ii] != xcmparray[ii]) {
return false;
}
}
return true;
}
default:
alwaysAssertM(false, "Unknown type.");
return false;
} // switch (m_type)
}
bool Any::operator!=(const Any& x) const {
beforeRead();
x.beforeRead();
return !operator==(x);
}
static void getDeserializeSettings(TextInput::Settings& settings) {
settings.cppBlockComments = true;
settings.cppLineComments = true;
settings.otherLineComments = false;
settings.generateCommentTokens = true;
settings.singleQuotedStrings = false;
settings.msvcFloatSpecials = false;
settings.caseSensitive = false;
}
std::string Any::unparse() const {
beforeRead();
TextOutput::Settings settings;
TextOutput to(settings);
serialize(to);
return to.commitString();
}
void Any::parse(const std::string& src) {
beforeRead();
TextInput::Settings settings;
getDeserializeSettings(settings);
TextInput ti(TextInput::FROM_STRING, src, settings);
deserialize(ti);
}
void Any::load(const std::string& filename) {
beforeRead();
TextInput::Settings settings;
getDeserializeSettings(settings);
TextInput ti(FileSystem::resolve(filename), settings);
deserialize(ti);
}
void Any::save(const std::string& filename) const {
beforeRead();
TextOutput::Settings settings;
settings.wordWrap = TextOutput::Settings::WRAP_NONE;
TextOutput to(filename,settings);
serialize(to);
to.commit();
}
static bool needsQuotes(const std::string& s) {
if (! isLetter(s[0]) && (s[0] != '_')) {
return true;
}
for (int i = 0; i < (int)s.length(); ++i) {
char c = s[i];
// peek character
char p = (i == (int)s.length() - 1) ? '_' : s[i + 1];
// Identify separators
if ((c == '-' && p == '>') ||
(c == ':' && p == ':')) {
// Skip over this symbol
++i;
continue;
}
if (! isDigit(c) && ! isLetter(c) & (c != '.')) {
// This is an illegal character for an identifier, so we need quotes
return true;
}
}
return false;
}
// TODO: if the output will fit on one line, compress tables and arrays into a single line
void Any::serialize(TextOutput& to) const {
beforeRead();
if (m_data && ! m_data->comment.empty()) {
to.printf("\n/* %s */\n", m_data->comment.c_str());
}
switch (m_type) {
case NONE:
to.writeSymbol("NONE");
break;
case BOOLEAN:
to.writeBoolean(m_simpleValue.b);
break;
case NUMBER:
to.writeNumber(m_simpleValue.n);
break;
case STRING:
debugAssert(m_data != NULL);
to.writeString(*(m_data->value.s));
break;
case TABLE: {
debugAssert(m_data != NULL);
if (! m_data->name.empty()) {
if (needsQuotes(m_data->name)) {
to.writeString(m_data->name);
} else {
to.writeSymbol(m_data->name);
}
}
to.writeSymbol("{");
to.writeNewline();
to.pushIndent();
AnyTable& table = *(m_data->value.t);
Array<std::string> keys;
table.getKeys(keys);
keys.sort();
for (int i = 0; i < keys.size(); ++i) {
to.writeSymbol(keys[i]);
to.writeSymbol("=");
table[keys[i]].serialize(to);
if (i < keys.size() - 1) {
to.writeSymbol(",");
}
to.writeNewline();
// Skip a line between table entries
to.writeNewline();
}
to.popIndent();
to.writeSymbol("}");
break;
}
case ARRAY: {
debugAssert(m_data != NULL);
if (! m_data->name.empty()) {
// For arrays, leave no trailing space between the name and the paren
to.writeSymbol(format("%s(", m_data->name.c_str()));
} else {
to.writeSymbol("(");
}
to.writeNewline();
to.pushIndent();
Array<Any>& array = *(m_data->value.a);
for (int ii = 0; ii < size(); ++ii) {
array[ii].serialize(to);
if (ii < size() - 1) {
to.writeSymbol(",");
to.writeNewline();
}
// Put the close paren on an array right behind the last element
}
to.popIndent();
to.writeSymbol(")");
break;
}
}
}
void Any::deserializeComment(TextInput& ti, Token& token, std::string& comment) {
// Parse comments
while (token.type() == Token::COMMENT) {
comment += trimWhitespace(token.string()) + "\n";
// Allow comments to contain newlines.
do {
token = ti.read();
comment += "\n";
} while (token.type() == Token::NEWLINE);
}
comment = trimWhitespace(comment);
}
/** True if \a c is an open paren of some form */
static bool isOpen(const char c) {
return c == '(' || c == '[' || c == '{';
}
/** True if \a c is an open paren of some form */
static bool isClose(const char c) {
return c == ')' || c == ']' || c == '}';
}
void Any::deserializeName(TextInput& ti, Token& token, std::string& name) {
debugAssert(token.type() == Token::SYMBOL);
std::string s = token.string();
while (! isOpen(s[0])) {
name += s;
// Skip newlines and comments
token = ti.readSignificant();
if (token.type() != Token::SYMBOL) {
throw ParseError(ti.filename(), token.line(), token.character(),
"Expected symbol while parsing Any");
}
s = token.string();
}
}
void Any::deserialize(TextInput& ti) {
beforeRead();
Token token = ti.read();
deserialize(ti, token);
// Restore the last token
ti.push(token);
}
void Any::deserialize(TextInput& ti, Token& token) {
// Deallocate old data
dropReference();
m_type = NONE;
m_simpleValue.b = false;
// Skip leading newlines
while (token.type() == Token::NEWLINE) {
token = ti.read();
}
std::string comment;
if (token.type() == Token::COMMENT) {
deserializeComment(ti, token, comment);
}
if (token.type() == Token::END) {
// There should never be a comment without an Any following it; even
// if the file ends with some commented out stuff,
// that should not happen after a comma, so we'd never read that
// far in a proper file.
throw ParseError(ti.filename(), token.line(), token.character(),
"File ended without a properly formed Any");
}
// Do we need to read one more token after the end?
bool needRead = true;
switch (token.type()) {
case Token::STRING:
m_type = STRING;
ensureData();
*(m_data->value.s) = token.string();
m_data->source.set(ti, token);
break;
case Token::NUMBER:
m_type = NUMBER;
m_simpleValue.n = token.number();
ensureData();
m_data->source.set(ti, token);
break;
case Token::BOOLEAN:
m_type = BOOLEAN;
m_simpleValue.b = token.boolean();
ensureData();
m_data->source.set(ti, token);
break;
case Token::SYMBOL:
// Pragma, Named Array, Named Table, Array, Table, or NONE
if (token.string() == "#") {
// Pragma
// Currently, "include" is the only pragma allowed
token = ti.read();
if (! ((token.type() == Token::SYMBOL) &&
(token.string() == "include"))) {
throw ParseError(ti.filename(), token.line(), token.character(),
"Expected 'include' pragma after '#'");
}
ti.readSymbol("(");
const std::string& includeName = ti.readString();
// Find the include file
const std::string& myPath = filenamePath(ti.filename());
std::string t = pathConcat(myPath, includeName);
if (! FileSystem::exists(t)) {
// Try and find it, starting with cwd
t = System::findDataFile(includeName);
}
// Read the included file
load(t);
// Update the source information
ensureData();
m_data->source.filename +=
format(" [included from %s:%d(%d)]", ti.filename().c_str(), token.line(), token.character());
ti.readSymbol(")");
} else if (toUpper(token.string()) == "NONE") {
// Nothing left to do; we initialized to NONE originally
ensureData();
m_data->source.set(ti, token);
} else {
// Array or Table
// Parse the name
// s must have at least one element or this would not have
// been parsed as a symbol
std::string name;
deserializeName(ti, token, name);
if (token.type() != Token::SYMBOL) {
throw ParseError(ti.filename(), token.line(), token.character(),
"Malformed Any TABLE or ARRAY; must start with [, (, or {");
}
if (isOpen(token.string()[0])) {
// Array or table
deserializeBody(ti, token);
} else {
throw ParseError(ti.filename(), token.line(), token.character(),
"Malformed Any TABLE or ARRAY; must start with [, (, or {");
}
if (! name.empty()) {
ensureData();
m_data->name = name;
}
needRead = false;
} // if NONE
break;
default:
throw ParseError(ti.filename(), token.line(), token.character(),
"Unexpected token");
} // switch
if (! comment.empty()) {
ensureData();
m_data->comment = comment;
}
if (needRead) {
// Array and table already consumed their last token
token = ti.read();
}
}
void Any::ensureData() {
if (m_data == NULL) {
m_data = Data::create(m_type);
}
}
static bool isSeparator(char c) {
return c == ',' || c == ';';
}
void Any::readUntilCommaOrClose(TextInput& ti, Token& token) {
bool atClose = (token.type() == Token::SYMBOL) && isClose(token.string()[0]);
bool atComma = isSeparator(token.string()[0]);
while (! (atClose || atComma)) {
switch (token.type()) {
case Token::NEWLINE:
case Token::COMMENT:
// Consume
token = ti.read();
break;
default:
throw ParseError(ti.filename(), token.line(), token.character(),
"Expected a comma or close paren");
}
// Update checks
atComma = isSeparator(token.string()[0]);
atClose = (token.type() == Token::SYMBOL) && isClose(token.string()[0]);
}
}
void Any::deserializeBody(TextInput& ti, Token& token) {
char closeSymbol = '}';
m_type = TABLE;
const char c = token.string()[0];
if (c != '{') {
m_type = ARRAY;
// Chose the appropriate close symbol
closeSymbol = (c == '(') ? ')' : ']';
}
// Allocate the underlying data structure
ensureData();
m_data->source.set(ti, token);
// Consume the open token
token = ti.read();
while (! ((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol))) {
// Read any leading comment. This must be done here (and not in the recursive deserialize
// call) in case the body contains only a comment.
std::string comment;
deserializeComment(ti, token, comment);
if ((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol)) {
// We're done; this catches the case where the array is empty
break;
}
// Pointer the value being read
Any a = NULL;
std::string key;
if (m_type == TABLE) {
// Read the key
if (token.type() != Token::SYMBOL && token.type() != Token::STRING) {
throw ParseError(ti.filename(), token.line(), token.character(), "Expected a name");
}
key = token.string();
// Consume everything up to the = sign, returning the "=" sign.
token = ti.readSignificant();
if ((token.type() != Token::SYMBOL) || (token.string() != "=")) {
throw ParseError(ti.filename(), token.line(), token.character(), "Expected =");
} else {
// Read the next token, which is the value (don't consume comments--we want the value pointed to by a to get those).
token = ti.read();
}
}
a.deserialize(ti, token);
if (! comment.empty()) {
// Prepend the comment we read earlier
a.ensureData();
a.m_data->comment = trimWhitespace(comment + "\n" + a.m_data->comment);
}
if (m_type == TABLE) {
set(key, a);
} else {
append(a);
}
// Read until the comma or close paren, discarding trailing comments and newlines
readUntilCommaOrClose(ti, token);
// Consume the comma
if (isSeparator(token.string()[0])) {
token = ti.read();
}
}
// Consume the close paren (to match other deserialize methods)
token = ti.read();
}
Any::operator int() const {
beforeRead();
return iRound(number());
}
Any::operator float() const {
beforeRead();
return float(number());
}
Any::operator double() const {
beforeRead();
return number();
}
Any::operator bool() const {
beforeRead();
return boolean();
}
Any::operator std::string() const {
beforeRead();
return string();
}
const Any::Source& Any::source() const {
static Source s;
if (m_data) {
return m_data->source;
} else {
return s;
}
}
std::string Any::sourceDirectory() const {
if (m_data) {
return FilePath::parent(m_data->source.filename);
} else {
return "";
}
}
void Any::verify(bool value, const std::string& message) const {
beforeRead();
if (! value) {
ParseError p;
if (m_data) {
p.filename = m_data->source.filename;
p.line = m_data->source.line;
p.character = m_data->source.character;
}
if (name().empty()) {
p.message = "Parse error";
} else {
p.message = "Parse error while reading the contents of " + name();
}
if (! message.empty()) {
p.message = p.message + ": " + message;
}
throw p;
}
}
void Any::verifyName(const std::string& n) const {
beforeRead();
verify(beginsWith(toUpper(name()), toUpper(n)), "Name must begin with " + n);
}
void Any::verifyName(const std::string& n, const std::string& m) const {
beforeRead();
const std::string& x = toUpper(name());
verify(beginsWith(x, toUpper(n)) ||
beginsWith(x, toUpper(m)), "Name must begin with " + n + " or " + m);
}
void Any::verifyType(Type t) const {
beforeRead();
if (type() != t) {
verify(false, "Must have type " + toString(t));
}
}
void Any::verifyType(Type t0, Type t1) const {
beforeRead();
if (type() != t0 && type() != t1) {
verify(false, "Must have type " + toString(t0) + " or " + toString(t1));
}
}
void Any::verifySize(int low, int high) const {
beforeRead();
verifyType(ARRAY, TABLE);
if (size() < low || size() > high) {
verify(false, format("Size must be between %d and %d", low, high));
}
}
void Any::verifySize(int s) const {
beforeRead();
verifyType(ARRAY, TABLE);
if (size() != s) {
verify(false, format("Size must be %d", s));
}
}
std::string Any::toString(Type t) {
switch(t) {
case NONE: return "NONE";
case BOOLEAN: return "BOOLEAN";
case NUMBER: return "NUMBER";
case STRING: return "STRING";
case ARRAY: return "ARRAY";
case TABLE: return "TABLE";
default:
alwaysAssertM(false, "Illegal Any::Type");
return "";
}
}
} // namespace G3D