![]() |
Vault
4.1
|
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