00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "xmlpull/XmlSerializer.h"
00025 #include "xmlpull/XmlPullParserException.h"
00026 #include "xmlpull/XmlUtils.h"
00027
00028 XmlSerializer::XmlSerializer(std::ostream &os,std::string enc)
00029 :writer(os),
00030 encoding(enc),
00031 elementStack(16),
00032 nspStack(16),
00033 nspCounts(8),
00034 indent(8)
00035 {
00036 init();
00037 }
00038
00039 XmlSerializer::XmlSerializer(std::string enc)
00040 :writer(std::cout),
00041 encoding(enc),
00042 elementStack(16),
00043 nspStack(16),
00044 nspCounts(8),
00045 indent(8)
00046 {
00047 init();
00048 }
00049
00050 XmlSerializer::~XmlSerializer()
00051 {
00052 }
00053
00054 void
00055 XmlSerializer::init()
00056 {
00057 nspCounts[0] = 2;
00058 nspCounts[1] = 2;
00059 nspStack[0] = "";
00060 nspStack[1] = "";
00061 nspStack[2] = "xml";
00062 nspStack[3] = "http://www.w3.org/XML/1998/namespace";
00063 pending = false;
00064 auto_ = 0;
00065 depth = 0;
00066 if (encoding.find("utf",0)!=std::string::npos)
00067 unicode = true;
00068 else
00069 unicode = false;
00070 }
00071
00072 void
00073 XmlSerializer::check(bool close)
00074 {
00075 if (!pending)
00076 return;
00077 depth++;
00078 pending = false;
00079
00080 if (indent.size() <= depth) {
00081 indent.resize(depth + 4);
00082 }
00083 indent[depth] = indent[depth - 1];
00084
00085 for (int i = nspCounts[depth - 1];
00086 i < nspCounts[depth];
00087 i++) {
00088
00089 writer<<' ';
00090 writer<<"xmlns";
00091 if (""!=nspStack[i * 2]) {
00092 writer<<':';
00093 writer<<nspStack[i * 2];
00094 }
00095 else if (getNamespace() != "" && nspStack[i * 2 + 1]!="")
00096 exception("Cannot set default namespace for elements in no namespace");
00097 writer<<"=\"";
00098 writeEscaped(nspStack[i * 2 + 1], '"');
00099 writer<<'"';
00100 }
00101
00102 if (nspCounts.size() <= depth + 1) {
00103 nspCounts.resize(depth + 8);
00104 }
00105
00106 nspCounts[depth + 1] = nspCounts[depth];
00107
00108
00109 writer<<(close ? " />" : ">");
00110 }
00111
00112 void
00113 XmlSerializer::writeEscaped(std::string s, int quot)
00114 {
00115
00116 for (size_t i = 0; i < s.length(); i++) {
00117 unsigned char c = s[i];
00118 switch (c) {
00119 case '\n':
00120 case '\r':
00121 case '\t':
00122 if(quot == -1)
00123 writer<<c;
00124 else
00125 writer<<"&#"+((int) c)+';';
00126 break;
00127 case '&' :
00128 writer<<"&";
00129 break;
00130 case '>' :
00131 writer<<">";
00132 break;
00133 case '<' :
00134 writer<<"<";
00135 break;
00136 case '"' :
00137 case '\'' :
00138 if (c == quot) {
00139 writer<<(c == '"' ? """ : "'");
00140 break;
00141 }
00142 default :
00143
00144
00145
00146 if (c >= ' ' && c !='@' && (c < 127 || unicode))
00147 writer<<c;
00148 else
00149 writer<<"&#" << ((int) c) << ";";
00150 }
00151 }
00152 }
00153
00154
00155 void
00156 XmlSerializer::docdecl(std::string dd)
00157 {
00158 writer<<"<!DOCTYPE";
00159 writer<<dd;
00160 writer<<">";
00161 }
00162
00163 void
00164 XmlSerializer::endDocument() {
00165 while (depth > 0) {
00166 endTag(
00167 elementStack[depth * 3 - 3],
00168 elementStack[depth * 3 - 1]);
00169 }
00170 flush();
00171 }
00172
00173 void
00174 XmlSerializer::entityRef(std::string name)
00175 {
00176 check(false);
00177 writer<<'&';
00178 writer<<name;
00179 writer<<';';
00180 }
00181
00182 bool
00183 XmlSerializer:: getFeature(std::string name) {
00184
00185 return ("http://xmlpull.org/v1/doc/features.html#indent-output" == name)
00186 ? indent[depth]
00187 : false;
00188 }
00189
00190 std::string
00191 XmlSerializer::getPrefix(std::string ns, bool create)
00192 {
00193 return getPrefix(ns, false, create);
00194 }
00195
00196
00197 std::string
00198 XmlSerializer::getPrefix(std::string ns,
00199 bool includeDefault,
00200 bool create)
00201 {
00202
00203 for (int i = nspCounts[depth + 1] * 2 - 2;
00204 i >= 0;
00205 i -= 2) {
00206
00207 if (nspStack[i + 1] == ns
00208 && (includeDefault || nspStack[i]!="")) {
00209 std::string cand = nspStack[i];
00210 for (int j = i + 2;
00211 j < nspCounts[depth + 1] * 2;
00212 j++) {
00213 if (nspStack[j]==cand) {
00214 cand = "";
00215 break;
00216 }
00217 }
00218 if (cand != "")
00219 return cand;
00220 }
00221 }
00222
00223 if (!create)
00224 return "";
00225
00226 std::string prefix;
00227
00228 if (ns=="")
00229 prefix = "";
00230 else {
00231 do {
00232 prefix = "n" + (auto_++);
00233 for (int i = nspCounts[depth + 1] * 2 - 2;
00234 i >= 0;
00235 i -= 2) {
00236 if (prefix==nspStack[i]) {
00237 prefix = "";
00238 break;
00239 }
00240 }
00241 }
00242 while (prefix == "");
00243 }
00244
00245 bool p = pending;
00246 pending = false;
00247 setPrefix(prefix, ns);
00248 pending = p;
00249 return prefix;
00250 }
00251
00252 void
00253 XmlSerializer::ignorableWhitespace(std::string s)
00254 {
00255 text(s);
00256 }
00257
00258 void
00259 XmlSerializer::setFeature(std::string name, bool value)
00260 {
00261 if ("http://xmlpull.org/v1/doc/features.html#indent-output"==name) {
00262 indent[depth] = value;
00263 }
00264 else
00265 exception("Unsupported Feature");
00266 }
00267
00268 void
00269 XmlSerializer::setPrefix(std::string prefix, std::string nsp)
00270 {
00271 check(false);
00272 std::string defined = getPrefix(nsp, true, false);
00273
00274
00275
00276 if (prefix==defined)
00277 return;
00278
00279 int pos = (nspCounts[depth + 1]++) << 1;
00280
00281 if (nspStack.size() < pos + 1) {
00282 nspStack.resize(nspStack.size() + 16);
00283 }
00284
00285 nspStack[pos++] = prefix;
00286 nspStack[pos] = nsp;
00287 }
00288
00289 void
00290 XmlSerializer::startDocument(std::string enc,
00291 bool standalone)
00292 {
00293 writer<<"<?xml version='1.0' ";
00294
00295 if (encoding != "") {
00296 this->encoding = enc;
00297 if (encoding.find("utf",0)!=std::string::npos)
00298 unicode = true;
00299 }
00300
00301 if (encoding != "") {
00302 writer<<"encoding='";
00303 writer<<encoding;
00304 writer<<"' ";
00305 }
00306
00307 writer<<"standalone='";
00308 writer<<(standalone ? "yes" : "no");
00309 writer<<"' ";
00310 writer<<"?>";
00311 }
00312
00313 XmlSerializer&
00314 XmlSerializer::startTag(std::string nsp, std::string name)
00315 {
00316 check(false);
00317
00318
00319
00320
00321 if (indent[depth]) {
00322 writer<<"\r\n";
00323 for (int i = 0; i < depth; i++)
00324 writer<<" ";
00325 }
00326
00327 int esp = depth * 3;
00328 if (elementStack.size() < esp + 3) {
00329 elementStack.resize(elementStack.size() + 16);
00330 }
00331
00332 std::string prefix =
00333 nsp == ""
00334 ? ""
00335 : getPrefix(nsp, true, true);
00336
00337 if (nsp=="") {
00338 for (int i = nspCounts[depth];
00339 i < nspCounts[depth + 1];
00340 i++) {
00341
00342 if (nspStack[i * 2]== "" && nspStack[i * 2 + 1]!= "") {
00343 exception("Cannot set default namespace for elements in no namespace");
00344 }
00345 }
00346 }
00347
00348 elementStack[esp++] = nsp;
00349 elementStack[esp++] = prefix;
00350 elementStack[esp] = name;
00351
00352 writer<<'<';
00353 if (prefix!="") {
00354 writer<<prefix;
00355 writer<<':';
00356 }
00357
00358 writer<<name;
00359
00360 pending = true;
00361 return *this;
00362 }
00363
00364 XmlSerializer&
00365 XmlSerializer::attribute(std::string nsp,
00366 std::string name,
00367 std::string value)
00368 {
00369 if (!pending)
00370 exception("illegal position for attribute");
00371
00372
00373
00374 if (nsp == "")
00375 nsp = "";
00376
00377
00378
00379
00380 std::string prefix =(nsp=="")? "": getPrefix(nsp, false, true);
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398 writer<<' ';
00399 if (prefix!="") {
00400 writer<<prefix;
00401 writer<<':';
00402 }
00403 writer<<name;
00404 writer<<'=';
00405 char q = value.find('"') == std::string::npos ? '"' : '\'';
00406 writer<<q;
00407 writeEscaped(value, q);
00408 writer<<q;
00409 return *this;
00410 }
00411
00412 void
00413 XmlSerializer::flush()
00414 {
00415 check(false);
00416 writer.flush();
00417 }
00418
00419 XmlSerializer&
00420 XmlSerializer::endTag(std::string nsp, std::string name)
00421 {
00422
00423 if (!pending)
00424 depth--;
00425
00426
00427
00428 if ((nsp == ""
00429 && elementStack[depth * 3] != "")
00430 || (nsp != ""
00431 && nsp!=elementStack[depth * 3])
00432 || elementStack[depth * 3 + 2] != name)
00433
00434 exception("</{"+nsp+"}"+name+"> does not match start");
00435
00436 if (pending) {
00437 check(true);
00438 depth--;
00439 }
00440 else {
00441 if (indent[depth + 1]) {
00442 writer<<"\r\n";
00443 for (int i = 0; i < depth; i++)
00444 writer<<" ";
00445 }
00446
00447 writer<<"</";
00448 std::string prefix = elementStack[depth * 3 + 1];
00449 if (prefix != "") {
00450 writer<<prefix;
00451 writer<<':';
00452 }
00453 writer<<name;
00454 writer<<'>';
00455 }
00456
00457 nspCounts[depth + 1] = nspCounts[depth];
00458 return *this;
00459 }
00460
00461
00462 std::string
00463 XmlSerializer::getNamespace()
00464 {
00465 return getDepth() == 0 ? "" : elementStack[getDepth() * 3 - 3];
00466 }
00467
00468 std::string
00469 XmlSerializer::getName()
00470 {
00471 return getDepth() == 0 ? "" : elementStack[getDepth() * 3 - 1];
00472 }
00473
00474 int
00475 XmlSerializer::getDepth() {
00476 return pending ? depth + 1 : depth;
00477 }
00478
00479 XmlSerializer&
00480 XmlSerializer::text(std::string txt){
00481 check(false);
00482 indent[depth] = false;
00483 writeEscaped(txt, -1);
00484 return *this;
00485 }
00486
00487 XmlSerializer&
00488 XmlSerializer::text(std::string txt, int start, int len)
00489 {
00490 text(txt.substr(start, len));
00491 return *this;
00492 }
00493
00494 void
00495 XmlSerializer::cdsect(std::string data)
00496 {
00497 check(false);
00498 writer<<"<![CDATA[";
00499 writer<<data;
00500 writer<<"]]>";
00501 }
00502
00503 void
00504 XmlSerializer::comment(std::string comment) {
00505 check(false);
00506 writer<<"<!--";
00507 writer<<comment;
00508 writer<<"-->";
00509 }
00510
00511 void
00512 XmlSerializer::processingInstruction(std::string pi)
00513 {
00514 check(false);
00515 writer<<"<?";
00516 writer<<pi;
00517 writer<<"?>";
00518 }
00519
00520 void
00521 XmlSerializer::exception (std::string desc)
00522 {
00523 XmlPullParserException e (desc,elementStack.front(),0,0);
00524 throw e;
00525 }