Oi, it’s done I guess. Beta phase though, but it seems to work rather well! This code parses the “SXML” of the post right before this one.
It’s tested with this file:
<window 100 456 789 120 windone SimpleXML>
<button 101 258 369 123 buttone Ok 1337/>
<button 102 258 369 123 buttwo Cancel 1338/>
</>
<window 103 456 789 120 windtwo SimpleXML1>
<window 104 456 789 120 windthree SimpleXML2>
<button 105 258 369 123 buttwo yes 1339/>
<button 106 258 369 123 buttwo nay 1340/>
</>
<button 106 258 369 123 buttwo nay 1340/>
</>
/*Free to do anything (with this source file) but leave this comment block alone!
Created by .simplicity's Nick Overdijk (nick@dotsimplicity.net)
*/
#include <iostream>
#include <cstdio>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <stack>
using namespace std;
/*Load file into memory as string
create vector of element pointers
parse the document, create elements on the fly
*/
class element {
public:
element();
element(string &amp;amp;amp;type, unsigned int x, unsigned int y, unsigned int height, unsigned int width, element *parent) :
type(type), x(x), y(y), height(height), width(width), parent(parent) {
}
element(string &amp;amp;amp;attributes) :
attributes(attributes) {
attributeStream.str(attributes);
parse();
}
virtual ~element(){ cout << "Finishing element" << endl; }
virtual void printInfo() {
cout << "---" << type << " @ " << this << "---" << endl;
cout << "Width: " << width << endl;
cout << "Height: " << height << endl;
cout << "x: " << x << endl;
cout << "y: " << y << endl;
if(parent != NULL)
cout << "Parent: " << parent << "(" << parent->type << "::" << parent->name << ")" << endl;
}
void printTree() {
printInfo();
if (parent != NULL) {
parent->printTree();
}
}
virtual void parse(){
attributeStream >> type;
attributeStream >> x;
attributeStream >> y;
attributeStream >> width;
attributeStream >> height;
}
void setParent(element *parent){
this->parent = parent;
}
string name;
string type;
string attributes;
stringstream attributeStream;
unsigned int x;
unsigned int y;
unsigned int height;
unsigned int width;
element *parent;
};
class button : public element {
public:
button(string &amp;amp;amp;attributes) :
element(attributes) {
parse();
}
virtual ~button() { cout << endl; }
virtual void parse(){
attributeStream >> name;
attributeStream >> caption;
attributeStream >> style;
}
virtual void printInfo(){
element::printInfo();
cout << "Name: " << name << endl;
cout << "Caption: " << caption << endl;
cout << "Style: " << style << endl;
}
string caption;
int style;
};
class window : public element {
public:
window(string &amp;amp;amp;attributes) :
element(attributes) {
parse();
}
virtual ~window() { cout << endl; }
virtual void parse(){
attributeStream >> name;
attributeStream >> title;
}
virtual void printInfo(){
element::printInfo();
cout << "Name: " << name << endl;
cout << "Title: " << title << endl;
}
string title;
};
element *createElement(string &amp;amp;amp;type, string &amp;amp;amp;attributes){
#define \
createThis(x)\
if(type == #x) return new x(attributes)
createThis(button);
else createThis(window);
else return NULL;
}
/*Each part of the code is divided into:
Letting the user know what we're doing
Do it
Perform a check
Tell the user fail or done
exit or continue
At any given time, the user knows what the program is doing and it helps debugging a lot.
*/
int main(int argc, char *argv[]) {
vector<element*> elementList;
cout << "Opening file: ";
ifstream document;
document.open("test.sxml", ios::binary);
if (!document.is_open()) {
cout << "[fail]" << endl;
exit(-1);
} else {
cout << "[done]" << endl;
}
cout << "Getting filesize: ";
document.seekg(0, ios::end);
unsigned int docLen = document.tellg();
document.seekg(0, ios::beg);
cout << docLen << " bytes. [done]" << endl;
cout << "Loading file into memory: ";
char *buf = new char [docLen];
document.read(buf, docLen);
if (document.bad()) {
cout << "[fail]" << endl;
} else {
cout << "[done]" << endl;
}
cout << "Parsing file for elements: ";
/*Algorithm:
Find a '<'
if nextchar != '/''
read in data until >
parse data, create element
if char before > != '/'
push this element as curent parent
else if nextchar == '/'
pop the last parent
*/
stack <element*> parentStack;
parentStack.push(NULL);
unsigned int n, i;
for (i = 0; i < docLen; i++) {
if (buf[i] == '<') {
if (buf[i+1] != '/') {
for(n = i+1; buf[n] != '>' &amp;amp;amp;&amp;amp;amp; buf[n] != '/'; n++);
unsigned int lineLength = n - i;
string line(&amp;amp;amp;buf[i+1], lineLength-1);
//cout << "DEBUG LINE READ: " << line << endl;
stringstream lineStream(line);
string type;
lineStream >> type;
element *curElem = createElement(type, line);
curElem->setParent(parentStack.top());
for (n = i+2; buf[n] != '>'; n++);
if (buf[n-1] != '/') {
parentStack.push(curElem);
}
elementList.push_back(curElem);
} else {
parentStack.pop();
}
}
}
cout << "[done]" << endl;
for (unsigned int n = 0; n < elementList.size(); n++) {
elementList[n]->printInfo();
}
return 0;
}
Sooo, feel free to comment on the code, but make it useful.
