/////////////////////////////////////////////////////////////// // xml.cpp - build, format, and parse xml objects // // ver 1.0 // // // // Language: Visual C++, ver 6.0 // // Platform: Del XPS1000, Windows 2000 Professional // // Application: CSE691, Pr1 demo, Fall 2000 // // Author: Jim Fawcett, CST 2-187, Syracuse Univ. // // (315) 443-3948, jfawcett@twcny.rr.com // /////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include #include using namespace std; #include "xml.h" inline std::string xmlRep::str(int i) { if (i==OPEN) xmlStr = xmlStr.substr(0, xmlStr.find_last_of("<")); return xmlStr; } ///////////////////////////////////////////////////////////////////////////// // class xmlRep - base class for xml data representation // //----< insert tag at beginning of rep string >---------------- xmlRep& xmlRep::tag(const string &tg) { string temp = string("<") += tg + string(">") += string(""); xmlStr.insert(0,temp); return *this; } //----< insert attribute at end of opening tag >--------------- xmlRep& xmlRep::attr(const string &name, const string &value) { int pos = xmlStr.find(">"); if(pos > 1 && xmlStr[pos-1] == '/') --pos; if(pos < xmlStr.size()) { string temp = " "; temp += name; temp += '='; temp += '\"'; temp += value; temp += '\"'; xmlStr.insert(pos,temp); } else throw "attr(...) - no tag to accept attribute"; return *this; } // //----< insert payload data into element >--------------------- xmlRep& xmlRep::data(const string &payload) { if(xmlStr.size() == 0) { xmlStr = payload; return *this; } int pos = xmlStr.find_last_of("-------- xmlRep& xmlRep::closedTag(const string &tg) { string temp = string("<") += tg + string("/>"); data(temp); return *this; } //----< extract tag string >----------------------------------- // // silently does nothing if no tag // std::string xmlRep::tagStr() const { string temp; int pos = xmlStr.find('<') + 1; if(pos < xmlStr.size()) { int end = pos; while(xmlStr[end] != ' ' && xmlStr[end] != '>') { if(end >= xmlStr.size()) break; temp += xmlStr[end]; end++; } } return temp; } // //----< extract string of all attributes >--------------------- // // silently does nothing if no attributes // std::string xmlRep::attrStr() const { string temp; int tagEnd = xmlStr.find('>'); if(tagEnd < xmlStr.size()) { int pos = xmlStr.find(' ') + 1; while(xmlStr[pos] == ' ') ++pos; if(pos >= tagEnd) return temp; while(pos < tagEnd) { temp += xmlStr[pos]; pos++; } } return temp; } //----< extract payload string >------------------------------- // // silently does nothing if no payload // std::string xmlRep::dataStr() const { string temp; int tagEnd = xmlStr.find('>'); if(tagEnd < xmlStr.size()) { int dataEnd = xmlStr.find("-------------------------- xmlRep& xmlElement::data(const string &elementStr) { xmlRep::data(elementStr); return *this; } //----< insert element as payload into another element >------- xmlElement& xmlElement::data(xmlElement &element) { xmlRep::data(element.str()); return *this; } ///////////////////////////////////////////////////////////////////////////// // class xmlDocument : public xmlRep - represents an xml document // //----< insert document tag at beginning of document >--------- xmlRep& xmlDocument::tag(const std::string &tag) { xmlStr = ""; xmlStr += string("<") + tag + string(">"); return *this; } //----< insert payload into document >------------------------- xmlRep& xmlDocument::data(const string &elementStr) { xmlRep::data(elementStr); return *this; } //----< insert element as payload into document >-------------- xmlDocument& xmlDocument::data(xmlElement &element) { xmlRep::data(element.str()); return *this; } // ///////////////////////////////////////////////////////////////////////////// // class xmlFormat - formats xml representation strings for human consumption // //----< format constructor >----------------------------------- xmlFormat::xmlFormat() : pos(0), indent(0) { } //----< find next begin tag >---------------------------------- void xmlFormat::nextTag() { pos = xmlStr.find("<",pos); } //----< move to one past end of current tag >------------------ void xmlFormat::eatTag() { pos = xmlStr.find('>',pos) + 1; } //----< is this a begin tag >---------------------------------- bool xmlFormat::isBegTag() { if(isEndTag() || isClosedTag() || isPayload()) return false; return true; } //----< is this an end tag >----------------------------------- bool xmlFormat::isEndTag() { if(xmlStr[pos] == '<' && xmlStr[pos+1] == '/') return true; return false; } //----< dose a payload begin at this position? >--------------- bool xmlFormat::isPayload() { eatWhiteSpace(); if(pos >= xmlStr.size()) return false; if(xmlStr[pos] != '<') return true; return false; } // //----< discard white space >---------------------------------- void xmlFormat::eatWhiteSpace() { while(isspace(xmlStr[pos])) ++pos; } //----< is this a closed tag >--------------------------------- bool xmlFormat::isClosedTag() { if(xmlStr[pos] != '<') return false; int mark = pos; eatTag(); if(xmlStr[pos-2] == '/') { pos = mark; return true; } pos = mark; return false; } //----< insert return and indenting string >------------------- void xmlFormat::insertBreak() { xmlStr.insert(pos,"\n"); ++pos; if(indent > 0) { xmlStr.insert(pos,string(indent,' ')); pos += indent; } } // //----< format a single element >------------------------------ // // assumes that xmlStr starts with a begin tag // void xmlFormat::eatElement() { eatTag(); while(pos < xmlStr.size()) { if(isBegTag()) { indent += 2; insertBreak(); eatElement(); indent -=2; if(!isBegTag() && !isClosedTag()) { insertBreak(); } } else if(isPayload()) { indent += 2; insertBreak(); nextTag(); indent -= 2; insertBreak(); } else if(isClosedTag()) { indent += 2; insertBreak(); indent -= 2; eatTag(); } else if(isEndTag()) { eatTag(); break; } else break; // ate whole string } } //----< format input xml string for human consumption >-------- string xmlFormat::format(const string &rep) { xmlStr = rep; nextTag(); eatElement(); return xmlStr; } //----< initialize position and indent level >----------------- void xmlFormat::reset() { pos = indent = 0; } // ///////////////////////////////////////////////////////////////////////////// // class xmlParse - extracts tags, attributes, and data from xml strings // //----< strip xml declaration from string >-------------------- void xmlParse::stripDecl() { int pos = xmlStr.find("= xmlStr.size()) return; pos = xmlStr.find(">",pos+5) + 1; xmlStr.erase(0,pos); } //----< xmlParse constructor >--------------------------------- xmlParse::xmlParse(const std::string &repStr) { accept(repStr); } //----< xmlParse constructor >--------------------------------- xmlParse::xmlParse(xmlRep &rep) { accept(rep); } //----< accept new xml representation string >----------------- void xmlParse::accept(const std::string &repStr) { xmlStr = repStr; stripDecl(); } //----< accept new xml object >-------------------------------- void xmlParse::accept(xmlRep &rep) { xmlStr = rep.str(); stripDecl(); } // //----< select next sibling element >-------------------------- // // if there is a next sibling next() selects it // by erasing its predecessor and returns true, // else returns false // bool xmlParse::next() { int pos1 = xmlStr.find("/>"); int pos2 = xmlStr.find("",pos2+1); if(pos2 < xmlStr.size()) { xmlStr.erase(0,pos2+1); return (xmlStr.size() > 0); } } return false; } //----< return tag's label >----------------------------------- std::string xmlParse::tagStr() const { int pos = xmlStr.find("/>"); if(pos >= xmlStr.size()) return xmlRep::tagStr(); int tagBeg = xmlStr.find("<") + 1; int tagEnd = tagBeg+1; while( xmlStr[tagEnd] != ' ' && xmlStr[tagEnd] != '/' && xmlStr[tagEnd] != '>' ) ++tagEnd; return xmlStr.substr(tagBeg,tagEnd-tagBeg); } //----< return tag's attribute string >------------------------ std::string xmlParse::attrStr() const { return xmlRep::attrStr(); } // //----< find end of tag at current position >------------------ int xmlParse::eatTag() const { return (xmlStr.find(">") + 1); } //----< find matching end tag >-------------------------------- int xmlParse::endTag(const std::string &tag) const { string temp; temp += '/'; temp += tag; int pos = xmlStr.find(temp) - 1; const char *debug = xmlStr.c_str() + pos; temp; return pos; } //----< return payload string >-------------------------------- std::string xmlParse::dataStr() const { // element is a closed tag, e.g., < ... /> int pos1 = xmlStr.find(">"); int pos2 = xmlStr.find("/>"); if(pos2 < xmlStr.size() && pos2 <= pos1) return ""; // element is not a closed tag int begData = eatTag(); string tag = tagStr(); int endData = endTag(tag); const char *debug = xmlStr.c_str() + endData; return xmlStr.substr(begData,endData-begData); } //----< parser constructor >------------------------------------ parser::parser(const std::string &repStr) : rep(repStr) { } //----< validate string position >----------------------------- void parser::validate(int pos) { if(pos >= rep.size()) std::cout << "Unsuccessful search in parser"; } //----< find tag >--------------------------------------------- int parser::getTag(const std::string &tag) { int pos = rep.find(tag); validate(pos); if(rep[pos-1] != '<') std::cout << "Unsuccessful search in parser"; int posEnd = rep.find('>',pos); validate(posEnd); return pos+1; } //----< get payload data from tagged item >-------------------- bool parser::getData(const std::string &tag, std::string &data) { try { int pos = getTag(tag); int posBeg = rep.find('>',pos) + 1; validate(posBeg); int posEnd = rep.find("------------------------- bool parser::getFirstAttr(const std::string &tag, std::string &attr) { try { int pos = getTag(tag); int posBeg = rep.find('\"',pos) + 1; validate(posBeg); int posEnd = rep.find('\"',posBeg+1); validate(posEnd); attr = rep.substr(posBeg,posEnd-posBeg); } catch(char *msg) { std::cout << "\n " << msg << "\n"; return false; } return true; } // ///////////////////////////////////////////////////////////////////////////// // test stub #ifdef TEST_XML //----< display xml representation string >-------------------- void displayXMLstr(const char *msg, const string &str) { cout << "\n" << msg << "\n" << str << endl; } //----< display formatted xml representation >----------------- void displayXMLrep(xmlRep &rep) { cout << "\n" << rep.format() << endl; } //----< display parsed tag, attributes, and data >------------- void displayParseResults(xmlParse &parse, bool format = true) { string temp = parse.str(); temp = string("") + temp + string(""); if(format) { xmlFormat view; cout << view.format(temp); } else cout << endl << parse.str() << endl; cout << endl; cout << "\n tag = " << parse.tagStr(); cout << "\n attributes = " << parse.attrStr(); cout << "\n data = " << parse.dataStr() << endl; } //----< tests start here >------------------------------------- int main() { try { cout << "\n Test XML formatting " << "\n=====================\n"; cout << "\n formatting simple xml element " << "\n-------------------------------\n"; string xmlStr1 = "payload string"; displayXMLstr("formatting xmlStr1:",xmlStr1); xmlFormat view; cout << "\n" << view.format(xmlStr1) << endl; cout << "\n formatting compound xml element " << "\n---------------------------------\n"; string xmlStr2 = string("payload1") + string("payload4"); displayXMLstr("formatting xmlStr2:",xmlStr2); view.reset(); cout << "\n" << view.format(xmlStr2) << endl; cout << "\n formatting xml document " << "\n-------------------------\n"; string xmlStr3 = string("") + string("") + string("payload1") + string("payload4") + string(""); displayXMLstr("formatting xmlStr3:", xmlStr3); view.reset(); cout << "\n" << view.format(xmlStr3) << endl; cout << "\f\n"; // cout << "\n Test XML Representation " << "\n=========================\n"; cout << "\n Test construction " << "\n-------------------\n"; xmlRep rep1; rep1.tag("test1"); cout << rep1.str() << endl; rep1.attr("att1","5").attr("att2","a string"); cout << rep1.str() << endl; rep1.data("this is a payload string"); cout << rep1.str() << endl; displayXMLrep(rep1); cout << "\n Test rips " << "\n-----------\n"; cout << "tag = " << rep1.tagStr() << endl; cout << "attributes = " << rep1.attrStr() << endl; cout << "payload = " << rep1.dataStr() << endl; cout << "\n"; cout << "\n Test composite XML elements " << "\n=============================\n"; xmlElement el0; el0.closedTag("closedTagAtBottomLevel"); cout << el0.str() << endl; xmlElement el1; el1.tag("el1"); el1.attr("level","bottom"); el1.data(string("first element")); cout << el1.str() << endl; xmlElement el2; el2.tag("el2").attr("level","bottom"); el2.data(string("second element")); cout << el2.str() << endl; xmlElement el3; el3.tag("el3").attr("level","top"); el3.data(el0).data(el1).data(el2); cout << el3.str() << "\n"; displayXMLrep(el3); // cout << "\n Test composite XML documents " << "\n==============================\n"; xmlDocument doc1; doc1.tag("testDoc"); doc1.data(el3); cout << doc1.str() << endl; displayXMLrep(doc1); cout << "\f\n"; cout << "\n Test parsing composite XML documents " << "\n======================================\n"; cout << "\n parsing testDoc " << "\n-----------------\n"; displayXMLrep(doc1); xmlParse parse(doc1); displayParseResults(parse,false); cout << "\n parsing el3 " << "\n-------------\n"; parse.accept(parse.dataStr()); displayParseResults(parse); cout << "\f\n"; cout << "\n parsing first - closed tag " << "\n----------------------------\n"; parse.accept(parse.dataStr()); displayParseResults(parse); cout << "\n parsing second - el1 " << "\n----------------------\n"; if(parse.next()) { displayParseResults(parse); } cout << "\n parsing last - el2 " << "\n--------------------\n"; if(parse.next()) { displayParseResults(parse); } // cout << "\n attempting to parse a non-existing element at this level " << "\n----------------------------------------------------------\n"; if(parse.next()) { displayParseResults(parse); } else cout << "\n no more elements\n"; } catch(char* msg) { cout << "\n-- exception --\n" << msg << endl; } cout << endl; return 0; } #endif