00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "adchpp.h"
00020
00021 #include "SimpleXML.h"
00022
00023 namespace adchpp {
00024
00025 using namespace std;
00026
00027 SimpleXML::SimpleXML(int numAttribs) : attribs(numAttribs), found(false) {
00028 root = current = new Tag("BOGUSROOT", Util::emptyString, NULL);
00029 }
00030
00031 SimpleXML::~SimpleXML() {
00032 delete root;
00033 }
00034
00035 void SimpleXML::escape(string& aString, bool aAttrib, bool aLoading ) {
00036 string::size_type i = 0;
00037 const char* chars = aAttrib ? "<&>'\"" : "<&>";
00038
00039 if(aLoading) {
00040 while((i = aString.find('&', i)) != string::npos) {
00041 if(aString.compare(i+1, 3, "lt;") == 0) {
00042 aString.replace(i, 4, 1, '<');
00043 } else if(aString.compare(i+1, 4, "amp;") == 0) {
00044 aString.replace(i, 5, 1, '&');
00045 } else if(aString.compare(i+1, 3, "gt;") == 0) {
00046 aString.replace(i, 4, 1, '>');
00047 } else if(aAttrib) {
00048 if(aString.compare(i+1, 5, "apos;") == 0) {
00049 aString.replace(i, 6, 1, '\'');
00050 } else if(aString.compare(i+1, 5, "quot;") == 0) {
00051 aString.replace(i, 6, 1, '"');
00052 }
00053 }
00054 i++;
00055 }
00056 i = 0;
00057 if( (i = aString.find('\n')) != string::npos) {
00058 if(i > 0 && aString[i-1] != '\r') {
00059
00060 i = 0;
00061 while( (i = aString.find('\n', i) ) != string::npos) {
00062 if(aString[i-1] != '\r')
00063 aString.insert(i, 1, '\r');
00064
00065 i+=2;
00066 }
00067 }
00068 }
00069 } else {
00070 while( (i = aString.find_first_of(chars, i)) != string::npos) {
00071 switch(aString[i]) {
00072 case '<': aString.replace(i, 1, "<"); i+=4; break;
00073 case '&': aString.replace(i, 1, "&"); i+=5; break;
00074 case '>': aString.replace(i, 1, ">"); i+=4; break;
00075 case '\'': aString.replace(i, 1, "'"); i+=6; break;
00076 case '"': aString.replace(i, 1, """); i+=6; break;
00077 default: dcasserta(0);
00078 }
00079 }
00080 }
00081 }
00082
00083 void SimpleXML::Tag::appendAttribString(string& tmp) {
00084 for(AttribIter i = attribs.begin(); i!= attribs.end(); ++i) {
00085 tmp.append(i->first);
00086 tmp.append("=\"", 2);
00087 if(needsEscape(i->second, true)) {
00088 string tmp2(i->second);
00089 escape(tmp2, true);
00090 tmp.append(tmp2);
00091 } else {
00092 tmp.append(i->second);
00093 }
00094 tmp.append("\" ", 2);
00095 }
00096 tmp.erase(tmp.size()-1);
00097 }
00098
00099 string SimpleXML::Tag::toXML(int indent) {
00100 if(children.empty() && data.empty()) {
00101 string tmp;
00102 tmp.reserve(indent + name.length() + 30);
00103 tmp.append(indent, '\t');
00104 tmp.append(1, '<');
00105 tmp.append(name);
00106 tmp.append(1, ' ');
00107 appendAttribString(tmp);
00108 tmp.append("/>\r\n", 4);
00109 return tmp;
00110 } else {
00111 string tmp;
00112 tmp.append(indent, '\t');
00113 tmp.append(1, '<');
00114 tmp.append(name);
00115 tmp.append(1, ' ');
00116 appendAttribString(tmp);
00117 if(children.empty()) {
00118 tmp.append(1, '>');
00119 if(needsEscape(data, false)) {
00120 string tmp2(data);
00121 escape(tmp2, false);
00122 tmp.append(tmp2);
00123 } else {
00124 tmp.append(data);
00125 }
00126 } else {
00127 tmp.append(">\r\n", 3);
00128 for(Iter i = children.begin(); i!=children.end(); ++i) {
00129 tmp.append((*i)->toXML(indent + 1));
00130 }
00131 tmp.append(indent, '\t');
00132 }
00133 tmp.append("</", 2);
00134 tmp.append(name);
00135 tmp.append(">\r\n", 3);
00136 return tmp;
00137 }
00138 }
00139
00140 bool SimpleXML::findChild(const string& aName) const throw() {
00141 dcassert(current != NULL);
00142
00143 if(found && currentChild != current->children.end())
00144 currentChild++;
00145
00146 while(currentChild!=current->children.end()) {
00147 if(aName.empty() || (*currentChild)->name == aName) {
00148 found = true;
00149 return true;
00150 } else
00151 currentChild++;
00152 }
00153 return false;
00154 }
00155
00156 void SimpleXML::stepIn() const throw(SimpleXMLException) {
00157 checkChildSelected();
00158 current = *currentChild;
00159 currentChild = current->children.begin();
00160 found = false;
00161 }
00162
00163 void SimpleXML::stepOut() const throw(SimpleXMLException) {
00164 if(current == root)
00165 throw SimpleXMLException("Already at lowest level");
00166
00167 dcassert(current->parent != NULL);
00168
00169 currentChild = find(current->parent->children.begin(), current->parent->children.end(), current);
00170
00171 current = current->parent;
00172 found = true;
00173 }
00174
00175 string::size_type SimpleXML::Tag::loadAttribs(const string& tmp, string::size_type start) throw(SimpleXMLException) {
00176 string::size_type i = start;
00177 string::size_type j;
00178 for(;;) {
00179 j = tmp.find('=', i);
00180 if(j == string::npos) {
00181 throw SimpleXMLException("Missing '=' in " + name);
00182 }
00183 if(tmp[j+1] != '"' && tmp[j+1] != '\'') {
00184 throw SimpleXMLException("Invalid character after '=' in " + name);
00185 }
00186 string::size_type x = j + 2;
00187 string::size_type y = tmp.find(tmp[j+1], x);
00188 if(y == string::npos) {
00189 throw SimpleXMLException("Missing '" + string(1, tmp[j+1]) + "' in " + name);
00190 }
00191
00192 attribs.push_back(make_pair(tmp.substr(i, j-i), tmp.substr(x, y-x)));
00193 escape(attribs.back().second, true, true);
00194
00195 i = tmp.find_first_not_of("\r\n\t ", y + 1);
00196 if(tmp[i] == '/' || tmp[i] == '>')
00197 return i;
00198 }
00199 }
00200
00201 string::size_type SimpleXML::Tag::fromXML(const string& tmp, string::size_type start, int aa, bool isRoot ) throw(SimpleXMLException) {
00202 string::size_type i = start;
00203 string::size_type j;
00204
00205 bool hasChildren = false;
00206 dcassert(tmp.size() > 0);
00207
00208 for(;;) {
00209 j = tmp.find('<', i);
00210 if(j == string::npos) {
00211 if(isRoot) {
00212 throw SimpleXMLException("Invalid XML file, missing root tag");
00213 } else {
00214 throw SimpleXMLException("Missing end tag in " + name);
00215 }
00216 }
00217
00218
00219 if((j + 3) > tmp.size()) {
00220 throw SimpleXMLException("Missing end tag in " + name);
00221 }
00222
00223 Ptr child = NULL;
00224
00225 i = j + 1;
00226
00227 if(tmp[i] == '?') {
00228
00229 i = tmp.find("?>", i);
00230 if(i == string::npos) {
00231 throw SimpleXMLException("Missing '?>' in " + name);
00232 }
00233 i+= 2;
00234 continue;
00235 }
00236
00237 if(tmp[i] == '!' && tmp[i+1] == '-' && tmp[i+2] == '-') {
00238
00239 i = tmp.find("-->", i);
00240 if(i == string::npos) {
00241 throw SimpleXMLException("Missing '-->' in " + name);
00242 }
00243 continue;
00244 }
00245
00246
00247 if(tmp[i] == '/') {
00248 i++;
00249 if( (tmp.compare(i, name.length(), name) == 0) &&
00250 (tmp[i + name.length()] == '>') )
00251 {
00252 if(!hasChildren) {
00253 data = tmp.substr(start, i - start - 2);
00254 escape(data, false, true);
00255 }
00256 return i + name.length() + 1;
00257 } else {
00258 throw SimpleXMLException("Missing end tag in " + name);
00259 }
00260 }
00261
00262
00263 j = tmp.find_first_of("\r\n\t />", i);
00264 if(j == string::npos) {
00265 throw SimpleXMLException("Missing '>' in " + name);
00266 }
00267
00268 child = new Tag(tmp.substr(i, j-i), Util::emptyString, this, aa);
00269
00270 children.push_back(child);
00271
00272 if(tmp[j] == ' ')
00273 j = tmp.find_first_not_of("\r\n\t ", j+1);
00274
00275 if(j == string::npos) {
00276 throw SimpleXMLException("Missing '>' in " + name);
00277 }
00278
00279 if(tmp[j] != '/' && tmp[j] != '>') {
00280
00281 j = child->loadAttribs(tmp, j);
00282 }
00283
00284 if(tmp[j] == '>') {
00285
00286 hasChildren = true;
00287 j = child->fromXML(tmp, j+1, aa);
00288 } else {
00289
00290 j++;
00291 }
00292 i = j;
00293 if(isRoot) {
00294 if(tmp.find('<', i) != string::npos) {
00295 throw SimpleXMLException("Invalid XML file, multiple root tags");
00296 }
00297 return tmp.length();
00298 }
00299 }
00300 }
00301
00302 void SimpleXML::addTag(const string& aName, const string& aData ) throw(SimpleXMLException) {
00303 if(aName.empty()) {
00304 throw SimpleXMLException("Empty tag names not allowed");
00305 }
00306
00307 if(current == root) {
00308 if(current->children.empty()) {
00309 current->children.push_back(new Tag(aName, aData, root, attribs));
00310 currentChild = current->children.begin();
00311 } else {
00312 throw SimpleXMLException("Only one root tag allowed");
00313 }
00314 } else {
00315 current->children.push_back(new Tag(aName, aData, current, attribs));
00316 currentChild = current->children.end() - 1;
00317 }
00318 }
00319
00320 void SimpleXML::addAttrib(const string& aName, const string& aData) throw(SimpleXMLException) {
00321 if(current==root)
00322 throw SimpleXMLException("No tag is currently selected");
00323
00324 current->attribs.push_back(make_pair(aName, aData));
00325 }
00326
00327 void SimpleXML::addChildAttrib(const string& aName, const string& aData) throw(SimpleXMLException) {
00328 checkChildSelected();
00329
00330 (*currentChild)->attribs.push_back(make_pair(aName, aData));
00331 }
00332
00333 void SimpleXML::fromXML(const string& aXML) throw(SimpleXMLException) {
00334 if(root) {
00335 delete root;
00336 }
00337 root = new Tag("BOGUSROOT", Util::emptyString, NULL, 0);
00338
00339 root->fromXML(aXML, 0, attribs, true);
00340
00341 if(root->children.size() != 1) {
00342 throw SimpleXMLException("Invalid XML file, missing or multiple root tags");
00343 }
00344
00345 current = root;
00346 resetCurrentChild();
00347 }
00348
00349 }