2012-12-19 17:54:10 +03:00
/*
* JsonNode . h , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2012-07-16 19:18:02 +03:00
# pragma once
class JsonNode ;
typedef std : : map < std : : string , JsonNode > JsonMap ;
typedef std : : vector < JsonNode > JsonVector ;
2012-11-13 14:52:23 +03:00
DLL_LINKAGE std : : ostream & operator < < ( std : : ostream & out , const JsonNode & node ) ;
2012-07-19 21:52:44 +03:00
struct Bonus ;
2012-08-02 14:03:26 +03:00
class ResourceID ;
2012-07-16 19:18:02 +03:00
class DLL_LINKAGE JsonNode
{
public :
enum JsonType
{
DATA_NULL ,
DATA_BOOL ,
DATA_FLOAT ,
DATA_STRING ,
DATA_VECTOR ,
DATA_STRUCT
} ;
private :
union JsonData
{
bool Bool ;
double Float ;
std : : string * String ;
JsonVector * Vector ;
JsonMap * Struct ;
} ;
JsonType type ;
JsonData data ;
public :
2013-04-25 17:03:35 +03:00
/// free to use metadata field
std : : string meta ;
2012-07-16 19:18:02 +03:00
//Create empty node
JsonNode ( JsonType Type = DATA_NULL ) ;
//Create tree from Json-formatted input
explicit JsonNode ( const char * data , size_t datasize ) ;
//Create tree from JSON file
2012-08-02 14:03:26 +03:00
explicit JsonNode ( ResourceID & & fileURI ) ;
2012-07-16 19:18:02 +03:00
//Copy c-tor
JsonNode ( const JsonNode & copy ) ;
~ JsonNode ( ) ;
void swap ( JsonNode & b ) ;
JsonNode & operator = ( JsonNode node ) ;
bool operator = = ( const JsonNode & other ) const ;
bool operator ! = ( const JsonNode & other ) const ;
2013-04-25 17:03:35 +03:00
void setMeta ( std : : string metadata , bool recursive = true ) ;
2013-06-26 14:18:27 +03:00
/// Convert node to another type. Converting to nullptr will clear all data
2012-07-16 19:18:02 +03:00
void setType ( JsonType Type ) ;
JsonType getType ( ) const ;
bool isNull ( ) const ;
2013-04-02 20:06:43 +03:00
/// removes all data from node and sets type to null
void clear ( ) ;
2012-07-16 19:18:02 +03:00
2013-04-02 20:06:43 +03:00
/// non-const accessors, node will change type on type mismatch
2012-07-16 19:18:02 +03:00
bool & Bool ( ) ;
double & Float ( ) ;
std : : string & String ( ) ;
JsonVector & Vector ( ) ;
JsonMap & Struct ( ) ;
2013-04-02 20:06:43 +03:00
/// const accessors, will cause assertion failure on type mismatch
2012-07-16 19:18:02 +03:00
const bool & Bool ( ) const ;
const double & Float ( ) const ;
const std : : string & String ( ) const ;
const JsonVector & Vector ( ) const ;
const JsonMap & Struct ( ) const ;
2013-04-02 20:06:43 +03:00
/// returns resolved "json pointer" (string in format "/path/to/node")
const JsonNode & resolvePointer ( const std : : string & jsonPointer ) const ;
JsonNode & resolvePointer ( const std : : string & jsonPointer ) ;
2012-12-02 15:21:44 +03:00
/// convert json tree into specified type. Json tree must have same type as Type
/// Valid types: bool, string, any numeric, map and vector
/// example: convertTo< std::map< std::vector<int> > >();
template < typename Type >
Type convertTo ( ) const ;
2012-07-16 19:18:02 +03:00
//operator [], for structs only - get child node by name
JsonNode & operator [ ] ( std : : string child ) ;
const JsonNode & operator [ ] ( std : : string child ) const ;
2012-11-13 14:52:23 +03:00
template < typename Handler > void serialize ( Handler & h , const int version )
{
2013-04-25 17:03:35 +03:00
h & meta ;
2012-12-02 15:21:44 +03:00
// simple saving - save json in its string interpretation
2012-11-13 14:52:23 +03:00
if ( h . saving )
{
std : : ostringstream stream ;
stream < < * this ;
std : : string str = stream . str ( ) ;
h & str ;
}
else
{
std : : string str ;
h & str ;
JsonNode ( str . c_str ( ) , str . size ( ) ) . swap ( * this ) ;
}
}
2012-07-16 19:18:02 +03:00
} ;
2012-12-02 15:21:44 +03:00
namespace JsonUtils
2012-10-05 16:11:26 +03:00
{
2013-02-25 15:18:45 +03:00
/**
* @ brief parse short bonus format , excluding type
* @ note sets duration to Permament
*/
DLL_LINKAGE void parseTypedBonusShort ( const JsonVector & source , Bonus * dest ) ;
///
2012-12-02 15:21:44 +03:00
DLL_LINKAGE Bonus * parseBonus ( const JsonVector & ability_vec ) ;
DLL_LINKAGE Bonus * parseBonus ( const JsonNode & bonus ) ;
DLL_LINKAGE void unparseBonus ( JsonNode & node , const Bonus * bonus ) ;
2012-12-26 12:46:09 +03:00
DLL_LINKAGE void resolveIdentifier ( si32 & var , const JsonNode & node , std : : string name ) ;
2013-01-16 16:02:01 +03:00
DLL_LINKAGE void resolveIdentifier ( const JsonNode & node , si32 & var ) ;
2012-12-02 15:21:44 +03:00
2013-02-25 15:18:45 +03:00
/**
* @ brief recursivly merges source into dest , replacing identical fields
* struct : recursively calls this function
* arrays : each entry will be merged recursively
* values : value in source will replace value in dest
* null : if value in source is present but set to null it will delete entry in dest
* @ note this function will destroy data in source
*/
2012-12-02 15:21:44 +03:00
DLL_LINKAGE void merge ( JsonNode & dest , JsonNode & source ) ;
2013-02-25 15:18:45 +03:00
/**
* @ brief recursivly merges source into dest , replacing identical fields
* struct : recursively calls this function
* arrays : each entry will be merged recursively
* values : value in source will replace value in dest
* null : if value in source is present but set to null it will delete entry in dest
* @ note this function will preserve data stored in source by creating copy
*/
2012-12-02 15:21:44 +03:00
DLL_LINKAGE void mergeCopy ( JsonNode & dest , JsonNode source ) ;
2012-12-12 14:13:57 +03:00
/**
* @ brief generate one Json structure from multiple files
* @ param files - list of filenames with parts of json structure
*/
DLL_LINKAGE JsonNode assembleFromFiles ( std : : vector < std : : string > files ) ;
2013-04-26 00:50:55 +03:00
/// This version loads all files with same name (overriden by mods)
DLL_LINKAGE JsonNode assembleFromFiles ( std : : string filename ) ;
2013-04-02 20:06:43 +03:00
/**
* @ brief removes all nodes that are identical to default entry in schema
* @ param node - JsonNode to minimize
* @ param schemaName - name of schema to use
* @ note for minimizing data must be valid against given schema
*/
DLL_LINKAGE void minimize ( JsonNode & node , std : : string schemaName ) ;
/// opposed to minimize, adds all missing, required entries that have default value
DLL_LINKAGE void maximize ( JsonNode & node , std : : string schemaName ) ;
2012-12-02 15:21:44 +03:00
2013-04-02 20:06:43 +03:00
/**
* @ brief validate node against specified schema
* @ param node - JsonNode to check
* @ param schemaName - name of schema to use
* @ param dataName - some way to identify data ( printed in console in case of errors )
* @ returns true if data in node fully compilant with schema
*/
DLL_LINKAGE bool validate ( const JsonNode & node , std : : string schemaName , std : : string dataName ) ;
/// get schema by json URI: vcmi:<name of file in schemas directory>#<entry in file, optional>
/// example: schema "vcmi:settings" is used to check user settings
DLL_LINKAGE const JsonNode & getSchema ( std : : string URI ) ;
2012-10-05 16:11:26 +03:00
}
2012-12-02 15:21:44 +03:00
//////////////////////////////////////////////////////////////////////////////////////////////////////
// End of public section of the file. Anything below should be only used internally in JsonNode.cpp //
//////////////////////////////////////////////////////////////////////////////////////////////////////
namespace JsonDetail
2012-10-05 16:11:26 +03:00
{
2012-12-02 15:21:44 +03:00
// convertion helpers for JsonNode::convertTo (partial template function instantiation is illegal in c++)
2013-02-04 00:05:44 +03:00
template < typename T , int arithm >
struct JsonConvImpl ;
template < typename T >
2013-02-11 02:24:57 +03:00
struct JsonConvImpl < T , 1 >
2013-02-04 00:05:44 +03:00
{
static T convertImpl ( const JsonNode & node )
{
2013-02-11 02:24:57 +03:00
return T ( ( int ) node . Float ( ) ) ;
2013-02-04 00:05:44 +03:00
}
} ;
template < typename T >
2013-02-11 02:24:57 +03:00
struct JsonConvImpl < T , 0 >
2013-02-04 00:05:44 +03:00
{
static T convertImpl ( const JsonNode & node )
{
return node . Float ( ) ;
}
} ;
2012-12-02 15:21:44 +03:00
template < typename Type >
struct JsonConverter
2012-10-05 16:11:26 +03:00
{
2012-12-02 15:21:44 +03:00
static Type convert ( const JsonNode & node )
{
2013-02-04 00:05:44 +03:00
///this should be triggered only for numeric types and enums
2013-02-11 02:24:57 +03:00
static_assert ( boost : : mpl : : or_ < std : : is_arithmetic < Type > , std : : is_enum < Type > , boost : : is_class < Type > > : : value , " Unsupported type for JsonNode::convertTo()! " ) ;
return JsonConvImpl < Type , boost : : mpl : : or_ < std : : is_enum < Type > , boost : : is_class < Type > > : : value > : : convertImpl ( node ) ;
2013-02-04 00:05:44 +03:00
2012-12-02 15:21:44 +03:00
}
} ;
2012-10-05 16:11:26 +03:00
2012-12-02 15:21:44 +03:00
template < typename Type >
struct JsonConverter < std : : map < std : : string , Type > >
{
static std : : map < std : : string , Type > convert ( const JsonNode & node )
{
std : : map < std : : string , Type > ret ;
2013-06-29 16:05:48 +03:00
for ( const JsonMap : : value_type & entry : node . Struct ( ) )
2012-12-02 15:21:44 +03:00
{
ret . insert ( entry . first , entry . second . convertTo < Type > ( ) ) ;
}
return ret ;
}
} ;
2012-07-16 19:18:02 +03:00
2013-01-10 21:53:49 +03:00
template < typename Type >
struct JsonConverter < std : : set < Type > >
{
static std : : set < Type > convert ( const JsonNode & node )
{
std : : set < Type > ret ;
2013-06-29 16:05:48 +03:00
for ( const JsonVector : : value_type & entry : node . Vector ( ) )
2013-01-10 21:53:49 +03:00
{
ret . insert ( entry . convertTo < Type > ( ) ) ;
}
return ret ;
}
} ;
2012-12-02 15:21:44 +03:00
template < typename Type >
struct JsonConverter < std : : vector < Type > >
2012-07-16 19:18:02 +03:00
{
2012-12-02 15:21:44 +03:00
static std : : vector < Type > convert ( const JsonNode & node )
{
std : : vector < Type > ret ;
2013-06-29 16:05:48 +03:00
for ( const JsonVector : : value_type & entry : node . Vector ( ) )
2012-12-02 15:21:44 +03:00
{
ret . push_back ( entry . convertTo < Type > ( ) ) ;
}
return ret ;
}
} ;
2012-07-16 19:18:02 +03:00
2012-12-02 15:21:44 +03:00
template < >
struct JsonConverter < std : : string >
2012-07-16 19:18:02 +03:00
{
2012-12-02 15:21:44 +03:00
static std : : string convert ( const JsonNode & node )
{
return node . String ( ) ;
}
2012-07-16 19:18:02 +03:00
} ;
2012-12-02 15:21:44 +03:00
template < >
struct JsonConverter < bool >
2012-07-16 19:18:02 +03:00
{
2012-12-02 15:21:44 +03:00
static bool convert ( const JsonNode & node )
{
return node . Bool ( ) ;
}
} ;
2012-07-16 19:18:02 +03:00
2012-12-02 15:21:44 +03:00
class JsonWriter
{
//prefix for each line (tabulation)
std : : string prefix ;
std : : ostream & out ;
public :
template < typename Iterator >
void writeContainer ( Iterator begin , Iterator end ) ;
void writeEntry ( JsonMap : : const_iterator entry ) ;
void writeEntry ( JsonVector : : const_iterator entry ) ;
void writeString ( const std : : string & string ) ;
void writeNode ( const JsonNode & node ) ;
JsonWriter ( std : : ostream & output , const JsonNode & node ) ;
} ;
2012-07-16 19:18:02 +03:00
2012-12-02 15:21:44 +03:00
//Tiny string class that uses const char* as data for speed, members are private
//for ease of debugging and some compatibility with std::string
class constString
{
const char * data ;
const size_t datasize ;
2012-07-16 19:18:02 +03:00
2012-12-02 15:21:44 +03:00
public :
constString ( const char * inputString , size_t stringSize ) :
data ( inputString ) ,
datasize ( stringSize )
{
}
2012-07-16 19:18:02 +03:00
2012-12-02 15:21:44 +03:00
inline size_t size ( ) const
{
return datasize ;
} ;
2012-07-16 19:18:02 +03:00
2012-12-02 15:21:44 +03:00
inline const char & operator [ ] ( size_t position )
{
assert ( position < datasize ) ;
return data [ position ] ;
}
} ;
2012-07-16 19:18:02 +03:00
2012-12-02 15:21:44 +03:00
//Internal class for string -> JsonNode conversion
class JsonParser
{
std : : string errors ; // Contains description of all encountered errors
constString input ; // Input data
ui32 lineCount ; // Currently parsed line, starting from 1
size_t lineStart ; // Position of current line start
size_t pos ; // Current position of parser
//Helpers
bool extractEscaping ( std : : string & str ) ;
bool extractLiteral ( const std : : string & literal ) ;
bool extractString ( std : : string & string ) ;
bool extractWhitespace ( bool verbose = true ) ;
bool extractSeparator ( ) ;
bool extractElement ( JsonNode & node , char terminator ) ;
//Methods for extracting JSON data
bool extractArray ( JsonNode & node ) ;
bool extractFalse ( JsonNode & node ) ;
bool extractFloat ( JsonNode & node ) ;
bool extractNull ( JsonNode & node ) ;
bool extractString ( JsonNode & node ) ;
bool extractStruct ( JsonNode & node ) ;
bool extractTrue ( JsonNode & node ) ;
bool extractValue ( JsonNode & node ) ;
//Add error\warning message to list
bool error ( const std : : string & message , bool warning = false ) ;
public :
2013-03-09 16:16:35 +03:00
JsonParser ( const char * inputString , size_t stringSize ) ;
/// do actual parsing. filename is name of file that will printed to console if any errors were found
JsonNode parse ( std : : string fileName ) ;
2012-12-02 15:21:44 +03:00
} ;
2013-04-02 20:06:43 +03:00
//Internal class for Json validation. Mostly compilant with json-schema v4 draft
2012-12-02 15:21:44 +03:00
class JsonValidator
{
2013-04-02 20:06:43 +03:00
// path from root node to current one.
// JsonNode is used as variant - either string (name of node) or as float (index in list)
std : : vector < JsonNode > currentPath ;
// Stack of used schemas. Last schema is the one used currently.
// May contain multiple items in case if remote references were found
std : : vector < std : : string > usedSchemas ;
/// helpers for other validation methods
std : : string validateVectorItem ( const JsonVector items , const JsonNode & schema , const JsonNode & additional , size_t index ) ;
std : : string validateStructItem ( const JsonNode & node , const JsonNode & schema , const JsonNode & additional , std : : string nodeName ) ;
std : : string validateEnum ( const JsonNode & node , const JsonVector & enumeration ) ;
std : : string validateNodeType ( const JsonNode & node , const JsonNode & schema ) ;
std : : string validatesSchemaList ( const JsonNode & node , const JsonNode & schemas , std : : string errorMsg , std : : function < bool ( size_t ) > isValid ) ;
/// contains all type-independent checks
std : : string validateNode ( const JsonNode & node , const JsonNode & schema ) ;
/// type-specific checks
std : : string validateVector ( const JsonNode & node , const JsonNode & schema ) ;
std : : string validateStruct ( const JsonNode & node , const JsonNode & schema ) ;
std : : string validateString ( const JsonNode & node , const JsonNode & schema ) ;
std : : string validateNumber ( const JsonNode & node , const JsonNode & schema ) ;
/// validation of root node of both schema and input data
std : : string validateRoot ( const JsonNode & node , std : : string schemaName ) ;
/// add error message to list and return false
std : : string fail ( const std : : string & message ) ;
2012-12-02 15:21:44 +03:00
public :
2013-04-02 20:06:43 +03:00
/// returns true if parsed data is fully compilant with schema
bool validate ( const JsonNode & root , std : : string schemaName , std : : string name ) ;
2012-12-02 15:21:44 +03:00
} ;
} // namespace JsonDetail
template < typename Type >
Type JsonNode : : convertTo ( ) const
{
return JsonDetail : : JsonConverter < Type > : : convert ( * this ) ;
2013-03-02 19:55:51 +03:00
}