Blame |
Last modification |
View Log
| RSS feed
#include "XMLDemoApp.h"
#include "SexyAppFramework/WidgetManager.h"
#include "SexyAppFramework/ResourceManager.h"
#include "SexyAppFramework/XMLParser.h"
#include "Board.h"
#include "Res.h"
using namespace Sexy;
XMLDemoApp::XMLDemoApp()
{
mProdName = "XMLDemo";
mProductVersion = "1.0";
mTitle = StringToSexyStringFast("SexyAppFramework: " + mProdName + " - " + mProductVersion);
mRegKey = "PopCap\\SexyAppFramework\\XMLDemo";
mBoard = NULL;
mParser = NULL;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
XMLDemoApp::~XMLDemoApp()
{
delete mBoard;
delete mParser;
mResourceManager->DeleteResources("");
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void XMLDemoApp::ShutdownHook()
{
if (mBoard != NULL)
mWidgetManager->RemoveWidget(mBoard);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void XMLDemoApp::InitHook()
{
LoadResourceManifest();
// We're just going to wind up using the default font from this resource group.
// Since there's very little to load, we'll dispense with the whole
// title screen loading stuff.
if (!mResourceManager->LoadResources("Init"))
{
mLoadingFailed = true;
ShowResourceError(true);
return;
}
if (!ExtractInitResources(mResourceManager))
{
mLoadingFailed = true;
ShowResourceError(true);
return;
}
// Create our XML Parser object
mParser = new XMLParser();
// By default, comments are ignored and you won't see them at all once the file is read in.
// If you would like to not strip out the comments, you can call the AllowComments method
// of the XMLParser class and pass in true. Uncomment the line below to let XML comments
// be retained when loading the demo.xml file:
// mParser->AllowComments(true);
// Load in our demo XML file. If an error occurs, it'll return false.
if (!mParser->OpenFile("properties/demo.xml"))
{
mLoadingFailed = true;
// The parser will set an error string in the event that a problem occurred.
// You get this string via a call to GetErrorText().
MsgBox(StrFormat("Couldn't open properties/demo.xml. Error returned:\n\"%s\"",
mParser->GetErrorText().c_str()), "Error");
return;
}
ExtractXMLData();
mBoard = new Board(this);
mBoard->Resize(0, 0, mWidth, mHeight);
mWidgetManager->AddWidget(mBoard);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
bool XMLDemoApp::ExtractXMLData()
{
// Everything will revolve around XML elements. We'll create
// one on the stack and pass it to the NextElement function below.
// The XMLElement class contains information on each element, such as
// whether it's a comment, start of a tag, end of a tag, what its
// attributes/values are, etc.
XMLElement e;
// We loop until there are no more elements left to process. NextElement
// returns false when done.
while (mParser->NextElement(&e))
{
// If you had enabled the option to not strip out comments (by default, comments
// are removed when loading an XML file, see line 65 of this file for more info),
// the type would be TYPE_COMMENT, the mSection field would be filled in as per usual,
// the mValue field would be the comment declaration syntax, !--, and the entire comment
// text (even if split across many lines) would be placed in mInstruction. Course, the
// default setup for this demo is to ignore comments, so you won't see them at all.
// TYPE_START denotes the start of a new section.
// For every TYPE_START, there will eventually be a corresponding
// TYPE_END with a matching mValue. For our current demo, we actually
// don't care about the end of a tag.
if (e.mType == XMLElement::TYPE_START)
{
// The type stays as TYPE_START until another item type appears, such as the
// end of a section/item, TYPE_ELEMENT is found (see a few paragraphs below),
// a comment is found, etc.
// In the meantime, the mSection field contains the full name of the current
// section.
if (e.mSection == _S("Section1"))
{
// Once inside a section, each item is read in. When an item is read in,
// it's tag is placed in the mValue field, and all attributes will be stored
// in a map (mAttributes) keyed by the attribute's string name, with a result of
// type std::string.
if (e.mValue == _S("Item1"))
{
// At this point, we've read the line from our demo.xml file:
// <Item1 text="Some text for Section1/Item1"/>
// that is, assuming you didn't change the contents of that file.
// As you can see, "Item1" is in the mValue field, and "text" is
// the key in our mAttributes map, with "Some text for Section1/Item1" as its value.
mSection1.mItem1Text = e.mAttributes[_S("text")];
}
else if (e.mValue == _S("BoolParam"))
{
// You'll notice that in demo.xml the line containing this part of the XML code reads:
// <BoolParam text="Some text for Section1/BoolParam">true</BoolParam>
//
// At this point in the processing though, we have only covered this part:
// <BoolParam text="Some text for Section1/BoolParam">
//
// The "true" part will be covered a few paragraphs below where we check for TYPE_ELEMENT.
// For now though, we can read in all parameters (in this case, just "text") stored
// as part of this item.
mSection1.mBoolParamText = e.mAttributes[_S("text")];
}
}
else if (e.mSection == _S("Section2"))
{
if (e.mValue == _S("IntParam"))
mSection2.mIntParamText = e.mAttributes[_S("text")];
else if (e.mValue == _S("MultiAttrib"))
{
// If you check out the demo.xml file, you'll see an entry declared like this:
//
// <MultiAttrib
// attrib1="attrib1"
// attrib2="2"
// attrib3="woo! attrib3!"
// />
//
// Again, the tag is in mValue, and all attributes are stored as key/value pairs
// in the mAttributes map. Note that the whitespace doesn't matter: you could
// have declared the above line on just a single line if you wanted to.
mSection2.mMultiAttrib1 = e.mAttributes[_S("attrib1")];
mSection2.mMultiAttrib2 = e.mAttributes[_S("attrib2")];
mSection2.mMultiAttrib3 = e.mAttributes[_S("attrib3")];
}
}
else if (e.mValue == _S("Section2"))
{
// Confused about this part? Don't be: When the very first line of a section
// is read in (in this case, it is: <Section2 s2attrib="Section2 attrib text">),
// if it doesn't also end on the same line (which in this case it doesn't, we get
// the </Section2> a few lines later) then mSection isn't updated and instead, the
// section name is stored in mValue. In this case, you'll see that mSection is "".
// That's because whitespace is ignored, so it's possible that this isn't a section
// and is perhaps just an item like the <MultiAttrib example above, split across several lines.
// Since when a line is read in, if it contains any attributes they too are read in,
// this is a good time to extract out the "s2attrib" attribute and record it in our mSection2
// structure.
mSection2.mSectionAttribText = e.mAttributes[_S("s2attrib")];
}
}
else if (e.mType == XMLElement::TYPE_ELEMENT)
{
// For lines of the form:
// <BoolParam text="Some text for Section1/BoolParam"> true </BoolParam>
// where there is data in between the start/end of the item (in this case, the data is "true"),
// the item is considered to be of type TYPE_ELEMENT. An element looks like a section declaration,
// except that it just contains a singular data value rather than nested XML syntax. For elements,
// the section name is the name of the previous sections + the tag name of the element. So in the
// above example, the section name is "Section1/BoolParam" since the previous (parent) section is
// "Section1" and the tag is "BoolParam". The data is contained in the mValue field.
if (e.mSection == _S("Section1/BoolParam"))
mSection1.mBoolParamElement = StringToLower(e.mValue) == _S("false") ? false : true;
else if (e.mSection == _S("Section2/IntParam"))
{
// This is just a handy std::string to integer function, that will return a boolean
// indicating success or failure. I'm ignoring the return type though in this
// particular case as I don't care for this particular example.
(void)StringToInt(e.mValue, &mSection2.mIntParamElement);
}
else if (e.mSection == _S("Section2/Subsection/Item2"))
mSection2.mSubsectionItem2 = e.mValue;
}
}
return true;
}