Vault  4.1
vsettings.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved.
00003 This file is part of the Code Vault version 4.1
00004 http://www.bombaydigital.com/
00005 License: MIT. See LICENSE.md in the Vault top level directory.
00006 */
00007 
00010 #include "vsettings.h"
00011 
00012 #include "vexception.h"
00013 #include "vmemorystream.h"
00014 #include "vtextiostream.h"
00015 #include "vbinaryiostream.h"
00016 #include "vchar.h"
00017 #include "vbento.h"
00018 #include "vfilewriter.h"
00019 
00020 V_STATIC_INIT_TRACE
00021 
00022 #undef strlen
00023 
00024 // VSettingsNode ------------------------------------------------------------------
00025 
00026 VSettingsNode::VSettingsNode(VSettingsTag* parent, const VString& name)
00027     : mParent(parent)
00028     , mName(name)
00029     {
00030 }
00031 
00032 VSettingsNode::VSettingsNode(const VSettingsNode& other)
00033     : mParent(other.mParent)
00034     , mName(other.mName)
00035     {
00036 }
00037 
00038 VSettingsNode& VSettingsNode::operator=(const VSettingsNode& other) {
00039     mParent = other.mParent;
00040     mName = other.mName;
00041 
00042     return *this;
00043 }
00044 
00045 const VSettingsNode* VSettingsNode::findNode(const VString& path) const {
00046     if (path.isEmpty()) {
00047         return this;
00048     }
00049 
00050     VString nextNodeName;
00051     VString theRemainder;
00052 
00053     VSettings::splitPathFirst(path, nextNodeName, theRemainder);
00054 
00055     if (theRemainder.isEmpty()) {
00056         VSettingsAttribute*    attribute = this->_findAttribute(nextNodeName);
00057         if (attribute != NULL) {
00058             return attribute;
00059         }
00060     }
00061 
00062     VSettingsTag* child = this->_findChildTag(nextNodeName);
00063     if (child != NULL) {
00064         return child->findNode(theRemainder);
00065     } else {
00066         return NULL;
00067     }
00068 }
00069 
00070 VSettingsNode* VSettingsNode::findMutableNode(const VString& path) {
00071     return const_cast<VSettingsNode*>(this->findNode(path)); // const_cast: NON-CONST WRAPPER
00072 }
00073 
00074 int VSettingsNode::countNodes(const VString& path) const {
00075     int     result = 0;
00076     VString leadingPath;
00077     VString lastNode;
00078 
00079     VSettings::splitPathLast(path, leadingPath, lastNode);
00080 
00081     const VSettingsNode* parent = this->findNode(leadingPath);
00082 
00083     if (parent != NULL) {
00084         result = parent->countNamedChildren(lastNode);
00085     }
00086 
00087     return result;
00088 }
00089 
00090 void VSettingsNode::deleteNode(const VString& path) {
00091     VString leadingPath;
00092     VString lastNode;
00093 
00094     VSettings::splitPathLast(path, leadingPath, lastNode);
00095 
00096     VSettingsNode* parent = this->findMutableNode(leadingPath);
00097 
00098     if (parent != NULL) {
00099         parent->deleteNamedChildren(lastNode);
00100     } else if (leadingPath.isEmpty()) {
00101         this->deleteNamedChildren(lastNode);
00102     }
00103 }
00104 
00105 const VString& VSettingsNode::getName() const {
00106     return mName;
00107 }
00108 
00109 VString VSettingsNode::getPath() const {
00110     if (mParent == NULL) {
00111         return mName;
00112     }
00113 
00114     VString path = mParent->getPath();
00115     path += kPathDelimiterChar;
00116     path += mName;
00117     return path;
00118 }
00119 
00120 bool VSettingsNode::isNamed(const VString& name) const {
00121     return mName == name;
00122 }
00123 
00124 int VSettingsNode::getInt(const VString& path, int defaultValue) const {
00125     const VSettingsNode* nodeForPath = this->findNode(path);
00126 
00127     if (nodeForPath != NULL)
00128         return nodeForPath->getIntValue();
00129     else
00130         return defaultValue;
00131 }
00132 
00133 int VSettingsNode::getInt(const VString& path) const {
00134     const VSettingsNode* nodeForPath = this->findNode(path);
00135 
00136     if (nodeForPath != NULL)
00137         return nodeForPath->getIntValue();
00138 
00139     this->throwNotFound("Integer", path);
00140 
00141     return 0; // (will never reach this statement because of throw)
00142 }
00143 
00144 int VSettingsNode::getIntValue() const {
00145     return static_cast<int>(this->getS64Value());
00146 }
00147 
00148 Vs64 VSettingsNode::getS64(const VString& path, Vs64 defaultValue) const {
00149     const VSettingsNode* nodeForPath = this->findNode(path);
00150 
00151     if (nodeForPath != NULL)
00152         return nodeForPath->getS64Value();
00153     else
00154         return defaultValue;
00155 }
00156 
00157 Vs64 VSettingsNode::getS64(const VString& path) const {
00158     const VSettingsNode* nodeForPath = this->findNode(path);
00159 
00160     if (nodeForPath != NULL)
00161         return nodeForPath->getS64Value();
00162 
00163     this->throwNotFound("Integer", path);
00164 
00165     return 0; // (will never reach this statement because of throw)
00166 }
00167 
00168 bool VSettingsNode::getBoolean(const VString& path, bool defaultValue) const {
00169     const VSettingsNode* nodeForPath = this->findNode(path);
00170 
00171     if (nodeForPath != NULL)
00172         return nodeForPath->getBooleanValue();
00173     else
00174         return defaultValue;
00175 }
00176 
00177 bool VSettingsNode::getBoolean(const VString& path) const {
00178     const VSettingsNode* nodeForPath = this->findNode(path);
00179 
00180     if (nodeForPath != NULL)
00181         return nodeForPath->getBooleanValue();
00182 
00183     this->throwNotFound("Boolean", path);
00184 
00185     return false; // (will never reach this statement because of throw)
00186 }
00187 
00188 VString VSettingsNode::getString(const VString& path, const VString& defaultValue) const {
00189     const VSettingsNode* nodeForPath = this->findNode(path);
00190 
00191     if (nodeForPath != NULL)
00192         return nodeForPath->getStringValue();
00193     else
00194         return defaultValue;
00195 }
00196 
00197 VString VSettingsNode::getString(const VString& path) const {
00198     const VSettingsNode* nodeForPath = this->findNode(path);
00199 
00200     if (nodeForPath != NULL)
00201         return nodeForPath->getStringValue();
00202     else
00203         this->throwNotFound("String", path);
00204 
00205     return VString::EMPTY(); // (will never reach this statement because of throw)
00206 }
00207 
00208 VDouble VSettingsNode::getDouble(const VString& path, VDouble defaultValue) const {
00209     const VSettingsNode* nodeForPath = this->findNode(path);
00210 
00211     if (nodeForPath != NULL)
00212         return nodeForPath->getDoubleValue();
00213     else
00214         return defaultValue;
00215 }
00216 
00217 VDouble VSettingsNode::getDouble(const VString& path) const {
00218     const VSettingsNode* nodeForPath = this->findNode(path);
00219 
00220     if (nodeForPath != NULL)
00221         return nodeForPath->getDoubleValue();
00222 
00223     this->throwNotFound("Double", path);
00224 
00225     return 0.0; // (will never reach this statement because of throw)
00226 }
00227 
00228 VSize VSettingsNode::getSize(const VString& path, const VSize& defaultValue) const {
00229     const VSettingsNode* nodeForPath = this->findNode(path);
00230 
00231     if (nodeForPath != NULL)
00232         return nodeForPath->getSizeValue();
00233     else
00234         return defaultValue;
00235 }
00236 
00237 VSize VSettingsNode::getSize(const VString& path) const {
00238     const VSettingsNode* nodeForPath = this->findNode(path);
00239 
00240     if (nodeForPath != NULL)
00241         return nodeForPath->getSizeValue();
00242 
00243     this->throwNotFound("Size", path);
00244 
00245     return VSize(); // (will never reach this statement because of throw)
00246 }
00247 
00248 VPoint VSettingsNode::getPoint(const VString& path, const VPoint& defaultValue) const {
00249     const VSettingsNode* nodeForPath = this->findNode(path);
00250 
00251     if (nodeForPath != NULL)
00252         return nodeForPath->getPointValue();
00253     else
00254         return defaultValue;
00255 }
00256 
00257 VPoint VSettingsNode::getPoint(const VString& path) const {
00258     const VSettingsNode* nodeForPath = this->findNode(path);
00259 
00260     if (nodeForPath != NULL)
00261         return nodeForPath->getPointValue();
00262 
00263     this->throwNotFound("Point", path);
00264 
00265     return VPoint(); // (will never reach this statement because of throw)
00266 }
00267 
00268 VRect VSettingsNode::getRect(const VString& path, const VRect& defaultValue) const {
00269     const VSettingsNode* nodeForPath = this->findNode(path);
00270 
00271     if (nodeForPath != NULL)
00272         return nodeForPath->getRectValue();
00273     else
00274         return defaultValue;
00275 }
00276 
00277 VRect VSettingsNode::getRect(const VString& path) const {
00278     const VSettingsNode* nodeForPath = this->findNode(path);
00279 
00280     if (nodeForPath != NULL)
00281         return nodeForPath->getRectValue();
00282 
00283     this->throwNotFound("Rect", path);
00284 
00285     return VRect(); // (will never reach this statement because of throw)
00286 }
00287 
00288 VPolygon VSettingsNode::getPolygon(const VString& path, const VPolygon& defaultValue) const {
00289     const VSettingsNode* nodeForPath = this->findNode(path);
00290 
00291     if (nodeForPath != NULL)
00292         return nodeForPath->getPolygonValue();
00293     else
00294         return defaultValue;
00295 }
00296 
00297 VPolygon VSettingsNode::getPolygon(const VString& path) const {
00298     const VSettingsNode* nodeForPath = this->findNode(path);
00299 
00300     if (nodeForPath != NULL)
00301         return nodeForPath->getPolygonValue();
00302 
00303     this->throwNotFound("Polygon", path);
00304 
00305     return VPolygon(); // (will never reach this statement because of throw)
00306 }
00307 
00308 VColor VSettingsNode::getColor(const VString& path, const VColor& defaultValue) const {
00309     const VSettingsNode* nodeForPath = this->findNode(path);
00310 
00311     if (nodeForPath != NULL)
00312         return nodeForPath->getColorValue();
00313     else
00314         return defaultValue;
00315 }
00316 
00317 VColor VSettingsNode::getColor(const VString& path) const {
00318     const VSettingsNode* nodeForPath = this->findNode(path);
00319 
00320     if (nodeForPath != NULL)
00321         return nodeForPath->getColorValue();
00322 
00323     this->throwNotFound("Color", path);
00324 
00325     return VColor(); // (will never reach this statement because of throw)
00326 }
00327 
00328 VDuration VSettingsNode::getDuration(const VString& path, const VDuration& defaultValue) const {
00329     const VSettingsNode* nodeForPath = this->findNode(path);
00330 
00331     if (nodeForPath != NULL)
00332         return nodeForPath->getDurationValue();
00333     else
00334         return defaultValue;
00335 }
00336 
00337 VDuration VSettingsNode::getDuration(const VString& path) const {
00338     const VSettingsNode* nodeForPath = this->findNode(path);
00339 
00340     if (nodeForPath != NULL)
00341         return nodeForPath->getDurationValue();
00342 
00343     this->throwNotFound("Duration", path);
00344 
00345     return VDuration(); // (will never reach this statement because of throw)
00346 }
00347 
00348 VDate VSettingsNode::getDate(const VString& path, const VDate& defaultValue) const {
00349     const VSettingsNode* nodeForPath = this->findNode(path);
00350 
00351     if (nodeForPath != NULL)
00352         return nodeForPath->getDateValue();
00353     else
00354         return defaultValue;
00355 }
00356 
00357 VDate VSettingsNode::getDate(const VString& path) const {
00358     const VSettingsNode* nodeForPath = this->findNode(path);
00359 
00360     if (nodeForPath != NULL)
00361         return nodeForPath->getDateValue();
00362 
00363     this->throwNotFound("Date", path);
00364 
00365     return VDate(); // (will never reach this statement because of throw)
00366 }
00367 
00368 VInstant VSettingsNode::getInstant(const VString& path, const VInstant& defaultValue) const {
00369     const VSettingsNode* nodeForPath = this->findNode(path);
00370 
00371     if (nodeForPath != NULL)
00372         return nodeForPath->getInstantValue();
00373     else
00374         return defaultValue;
00375 }
00376 
00377 VInstant VSettingsNode::getInstant(const VString& path) const {
00378     const VSettingsNode* nodeForPath = this->findNode(path);
00379 
00380     if (nodeForPath != NULL)
00381         return nodeForPath->getInstantValue();
00382 
00383     this->throwNotFound("Instant", path);
00384 
00385     return VInstant(); // (will never reach this statement because of throw)
00386 }
00387 
00388 bool VSettingsNode::nodeExists(const VString& path) const {
00389     return (this->findNode(path) != NULL);
00390 }
00391 
00392 void VSettingsNode::addIntValue(const VString& path, int value) {
00393     this->addStringValue(path, VSTRING_INT(value));
00394 }
00395 
00396 void VSettingsNode::addS64Value(const VString& path, Vs64 value) {
00397     this->addStringValue(path, VSTRING_S64(value));
00398 }
00399 
00400 void VSettingsNode::addBooleanValue(const VString& path, bool value) {
00401     this->addStringValue(path, VSTRING_BOOL(value));
00402 }
00403 
00404 void VSettingsNode::addStringValue(const VString& path, const VString& value) {
00405     this->add(path, true, value);
00406 }
00407 
00408 void VSettingsNode::addDoubleValue(const VString& path, VDouble value) {
00409     this->addStringValue(path, VSTRING_DOUBLE(value));
00410 }
00411 
00412 void VSettingsNode::addSizeValue(const VString& path, const VSize& value) {
00413     this->addDoubleValue(path + "/width", value.getWidth());
00414     this->addDoubleValue(path + "/height", value.getHeight());
00415 }
00416 
00417 void VSettingsNode::addPointValue(const VString& path, const VPoint& value) {
00418     this->addDoubleValue(path + "/x", value.getX());
00419     this->addDoubleValue(path + "/y", value.getY());
00420 }
00421 
00422 void VSettingsNode::addRectValue(const VString& path, const VRect& value) {
00423     this->addDoubleValue(path + "/position/x", value.getLeft());
00424     this->addDoubleValue(path + "/position/y", value.getTop());
00425     this->addDoubleValue(path + "/size/width", value.getWidth());
00426     this->addDoubleValue(path + "/size/height", value.getHeight());
00427 }
00428 
00429 void VSettingsNode::addPolygonValue(const VString& path, const VPolygon& value) {
00430     this->add(path + "/dummy-sub1/sub2", false, VString(/*dummy*/)); // creates polygon node, need to add points to it next
00431     this->deleteNode(path + "/dummy-sub1"); // dummy-sub1/sub2 hack to workaround API limitation of creating desired hierarchy
00432     VSettingsNode* polygonNode = this->findMutableNode(path);
00433 
00434     const VPointVector& points = value.getPoints();
00435     for (VPointVector::const_iterator i = points.begin(); i != points.end(); ++i) {
00436         VSettingsTag* pointNode = new VSettingsTag(static_cast<VSettingsTag*>(polygonNode), "point");
00437         polygonNode->addChildNode(pointNode);
00438         pointNode->addDoubleValue("x", (*i).getX());
00439         pointNode->addDoubleValue("y", (*i).getY());
00440     }
00441 }
00442 
00443 void VSettingsNode::addColorValue(const VString& path, const VColor& value) {
00444     VString valueString(VSTRING_ARGS("#%02x%02x%02x", (Vu8)value.getRed(), (Vu8)value.getGreen(), (Vu8)value.getBlue()));
00445     this->addStringValue(path, valueString);
00446 }
00447 
00448 void VSettingsNode::addDurationValue(const VString& path, const VDuration& value) {
00449     VString valueString(VSTRING_ARGS(VSTRING_FORMATTER_S64 "ms", value.getDurationMilliseconds()));
00450     this->addStringValue(path, valueString);
00451 }
00452 
00453 void VSettingsNode::addItem(const VString& path) {
00454     VString dummy;
00455     this->add(path, false, dummy);
00456 }
00457 
00458 void VSettingsNode::setIntValue(const VString& path, int value) {
00459     this->setStringValue(path, VSTRING_INT(value));
00460 }
00461 
00462 void VSettingsNode::setBooleanValue(const VString& path, bool value) {
00463     this->setStringValue(path, VSTRING_BOOL(value));
00464 }
00465 
00466 void VSettingsNode::setStringValue(const VString& path, const VString& value) {
00467     VSettingsNode* node = this->findMutableNode(path);
00468 
00469     if (node == NULL)
00470         this->addStringValue(path, value);
00471     else
00472         node->setLiteral(value);
00473 }
00474 
00475 void VSettingsNode::setDoubleValue(const VString& path, VDouble value) {
00476     this->setStringValue(path, VSTRING_DOUBLE(value));
00477 }
00478 
00479 void VSettingsNode::setSizeValue(const VString& path, const VSize& value) {
00480     this->deleteNode(path);
00481     this->addSizeValue(path, value);
00482 }
00483 
00484 void VSettingsNode::setPointValue(const VString& path, const VPoint& value) {
00485     this->deleteNode(path);
00486     this->addPointValue(path, value);
00487 }
00488 
00489 void VSettingsNode::setRectValue(const VString& path, const VRect& value) {
00490     this->deleteNode(path);
00491     this->addRectValue(path, value);
00492 }
00493 
00494 void VSettingsNode::setPolygonValue(const VString& path, const VPolygon& value) {
00495     this->deleteNode(path);
00496     this->addPolygonValue(path, value);
00497 }
00498 
00499 void VSettingsNode::setColorValue(const VString& path, const VColor& value) {
00500     this->setStringValue(path, value.getCSSColor());
00501 }
00502 
00503 void VSettingsNode::setDurationValue(const VString& path, const VDuration& value) {
00504     VString valueString(VSTRING_ARGS(VSTRING_FORMATTER_S64 "ms", value.getDurationMilliseconds()));
00505     this->setStringValue(path, valueString);
00506 }
00507 
00508 void VSettingsNode::add(const VString& path, bool hasValue, const VString& value) {
00509     VString nextNodeName;
00510     VString theRemainder;
00511 
00512     VSettings::splitPathFirst(path, nextNodeName, theRemainder);
00513 
00514     /*
00515     path = a.b: next=a, rem=b -> add child a, add b to it
00516     path =
00517     */
00518 
00519     if (theRemainder.isEmpty()) {
00520         this->_addLeafValue(nextNodeName, hasValue, value);
00521     } else {
00522         VSettingsTag* child = this->_findChildTag(nextNodeName);
00523         if (child == NULL) {
00524             // If there's an attribute, need to move it down as a child tag.
00525             VSettingsAttribute* attribute = this->_findAttribute(nextNodeName);
00526             if (attribute != NULL) {
00527                 child = new VSettingsTag(dynamic_cast<VSettingsTag*>(this), nextNodeName);
00528                 this->addChildNode(child);
00529 
00530                 child->addChildNode(new VSettingsCDATA(dynamic_cast<VSettingsTag*>(this), attribute->getStringValue()));
00531 
00532                 this->_removeAttribute(attribute);
00533                 delete attribute;
00534             }
00535         }
00536 
00537         if (child == NULL) {
00538             VString tagName(nextNodeName);
00539 
00540             if (nextNodeName.endsWith(']')) {
00541                 int leftBracketIndex = nextNodeName.indexOf('[');
00542                 nextNodeName.getSubstring(tagName, 0, leftBracketIndex);
00543             }
00544 
00545             child = new VSettingsTag(dynamic_cast<VSettingsTag*>(this), tagName);
00546             this->addChildNode(child);
00547         }
00548 
00549         child->add(theRemainder, hasValue, value);
00550     }
00551 }
00552 
00553 void VSettingsNode::addValue(const VString& path) {
00554     throw VStackTraceException(VSTRING_FORMAT("VSettingsNode::addValue called for invalid object at '%s'", path.chars()));
00555 }
00556 
00557 void VSettingsNode::addChildNode(VSettingsNode* /*node*/) {
00558     throw VStackTraceException(VSTRING_FORMAT("VSettingsNode::addChildNode called for invalid object at '%s'", this->getPath().chars()));
00559 }
00560 
00561 VSettingsTag* VSettingsNode::getParent() {
00562     return mParent;
00563 }
00564 
00565 void VSettingsNode::_addLeafValue(const VString& name, bool /*hasValue*/, const VString& value) {
00566     throw VStackTraceException(VSTRING_FORMAT("VSettingsNode::_addLeafValue (%s, %s) called for invalid object at '%s'", name.chars(), value.chars(), this->getPath().chars()));
00567 }
00568 
00569 void VSettingsNode::throwNotFound(const VString& dataKind, const VString& missingTrail) const {
00570     throw VException(VSTRING_FORMAT("%s setting '%s' not found starting at path '%s'.", dataKind.chars(), missingTrail.chars(), this->getPath().chars()));
00571 }
00572 
00573 const char VSettingsNode::kPathDelimiterChar = '/';
00574 
00575 // VSettings ----------------------------------------------------------------------
00576 
00577 VSettings::VSettings() :
00578     VSettingsNode(NULL, VString::EMPTY()),
00579     mNodes() { // -> empty
00580 }
00581 
00582 VSettings::VSettings(const VFSNode& file) :
00583     VSettingsNode(NULL, VString::EMPTY()),
00584     mNodes() { // -> empty
00585     this->readFromFile(file);
00586 }
00587 
00588 VSettings::VSettings(VTextIOStream& inputStream) :
00589     VSettingsNode(NULL, VString::EMPTY()),
00590     mNodes() { // -> empty
00591     this->readFromStream(inputStream);
00592 }
00593 
00594 VSettings::~VSettings() {
00595     for (VSizeType i = 0; i < mNodes.size(); ++i)
00596         delete mNodes[i];
00597 }
00598 
00599 void VSettings::readFromFile(const VFSNode& file) {
00600     VBufferedFileStream fs(file);
00601     fs.openReadOnly();
00602     VTextIOStream in(fs);
00603     this->readFromStream(in);
00604 }
00605 
00606 void VSettings::writeToFile(const VFSNode& file) const {
00607     VFileWriter writer(file);
00608     this->writeToStream(writer.getTextOutputStream());
00609     writer.save();
00610 }
00611 
00612 void VSettings::readFromStream(VTextIOStream& inputStream) {
00613     vault::vectorDeleteAll(mNodes);
00614 
00615     VSettingsXMLParser parser(inputStream, &mNodes);
00616 
00617     parser.parse();
00618 }
00619 
00620 void VSettings::writeToStream(VTextIOStream& outputStream, int indentLevel) const {
00621     for (VSizeType i = 0; i < mNodes.size(); ++i) {
00622         mNodes[i]->writeToStream(outputStream, indentLevel);
00623     }
00624 }
00625 
00626 VBentoNode* VSettings::writeToBento() const {
00627     VBentoNode* topNode = new VBentoNode();
00628 
00629     for (VSizeType i = 0; i < mNodes.size(); ++i) {
00630         VBentoNode* theNode = mNodes[i]->writeToBento();
00631         topNode->addChildNode(theNode);
00632     }
00633 
00634     return topNode;
00635 }
00636 
00637 void VSettings::debugPrint() const {
00638     VMemoryStream   memoryStream;
00639     VTextIOStream   outputStream(memoryStream);
00640 
00641     this->writeToStream(outputStream);
00642 
00643     std::cout << "Begin Settings:\n";
00644 
00645     // Avoid stdout flush problems: print buffer one line at a time.
00646     char*       buffer = reinterpret_cast<char*>(memoryStream.getBuffer());
00647     VSizeType   lengthRemaining = strlen(buffer);
00648     VString     s;
00649 
00650     while (lengthRemaining > 0) {
00651         s = VString::EMPTY();
00652 
00653         char c = *buffer;
00654         ++buffer;
00655         --lengthRemaining;
00656 
00657         while ((c != '\n') && (c != '\r') && (lengthRemaining > 0)) {
00658             s += c;
00659 
00660             c = *buffer;
00661             ++buffer;
00662             --lengthRemaining;
00663         }
00664 
00665         std::cout << s.chars() << '\n';
00666         fflush(stdout);
00667     }
00668 
00669     std::cout << "End Settings\n";
00670 
00671     fflush(stdout);
00672 }
00673 
00674 const VSettingsNode* VSettings::findNode(const VString& path)  const {
00675     VString nextNodeName;
00676     VString theRemainder;
00677 
00678     VSettings::splitPathFirst(path, nextNodeName, theRemainder);
00679 
00680     VSettingsTag* child = this->_findChildTag(nextNodeName);
00681     if (child != NULL)
00682         return child->findNode(theRemainder);
00683     else
00684         return NULL;
00685 }
00686 
00687 int VSettings::countNamedChildren(const VString& name) const {
00688     int     result = 0;
00689 
00690     for (VSizeType i = 0; i < mNodes.size(); ++i) {
00691         if (mNodes[i]->getName() == name) {
00692             ++result;
00693         }
00694     }
00695 
00696     return result;
00697 }
00698 
00699 const VSettingsNode* VSettings::getNamedChild(const VString& name, int index) const {
00700     int     numFound = 0;
00701 
00702     for (VSizeType i = 0; i < mNodes.size(); ++i) {
00703         VSettingsNode* child = mNodes[i];
00704 
00705         if (child->getName() == name) {
00706             if (numFound == index) {
00707                 return child;
00708             }
00709 
00710             ++numFound;
00711         }
00712     }
00713 
00714     return NULL;
00715 }
00716 
00717 void VSettings::deleteNamedChildren(const VString& name) {
00718     // Iterate backwards so it's safe to delete while iterating.
00719 
00720     for (VSizeType i = mNodes.size(); i > 0 ; --i) {
00721         VSettingsNode* child = mNodes[i-1];
00722 
00723         if (child->getName() == name) {
00724             delete child;
00725             mNodes.erase(mNodes.begin() + i - 1);
00726         }
00727     }
00728 }
00729 
00730 Vs64 VSettings::getS64Value() const {
00731     throw VStackTraceException("Tried to get raw int value on top level settings object.");
00732 }
00733 
00734 bool VSettings::getBooleanValue() const {
00735     throw VStackTraceException("Tried to get raw boolean value on top level settings object.");
00736 }
00737 
00738 VString VSettings::getStringValue() const {
00739     throw VStackTraceException("Tried to get raw string value on top level settings object.");
00740 }
00741 
00742 VSize VSettings::getSizeValue() const {
00743     throw VStackTraceException("Tried to get raw size value on top level settings object.");
00744 }
00745 
00746 VDouble VSettings::getDoubleValue() const {
00747     throw VStackTraceException("Tried to get raw double value on top level settings object.");
00748 }
00749 
00750 VPoint VSettings::getPointValue() const {
00751     throw VStackTraceException("Tried to get raw point value on top level settings object.");
00752 }
00753 
00754 VRect VSettings::getRectValue() const {
00755     throw VStackTraceException("Tried to get raw rect value on top level settings object.");
00756 }
00757 
00758 VPolygon VSettings::getPolygonValue() const {
00759     throw VStackTraceException("Tried to get raw polygon value on top level settings object.");
00760 }
00761 
00762 VColor VSettings::getColorValue() const {
00763     throw VStackTraceException("Tried to get raw color value on top level settings object.");
00764 }
00765 
00766 VDuration VSettings::getDurationValue() const {
00767     throw VStackTraceException("Tried to get raw duration value on top level settings object.");
00768 }
00769 
00770 VDate VSettings::getDateValue() const {
00771     throw VStackTraceException("Tried to get raw date value on top level settings object.");
00772 }
00773 
00774 VInstant VSettings::getInstantValue() const {
00775     throw VStackTraceException("Tried to get raw instant value on top level settings object.");
00776 }
00777 
00778 void VSettings::addChildNode(VSettingsNode* node) {
00779     mNodes.push_back(node);
00780 }
00781 
00782 // static
00783 bool VSettings::stringToBoolean(const VString& value) {
00784     return (value == "1" ||
00785             value == "T" ||
00786             value == "t" ||
00787             value == "Y" ||
00788             value == "y" ||
00789             value == "TRUE" ||
00790             value == "true" ||
00791             value == "YES" ||
00792             value == "yes");
00793 }
00794 
00795 // static
00796 bool VSettings::isPathLeaf(const VString& path) {
00797     return !path.contains(kPathDelimiterChar);
00798 }
00799 
00800 // static
00801 void VSettings::splitPathFirst(const VString& path, VString& nextNodeName, VString& outRemainder) {
00802     // This code handles a leaf even though we kind of expect callers to check that first.
00803 
00804     int dotLocation = path.indexOf(kPathDelimiterChar);
00805 
00806     path.getSubstring(nextNodeName, 0, dotLocation);
00807 
00808     if (dotLocation == -1)    // no dot found
00809         outRemainder = VString::EMPTY();
00810     else
00811         path.getSubstring(outRemainder, dotLocation + 1);
00812 }
00813 
00814 // static
00815 void VSettings::splitPathLast(const VString& path, VString& leadingPath, VString& lastNode) {
00816     // This code handles a leaf even though we kind of expect callers to check that first.
00817 
00818     int dotLocation = path.lastIndexOf(kPathDelimiterChar);
00819 
00820     if (dotLocation == -1)    // no dot found
00821         leadingPath = VString::EMPTY();
00822     else
00823         path.getSubstring(leadingPath, 0, dotLocation);
00824 
00825     path.getSubstring(lastNode, dotLocation + 1);
00826 }
00827 
00828 VSettingsTag* VSettings::_findChildTag(const VString& name) const {
00829     for (VSizeType i = 0; i < mNodes.size(); ++i) {
00830         if (mNodes[i]->isNamed(name)) {
00831             return static_cast<VSettingsTag*>(mNodes[i]);
00832         }
00833     }
00834 
00835     return NULL;
00836 }
00837 
00838 void VSettings::_addLeafValue(const VString& name, bool /*hasValue*/, const VString& value) {
00839     VString tagName(name);
00840 
00841     if (name.endsWith(']')) {
00842         int leftBracketIndex = name.indexOf('[');
00843         name.getSubstring(tagName, 0, leftBracketIndex);
00844     }
00845 
00846     VSettingsTag* tag = new VSettingsTag(NULL, tagName);
00847 
00848     tag->addChildNode(new VSettingsCDATA(tag, value));
00849 
00850     mNodes.push_back(tag);
00851 }
00852 
00853 // VSettingsTag ------------------------------------------------------------------
00854 
00855 VSettingsTag::VSettingsTag(VSettingsTag* parent, const VString& name) :
00856     VSettingsNode(parent, name),
00857     mAttributes(), // -> empty
00858     mChildNodes() { // -> empty
00859 }
00860 
00861 VSettingsTag::~VSettingsTag() {
00862     for (VSizeType i = 0; i < mAttributes.size(); ++i)
00863         delete mAttributes[i];
00864 
00865     for (VSizeType i = 0; i < mChildNodes.size(); ++i)
00866         delete mChildNodes[i];
00867 }
00868 
00869 void VSettingsTag::writeToStream(VTextIOStream& outputStream, int indentLevel) const {
00870     for (int i = 0; i < indentLevel; ++i)
00871         outputStream.writeString(" ");
00872 
00873     VString beginTag(VSTRING_ARGS("<%s", mName.chars()));
00874     outputStream.writeString(beginTag);
00875 
00876     if (!mAttributes.empty()) {
00877         // Write each attribute
00878         for (VSizeType i = 0; i < mAttributes.size(); ++i) {
00879             outputStream.writeString(" ");
00880             mAttributes[i]->writeToStream(outputStream);
00881         }
00882     }
00883 
00884     if (mChildNodes.empty()) {
00885         // Just close the tag and we're done.
00886         outputStream.writeLine(" />");
00887     } else {
00888         // Close the opening tag.
00889         outputStream.writeLine(">");
00890 
00891         // Write each child node
00892         for (VSizeType i = 0; i < mChildNodes.size(); ++i) {
00893             mChildNodes[i]->writeToStream(outputStream, indentLevel + 1);
00894         }
00895 
00896         // Write a closing tag.
00897         for (int i = 0; i < indentLevel; ++i) {
00898             outputStream.writeString(" ");
00899         }
00900 
00901         VString endTag(VSTRING_ARGS("</%s>", mName.chars()));
00902         outputStream.writeLine(endTag);
00903     }
00904 
00905 }
00906 
00907 VBentoNode* VSettingsTag::writeToBento() const {
00908     VBentoNode* tagNode = new VBentoNode(mName);
00909 
00910     if (!mAttributes.empty()) {
00911         // Write each attribute
00912         for (VSizeType i = 0; i < mAttributes.size(); ++i) {
00913             tagNode->addString(mAttributes[i]->getName(), mAttributes[i]->getStringValue());
00914         }
00915     }
00916 
00917     if (!mChildNodes.empty()) {
00918         // Write each child node
00919         for (VSizeType i = 0; i < mChildNodes.size(); ++i) {
00920             VBentoNode* childNode = mChildNodes[i]->writeToBento();
00921             tagNode->addChildNode(childNode);
00922         }
00923     }
00924 
00925     return tagNode;
00926 }
00927 
00928 int VSettingsTag::countNamedChildren(const VString& name) const {
00929     int     result = 0;
00930 
00931     for (VSizeType i = 0; i < mAttributes.size(); ++i) {
00932         if (mAttributes[i]->getName() == name) {
00933             ++result;
00934         }
00935     }
00936 
00937     for (VSizeType i = 0; i < mChildNodes.size(); ++i) {
00938         if (mChildNodes[i]->getName() == name) {
00939             ++result;
00940         }
00941     }
00942 
00943     return result;
00944 }
00945 
00946 const VSettingsNode* VSettingsTag::getNamedChild(const VString& name, int index) const {
00947     int     numFound = 0;
00948 
00949     for (VSizeType i = 0; i < mAttributes.size(); ++i) {
00950         VSettingsAttribute* attribute = mAttributes[i];
00951 
00952         if (attribute->getName() == name) {
00953             if (numFound == index) {
00954                 return attribute;
00955             }
00956 
00957             ++numFound;
00958         }
00959     }
00960 
00961     for (VSizeType i = 0; i < mChildNodes.size(); ++i) {
00962         VSettingsNode* child = mChildNodes[i];
00963 
00964         if (child->getName() == name) {
00965             if (numFound == index) {
00966                 return child;
00967             }
00968 
00969             ++numFound;
00970         }
00971     }
00972 
00973     return NULL;
00974 }
00975 
00976 void VSettingsTag::deleteNamedChildren(const VString& name) {
00977     // Iterate backwards so it's safe to delete while iterating.
00978 
00979     for (VSizeType i = mAttributes.size(); i > 0; --i) {
00980         VSettingsAttribute* attribute = mAttributes[i-1];
00981 
00982         if (attribute->getName() == name) {
00983             delete attribute;
00984             mAttributes.erase(mAttributes.begin() + i - 1);
00985         }
00986     }
00987 
00988     for (VSizeType i = mChildNodes.size(); i > 0 ; --i) {
00989         VSettingsNode* child = mChildNodes[i-1];
00990 
00991         if (child->getName() == name) {
00992             delete child;
00993             mChildNodes.erase(mChildNodes.begin() + i - 1);
00994         }
00995     }
00996 }
00997 
00998 void VSettingsTag::addAttribute(VSettingsAttribute* attribute) {
00999     mAttributes.push_back(attribute);
01000 }
01001 
01002 void VSettingsTag::addChildNode(VSettingsNode* node) {
01003     mChildNodes.push_back(node);
01004 }
01005 
01006 Vs64 VSettingsTag::getS64Value() const {
01007     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01008 
01009     if (cdataNode == NULL)
01010         this->throwNotFound("Integer", "<cdata>");
01011 
01012     return cdataNode->getS64Value();
01013 }
01014 
01015 bool VSettingsTag::getBooleanValue() const {
01016     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01017 
01018     if (cdataNode == NULL)
01019         this->throwNotFound("Boolean", "<cdata>");
01020 
01021     return cdataNode->getBooleanValue();
01022 }
01023 
01024 VString VSettingsTag::getStringValue() const {
01025     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01026 
01027     if (cdataNode == NULL)
01028         return VString::EMPTY(); // unlike other data types, an empty string in an explicit attribute is a legitimate "value"
01029     else
01030         return cdataNode->getStringValue();
01031 }
01032 
01033 VDouble VSettingsTag::getDoubleValue() const {
01034     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01035 
01036     if (cdataNode == NULL)
01037         this->throwNotFound("Double", "<cdata>");
01038 
01039     return cdataNode->getDoubleValue();
01040 }
01041 
01042 VSize VSettingsTag::getSizeValue() const {
01043     VSize size(this->getDouble("width"), this->getDouble("height"));
01044     return size;
01045 }
01046 
01047 VPoint VSettingsTag::getPointValue() const {
01048     VPoint point(this->getDouble("x"), this->getDouble("y"));
01049     return point;
01050 }
01051 
01052 VRect VSettingsTag::getRectValue() const {
01053     VRect rect(this->getDouble("position/x"), this->getDouble("position/y"),
01054                this->getDouble("size/width"), this->getDouble("size/height"));
01055     return rect;
01056 }
01057 
01058 VPolygon VSettingsTag::getPolygonValue() const {
01059     VPolygon polygon;
01060     int numPoints = this->countNamedChildren("point");
01061     for (int i = 0; i < numPoints; ++i) {
01062         const VSettingsTag* pointTag = static_cast<const VSettingsTag*>(this->getNamedChild("point", i));
01063         polygon.add(pointTag->getPointValue());
01064     }
01065 
01066     return polygon;
01067 }
01068 
01069 VColor VSettingsTag::getColorValue() const {
01070     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01071 
01072     if (cdataNode == NULL)
01073         this->throwNotFound("Color", "<cdata>");
01074 
01075     return cdataNode->getColorValue();
01076 }
01077 
01078 VDuration VSettingsTag::getDurationValue() const {
01079     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01080 
01081     if (cdataNode == NULL)
01082         this->throwNotFound("Duration", "<cdata>");
01083 
01084     return cdataNode->getDurationValue();
01085 }
01086 
01087 VDate VSettingsTag::getDateValue() const {
01088     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01089 
01090     if (cdataNode == NULL)
01091         this->throwNotFound("Date", "<cdata>");
01092 
01093     return cdataNode->getDateValue();
01094 }
01095 
01096 VInstant VSettingsTag::getInstantValue() const {
01097     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01098 
01099     if (cdataNode == NULL)
01100         this->throwNotFound("Instant", "<cdata>");
01101 
01102     return cdataNode->getInstantValue();
01103 }
01104 
01105 void VSettingsTag::setLiteral(const VString& value) {
01106     VSettingsNode* cdataNode = this->_findChildTag("<cdata>");
01107 
01108     if (cdataNode == NULL)
01109         this->throwNotFound("String", "<cdata>");
01110 
01111     cdataNode->setLiteral(value);
01112 }
01113 
01114 VSettingsAttribute* VSettingsTag::_findAttribute(const VString& name) const {
01115     for (VSizeType i = 0; i < mAttributes.size(); ++i) {
01116         if (mAttributes[i]->isNamed(name)) {
01117             return mAttributes[i];
01118         }
01119     }
01120 
01121     return NULL;
01122 }
01123 
01124 VSettingsTag* VSettingsTag::_findChildTag(const VString& name) const {
01125     if (name.endsWith(']')) {
01126         int     leftBracketIndex = name.indexOf('[');
01127         VString indexString;
01128         name.getSubstring(indexString, leftBracketIndex + 1, name.length() - 1);
01129         int     theIndex = indexString.parseInt();
01130 
01131         VString nameOnly;
01132         name.getSubstring(nameOnly, 0, leftBracketIndex);
01133 
01134         return static_cast<VSettingsTag*>(const_cast<VSettingsNode*>(this->getNamedChild(nameOnly, theIndex))); // const_cast: NON-CONST RETURN
01135     } else {
01136         for (VSizeType i = 0; i < mChildNodes.size(); ++i) {
01137             if (mChildNodes[i]->isNamed(name)) {
01138                 return static_cast<VSettingsTag*>(mChildNodes[i]);
01139             }
01140         }
01141     }
01142 
01143     return NULL;
01144 }
01145 
01146 void VSettingsTag::_addLeafValue(const VString& name, bool hasValue, const VString& value) {
01147     if (hasValue)
01148         this->addAttribute(new VSettingsAttribute(this, name, value));
01149     else
01150         this->addAttribute(new VSettingsAttribute(this, name));
01151 }
01152 
01153 void VSettingsTag::_removeAttribute(VSettingsAttribute* attribute) {
01154     for (VSettingsAttributePtrVector::iterator i = mAttributes.begin(); i != mAttributes.end(); ++i) {
01155         if ((*i) == attribute) {
01156             mAttributes.erase(i);
01157             return;
01158         }
01159     }
01160 }
01161 
01162 void VSettingsTag::_removeChildNode(VSettingsNode* child) {
01163     for (VSettingsNodePtrVector::iterator i = mChildNodes.begin(); i != mChildNodes.end(); ++i) {
01164         if ((*i) == child) {
01165             mChildNodes.erase(i);
01166             return;
01167         }
01168     }
01169 }
01170 
01171 // VSettingsAttribute ------------------------------------------------------------
01172 
01173 VSettingsAttribute::VSettingsAttribute(VSettingsTag* parent, const VString& name, const VString& value) :
01174     VSettingsNode(parent, name),
01175     mHasValue(true),
01176     mValue(value) {
01177 }
01178 
01179 VSettingsAttribute::VSettingsAttribute(VSettingsTag* parent, const VString& name) :
01180     VSettingsNode(parent, name),
01181     mHasValue(false),
01182     mValue() { // -> empty string
01183 }
01184 
01185 void VSettingsAttribute::writeToStream(VTextIOStream& outputStream, int /*indentLevel*/) const {
01186     if (mHasValue) {
01187         VString attributeString(VSTRING_ARGS("%s=\"%s\"", mName.chars(), mValue.chars()));
01188         outputStream.writeString(attributeString);
01189     } else {
01190         VString attributeString(VSTRING_ARGS("%s", mName.chars()));
01191         outputStream.writeString(attributeString);
01192     }
01193 }
01194 
01195 VBentoNode* VSettingsAttribute::writeToBento() const {
01196     return NULL;        // This doesn't create a node
01197 }
01198 
01199 Vs64 VSettingsAttribute::getS64Value() const {
01200     return mValue.parseS64();
01201 }
01202 
01203 bool VSettingsAttribute::getBooleanValue() const {
01204     return VSettings::stringToBoolean(mValue);
01205 }
01206 
01207 VString VSettingsAttribute::getStringValue() const {
01208     return mValue;
01209 }
01210 
01211 VDouble VSettingsAttribute::getDoubleValue() const {
01212     return mValue.parseDouble();
01213 }
01214 
01215 VSize VSettingsAttribute::getSizeValue() const {
01216     this->throwNotFound("Size", "attribute");
01217     return VSize(); // (will never reach this statement because of throw)
01218 }
01219 
01220 VPoint VSettingsAttribute::getPointValue() const {
01221     this->throwNotFound("Point", "attribute");
01222     return VPoint(); // (will never reach this statement because of throw)
01223 }
01224 
01225 VRect VSettingsAttribute::getRectValue() const {
01226     this->throwNotFound("Rect", "attribute");
01227     return VRect(); // (will never reach this statement because of throw)
01228 }
01229 
01230 VPolygon VSettingsAttribute::getPolygonValue() const {
01231     this->throwNotFound("Polygon", "attribute");
01232     return VPolygon(); // (will never reach this statement because of throw)
01233 }
01234 
01235 VColor VSettingsAttribute::getColorValue() const {
01236     return VColor(mValue);
01237 }
01238 
01239 VDuration VSettingsAttribute::getDurationValue() const {
01240     return VDuration::createFromDurationString(mValue);
01241 }
01242 
01243 VDate VSettingsAttribute::getDateValue() const {
01244     return VDate::createFromDateString(mValue, VCodePoint('-'));
01245 }
01246 
01247 VInstant VSettingsAttribute::getInstantValue() const {
01248     VInstant when;
01249     if (mValue.contains("UTC")) {
01250         when.setUTCString(mValue);
01251     } else {
01252         when.setLocalString(mValue);
01253     }
01254     return when;
01255 }
01256 
01257 void VSettingsAttribute::setLiteral(const VString& value) {
01258     mHasValue = true;
01259     mValue = value;
01260 }
01261 
01262 bool VSettingsAttribute::hasValue() const {
01263     return mHasValue;
01264 }
01265 
01266 // VSettingsCDATA ------------------------------------------------------------
01267 
01268 VSettingsCDATA::VSettingsCDATA(VSettingsTag* parent, const VString& cdata) :
01269     VSettingsNode(parent, "<cdata>"),
01270     mCDATA(cdata) {
01271 }
01272 
01273 void VSettingsCDATA::writeToStream(VTextIOStream& outputStream, int indentLevel) const {
01274     if (indentLevel > 1) {  // at indent level 1 we're just a top-level item, indenting is detrimental
01275         for (int i = 0; i < indentLevel; ++i) {
01276             outputStream.writeString(" ");
01277         }
01278     }
01279 
01280     outputStream.writeLine(mCDATA);
01281 }
01282 
01283 VBentoNode* VSettingsCDATA::writeToBento() const {
01284     VBentoNode* cDataNode = new VBentoNode(mName);
01285     cDataNode->addString(mName, mCDATA);
01286     return cDataNode;
01287 }
01288 
01289 Vs64 VSettingsCDATA::getS64Value() const {
01290     return mCDATA.parseS64();
01291 }
01292 
01293 bool VSettingsCDATA::getBooleanValue() const {
01294     return VSettings::stringToBoolean(mCDATA);
01295 }
01296 
01297 VString VSettingsCDATA::getStringValue() const {
01298     return mCDATA;
01299 }
01300 
01301 VDouble VSettingsCDATA::getDoubleValue() const {
01302     return mCDATA.parseDouble();
01303 }
01304 
01305 VSize VSettingsCDATA::getSizeValue() const {
01306     this->throwNotFound("Size", "attribute");
01307     return VSize(); // (will never reach this statement because of throw)
01308 }
01309 
01310 VPoint VSettingsCDATA::getPointValue() const {
01311     this->throwNotFound("Point", "attribute");
01312     return VPoint(); // (will never reach this statement because of throw)
01313 }
01314 
01315 VRect VSettingsCDATA::getRectValue() const {
01316     this->throwNotFound("Rect", "attribute");
01317     return VRect(); // (will never reach this statement because of throw)
01318 }
01319 
01320 VPolygon VSettingsCDATA::getPolygonValue() const {
01321     this->throwNotFound("Polygon", "attribute");
01322     return VPolygon(); // (will never reach this statement because of throw)
01323 }
01324 
01325 VColor VSettingsCDATA::getColorValue() const {
01326     return VColor(mCDATA);
01327 }
01328 
01329 VDuration VSettingsCDATA::getDurationValue() const {
01330     return VDuration::createFromDurationString(mCDATA);
01331 }
01332 
01333 VDate VSettingsCDATA::getDateValue() const {
01334     return VDate::createFromDateString(mCDATA, VCodePoint('-'));
01335 }
01336 
01337 VInstant VSettingsCDATA::getInstantValue() const {
01338     VInstant when;
01339     if (mCDATA.contains("UTC")) {
01340         when.setUTCString(mCDATA);
01341     } else {
01342         when.setLocalString(mCDATA);
01343     }
01344     return when;
01345 }
01346 
01347 void VSettingsCDATA::setLiteral(const VString& value) {
01348     mCDATA = value;
01349 }
01350 
01351 // VSettingsXMLParser --------------------------------------------------------
01352 
01353 VSettingsXMLParser::VSettingsXMLParser(VTextIOStream& inputStream, VSettingsNodePtrVector* nodes) :
01354     mInputStream(inputStream),
01355     mNodes(nodes),
01356     mCurrentLine(), // -> empty string
01357     mCurrentLineNumber(0),
01358     mCurrentColumnNumber(0),
01359     mParserState(kReady),
01360     mElement(), // -> empty string
01361     mCurrentTag(NULL),
01362     mPendingAttributeName() { // -> empty string
01363 }
01364 
01365 void VSettingsXMLParser::parse() {
01366     bool    done = false;
01367     VString line;
01368 
01369     mParserState = kReady;
01370 
01371     while (! done) {
01372         try {
01373             mInputStream.readLine(mCurrentLine);
01374 
01375             ++mCurrentLineNumber;
01376 
01377             this->parseLine();
01378         } catch (const VEOFException& /*ex*/) {
01379             done = true;
01380         }
01381     }
01382 }
01383 
01384 void VSettingsXMLParser::parseLine() {
01385 
01386     mCurrentColumnNumber = 0;
01387 
01388     if ((mCurrentLineNumber == 1) && mCurrentLine.startsWith("<?") && mCurrentLine.endsWith("?>")) {
01389         return; // skip the typical "<?xml version .... ?>" first line
01390     }
01391 
01392     for (VString::iterator i = mCurrentLine.begin(); i != mCurrentLine.end(); ++i) {
01393         VCodePoint c = (*i);
01394 
01395         ++mCurrentColumnNumber;
01396 
01397         switch (mParserState) {
01398             case kReady:
01399                 if (c == '<') {
01400                     this->emitCDATA();
01401                     this->changeState(kTag1_open);
01402                 } else {
01403                     this->accumulate(c);
01404                 }
01405                 break;
01406 
01407             case kComment1_bang:
01408                 if (c == '-') {
01409                     this->changeState(kComment2_bang_dash);
01410                 } else {
01411                     VString s(VSTRING_ARGS("Invalid character '%s' after presumed start of comment.", c.toString().chars()));
01412                     this->stateError(s);
01413                 }
01414                 break;
01415 
01416             case kComment2_bang_dash:
01417                 if (c == '-') {
01418                     this->changeState(kComment3_in_comment);
01419                 } else {
01420                     VString s(VSTRING_ARGS("Invalid character '%c' after presumed start of comment.", c.toString().chars()));
01421                     this->stateError(s);
01422                 }
01423                 break;
01424 
01425             case kComment3_in_comment:
01426                 if (c == '-') {
01427                     this->changeState(kComment4_traildash);
01428                 } else {
01429                     /*nothing*/
01430                 }
01431                 break;
01432 
01433             case kComment4_traildash:
01434                 if (c == '-') {
01435                     this->changeState(kComment5_traildash_dash);
01436                 } else {
01437                     this->changeState(kComment3_in_comment);
01438                 }
01439                 break;
01440 
01441             case kComment5_traildash_dash:
01442                 if (c == '-') {
01443                     // *nothing
01444                 } else if (c == '>') {
01445                     this->changeState(kReady);
01446                 } else {
01447                     this->changeState(kComment3_in_comment);
01448                 }
01449                 break;
01450 
01451             case kTag1_open:
01452                 if (c == '!') {
01453                     this->changeState(kComment1_bang);
01454                 } else if (c == '/') {
01455                     this->changeState(kCloseTag1_open_slash);
01456                 } else if (c.isAlpha()) {
01457                     this->changeState(kTag2_in_name);
01458                     this->accumulate(c);
01459                 } else if (c.isWhitespace()) {
01460                     // nothing
01461                 } else {
01462                     this->stateError("Invalid character after opening tag bracket.");
01463                 }
01464                 break;
01465 
01466             case kTag2_in_name:
01467                 if (VSettingsXMLParser::isValidTagNameChar(c)) {
01468                     this->accumulate(c);
01469                 } else if (c.isWhitespace()) {
01470                     this->emitOpenTagName();
01471                     this->changeState(kTag3_post_name);
01472                 } else if (c == '/') {
01473                     this->emitOpenTagName();
01474                     this->changeState(kTag8_solo_close_slash);
01475                 } else if (c == '>') {
01476                     this->emitOpenTagName();
01477                     this->changeState(kReady);
01478                 } else {
01479                     VString s(VSTRING_ARGS("Invalid character '%s' in tag name.", c.toString().chars()));
01480                     this->stateError(s);
01481                 }
01482                 break;
01483 
01484             case kTag3_post_name:
01485                 if (c.isWhitespace()) {
01486                     // nothing
01487                 } else if (c == '>') {
01488                     this->changeState(kReady);
01489                 } else if (c == '/') {
01490                     this->changeState(kTag8_solo_close_slash);
01491                 } else if (c.isAlpha()) {
01492                     this->changeState(kTag4_in_attribute_name);
01493                     this->accumulate(c);
01494                 } else {
01495                     VString s(VSTRING_ARGS("Invalid character '%s' in tag after name.", c.toString().chars()));
01496                     this->stateError(s);
01497                 }
01498                 break;
01499 
01500             case kTag4_in_attribute_name:
01501                 if (VSettingsXMLParser::isValidAttributeNameChar(c)) {
01502                     this->accumulate(c);
01503                 } else if (c == '=') {
01504                     this->emitAttributeName();
01505                     this->changeState(kTag5_attribute_equals);
01506                 } else if (c.isWhitespace()) {
01507                     this->emitAttributeNameOnly();
01508                     this->changeState(kTag3_post_name);
01509                 } else if (c == '/') {
01510                     this->emitAttributeNameOnly();
01511                     this->changeState(kTag8_solo_close_slash);
01512                 } else {
01513                     VString s(VSTRING_ARGS("Invalid character '%s' in attribute name.", c.toString().chars()));
01514                     this->stateError(s);
01515                 }
01516                 break;
01517 
01518             case kTag5_attribute_equals:
01519                 if (c == '\"') {
01520                     this->changeState(kTag6_attribute_quoted);
01521                 } else if (c == '/') {
01522                     this->emitAttributeValue();
01523                     this->changeState(kTag8_solo_close_slash);
01524                 } else if (c.isAlphaNumeric()) {
01525                     this->changeState(kTag7_attribute_unquoted);
01526                     this->accumulate(c);
01527                 }
01528                 break;
01529 
01530             case kTag6_attribute_quoted:
01531                 if (c.isAlphaNumeric()) {
01532                     this->accumulate(c);
01533                 } else if (c.isWhitespace()) {
01534                     this->accumulate(c);
01535                 } else if (c == '\"') {
01536                     this->emitAttributeValue();
01537                     this->changeState(kTag3_post_name);
01538                 } else {
01539                     this->accumulate(c);
01540                 }
01541                 break;
01542 
01543             case kTag7_attribute_unquoted:
01544                 if (VSettingsXMLParser::isValidAttributeValueChar(c)) {
01545                     this->accumulate(c);
01546                 } else if (c.isWhitespace()) {
01547                     this->emitAttributeValue();
01548                     this->changeState(kTag3_post_name);
01549                 } else if (c == '>') {
01550                     this->emitAttributeValue();
01551                     this->changeState(kReady);
01552                 } else if (c == '/') {
01553                     this->emitAttributeValue();
01554                     this->changeState(kTag8_solo_close_slash);
01555                 } else {
01556                     VString s(VSTRING_ARGS("Invalid character '%s' in unquoted attribute value.", c.toString().chars()));
01557                     this->stateError(s);
01558                 }
01559                 break;
01560 
01561             case kTag8_solo_close_slash:
01562                 if (c == '>') {
01563                     this->emitEndSoloTag();
01564                     this->changeState(kReady);
01565                 } else {
01566                     VString s(VSTRING_ARGS("Invalid character '%s' after solo close tag slash.", c.toString().chars()));
01567                     this->stateError(s);
01568                 }
01569                 break;
01570 
01571             case kCloseTag1_open_slash:
01572                 if (c.isWhitespace()) {
01573                     // nothing
01574                 } else if (VSettingsXMLParser::isValidTagNameChar(c)) {
01575                     this->changeState(kCloseTag2_in_name);
01576                     this->accumulate(c);
01577                 } else {
01578                     VString s(VSTRING_ARGS("Invalid character '%s' in closing tag.", c.toString().chars()));
01579                     this->stateError(s);
01580                 }
01581                 break;
01582 
01583             case kCloseTag2_in_name:
01584                 if (c == '>') {
01585                     this->emitCloseTagName();
01586                     this->changeState(kReady);
01587                 } else if (c.isWhitespace()) {
01588                     this->emitCloseTagName();
01589                     this->changeState(kCloseTag3_trailing_whitespace);
01590                 } else if (VSettingsXMLParser::isValidTagNameChar(c)) {
01591                     this->accumulate(c);
01592                 } else {
01593                     VString s(VSTRING_ARGS("Invalid character '%s' in closing tag.", c.toString().chars()));
01594                     this->stateError(s);
01595                 }
01596                 break;
01597 
01598             case kCloseTag3_trailing_whitespace:
01599                 if (c.isWhitespace()) {
01600                     // nothing
01601                 } else if (c == '>') {
01602                     this->changeState(kReady);
01603                 } else {
01604                     VString s(VSTRING_ARGS("Invalid character '%s' in closing tag.", c.toString().chars()));
01605                     this->stateError(s);
01606                 }
01607                 break;
01608         }
01609 
01610         if (c == '\t') {
01611             mCurrentColumnNumber += 3;    // already did ++, and we want tabs to be 4 "columns" in terms of syntax errors
01612         }
01613     }
01614 }
01615 
01616 void VSettingsXMLParser::resetElement() {
01617     mElement = VString::EMPTY();
01618 }
01619 
01620 void VSettingsXMLParser::accumulate(const VCodePoint& c) {
01621     mElement += c;
01622 }
01623 
01624 void VSettingsXMLParser::changeState(ParserState newState) {
01625     mParserState = newState;
01626     this->resetElement();
01627 }
01628 
01629 void VSettingsXMLParser::stateError(const VString& errorMessage) {
01630     VString completeMessage(VSTRING_ARGS("Syntax error in state %d at line %d, column %d: %s", mParserState, mCurrentLineNumber, mCurrentColumnNumber, errorMessage.chars()));
01631     throw VStackTraceException(completeMessage);
01632 }
01633 
01634 void VSettingsXMLParser::emitCDATA() {
01635     mElement.trim();
01636 
01637     if (! mElement.isEmpty()) {
01638         if (mCurrentTag == NULL)
01639             mNodes->push_back(new VSettingsCDATA(NULL, mElement));
01640         else
01641             mCurrentTag->addChildNode(new VSettingsCDATA(mCurrentTag, mElement));
01642     }
01643 }
01644 
01645 void VSettingsXMLParser::emitOpenTagName() {
01646     VSettingsTag* tag = new VSettingsTag(mCurrentTag, mElement);
01647 
01648     if (mCurrentTag == NULL)
01649         mNodes->push_back(tag);
01650     else
01651         mCurrentTag->addChildNode(tag);
01652 
01653     mCurrentTag = tag;
01654 }
01655 
01656 void VSettingsXMLParser::emitAttributeName() {
01657     mPendingAttributeName = mElement;
01658 }
01659 
01660 void VSettingsXMLParser::emitAttributeNameOnly() {
01661     VSettingsAttribute* attribute = new VSettingsAttribute(mCurrentTag, mElement);
01662     mCurrentTag->addAttribute(attribute);
01663 }
01664 
01665 void VSettingsXMLParser::emitAttributeValue() {
01666     VSettingsAttribute* attribute = new VSettingsAttribute(mCurrentTag, mPendingAttributeName, mElement);
01667     mCurrentTag->addAttribute(attribute);
01668 }
01669 
01670 void VSettingsXMLParser::emitCloseTagName() {
01671     if (mCurrentTag->getName() != mElement)
01672         this->stateError(VSTRING_FORMAT("Closing tag name '%s' does not balance opening tag '%s'.", mElement.chars(), mCurrentTag->getName().chars()));
01673 
01674     mCurrentTag = mCurrentTag->getParent();
01675 }
01676 
01677 void VSettingsXMLParser::emitEndSoloTag() {
01678     mCurrentTag = mCurrentTag->getParent();
01679 }
01680 
01681 // static
01682 bool VSettingsXMLParser::isValidTagNameChar(const VCodePoint& c) {
01683     int value = c.intValue();
01684     return ((value > 0x20) && (value < 0x7F) && (value != '<') && (value != '>') && (value != '/') && (value != '='));
01685 }
01686 
01687 // static
01688 bool VSettingsXMLParser::isValidAttributeNameChar(const VCodePoint& c) {
01689     int value = c.intValue();
01690     return ((value > 0x20) && (value < 0x7F) && (value != '<') && (value != '>') && (value != '/') && (value != '='));
01691 }
01692 
01693 // static
01694 bool VSettingsXMLParser::isValidAttributeValueChar(const VCodePoint& c) {
01695     int value = c.intValue();
01696     return ((value > 0x20) && (value < 0x7F) && (value != '<') && (value != '>') && (value != '/') && (value != '='));
01697 }
01698 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.