2013-01-16 16:02:01 +03:00
/*
* JsonNode . cpp , 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
*
*/
# include "StdInc.h"
# include "JsonNode.h"
2013-04-02 20:06:43 +03:00
# include "ScopeGuard.h"
2013-01-16 16:02:01 +03:00
# include "HeroBonus.h"
2013-07-28 17:49:50 +03:00
# include "filesystem/Filesystem.h"
2013-01-16 16:02:01 +03:00
# include "VCMI_Lib.h" //for identifier resolution
# include "CModHandler.h"
2013-10-26 00:45:14 +03:00
# include "CGeneralTextHandler.h"
2013-10-26 22:33:34 +03:00
# include "JsonDetail.h"
2013-01-16 16:02:01 +03:00
using namespace JsonDetail ;
class LibClasses ;
class CModHandler ;
static const JsonNode nullNode ;
2015-02-10 13:43:26 +02:00
std : : ostream & operator < < ( std : : ostream & out , const JsonNode & node )
{
JsonWriter writer ( out , node ) ;
return out < < " \n " ;
}
2013-01-16 16:02:01 +03:00
JsonNode : : JsonNode ( JsonType Type ) :
type ( DATA_NULL )
{
setType ( Type ) ;
}
JsonNode : : JsonNode ( const char * data , size_t datasize ) :
type ( DATA_NULL )
{
2013-03-09 16:16:35 +03:00
JsonParser parser ( data , datasize ) ;
* this = parser . parse ( " <unknown> " ) ;
2013-01-16 16:02:01 +03:00
}
JsonNode : : JsonNode ( ResourceID & & fileURI ) :
type ( DATA_NULL )
{
2013-07-28 17:49:50 +03:00
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
2013-01-16 16:02:01 +03:00
2013-03-09 16:16:35 +03:00
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
2013-01-16 16:02:01 +03:00
}
2016-01-27 10:38:35 +02:00
JsonNode : : JsonNode ( const ResourceID & fileURI ) :
type ( DATA_NULL )
{
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
}
2013-11-08 23:36:26 +03:00
JsonNode : : JsonNode ( ResourceID & & fileURI , bool & isValidSyntax ) :
type ( DATA_NULL )
{
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
isValidSyntax = parser . isValid ( ) ;
}
2013-01-16 16:02:01 +03:00
JsonNode : : JsonNode ( const JsonNode & copy ) :
2013-11-06 16:42:58 +03:00
type ( DATA_NULL ) ,
meta ( copy . meta )
2013-01-16 16:02:01 +03:00
{
setType ( copy . getType ( ) ) ;
switch ( type )
{
break ; case DATA_NULL :
break ; case DATA_BOOL : Bool ( ) = copy . Bool ( ) ;
break ; case DATA_FLOAT : Float ( ) = copy . Float ( ) ;
break ; case DATA_STRING : String ( ) = copy . String ( ) ;
break ; case DATA_VECTOR : Vector ( ) = copy . Vector ( ) ;
break ; case DATA_STRUCT : Struct ( ) = copy . Struct ( ) ;
}
}
JsonNode : : ~ JsonNode ( )
{
setType ( DATA_NULL ) ;
}
void JsonNode : : swap ( JsonNode & b )
{
using std : : swap ;
2013-04-25 17:03:35 +03:00
swap ( meta , b . meta ) ;
2013-01-16 16:02:01 +03:00
swap ( data , b . data ) ;
swap ( type , b . type ) ;
}
JsonNode & JsonNode : : operator = ( JsonNode node )
{
swap ( node ) ;
return * this ;
}
bool JsonNode : : operator = = ( const JsonNode & other ) const
{
if ( getType ( ) = = other . getType ( ) )
{
switch ( type )
{
2013-11-07 15:48:41 +03:00
case DATA_NULL : return true ;
case DATA_BOOL : return Bool ( ) = = other . Bool ( ) ;
case DATA_FLOAT : return Float ( ) = = other . Float ( ) ;
case DATA_STRING : return String ( ) = = other . String ( ) ;
case DATA_VECTOR : return Vector ( ) = = other . Vector ( ) ;
case DATA_STRUCT : return Struct ( ) = = other . Struct ( ) ;
2013-01-16 16:02:01 +03:00
}
}
return false ;
}
bool JsonNode : : operator ! = ( const JsonNode & other ) const
{
return ! ( * this = = other ) ;
}
JsonNode : : JsonType JsonNode : : getType ( ) const
{
return type ;
}
2013-04-25 17:03:35 +03:00
void JsonNode : : setMeta ( std : : string metadata , bool recursive )
{
meta = metadata ;
if ( recursive )
{
switch ( type )
{
break ; case DATA_VECTOR :
{
2013-06-29 16:05:48 +03:00
for ( auto & node : Vector ( ) )
2013-04-25 17:03:35 +03:00
{
node . setMeta ( metadata ) ;
}
}
break ; case DATA_STRUCT :
{
2013-06-29 16:05:48 +03:00
for ( auto & node : Struct ( ) )
2013-04-25 17:03:35 +03:00
{
node . second . setMeta ( metadata ) ;
}
}
}
}
}
2013-01-16 16:02:01 +03:00
void JsonNode : : setType ( JsonType Type )
{
if ( type = = Type )
return ;
2013-06-26 14:18:27 +03:00
//Reset node to nullptr
2013-01-16 16:02:01 +03:00
if ( Type ! = DATA_NULL )
setType ( DATA_NULL ) ;
switch ( type )
{
break ; case DATA_STRING : delete data . String ;
break ; case DATA_VECTOR : delete data . Vector ;
break ; case DATA_STRUCT : delete data . Struct ;
break ; default :
break ;
}
//Set new node type
type = Type ;
switch ( type )
{
break ; case DATA_NULL :
break ; case DATA_BOOL : data . Bool = false ;
break ; case DATA_FLOAT : data . Float = 0 ;
2013-03-14 23:44:00 +03:00
break ; case DATA_STRING : data . String = new std : : string ( ) ;
break ; case DATA_VECTOR : data . Vector = new JsonVector ( ) ;
break ; case DATA_STRUCT : data . Struct = new JsonMap ( ) ;
2013-01-16 16:02:01 +03:00
}
}
bool JsonNode : : isNull ( ) const
{
return type = = DATA_NULL ;
}
2013-04-02 20:06:43 +03:00
void JsonNode : : clear ( )
{
setType ( DATA_NULL ) ;
}
2013-01-16 16:02:01 +03:00
bool & JsonNode : : Bool ( )
{
setType ( DATA_BOOL ) ;
return data . Bool ;
}
double & JsonNode : : Float ( )
{
setType ( DATA_FLOAT ) ;
return data . Float ;
}
std : : string & JsonNode : : String ( )
{
setType ( DATA_STRING ) ;
return * data . String ;
}
JsonVector & JsonNode : : Vector ( )
{
setType ( DATA_VECTOR ) ;
return * data . Vector ;
}
JsonMap & JsonNode : : Struct ( )
{
setType ( DATA_STRUCT ) ;
return * data . Struct ;
}
const bool boolDefault = false ;
const bool & JsonNode : : Bool ( ) const
{
if ( type = = DATA_NULL )
return boolDefault ;
assert ( type = = DATA_BOOL ) ;
return data . Bool ;
}
const double floatDefault = 0 ;
const double & JsonNode : : Float ( ) const
{
if ( type = = DATA_NULL )
return floatDefault ;
assert ( type = = DATA_FLOAT ) ;
return data . Float ;
}
const std : : string stringDefault = std : : string ( ) ;
const std : : string & JsonNode : : String ( ) const
{
if ( type = = DATA_NULL )
return stringDefault ;
assert ( type = = DATA_STRING ) ;
return * data . String ;
}
const JsonVector vectorDefault = JsonVector ( ) ;
const JsonVector & JsonNode : : Vector ( ) const
{
if ( type = = DATA_NULL )
return vectorDefault ;
assert ( type = = DATA_VECTOR ) ;
return * data . Vector ;
}
const JsonMap mapDefault = JsonMap ( ) ;
const JsonMap & JsonNode : : Struct ( ) const
{
if ( type = = DATA_NULL )
return mapDefault ;
assert ( type = = DATA_STRUCT ) ;
return * data . Struct ;
}
JsonNode & JsonNode : : operator [ ] ( std : : string child )
{
return Struct ( ) [ child ] ;
}
const JsonNode & JsonNode : : operator [ ] ( std : : string child ) const
{
2013-06-29 16:05:48 +03:00
auto it = Struct ( ) . find ( child ) ;
2013-01-16 16:02:01 +03:00
if ( it ! = Struct ( ) . end ( ) )
return it - > second ;
return nullNode ;
}
2013-04-02 20:06:43 +03:00
// to avoid duplicating const and non-const code
template < typename Node >
Node & resolvePointer ( Node & in , const std : : string & pointer )
{
if ( pointer . empty ( ) )
return in ;
assert ( pointer [ 0 ] = = ' / ' ) ;
size_t splitPos = pointer . find ( ' / ' , 1 ) ;
std : : string entry = pointer . substr ( 1 , splitPos - 1 ) ;
std : : string remainer = splitPos = = std : : string : : npos ? " " : pointer . substr ( splitPos ) ;
if ( in . getType ( ) = = JsonNode : : DATA_VECTOR )
{
if ( entry . find_first_not_of ( " 0123456789 " ) ! = std : : string : : npos ) // non-numbers in string
throw std : : runtime_error ( " Invalid Json pointer " ) ;
if ( entry . size ( ) > 1 & & entry [ 0 ] = = ' 0 ' ) // leading zeros are not allowed
throw std : : runtime_error ( " Invalid Json pointer " ) ;
size_t index = boost : : lexical_cast < size_t > ( entry ) ;
if ( in . Vector ( ) . size ( ) > index )
return in . Vector ( ) [ index ] . resolvePointer ( remainer ) ;
}
return in [ entry ] . resolvePointer ( remainer ) ;
}
const JsonNode & JsonNode : : resolvePointer ( const std : : string & jsonPointer ) const
{
return : : resolvePointer ( * this , jsonPointer ) ;
}
JsonNode & JsonNode : : resolvePointer ( const std : : string & jsonPointer )
{
return : : resolvePointer ( * this , jsonPointer ) ;
}
2013-02-25 15:18:45 +03:00
///JsonUtils
2016-09-19 23:36:35 +02:00
void JsonUtils : : parseTypedBonusShort ( const JsonVector & source , std : : shared_ptr < Bonus > dest )
2013-02-25 15:18:45 +03:00
{
dest - > val = source [ 1 ] . Float ( ) ;
resolveIdentifier ( source [ 2 ] , dest - > subtype ) ;
dest - > additionalInfo = source [ 3 ] . Float ( ) ;
dest - > duration = Bonus : : PERMANENT ; //TODO: handle flags (as integer)
2016-01-27 10:38:35 +02:00
dest - > turnsRemain = 0 ;
2013-02-25 15:18:45 +03:00
}
2016-09-19 23:36:35 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonVector & ability_vec ) //TODO: merge with AddAbility, create universal parser for all bonus properties
2013-01-16 16:02:01 +03:00
{
2016-09-19 23:36:35 +02:00
auto b = std : : make_shared < Bonus > ( ) ;
2013-01-16 16:02:01 +03:00
std : : string type = ability_vec [ 0 ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
if ( it = = bonusNameMap . end ( ) )
{
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error: invalid ability type " < < type ;
2013-01-16 16:02:01 +03:00
return b ;
}
b - > type = it - > second ;
2016-01-27 10:38:35 +02:00
2013-02-25 15:18:45 +03:00
parseTypedBonusShort ( ability_vec , b ) ;
2013-01-16 16:02:01 +03:00
return b ;
}
template < typename T >
const T & parseByMap ( const std : : map < std : : string , T > & map , const JsonNode * val , std : : string err )
{
2013-04-21 15:49:26 +03:00
static T defaultValue = T ( ) ;
2013-01-16 16:02:01 +03:00
if ( ! val - > isNull ( ) )
{
std : : string type = val - > String ( ) ;
auto it = map . find ( type ) ;
if ( it = = map . end ( ) )
{
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error: invalid " < < err < < type ;
2013-01-16 16:02:01 +03:00
return defaultValue ;
}
else
{
return it - > second ;
}
}
else
return defaultValue ;
2013-04-21 15:49:26 +03:00
}
2013-01-16 16:02:01 +03:00
2016-10-22 10:00:55 +02:00
void JsonUtils : : resolveIdentifier ( si32 & var , const JsonNode & node , std : : string name )
2013-01-16 16:02:01 +03:00
{
2013-04-25 17:03:35 +03:00
const JsonNode & value = node [ name ] ;
if ( ! value . isNull ( ) )
2013-01-16 16:02:01 +03:00
{
2013-04-25 17:03:35 +03:00
switch ( value . getType ( ) )
2013-01-16 16:02:01 +03:00
{
case JsonNode : : DATA_FLOAT :
2013-04-25 17:03:35 +03:00
var = value . Float ( ) ;
2013-01-16 16:02:01 +03:00
break ;
case JsonNode : : DATA_STRING :
2013-04-25 17:03:35 +03:00
VLC - > modh - > identifiers . requestIdentifier ( value , [ & ] ( si32 identifier )
2013-01-16 16:02:01 +03:00
{
var = identifier ;
} ) ;
break ;
default :
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error! Wrong identifier used for value of " < < name ;
2013-01-16 16:02:01 +03:00
}
}
}
2016-10-22 10:00:55 +02:00
void JsonUtils : : resolveIdentifier ( const JsonNode & node , si32 & var )
2013-01-16 16:02:01 +03:00
{
switch ( node . getType ( ) )
{
case JsonNode : : DATA_FLOAT :
var = node . Float ( ) ;
break ;
case JsonNode : : DATA_STRING :
2016-10-22 10:00:55 +02:00
VLC - > modh - > identifiers . requestIdentifier ( node , [ & ] ( si32 identifier )
2013-01-16 16:02:01 +03:00
{
var = identifier ;
} ) ;
break ;
default :
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error! Wrong identifier used for identifier! " ;
2013-01-16 16:02:01 +03:00
}
}
2016-09-19 23:36:35 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonNode & ability )
2013-01-16 16:02:01 +03:00
{
2016-09-19 23:36:35 +02:00
auto b = std : : make_shared < Bonus > ( ) ;
2016-10-22 10:00:55 +02:00
if ( ! parseBonus ( ability , b . get ( ) ) )
{
return nullptr ;
}
return b ;
}
bool JsonUtils : : parseBonus ( const JsonNode & ability , Bonus * b )
{
2013-01-16 16:02:01 +03:00
const JsonNode * value ;
std : : string type = ability [ " type " ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
if ( it = = bonusNameMap . end ( ) )
{
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error: invalid ability type " < < type ;
2016-10-22 10:00:55 +02:00
return false ;
2013-01-16 16:02:01 +03:00
}
b - > type = it - > second ;
2016-10-22 10:00:55 +02:00
resolveIdentifier ( b - > subtype , ability , " subtype " ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > val = ability [ " val " ] . Float ( ) ;
2013-01-16 16:02:01 +03:00
value = & ability [ " valueType " ] ;
if ( ! value - > isNull ( ) )
2013-02-02 01:04:25 +03:00
b - > valType = static_cast < Bonus : : ValueType > ( parseByMap ( bonusValueMap , value , " value type " ) ) ;
2013-01-16 16:02:01 +03:00
2016-10-22 10:00:55 +02:00
resolveIdentifier ( b - > additionalInfo , ability , " addInfo " ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > turnsRemain = ability [ " turns " ] . Float ( ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > sid = ability [ " sourceID " ] . Float ( ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > description = ability [ " description " ] . String ( ) ;
2013-01-16 16:02:01 +03:00
value = & ability [ " effectRange " ] ;
if ( ! value - > isNull ( ) )
2013-02-02 01:04:25 +03:00
b - > effectRange = static_cast < Bonus : : LimitEffect > ( parseByMap ( bonusLimitEffect , value , " effect range " ) ) ;
2013-01-16 19:41:01 +03:00
2013-01-16 16:02:01 +03:00
value = & ability [ " duration " ] ;
if ( ! value - > isNull ( ) )
2013-01-16 19:41:01 +03:00
{
switch ( value - > getType ( ) )
{
case JsonNode : : DATA_STRING :
b - > duration = parseByMap ( bonusDurationMap , value , " duration type " ) ;
break ;
case JsonNode : : DATA_VECTOR :
{
ui16 dur = 0 ;
2013-06-29 16:05:48 +03:00
for ( const JsonNode & d : value - > Vector ( ) )
2013-01-16 19:41:01 +03:00
{
dur | = parseByMap ( bonusDurationMap , & d , " duration type " ) ;
}
b - > duration = dur ;
}
break ;
default :
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error! Wrong bonus duration format. " ;
2013-01-16 19:41:01 +03:00
}
}
2013-01-16 16:02:01 +03:00
value = & ability [ " source " ] ;
if ( ! value - > isNull ( ) )
2013-02-02 01:04:25 +03:00
b - > source = static_cast < Bonus : : BonusSource > ( parseByMap ( bonusSourceMap , value , " source type " ) ) ;
2013-01-16 16:02:01 +03:00
value = & ability [ " limiters " ] ;
if ( ! value - > isNull ( ) )
{
2013-06-29 16:05:48 +03:00
for ( const JsonNode & limiter : value - > Vector ( ) )
2013-01-16 16:02:01 +03:00
{
2013-01-16 19:41:01 +03:00
switch ( limiter . getType ( ) )
2013-01-16 16:02:01 +03:00
{
case JsonNode : : DATA_STRING : //pre-defined limiters
b - > limiter = parseByMap ( bonusLimiterMap , & limiter , " limiter type " ) ;
break ;
2013-01-19 20:38:37 +03:00
case JsonNode : : DATA_STRUCT : //customizable limiters
2013-01-16 16:02:01 +03:00
{
2015-12-29 04:43:33 +02:00
std : : shared_ptr < ILimiter > l ;
2013-01-16 16:02:01 +03:00
if ( limiter [ " type " ] . String ( ) = = " CREATURE_TYPE_LIMITER " )
{
2015-12-29 04:43:33 +02:00
std : : shared_ptr < CCreatureTypeLimiter > l2 = std : : make_shared < CCreatureTypeLimiter > ( ) ; //TODO: How the hell resolve pointer to creature?
2013-01-18 14:49:15 +03:00
const JsonVector vec = limiter [ " parameters " ] . Vector ( ) ;
2013-04-25 17:03:35 +03:00
VLC - > modh - > identifiers . requestIdentifier ( " creature " , vec [ 0 ] , [ = ] ( si32 creature )
2013-01-18 14:49:15 +03:00
{
2016-10-22 10:00:55 +02:00
l2 - > setCreature ( CreatureID ( creature ) ) ;
2013-01-18 14:49:15 +03:00
} ) ;
if ( vec . size ( ) > 1 )
{
l2 - > includeUpgrades = vec [ 1 ] . Bool ( ) ;
}
else
l2 - > includeUpgrades = false ;
2013-01-19 21:52:13 +03:00
l = l2 ;
2013-01-16 16:02:01 +03:00
}
if ( limiter [ " type " ] . String ( ) = = " HAS_ANOTHER_BONUS_LIMITER " )
{
2015-12-29 04:43:33 +02:00
std : : shared_ptr < HasAnotherBonusLimiter > l2 = std : : make_shared < HasAnotherBonusLimiter > ( ) ;
2013-01-16 16:02:01 +03:00
const JsonVector vec = limiter [ " parameters " ] . Vector ( ) ;
std : : string anotherBonusType = vec [ 0 ] . String ( ) ;
2016-10-22 10:00:55 +02:00
auto it = bonusNameMap . find ( anotherBonusType ) ;
2013-01-16 16:02:01 +03:00
if ( it = = bonusNameMap . end ( ) )
{
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error: invalid ability type " < < anotherBonusType ;
2013-01-16 16:02:01 +03:00
continue ;
}
l2 - > type = it - > second ;
if ( vec . size ( ) > 1 )
{
2016-10-22 10:00:55 +02:00
resolveIdentifier ( vec [ 1 ] , l2 - > subtype ) ;
2013-01-16 16:02:01 +03:00
l2 - > isSubtypeRelevant = true ;
}
l = l2 ;
}
b - > addLimiter ( l ) ;
}
break ;
}
}
}
value = & ability [ " propagator " ] ;
if ( ! value - > isNull ( ) )
b - > propagator = parseByMap ( bonusPropagatorMap , value , " propagator type " ) ;
2016-10-22 10:00:55 +02:00
return true ;
2013-01-16 16:02:01 +03:00
}
//returns first Key with value equal to given one
template < class Key , class Val >
2013-11-07 15:48:41 +03:00
Key reverseMapFirst ( const Val & val , const std : : map < Key , Val > & map )
2013-01-16 16:02:01 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto it : map )
2013-01-16 16:02:01 +03:00
{
if ( it . second = = val )
{
return it . first ;
}
}
assert ( 0 ) ;
return " " ;
}
2016-09-19 23:36:35 +02:00
void JsonUtils : : unparseBonus ( JsonNode & node , const std : : shared_ptr < Bonus > & bonus )
2013-01-16 16:02:01 +03:00
{
2013-02-16 17:03:47 +03:00
node [ " type " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : BonusType > ( bonus - > type , bonusNameMap ) ;
2013-01-16 16:02:01 +03:00
node [ " subtype " ] . Float ( ) = bonus - > subtype ;
node [ " val " ] . Float ( ) = bonus - > val ;
2013-02-16 17:03:47 +03:00
node [ " valueType " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : ValueType > ( bonus - > valType , bonusValueMap ) ;
2013-01-16 16:02:01 +03:00
node [ " additionalInfo " ] . Float ( ) = bonus - > additionalInfo ;
node [ " turns " ] . Float ( ) = bonus - > turnsRemain ;
node [ " sourceID " ] . Float ( ) = bonus - > source ;
node [ " description " ] . String ( ) = bonus - > description ;
2013-02-16 17:03:47 +03:00
node [ " effectRange " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : LimitEffect > ( bonus - > effectRange , bonusLimitEffect ) ;
node [ " duration " ] . String ( ) = reverseMapFirst < std : : string , ui16 > ( bonus - > duration , bonusDurationMap ) ;
node [ " source " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : BonusSource > ( bonus - > source , bonusSourceMap ) ;
2013-01-16 16:02:01 +03:00
if ( bonus - > limiter )
{
node [ " limiter " ] . String ( ) = reverseMapFirst < std : : string , TLimiterPtr > ( bonus - > limiter , bonusLimiterMap ) ;
}
if ( bonus - > propagator )
{
node [ " propagator " ] . String ( ) = reverseMapFirst < std : : string , TPropagatorPtr > ( bonus - > propagator , bonusPropagatorMap ) ;
}
}
2013-04-02 20:06:43 +03:00
void minimizeNode ( JsonNode & node , const JsonNode & schema )
{
2013-04-03 16:39:46 +03:00
if ( schema [ " type " ] . String ( ) = = " object " )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
std : : set < std : : string > foundEntries ;
2013-06-29 16:05:48 +03:00
for ( auto & entry : schema [ " required " ] . Vector ( ) )
2013-04-03 16:39:46 +03:00
{
std : : string name = entry . String ( ) ;
foundEntries . insert ( name ) ;
2013-04-02 20:06:43 +03:00
minimizeNode ( node [ name ] , schema [ " properties " ] [ name ] ) ;
2013-04-03 16:39:46 +03:00
if ( vstd : : contains ( node . Struct ( ) , name ) & &
node [ name ] = = schema [ " properties " ] [ name ] [ " default " ] )
{
node . Struct ( ) . erase ( name ) ;
}
}
// erase all unhandled entries
for ( auto it = node . Struct ( ) . begin ( ) ; it ! = node . Struct ( ) . end ( ) ; )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
if ( ! vstd : : contains ( foundEntries , it - > first ) )
it = node . Struct ( ) . erase ( it ) ;
else
it + + ;
2013-04-02 20:06:43 +03:00
}
}
}
void JsonUtils : : minimize ( JsonNode & node , std : : string schemaName )
{
minimizeNode ( node , getSchema ( schemaName ) ) ;
}
2013-04-03 16:39:46 +03:00
// FIXME: except for several lines function is identical to minimizeNode. Some way to reduce duplication?
2013-04-02 20:06:43 +03:00
void maximizeNode ( JsonNode & node , const JsonNode & schema )
2013-01-16 16:02:01 +03:00
{
2013-04-03 16:39:46 +03:00
// "required" entry can only be found in object/struct
if ( schema [ " type " ] . String ( ) = = " object " )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
std : : set < std : : string > foundEntries ;
2013-04-02 20:06:43 +03:00
2013-04-03 16:39:46 +03:00
// check all required entries that have default version
2013-06-29 16:05:48 +03:00
for ( auto & entry : schema [ " required " ] . Vector ( ) )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
std : : string name = entry . String ( ) ;
foundEntries . insert ( name ) ;
if ( node [ name ] . isNull ( ) & &
! schema [ " properties " ] [ name ] [ " default " ] . isNull ( ) )
{
node [ name ] = schema [ " properties " ] [ name ] [ " default " ] ;
}
2013-04-02 20:06:43 +03:00
maximizeNode ( node [ name ] , schema [ " properties " ] [ name ] ) ;
2013-04-03 16:39:46 +03:00
}
// erase all unhandled entries
for ( auto it = node . Struct ( ) . begin ( ) ; it ! = node . Struct ( ) . end ( ) ; )
{
if ( ! vstd : : contains ( foundEntries , it - > first ) )
it = node . Struct ( ) . erase ( it ) ;
else
it + + ;
}
2013-04-02 20:06:43 +03:00
}
2013-01-16 16:02:01 +03:00
}
2013-04-02 20:06:43 +03:00
void JsonUtils : : maximize ( JsonNode & node , std : : string schemaName )
2013-01-16 16:02:01 +03:00
{
2013-04-02 20:06:43 +03:00
maximizeNode ( node , getSchema ( schemaName ) ) ;
}
bool JsonUtils : : validate ( const JsonNode & node , std : : string schemaName , std : : string dataName )
{
2013-10-26 22:33:34 +03:00
std : : string log = Validation : : check ( schemaName , node ) ;
if ( ! log . empty ( ) )
{
2013-11-08 23:36:26 +03:00
logGlobal - > warnStream ( ) < < " Data in " < < dataName < < " is invalid! " ;
logGlobal - > warnStream ( ) < < log ;
2013-10-26 22:33:34 +03:00
}
return log . empty ( ) ;
2013-04-02 20:06:43 +03:00
}
const JsonNode & getSchemaByName ( std : : string name )
{
// cached schemas to avoid loading json data multiple times
static std : : map < std : : string , JsonNode > loadedSchemas ;
if ( vstd : : contains ( loadedSchemas , name ) )
return loadedSchemas [ name ] ;
std : : string filename = " config/schemas/ " + name + " .json " ;
if ( CResourceHandler : : get ( ) - > existsResource ( ResourceID ( filename ) ) )
{
loadedSchemas [ name ] = JsonNode ( ResourceID ( filename ) ) ;
return loadedSchemas [ name ] ;
}
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error: missing schema with name " < < name < < " ! " ;
2013-04-02 20:06:43 +03:00
assert ( 0 ) ;
return nullNode ;
}
const JsonNode & JsonUtils : : getSchema ( std : : string URI )
{
std : : vector < std : : string > segments ;
2013-04-12 23:49:02 +03:00
size_t posColon = URI . find ( ' : ' ) ;
size_t posHash = URI . find ( ' # ' ) ;
assert ( posColon ! = std : : string : : npos ) ;
2013-04-02 20:06:43 +03:00
2013-04-12 23:49:02 +03:00
std : : string protocolName = URI . substr ( 0 , posColon ) ;
std : : string filename = URI . substr ( posColon + 1 , posHash - posColon - 1 ) ;
2013-04-02 20:06:43 +03:00
2013-04-12 23:49:02 +03:00
if ( protocolName ! = " vcmi " )
2013-04-02 20:06:43 +03:00
{
2016-03-12 03:41:27 +02:00
logGlobal - > errorStream ( ) < < " Error: unsupported URI protocol for schema: " < < segments [ 0 ] ;
2013-04-02 20:06:43 +03:00
return nullNode ;
}
2013-04-12 23:49:02 +03:00
// check if json pointer if present (section after hash in string)
if ( posHash = = std : : string : : npos | | posHash = = URI . size ( ) - 1 )
return getSchemaByName ( filename ) ;
else
return getSchemaByName ( filename ) . resolvePointer ( URI . substr ( posHash + 1 ) ) ;
2013-01-16 16:02:01 +03:00
}
void JsonUtils : : merge ( JsonNode & dest , JsonNode & source )
{
if ( dest . getType ( ) = = JsonNode : : DATA_NULL )
{
std : : swap ( dest , source ) ;
return ;
}
switch ( source . getType ( ) )
{
2013-04-25 17:03:35 +03:00
case JsonNode : : DATA_NULL :
{
dest . clear ( ) ;
break ;
}
case JsonNode : : DATA_BOOL :
case JsonNode : : DATA_FLOAT :
case JsonNode : : DATA_STRING :
case JsonNode : : DATA_VECTOR :
2013-01-16 16:02:01 +03:00
{
2013-12-04 13:36:39 +03:00
std : : swap ( dest , source ) ;
2013-04-25 17:03:35 +03:00
break ;
2013-01-16 16:02:01 +03:00
}
2013-04-25 17:03:35 +03:00
case JsonNode : : DATA_STRUCT :
2013-01-16 16:02:01 +03:00
{
//recursively merge all entries from struct
2013-06-29 16:05:48 +03:00
for ( auto & node : source . Struct ( ) )
2013-01-16 16:02:01 +03:00
merge ( dest [ node . first ] , node . second ) ;
}
}
}
void JsonUtils : : mergeCopy ( JsonNode & dest , JsonNode source )
{
// uses copy created in stack to safely merge two nodes
merge ( dest , source ) ;
}
2014-05-19 01:28:44 +03:00
void JsonUtils : : inherit ( JsonNode & descendant , const JsonNode & base )
{
2014-06-15 19:43:01 +03:00
JsonNode inheritedNode ( base ) ;
2014-05-19 01:28:44 +03:00
merge ( inheritedNode , descendant ) ;
descendant . swap ( inheritedNode ) ;
}
2013-01-16 16:02:01 +03:00
JsonNode JsonUtils : : assembleFromFiles ( std : : vector < std : : string > files )
{
2013-11-08 23:36:26 +03:00
bool isValid ;
return assembleFromFiles ( files , isValid ) ;
}
JsonNode JsonUtils : : assembleFromFiles ( std : : vector < std : : string > files , bool & isValid )
{
isValid = true ;
2013-01-16 16:02:01 +03:00
JsonNode result ;
2013-06-29 16:05:48 +03:00
for ( std : : string file : files )
2013-01-16 16:02:01 +03:00
{
2013-11-08 23:36:26 +03:00
bool isValidFile ;
JsonNode section ( ResourceID ( file , EResType : : TEXT ) , isValidFile ) ;
2013-01-16 16:02:01 +03:00
merge ( result , section ) ;
2013-11-08 23:36:26 +03:00
isValid | = isValidFile ;
2013-01-16 16:02:01 +03:00
}
return result ;
}
2013-04-26 00:50:55 +03:00
JsonNode JsonUtils : : assembleFromFiles ( std : : string filename )
{
JsonNode result ;
2013-07-28 17:49:50 +03:00
ResourceID resID ( filename , EResType : : TEXT ) ;
2013-04-26 00:50:55 +03:00
2013-07-28 17:49:50 +03:00
for ( auto & loader : CResourceHandler : : get ( ) - > getResourcesWithName ( resID ) )
2013-04-26 00:50:55 +03:00
{
// FIXME: some way to make this code more readable
2013-07-28 17:49:50 +03:00
auto stream = loader - > load ( resID ) ;
2013-04-26 00:50:55 +03:00
std : : unique_ptr < ui8 [ ] > textData ( new ui8 [ stream - > getSize ( ) ] ) ;
stream - > read ( textData . get ( ) , stream - > getSize ( ) ) ;
JsonNode section ( ( char * ) textData . get ( ) , stream - > getSize ( ) ) ;
merge ( result , section ) ;
}
return result ;
}