1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00
- Implemented Json validator and schema for buildings.json
- several fixes for Json writing
- fixed several gcc warnings
This commit is contained in:
Ivan Savenko 2011-10-08 16:58:25 +00:00
parent 7b1ede449d
commit 89bd7e273d
7 changed files with 1330 additions and 1062 deletions

View File

@ -1184,8 +1184,8 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim)
CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen)
: queue(NULL), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1), spellSelMode(NO_LOCATION),
currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), stackCanCastSpell(false), spellDestSelectMode(false), spellToCast(NULL),
activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1),
currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), stackCanCastSpell(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(NULL),
siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0), givenCommand(NULL),
myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL)
@ -2043,7 +2043,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
const CSpell * spell = CGI->spellh->spells[creatureSpellToCast];
if (curInt->cb->battleCanCastThisSpell(spell, THex(myNumber)) == SpellCasting::OK)
{
if (spell->positiveness > -1 && ourStack || spell->positiveness < 1 && !ourStack)
if ((spell->positiveness > -1 && ourStack) || (spell->positiveness < 1 && !ourStack))
{
CCS->curh->changeGraphic(3, 0);
stackCastsSpell = true;
@ -2869,7 +2869,7 @@ void CBattleInterface::hexLclicked(int whichOne)
const CSpell * spell = CGI->spellh->spells[creatureSpellToCast];
if (curInt->cb->battleCanCastThisSpell(spell, THex(whichOne)) == SpellCasting::OK)
{
if (spell->positiveness > -1 && ourStack || spell->positiveness < 1 && !ourStack)
if ((spell->positiveness > -1 && ourStack) || (spell->positiveness < 1 && !ourStack))
{
giveCommand(BattleAction::MONSTER_SPELL, whichOne, actSt->ID, creatureSpellToCast);
}

View File

@ -2052,122 +2052,124 @@ void CSplitWindow::clickLeft(tribool down, bool previousState)
}
}
void CCreInfoWindow::show(SDL_Surface * to)
void CCreInfoWindow::show(SDL_Surface *to)
{
blitAt(*bitmap,pos.x,pos.y,to);
anim->show(to);
if(count.size())
printTo(count.c_str(),pos.x+114,pos.y+174,FONT_TIMES,zwykly,to);
if(upgrade)
upgrade->showAll(to);
if(dismiss)
dismiss->showAll(to);
if(ok)
ok->showAll(to);
CIntObject::show(to);
creatureCount->showAll(to);
}
CCreInfoWindow::CCreInfoWindow(const CStackInstance &st, int Type, boost::function<void()> Upg, boost::function<void()> Dsm, UpgradeInfo *ui)
: type(Type), dsm(Dsm), dismiss(0), upgrade(0), ok(0)
CCreInfoWindow::CCreInfoWindow(const CStackInstance &stack, bool LClicked, boost::function<void()> upgradeFunc, boost::function<void()> dismissFunc, UpgradeInfo *upgradeInfo)
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
init(st.type, &st, dynamic_cast<const CGHeroInstance*>(st.armyObj), st.count);
init(stack.type, &stack, dynamic_cast<const CGHeroInstance*>(stack.armyObj), stack.count, LClicked);
//print abilities text - if r-click popup
if(type)
//additional buttons if opened with left click
if(LClicked)
{
if(Upg && ui)
boost::function<void()> closeFunc = boost::bind(&CCreInfoWindow::close,this);
if(upgradeFunc && upgradeInfo)
{
TResources upgradeCost = ui->cost[0] * st.count;
TResources upgradeCost = upgradeInfo->cost[0] * stack.count;
for(TResources::nziterator i(upgradeCost); i.valid(); i++)
{
BLOCK_CAPTURING;
upgResCost.push_back(new SComponent(SComponent::resource, i->resType, i->resVal));
}
if(LOCPLINT->cb->getResourceAmount().canAfford(upgradeCost))
{
CFunctionList<void()> fs;
fs += Upg;
fs += boost::bind(&CCreInfoWindow::close,this);
CFunctionList<void()> cfl;
cfl = boost::bind(&CPlayerInterface::showYesNoDialog, LOCPLINT, CGI->generaltexth->allTexts[207], boost::ref(upgResCost), fs, 0, true);
upgrade = new AdventureMapButton("",CGI->generaltexth->zelp[446].second,cfl,76,237,"IVIEWCR.DEF",SDLK_u);
}
else
{
upgrade = new AdventureMapButton("",CGI->generaltexth->zelp[446].second,boost::function<void()>(),76,237,"IVIEWCR.DEF");
upgrade->callback.funcs.clear();
upgrade->setOffset(2);
}
CFunctionList<void()> onUpgrade;
onUpgrade += upgradeFunc;
onUpgrade += closeFunc;
boost::function<void()> dialog = boost::bind(&CPlayerInterface::showYesNoDialog,
LOCPLINT,
CGI->generaltexth->allTexts[207],
boost::ref(upgResCost),
onUpgrade, 0, false);
upgrade = new AdventureMapButton("", CGI->generaltexth->zelp[446].second, dialog, 76, 237, "IVIEWCR", SDLK_u);
upgrade->block(!LOCPLINT->cb->getResourceAmount().canAfford(upgradeCost));
}
if(Dsm)
if(dismissFunc)
{
CFunctionList<void()> fs[2];
//on dismiss confirmed
fs[0] += Dsm; //dismiss
fs[0] += boost::bind(&CCreInfoWindow::close,this);//close this window
CFunctionList<void()> cfl;
cfl = boost::bind(&CPlayerInterface::showYesNoDialog,LOCPLINT,CGI->generaltexth->allTexts[12],std::vector<SComponent*>(),fs[0],fs[1],true);
dismiss = new AdventureMapButton("",CGI->generaltexth->zelp[445].second,cfl,21,237,"IVIEWCR2.DEF",SDLK_d);
CFunctionList<void()> onDismiss;
onDismiss += dismissFunc;
onDismiss += closeFunc;
boost::function<void()> dialog = boost::bind(&CPlayerInterface::showYesNoDialog,
LOCPLINT,
CGI->generaltexth->allTexts[12],
std::vector<SComponent*>(),
onDismiss, 0, true);
dismiss = new AdventureMapButton("", CGI->generaltexth->zelp[445].second, dialog, 21, 237, "IVIEWCR2",SDLK_d);
}
ok = new AdventureMapButton("",CGI->generaltexth->zelp[445].second,boost::bind(&CCreInfoWindow::close,this),216,237,"IOKAY.DEF",SDLK_RETURN);
ok = new AdventureMapButton("", CGI->generaltexth->zelp[445].second,
boost::bind(&CCreInfoWindow::close,this), 216, 237, "IOKAY.DEF", SDLK_RETURN);
}
}
CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount)
: type(Type), dismiss(0), upgrade(0), ok(0)
CCreInfoWindow::CCreInfoWindow(int creatureID, bool LClicked, int creatureCount)
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
const CCreature *cre = CGI->creh->creatures[Cid];
init(cre, NULL, NULL, creatureCount);
const CCreature *creature = CGI->creh->creatures[creatureID];
init(creature, NULL, NULL, creatureCount, LClicked);
}
CCreInfoWindow::CCreInfoWindow(const CStack &st, int Type /*= 0*/)
: type(Type), dismiss(0), upgrade(0), ok(0)
CCreInfoWindow::CCreInfoWindow(const CStack &stack, bool LClicked)
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
init(st.getCreature(), &st, st.getMyHero(), st.count);
init(stack.getCreature(), &stack, stack.getMyHero(), stack.count, LClicked);
}
void CCreInfoWindow::printLine(int nr, const std::string &text, int baseVal, int val/*=-1*/, bool range/*=false*/)
CCreInfoWindow::~CCreInfoWindow()
{
printAt(text, 155, 48 + nr*19, FONT_SMALL, zwykly, *bitmap);
BOOST_FOREACH(SComponent* object, upgResCost)
delete object;
}
void CCreInfoWindow::printLine(int position, const std::string &text, int baseVal, int val/*=-1*/, bool range/*=false*/)
{
infoTexts[position].first = new CLabel(155, 48 + position*19, FONT_SMALL, TOPLEFT, zwykly, text);
std::string valueStr;
std::string hlp;
if(range && baseVal != val)
hlp = boost::str(boost::format("%d - %d") % baseVal % val);
else if(baseVal != val && val>=0)
hlp = boost::str(boost::format("%d (%d)") % baseVal % val);
else
hlp = boost::lexical_cast<std::string>(baseVal);
valueStr = boost::str(boost::format("%d - %d") % baseVal % val);
printTo(hlp, 276, 61 + nr*19, FONT_SMALL, zwykly, *bitmap);
else if(baseVal != val && val>=0)
valueStr = boost::str(boost::format("%d (%d)") % baseVal % val);
else
valueStr = boost::lexical_cast<std::string>(baseVal);
infoTexts[position].second = new CLabel(276, 63 + position*19, FONT_SMALL, BOTTOMRIGHT, zwykly, valueStr);
}
//void CCreInfoWindow::init(const CCreature *cre, const CStackInstance *stack, int creatureCount)
void CCreInfoWindow::init(const CCreature *cre, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner, int creatureCount)
void CCreInfoWindow::init(const CCreature *creature, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner, int count, bool LClicked)
{
c = cre;
if(!stackNode) stackNode = c;
used = 0;
if (!LClicked)
used |= RCLICK;
bitmap = new CPicture("CRSTKPU.bmp");
bitmap->colorizeAndConvert(LOCPLINT->playerID);
pos = bitmap->center();
if(!stackNode)
stackNode = creature;
anim = new CCreaturePic(21, 48, c);
background = new CPicture("CRSTKPU");
background->colorize(LOCPLINT->playerID);
pos = background->center();
count = boost::lexical_cast<std::string>(creatureCount);
animation = new CCreaturePic(21, 48, creature);
printAtMiddle(c->namePl,149,30,FONT_SMALL,tytulowy,*bitmap); //creature name
std::string countStr = boost::lexical_cast<std::string>(count);
creatureCount = new CLabel(114, 174, FONT_TIMES, BOTTOMRIGHT, zwykly, countStr);
creatureName = new CLabel(149, 30, FONT_SMALL, CENTER, tytulowy, creature->namePl);
printLine(0, CGI->generaltexth->primarySkillNames[0], cre->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), stackNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK));
printLine(1, CGI->generaltexth->primarySkillNames[1], cre->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), stackNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE));
//if(c->shots)
// printLine(2, CGI->generaltexth->allTexts[198], c->shots);
printLine(0, CGI->generaltexth->primarySkillNames[0], creature->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), stackNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK));
printLine(1, CGI->generaltexth->primarySkillNames[1], creature->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), stackNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE));
if(stackNode->valOfBonuses(Bonus::SHOTS))
printLine(2, CGI->generaltexth->allTexts[198], stackNode->valOfBonuses(Bonus::SHOTS));
@ -2176,67 +2178,35 @@ void CCreInfoWindow::init(const CCreature *cre, const CBonusSystemNode *stackNod
if(heroOwner && stackNode->hasBonusOfType(Bonus::SIEGE_WEAPON))
dmgMultiply += heroOwner->Attack();
printLine(3, CGI->generaltexth->allTexts[199], stackNode->getMinDamage() * dmgMultiply, stackNode->getMaxDamage() * dmgMultiply, true);
printLine(4, CGI->generaltexth->allTexts[388], cre->valOfBonuses(Bonus::STACK_HEALTH), stackNode->valOfBonuses(Bonus::STACK_HEALTH));
printLine(6, CGI->generaltexth->zelp[441].first, cre->valOfBonuses(Bonus::STACKS_SPEED), stackNode->valOfBonuses(Bonus::STACKS_SPEED));
printLine(3, CGI->generaltexth->allTexts[199], stackNode->getMinDamage() * dmgMultiply, stackNode->getMaxDamage() * dmgMultiply, true);
printLine(4, CGI->generaltexth->allTexts[388], creature->valOfBonuses(Bonus::STACK_HEALTH), stackNode->valOfBonuses(Bonus::STACK_HEALTH));
printLine(6, CGI->generaltexth->zelp[441].first, creature->valOfBonuses(Bonus::STACKS_SPEED), stackNode->valOfBonuses(Bonus::STACKS_SPEED));
//setting morale
morale = new MoraleLuckBox(true, genRect(42, 42, 24, 189));
morale = new MoraleLuckBox(true, genRect(42, 42, 22, 186));
morale->set(stackNode);
//setting luck
luck = new MoraleLuckBox(false, genRect(42, 42, 77, 189));
luck = new MoraleLuckBox(false, genRect(42, 42, 75, 186));
luck->set(stackNode);
//luck and morale
int luck = 3, morale = 3;
if(stackNode)
{
//add modifiers
luck += stackNode->LuckVal();
morale += stackNode->MoraleVal();
}
blitAt(graphics->morale42->ourImages[morale].bitmap, 24, 189, *bitmap);
blitAt(graphics->luck42->ourImages[luck].bitmap, 77, 189, *bitmap);
if(!type)
{
printAtWB(c->abilityText,17,231,FONT_SMALL,35,zwykly,*bitmap);
}
if(!LClicked)
abilityText = new CLabel(17, 231, FONT_SMALL, TOPLEFT, zwykly, creature->abilityText);
else
abilityText = NULL;
//if we are displying window fo r stack in battle, there are several more things that we need to display
if(const CStack *battleStack = dynamic_cast<const CStack*>(stackNode))
{
//spell effects
int printed=0; //how many effect pics have been printed
//print at most 3 spell effects
std::vector<si32> spells = battleStack->activeSpells();
BOOST_FOREACH(si32 effect, spells)
{
blitAt(graphics->spellEffectsPics->ourImages[effect + 1].bitmap, 127 + 52 * printed, 186, *bitmap);
++printed;
if(printed >= 3) //we can fit only 3 effects
break;
}
for (size_t i=0; i< std::min(spells.size(), size_t(3)); i++)
effects.push_back(new CAnimImage("SpellInt", spells[i]+1, 0, 127 + 52*i, 186));
//print current health
printLine(5, CGI->generaltexth->allTexts[200], battleStack->firstHPleft);
}
}
CCreInfoWindow::~CCreInfoWindow()
{
for(int i=0; i<upgResCost.size();i++)
delete upgResCost[i];
}
void CCreInfoWindow::activate()
{
CIntObject::activate();
if(!type)
activateRClick();
}
void CCreInfoWindow::close()
{
GH.popIntTotally(this);
@ -2244,26 +2214,8 @@ void CCreInfoWindow::close()
void CCreInfoWindow::clickRight(tribool down, bool previousState)
{
if(down)
return;
close();
}
void CCreInfoWindow::dismissF()
{
dsm();
close();
}
void CCreInfoWindow::keyPressed (const SDL_KeyboardEvent & key)
{
}
void CCreInfoWindow::deactivate()
{
if(!type)
deactivateRClick();
CIntObject::deactivate();
}
void CLevelWindow::close()
{

View File

@ -329,6 +329,7 @@ public:
bool redrawParentOnScrolling;
std::vector<std::string> lines;
std::vector<CAnimImage* > effects;
CSlider *slider;
//CTextBox( std::string Text, const Point &Pos, int w, int h, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = zwykly);
@ -943,31 +944,30 @@ public:
class CCreInfoWindow : public CIntObject
{
public:
//bool active; //TODO: comment me
int type;//0 - rclick popup; 1 - normal window
CPicture *bitmap; //background
std::string count; //creature count in text format
CPicture *background;
CLabel *creatureCount;
CLabel *creatureName;
CLabel *abilityText;
boost::function<void()> dsm; //dismiss button callback
CCreaturePic *anim; //related creature's animation
const CCreature *c; //related creature
CCreaturePic *animation;
std::vector<SComponent*> upgResCost; //cost of upgrade (if not possible then empty)
std::vector<CAnimImage * > effects;
std::map<size_t, std::pair<CLabel*, CLabel* > > infoTexts;
MoraleLuckBox *luck, *morale;
AdventureMapButton *dismiss, *upgrade, *ok;
CCreInfoWindow(const CStackInstance &st, int Type = 0, boost::function<void()> Upg = 0, boost::function<void()> Dsm = 0, UpgradeInfo *ui = NULL); //c-tor
CCreInfoWindow(const CStack &st, int Type = 0); //c-tor
CCreInfoWindow(int Cid, int Type, int creatureCount); //c-tor
void init(const CCreature *cre, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner, int creatureCount);
CCreInfoWindow(const CStackInstance &st, bool LClicked, boost::function<void()> Upg = 0, boost::function<void()> Dsm = 0, UpgradeInfo *ui = NULL);
CCreInfoWindow(const CStack &st, bool LClicked = 0);
CCreInfoWindow(int Cid, bool LClicked, int creatureCount);
~CCreInfoWindow();
void init(const CCreature *cre, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner, int creatureCount, bool LClicked);
void printLine(int nr, const std::string &text, int baseVal, int val=-1, bool range=false);
~CCreInfoWindow(); //d-tor
void activate();
void clickRight(tribool down, bool previousState);
void close();
void clickRight(tribool down, bool previousState); //call-in
void dismissF();
void keyPressed (const SDL_KeyboardEvent & key); //call-in
void deactivate();
void show(SDL_Surface * to);
};
@ -975,7 +975,7 @@ public:
class CArtPlace: public LRClickableAreaWTextComp
{
public:
int slotID; //0 head 1 shoulders 2 neck 3 right hand 4 left hand 5 torso 6 right ring 7 left ring 8 feet 9 misc. slot 1 10 misc. slot 2 11 misc. slot 3 12 misc. slot 4 13 ballista (war machine 1) 14 ammo cart (war machine 2) 15 first aid tent (war machine 3) 16 catapult 17 spell book 18 misc. slot 5 19+ backpack slots
int slotID; //Arts::EPOS enum + backpack starting from Arts::BACKPACK_START
bool picked;
bool marked;

File diff suppressed because it is too large Load Diff

View File

@ -2856,7 +2856,7 @@ void CPathfinder::calculatePaths(int3 src /*= int3(-1,-1,-1)*/, int movement /*=
&& dp->accessible == CGPathNode::BLOCKVIS;
if (dp->accessible == CGPathNode::ACCESSIBLE
|| useEmbarkCost && allowEmbarkAndDisembark
|| (useEmbarkCost && allowEmbarkAndDisembark)
|| destTopVisObjID == SUBTERRANEAN_GATE_TYPE
|| (guardedDst && !guardedSource)) // Can step into a hostile tile once.
{
@ -2959,7 +2959,7 @@ bool CPathfinder::goodForLandSeaTransition()
return true;
}
CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : out(_out), CGameInfoCallback(_gs, -1), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, -1), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
{
useSubterraneanGates = true;
allowEmbarkAndDisembark = true;

View File

@ -1,6 +1,9 @@
#define VCMI_DLL
#include "JsonNode.h"
#include <boost/assign.hpp>
#include <boost/foreach.hpp>
#include <assert.h>
#include <fstream>
#include <sstream>
@ -18,6 +21,7 @@ JsonNode::JsonNode(const char *data, size_t datasize):
type(DATA_NULL)
{
JsonParser parser(data, datasize, *this);
JsonValidator validator(*this);
}
JsonNode::JsonNode(std::string filename):
@ -27,18 +31,29 @@ JsonNode::JsonNode(std::string filename):
fseek(file, 0, SEEK_END);
size_t datasize = ftell(file);
fseek(file, 0, SEEK_SET);
char *input = new char[datasize];
fread((void*)input, 1, datasize, file);
datasize = fread((void*)input, 1, datasize, file);
fclose(file);
JsonParser parser(input, datasize, *this);
JsonValidator validator(*this);
delete [] input;
}
JsonNode::JsonNode(const JsonNode &copy):
type(DATA_NULL)
{
*this = copy;
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()
@ -46,18 +61,16 @@ JsonNode::~JsonNode()
setType(DATA_NULL);
}
JsonNode & JsonNode::operator =(const JsonNode &node)
void JsonNode::swap(JsonNode &b)
{
setType(node.getType());
switch(type)
{
break; case DATA_NULL:
break; case DATA_BOOL: Bool() = node.Bool();
break; case DATA_FLOAT: Float() = node.Float();
break; case DATA_STRING: String() = node.String();
break; case DATA_VECTOR: Vector() = node.Vector();
break; case DATA_STRUCT: Struct() = node.Struct();
}
using std::swap;
swap(data, b.data);
swap(type, b.type);
}
JsonNode & JsonNode::operator =(JsonNode node)
{
swap(node);
return *this;
}
@ -191,71 +204,100 @@ const JsonNode & JsonNode::operator[](std::string child) const
////////////////////////////////////////////////////////////////////////////////
//Helper to write content of map/vector
template<class iterator>
void writeContainer(const iterator &begin, const iterator &end, std::ostream &out, std::string prefix)
template<typename Iterator>
void JsonWriter::writeContainer(Iterator begin, Iterator end)
{
if (begin == end)
return;
iterator last = end;
last--;
for (iterator it=begin; it != last; ++it)
prefix += '\t';
end--;
while (begin != end)
{
writeNode(it, out, prefix);
writeEntry(begin++);
out<<",\n";
}
writeNode(last, out, prefix);
writeEntry(begin);
out<<"\n";
prefix.resize(prefix.size()-1);
}
void writeNode(JsonVector::const_iterator it, std::ostream &out, std::string prefix)
void JsonWriter::writeEntry(JsonMap::const_iterator entry)
{
out << prefix;
it->write(out, prefix);
writeString(entry->first);
out << " : ";
writeNode(entry->second);
}
void writeNode(JsonMap::const_iterator it, std::ostream &out, std::string prefix)
void JsonWriter::writeEntry(JsonVector::const_iterator entry)
{
out << prefix << '\"' << it->first << '\"' << " : ";
it->second.write(out, prefix);
out << prefix;
writeNode(*entry);
}
void JsonNode::write(std::ostream &out, std::string prefix) const
void JsonWriter::writeString(const std::string &string)
{
switch(type)
static const std::string escaped = "\"\\/\b\f\n\r\t";
out <<'\"';
size_t pos=0, start=0;
for (; pos<string.size(); pos++)
{
break; case DATA_NULL:
size_t escapedChar = escaped.find(string[pos]);
if (escapedChar != std::string::npos)
{
out.write(string.data()+start, pos - start);
out << '\\' << escaped[escapedChar];
start = pos;
}
}
out.write(string.data()+start, pos - start);
out <<'\"';
}
void JsonWriter::writeNode(const JsonNode &node)
{
switch(node.getType())
{
break; case JsonNode::DATA_NULL:
out << "null";
break; case DATA_BOOL:
if (Bool())
break; case JsonNode::DATA_BOOL:
if (node.Bool())
out << "true";
else
out << "false";
break; case DATA_FLOAT:
out << Float();
break; case JsonNode::DATA_FLOAT:
out << node.Float();
break; case DATA_STRING:
out << "\"" << String() << "\"";
break; case JsonNode::DATA_STRING:
writeString(node.String());
break; case DATA_VECTOR:
break; case JsonNode::DATA_VECTOR:
out << "[" << "\n";
writeContainer(Vector().begin(), Vector().end(), out, prefix+'\t');
writeContainer(node.Vector().begin(), node.Vector().end());
out << prefix << "]";
break; case DATA_STRUCT:
break; case JsonNode::DATA_STRUCT:
out << "{" << "\n";
writeContainer(Struct().begin(), Struct().end(), out, prefix+'\t');
writeContainer(node.Struct().begin(), node.Struct().end());
out << prefix << "}";
}
}
JsonWriter::JsonWriter(std::ostream &output, const JsonNode &node):
out(output)
{
writeNode(node);
}
std::ostream & operator<<(std::ostream &out, const JsonNode &node)
{
node.write(out);
JsonWriter(out, node);
return out << "\n";
}
@ -275,7 +317,7 @@ JsonParser::JsonParser(const char * inputString, size_t stringSize, JsonNode &ro
error("Not all file was parsed!", true);
//TODO: better way to show errors (like printing file name as well)
tlog2<<errors;
std::cout<<errors;
}
bool JsonParser::extractSeparator()
@ -335,7 +377,7 @@ bool JsonParser::extractWhitespace(bool verbose)
if (input[pos] == '/')
pos++;
else
error("Comments should have two slashes!", true);
error("Comments must consist from two slashes!", true);
while (pos < input.size() && input[pos] != '\n')
pos++;
@ -358,7 +400,7 @@ bool JsonParser::extractEscaping(std::string &str)
break; case '\n': str += '\n';
break; case '\r': str += '\r';
break; case '\t': str += '\t';
break; default: return error("Uknown escape sequence!", true);
break; default: return error("Unknown escape sequence!", true);
};
return true;
}
@ -442,7 +484,6 @@ bool JsonParser::extractTrue(JsonNode &node)
if (!extractLiteral("true"))
return false;
node.setType(JsonNode::DATA_BOOL);
node.Bool() = true;
return true;
}
@ -452,7 +493,6 @@ bool JsonParser::extractFalse(JsonNode &node)
if (!extractLiteral("false"))
return false;
node.setType(JsonNode::DATA_BOOL);
node.Bool() = false;
return true;
}
@ -484,33 +524,17 @@ bool JsonParser::extractStruct(JsonNode &node)
if (node.Struct().find(key) != node.Struct().end())
error("Dublicated element encountered!", true);
JsonNode &child = node.Struct()[key];
if (!extractSeparator())
return false;
if (!extractValue(child))
if (!extractElement(node.Struct()[key], '}'))
return false;
if (!extractWhitespace())
return false;
bool comma = (input[pos] == ',');
if (comma )
{
pos++;
if (!extractWhitespace())
return false;
}
if (input[pos] == '}')
{
pos++;
return true;
}
if (!comma)
error("Comma expected!", true);
}
}
@ -531,33 +555,46 @@ bool JsonParser::extractArray(JsonNode &node)
while (true)
{
//NOTE: currently 50% of time is this vector resizing.
//May be useful to use list during parsing and then swap() all items to vector
node.Vector().resize(node.Vector().size()+1);
if (!extractValue(node.Vector().back()))
if (!extractElement(node.Vector().back(), ']'))
return false;
if (!extractWhitespace())
return false;
bool comma = (input[pos] == ',');
if (comma )
{
pos++;
if (!extractWhitespace())
return false;
}
if (input[pos] == ']')
{
pos++;
return true;
}
if (!comma)
error("Comma expected!", true);
}
}
bool JsonParser::extractElement(JsonNode &node, char terminator)
{
if (!extractValue(node))
return false;
if (!extractWhitespace())
return false;
bool comma = (input[pos] == ',');
if (comma )
{
pos++;
if (!extractWhitespace())
return false;
}
if (input[pos] == terminator)
return true;
if (!comma)
error("Comma expected!", true);
return true;
}
bool JsonParser::extractFloat(JsonNode &node)
{
assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9'));
@ -572,6 +609,7 @@ bool JsonParser::extractFloat(JsonNode &node)
if (input[pos] < '0' || input[pos] > '9')
return error("Number expected!");
//Extract integer part
while (input[pos] >= '0' && input[pos] <= '9')
{
@ -614,3 +652,164 @@ bool JsonParser::error(const std::string &message, bool warning)
return warning;
}
static const std::map<std::string, JsonNode::JsonType> stringToType =
boost::assign::map_list_of
("null", JsonNode::DATA_NULL) ("bool", JsonNode::DATA_BOOL)
("number", JsonNode::DATA_FLOAT) ("string", JsonNode::DATA_STRING)
("array", JsonNode::DATA_VECTOR) ("object", JsonNode::DATA_STRUCT);
//Check current schema entry for validness and converts "type" string to JsonType
bool JsonValidator::validateSchema(JsonNode::JsonType &type, const JsonNode &schema)
{
if (schema.isNull())
return addMessage("Missing schema for current entry!");
const JsonNode &nodeType = schema["type"];
if (nodeType.isNull())
return addMessage("Entry type is not defined in schema!");
if (nodeType.getType() != JsonNode::DATA_STRING)
return addMessage("Entry type must be string!");
std::map<std::string, JsonNode::JsonType>::const_iterator iter = stringToType.find(nodeType.String());
if (iter == stringToType.end())
return addMessage("Unknown entry type found!");
type = iter->second;
return true;
}
//Replaces node with default value if needed and calls type-specific validators
bool JsonValidator::validateType(JsonNode &node, const JsonNode &schema, JsonNode::JsonType type)
{
if (node.isNull())
{
const JsonNode & defaultValue = schema["default"];
if (defaultValue.isNull())
return addMessage("Null entry without default entry!");
else
node = defaultValue;
}
if (type != node.getType())
{
node.setType(JsonNode::DATA_NULL);
return addMessage("Type mismatch!");
}
if (type == JsonNode::DATA_VECTOR)
return validateItems(node, schema["items"]);
if (type == JsonNode::DATA_STRUCT)
return validateProperties(node, schema["properties"]);
return true;
}
// Basic checks common for any nodes
bool JsonValidator::validateNode(JsonNode &node, const JsonNode &schema, const std::string &name)
{
currentPath.push_back(name);
JsonNode::JsonType type = JsonNode::DATA_NULL;
if (!validateSchema(type, schema))
{
currentPath.pop_back();
return false;
}
if (!validateType(node, schema, type))
{
currentPath.pop_back();
return false;
}
currentPath.pop_back();
return true;
}
//Checks "items" entry from schema (type-specific check for Vector)
bool JsonValidator::validateItems(JsonNode &node, const JsonNode &schema)
{
JsonNode::JsonType type = JsonNode::DATA_NULL;
if (!validateSchema(type, schema))
return false;
BOOST_FOREACH(JsonNode &entry, node.Vector())
{
if (!validateType(entry, schema, type))
return false;
}
return true;
}
//Checks "propertries" entry from schema (type-specific check for Struct)
//Function is similar to merging of two sorted lists - check every entry that present in one of the input nodes
bool JsonValidator::validateProperties(JsonNode &node, const JsonNode &schema)
{
if (schema.isNull())
return addMessage("Properties entry is missing for struct in schema");
JsonMap::iterator nodeIter = node.Struct().begin();
JsonMap::const_iterator schemaIter = schema.Struct().begin();
while (nodeIter != node.Struct().end() && schemaIter != schema.Struct().end())
{
std::string current = std::min(nodeIter->first, schemaIter->first);
validateNode(node[current], schema[current], current);
if (nodeIter->first < schemaIter->first)
nodeIter++;
else
if (schemaIter->first < nodeIter->first)
schemaIter++;
else
{
nodeIter++;
schemaIter++;
}
}
while (nodeIter != node.Struct().end())
{
validateNode(nodeIter->second, JsonNode(), nodeIter->first);
nodeIter++;
}
while (schemaIter != schema.Struct().end())
{
validateNode(node[schemaIter->first], schemaIter->second, schemaIter->first);
schemaIter++;
}
return true;
}
bool JsonValidator::addMessage(const std::string &message)
{
std::ostringstream stream;
stream << "At ";
BOOST_FOREACH(const std::string &path, currentPath)
stream << path<<"/";
stream << "\t Error: " << message <<"\n";
errors += stream.str();
return false;
}
JsonValidator::JsonValidator(JsonNode &root)
{
const JsonNode schema = root["schema"];
if (!schema.isNull())
{
root.Struct().erase("schema");
validateProperties(root, schema);
}
//This message is quite annoying now - most files do not have schemas. May be re-enabled later
//else
// addMessage("Schema not found!", true);
//TODO: better way to show errors (like printing file name as well)
std::cout<<errors;
}

View File

@ -3,6 +3,7 @@
#include "../global.h"
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <vector>
@ -49,8 +50,8 @@ public:
~JsonNode();
// Deep copy of this node
JsonNode& operator =(const JsonNode &node);
void swap(JsonNode &b);
JsonNode& operator =(JsonNode node);
//Convert node to another type. Converting to NULL will clear all data
void setType(JsonType Type);
@ -72,9 +73,6 @@ public:
const JsonVector & Vector() const;
const JsonMap & Struct() const;
//formatted output of this node in JSON format
void write(std::ostream &out, std::string prefix="") const;
//operator [], for structs only - get child node by name
JsonNode & operator[](std::string child);
const JsonNode & operator[](std::string child) const;
@ -83,9 +81,25 @@ public:
static const JsonNode nullNode;
};
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);
};
std::ostream & operator<<(std::ostream &out, const JsonNode &node);
//Tiny string class that use const char* as data for speed, members are private for ease of debugging
//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;
@ -110,10 +124,9 @@ public:
}
};
//Internal class for std::string -> JsonNode conversion
//Internal class for string -> JsonNode conversion
class JsonParser
{
std::string errors; // Contains description of all encountered errors
constString input; // Input data
unsigned int lineCount; // Currently parsed line, starting from 1
@ -126,6 +139,7 @@ class JsonParser
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);
@ -143,3 +157,24 @@ class JsonParser
public:
JsonParser(const char * inputString, size_t stringSize, JsonNode &root);
};
//Internal class for Json validation, used automaticaly in JsonNode constructor. Behaviour:
// - "schema" entry from root node is used for validation and will be removed
// - any missing entries will be replaced with default value from schema (if present)
// - if entry uses different type than defined in schema it will be removed
// - entries nod described in schema will be kept unchanged
class JsonValidator
{
std::string errors; // Contains description of all encountered errors
std::list<std::string> currentPath; // path from root node to current one
bool validateType(JsonNode &node, const JsonNode &schema, JsonNode::JsonType type);
bool validateSchema(JsonNode::JsonType &type, const JsonNode &schema);
bool validateNode(JsonNode &node, const JsonNode &schema, const std::string &name);
bool validateItems(JsonNode &node, const JsonNode &schema);
bool validateProperties(JsonNode &node, const JsonNode &schema);
bool addMessage(const std::string &message);
public:
JsonValidator(JsonNode &root);
};