diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llxml |
Print done when done.
Diffstat (limited to 'indra/llxml')
-rw-r--r-- | indra/llxml/llcontrol.cpp | 1401 | ||||
-rw-r--r-- | indra/llxml/llcontrol.h | 255 | ||||
-rw-r--r-- | indra/llxml/llxmlnode.cpp | 3008 | ||||
-rw-r--r-- | indra/llxml/llxmlnode.h | 266 | ||||
-rw-r--r-- | indra/llxml/llxmlparser.cpp | 398 | ||||
-rw-r--r-- | indra/llxml/llxmlparser.h | 109 | ||||
-rw-r--r-- | indra/llxml/llxmltree.cpp | 671 | ||||
-rw-r--r-- | indra/llxml/llxmltree.h | 216 |
8 files changed, 6324 insertions, 0 deletions
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp new file mode 100644 index 0000000000..a9651fafc7 --- /dev/null +++ b/indra/llxml/llcontrol.cpp @@ -0,0 +1,1401 @@ +/** + * @file llcontrol.cpp + * @brief Holds global state for viewer. + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include <iostream> +#include <fstream> +#include <algorithm> + +#include "llcontrol.h" + +#include "llstl.h" + +#include "linked_lists.h" +#include "llstring.h" +#include "v3math.h" +#include "v3dmath.h" +#include "v4coloru.h" +#include "v4color.h" +#include "v3color.h" +#include "llrect.h" +#include "llxmltree.h" +#include "llsdserialize.h" + +#if LL_RELEASE_FOR_DOWNLOAD +#define CONTROL_ERRS llwarns +#else +#define CONTROL_ERRS llerrs +#endif + +//this defines the current version of the settings file +U32 LLControlBase::sMaxControlNameLength = 0; + +//These lists are used to store the ID's of registered event listeners. +std::list<S32> LLControlBase::mFreeIDs; +std::list<S32> LLControlBase::mUsedIDs; + +S32 LLControlBase::mTopID; + +std::set<LLControlBase*> LLControlBase::mChangedControls; + +const S32 CURRENT_VERSION = 101; + +BOOL control_insert_before( LLControlBase* first, LLControlBase* second ); + +BOOL LLControl::llsd_compare(const LLSD& a, const LLSD & b) +{ + switch (mType) + { + case TYPE_U32: + case TYPE_S32: + return a.asInteger() == b.asInteger(); + case TYPE_BOOLEAN: + return a.asBoolean() == b.asBoolean(); + case TYPE_F32: + return a.asReal() == b.asReal(); + case TYPE_VEC3: + case TYPE_VEC3D: + return LLVector3d(a) == LLVector3d(b); + case TYPE_RECT: + return LLRect(a) == LLRect(b); + case TYPE_COL4: + return LLColor4(a) == LLColor4(b); + case TYPE_COL3: + return LLColor3(a) == LLColor3(b); + case TYPE_COL4U: + return LLColor4U(a) == LLColor4U(b); + case TYPE_STRING: + return a.asString() == b.asString(); + default: + // no-op + break; + } + + return FALSE; +} + +LLControlBase::~LLControlBase() +{ +} + +// virtual +void LLControlBase::resetToDefault() +{ +} + +LLControlGroup::LLControlGroup(): mNameTable() +{ + //mFreeStringOffset = 0; +} + +LLControlGroup::~LLControlGroup() +{ +} + +LLSD LLControlBase::registerListener(LLSimpleListenerObservable *listener, LLSD userdata) +{ + // Symmetric listener relationship + addListener(listener, "", userdata); + listener->addListener(this, "", userdata); + return getValue(); +} + +void LLControlGroup::cleanup() +{ + mNameTable.clear(); +} + +LLControlBase* LLControlGroup::getControl(const LLString& name) +{ + return mNameTable[name]; +} + +BOOL LLControlGroup::declareControl(const LLString& name, eControlType type, const LLSD initial_val, const LLString& comment, BOOL persist) +{ + if(!mNameTable[name]) + { + // if not, create the control and add it to the name table + LLControl* control = new LLControl(name, type, initial_val, comment, persist); + mNameTable[name] = control; + return TRUE; + } else + { + llwarns << "LLControlGroup::declareControl: Control named " << name << " already exists." << llendl; + return FALSE; + } +} + +BOOL LLControlGroup::declareU32(const LLString& name, const U32 initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_U32, (LLSD::Integer) initial_val, comment, persist); +} + +BOOL LLControlGroup::declareS32(const LLString& name, const S32 initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_S32, initial_val, comment, persist); +} + +BOOL LLControlGroup::declareF32(const LLString& name, const F32 initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_F32, initial_val, comment, persist); +} + +BOOL LLControlGroup::declareBOOL(const LLString& name, const BOOL initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_BOOLEAN, initial_val, comment, persist); +} + +BOOL LLControlGroup::declareString(const LLString& name, const LLString& initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_STRING, initial_val, comment, persist); +} + +BOOL LLControlGroup::declareVec3(const LLString& name, const LLVector3 &initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_VEC3, initial_val.getValue(), comment, persist); +} + +BOOL LLControlGroup::declareVec3d(const LLString& name, const LLVector3d &initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist); +} + +BOOL LLControlGroup::declareRect(const LLString& name, const LLRect &initial_val, const LLString& comment, BOOL persist) +{ + return declareControl(name, TYPE_RECT, initial_val.getValue(), comment, persist); +} + +BOOL LLControlGroup::declareColor4U(const LLString& name, const LLColor4U &initial_val, const LLString& comment, BOOL persist ) +{ + return declareControl(name, TYPE_COL4U, initial_val.getValue(), comment, persist); +} + +BOOL LLControlGroup::declareColor4(const LLString& name, const LLColor4 &initial_val, const LLString& comment, BOOL persist ) +{ + return declareControl(name, TYPE_COL4, initial_val.getValue(), comment, persist); +} + +BOOL LLControlGroup::declareColor3(const LLString& name, const LLColor3 &initial_val, const LLString& comment, BOOL persist ) +{ + return declareControl(name, TYPE_COL3, initial_val.getValue(), comment, persist); +} + +LLSD LLControlGroup::registerListener(const LLString& name, LLSimpleListenerObservable *listener) +{ + LLControlBase *control = mNameTable[name]; + if (control) + { + return control->registerListener(listener); + } + return LLSD(); +} + +BOOL LLControlGroup::getBOOL(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_BOOLEAN)) + return control->get().asBoolean(); + else + { + CONTROL_ERRS << "Invalid BOOL control " << name << llendl; + return FALSE; + } +} + +S32 LLControlGroup::getS32(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_S32)) + return control->get().asInteger(); + else + { + CONTROL_ERRS << "Invalid S32 control " << name << llendl; + return 0; + } +} + +U32 LLControlGroup::getU32(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_U32)) + return control->get().asInteger(); + else + { + CONTROL_ERRS << "Invalid U32 control " << name << llendl; + return 0; + } +} + +F32 LLControlGroup::getF32(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_F32)) + return (F32) control->get().asReal(); + else + { + CONTROL_ERRS << "Invalid F32 control " << name << llendl; + return 0.0f; + } +} + +LLString LLControlGroup::findString(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_STRING)) + return control->get().asString(); + return LLString::null; +} + +LLString LLControlGroup::getString(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_STRING)) + return control->get().asString(); + else + { + CONTROL_ERRS << "Invalid string control " << name << llendl; + return LLString::null; + } +} + +LLWString LLControlGroup::getWString(const LLString& name) +{ + return utf8str_to_wstring(getString(name)); +} + +LLString LLControlGroup::getText(const LLString& name) +{ + LLString utf8_string = getString(name); + LLString::replaceChar(utf8_string, '^', '\n'); + LLString::replaceChar(utf8_string, '%', ' '); + return (utf8_string); +} + +LLVector3 LLControlGroup::getVector3(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_VEC3)) + return control->get(); + else + { + CONTROL_ERRS << "Invalid LLVector3 control " << name << llendl; + return LLVector3::zero; + } +} + +LLVector3d LLControlGroup::getVector3d(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_VEC3D)) + return control->get(); + else + { + CONTROL_ERRS << "Invalid LLVector3d control " << name << llendl; + return LLVector3d::zero; + } +} + +LLRect LLControlGroup::getRect(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_RECT)) + return control->get(); + else + { + CONTROL_ERRS << "Invalid rect control " << name << llendl; + return LLRect::null; + } +} + + +LLColor4 LLControlGroup::getColor(const LLString& name) +{ + ctrl_name_table_t::const_iterator i = mNameTable.find(name); + + if (i != mNameTable.end()) + { + LLControlBase* control = i->second; + + switch(control->mType) + { + case TYPE_COL4: + { + return LLColor4(control->get()); + } + case TYPE_COL4U: + { + return LLColor4(LLColor4U(control->get())); + } + default: + { + CONTROL_ERRS << "Control " << name << " not a color" << llendl; + return LLColor4::white; + } + } + } + else + { + CONTROL_ERRS << "Invalid getColor control " << name << llendl; + return LLColor4::white; + } +} + +LLColor4U LLControlGroup::getColor4U(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_COL4U)) + return control->get(); + else + { + CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl; + return LLColor4U::white; + } +} + +LLColor4 LLControlGroup::getColor4(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_COL4)) + return control->get(); + else + { + CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl; + return LLColor4::white; + } +} + +LLColor3 LLControlGroup::getColor3(const LLString& name) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_COL3)) + return control->get(); + else + { + CONTROL_ERRS << "Invalid LLColor3 control " << name << llendl; + return LLColor3::white; + } +} + +BOOL LLControlGroup::controlExists(const LLString& name) +{ + void *control = mNameTable[name]; + + return (control != 0); +} + +//------------------------------------------------------------------- +// Set functions +//------------------------------------------------------------------- + +void LLControlGroup::setBOOL(const LLString& name, BOOL val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_BOOLEAN)) + { + control->set(val); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + + +void LLControlGroup::setS32(const LLString& name, S32 val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_S32)) + { + control->set(val); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + + +void LLControlGroup::setF32(const LLString& name, F32 val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_F32)) + { + control->set(val); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + + +void LLControlGroup::setU32(const LLString& name, U32 val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_U32)) + { + control->set((LLSD::Integer) val); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + + +void LLControlGroup::setString(const LLString& name, const LLString &val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_STRING)) + { + control->set(val); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + + +void LLControlGroup::setVector3(const LLString& name, const LLVector3 &val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_VEC3)) + { + control->set(val.getValue()); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + +void LLControlGroup::setVector3d(const LLString& name, const LLVector3d &val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_VEC3D)) + { + control->set(val.getValue()); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + +void LLControlGroup::setRect(const LLString& name, const LLRect &val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_RECT)) + { + control->set(val.getValue()); + } + else + { + CONTROL_ERRS << "Invalid rect control " << name << llendl; + } +} + +void LLControlGroup::setColor4U(const LLString& name, const LLColor4U &val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_COL4U)) + { + control->set(val.getValue()); + } + else + { + CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl; + } +} + +void LLControlGroup::setColor4(const LLString& name, const LLColor4 &val) +{ + LLControlBase* control = mNameTable[name]; + + if (control && control->isType(TYPE_COL4)) + { + control->set(val.getValue()); + } + else + { + CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl; + } +} + +void LLControlGroup::setValue(const LLString& name, const LLSD& val) +{ + if (name.empty()) + { + return; + } + + LLControlBase* control = mNameTable[name]; + + if (control) + { + control->set(val); + } + else + { + CONTROL_ERRS << "Invalid control " << name << llendl; + } +} + +//--------------------------------------------------------------- +// Load and save +//--------------------------------------------------------------- + +U32 LLControlGroup::loadFromFileLegacy(const LLString& filename, BOOL require_declaration, eControlType declare_as) +{ + U32 item = 0; + U32 validitems = 0; + llifstream file; + S32 version; + + file.open(filename.c_str()); + + if (!file) + { + llinfos << "LLControlGroup::loadFromFile unable to open." << llendl; + return 0; + } + + // Check file version + LLString name; + file >> name; + file >> version; + if (name != "version" || version != CURRENT_VERSION) + { + llinfos << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << llendl; + return 0; + } + + while (!file.eof()) + { + file >> name; + + if (name.empty()) + { + continue; + } + + if (name.substr(0,2) == "//") + { + // This is a comment. + char buffer[MAX_STRING]; + file.getline(buffer, MAX_STRING); + continue; + } + + BOOL declared = mNameTable.find(name) != mNameTable.end(); + + if (require_declaration && !declared) + { + // Declaration required, but this name not declared. + // Complain about non-empty names. + if (!name.empty()) + { + //read in to end of line + char buffer[MAX_STRING]; + file.getline(buffer, MAX_STRING); + llwarns << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << llendl; + } + continue; + } + + // Got an item. Load it up. + item++; + + // If not declared, assume it's a string + if (!declared) + { + switch(declare_as) + { + case TYPE_COL4: + declareColor4(name, LLColor4::white, LLString::null, NO_PERSIST); + break; + case TYPE_COL4U: + declareColor4U(name, LLColor4U::white, LLString::null, NO_PERSIST); + break; + case TYPE_STRING: + default: + declareString(name, LLString::null, LLString::null, NO_PERSIST); + break; + } + } + + // Control name has been declared in code. + LLControlBase *control = getControl(name); + + llassert(control); + + switch(control->mType) + { + case TYPE_F32: + { + F32 initial; + + file >> initial; + + control->set(initial); + validitems++; + } + break; + case TYPE_S32: + { + S32 initial; + + file >> initial; + + control->set(initial); + validitems++; + } + break; + case TYPE_U32: + { + U32 initial; + + file >> initial; + control->set((LLSD::Integer) initial); + validitems++; + } + break; + case TYPE_BOOLEAN: + { + char boolstring[256]; + BOOL valid = FALSE; + BOOL initial = FALSE; + + file >> boolstring; + if (!strcmp("TRUE", boolstring)) + { + initial = TRUE; + valid = TRUE; + } + else if (!strcmp("FALSE", boolstring)) + { + initial = FALSE; + valid = TRUE; + } + + if (valid) + { + control->set(initial); + } + else + { + llinfos << filename << "Item " << item << ": Invalid BOOL control " << name << ", " << boolstring << llendl; + } + + validitems++; + } + break; + case TYPE_STRING: + { + LLString string; + + file >> string; + + control->set(string); + validitems++; + } + break; + case TYPE_VEC3: + { + F32 x, y, z; + + file >> x >> y >> z; + + LLVector3 vector(x, y, z); + + control->set(vector.getValue()); + validitems++; + } + break; + case TYPE_VEC3D: + { + F64 x, y, z; + + file >> x >> y >> z; + + LLVector3d vector(x, y, z); + + control->set(vector.getValue()); + validitems++; + } + break; + case TYPE_RECT: + { + S32 left, bottom, width, height; + + file >> left >> bottom >> width >> height; + + LLRect rect; + rect.setOriginAndSize(left, bottom, width, height); + + control->set(rect.getValue()); + validitems++; + } + break; + case TYPE_COL4U: + { + S32 red, green, blue, alpha; + LLColor4U color; + file >> red >> green >> blue >> alpha; + color.setVec(red, green, blue, alpha); + control->set(color.getValue()); + validitems++; + } + break; + case TYPE_COL4: + { + LLColor4 color; + file >> color.mV[VRED] >> color.mV[VGREEN] + >> color.mV[VBLUE] >> color.mV[VALPHA]; + control->set(color.getValue()); + validitems++; + } + break; + case TYPE_COL3: + { + LLColor3 color; + file >> color.mV[VRED] >> color.mV[VGREEN] + >> color.mV[VBLUE]; + control->set(color.getValue()); + validitems++; + } + break; + } + } + + file.close(); + + return validitems; +} + +// Returns number of controls loaded, so 0 if failure +U32 LLControlGroup::loadFromFile(const LLString& filename, BOOL require_declaration, eControlType declare_as) +{ + LLString name; + + LLXmlTree xml_controls; + + if (!xml_controls.parseFile(filename)) + { + llwarns << "Unable to open control file " << filename << llendl; + return 0; + } + + LLXmlTreeNode* rootp = xml_controls.getRoot(); + if (!rootp || !rootp->hasAttribute("version")) + { + llwarns << "No valid settings header found in control file " << filename << llendl; + return 0; + } + + U32 item = 0; + U32 validitems = 0; + S32 version; + + rootp->getAttributeS32("version", version); + + // Check file version + if (version != CURRENT_VERSION) + { + llinfos << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << llendl; + return 0; + } + + LLXmlTreeNode* child_nodep = rootp->getFirstChild(); + while(child_nodep) + { + name = child_nodep->getName(); + + BOOL declared = (mNameTable[name].notNull()); + + if (require_declaration && !declared) + { + // Declaration required, but this name not declared. + // Complain about non-empty names. + if (!name.empty()) + { + //read in to end of line + llwarns << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << llendl; + } + child_nodep = rootp->getNextChild(); + continue; + } + + // Got an item. Load it up. + item++; + + // If not declared, assume it's a string + if (!declared) + { + switch(declare_as) + { + case TYPE_COL4: + declareColor4(name, LLColor4::white, "", NO_PERSIST); + break; + case TYPE_COL4U: + declareColor4U(name, LLColor4U::white, "", NO_PERSIST); + break; + case TYPE_STRING: + default: + declareString(name, LLString::null, "", NO_PERSIST); + break; + } + } + + // Control name has been declared in code. + LLControlBase *control = getControl(name); + + llassert(control); + + switch(control->mType) + { + case TYPE_F32: + { + F32 initial = 0.f; + + child_nodep->getAttributeF32("value", initial); + + control->set(initial); + validitems++; + } + break; + case TYPE_S32: + { + S32 initial = 0; + + child_nodep->getAttributeS32("value", initial); + + control->set(initial); + validitems++; + } + break; + case TYPE_U32: + { + U32 initial = 0; + child_nodep->getAttributeU32("value", initial); + control->set((LLSD::Integer) initial); + validitems++; + } + break; + case TYPE_BOOLEAN: + { + BOOL initial = FALSE; + + child_nodep->getAttributeBOOL("value", initial); + control->set(initial); + + validitems++; + } + break; + case TYPE_STRING: + { + LLString string; + child_nodep->getAttributeString("value", string); + if (string == LLString::null) + { + string = ""; + } + control->set(string); + validitems++; + } + break; + case TYPE_VEC3: + { + LLVector3 vector; + + child_nodep->getAttributeVector3("value", vector); + control->set(vector.getValue()); + validitems++; + } + break; + case TYPE_VEC3D: + { + LLVector3d vector; + + child_nodep->getAttributeVector3d("value", vector); + + control->set(vector.getValue()); + validitems++; + } + break; + case TYPE_RECT: + { + //RN: hack to support reading rectangles from a string + LLString rect_string; + + child_nodep->getAttributeString("value", rect_string); + std::istringstream istream(rect_string); + S32 left, bottom, width, height; + + istream >> left >> bottom >> width >> height; + + LLRect rect; + rect.setOriginAndSize(left, bottom, width, height); + + control->set(rect.getValue()); + validitems++; + } + break; + case TYPE_COL4U: + { + LLColor4U color; + + child_nodep->getAttributeColor4U("value", color); + control->set(color.getValue()); + validitems++; + } + break; + case TYPE_COL4: + { + LLColor4 color; + + child_nodep->getAttributeColor4("value", color); + control->set(color.getValue()); + validitems++; + } + break; + case TYPE_COL3: + { + LLVector3 color; + + child_nodep->getAttributeVector3("value", color); + control->set(LLColor3(color.mV).getValue()); + validitems++; + } + break; + } + + child_nodep = rootp->getNextChild(); + } + + return validitems; +} + + +U32 LLControlGroup::saveToFile(const LLString& filename, BOOL nondefault_only) +{ + const char ENDL = '\n'; + + llinfos << "Saving settings to file: " << filename << llendl; + + // place the objects in a temporary container that enforces a sort + // order to ease manual editing of the file + LLLinkedList< LLControlBase > controls; + controls.setInsertBefore( &control_insert_before ); + LLString name; + for (ctrl_name_table_t::iterator iter = mNameTable.begin(); + iter != mNameTable.end(); iter++) + { + name = iter->first; + if (name.empty()) + { + CONTROL_ERRS << "Control with no name found!!!" << llendl; + break; + } + + LLControlBase* control = (LLControlBase *)mNameTable[name]; + + if (!control) + { + llwarns << "Tried to save invalid control: " << name << llendl; + } + + if( control && control->mPersist ) + { + if (!(nondefault_only && (control->mIsDefault))) + { + controls.addDataSorted( control ); + } + else + { + // Debug spam + // llinfos << "Skipping " << control->getName() << llendl; + } + } + } + + llofstream file; + file.open(filename.c_str()); + + if (!file.is_open()) + { + // This is a warning because sometime we want to use settings files which can't be written... + llwarns << "LLControlGroup::saveToFile unable to open file for writing" << llendl; + return 0; + } + + // Write file version + file << "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n"; + file << "<settings version = \"" << CURRENT_VERSION << "\">\n"; + for( LLControlBase* control = controls.getFirstData(); + control != NULL; + control = controls.getNextData() ) + { + file << "\t<!--" << control->comment() << "-->" << ENDL; + name = control->name(); + switch (control->type()) + { + case TYPE_U32: + { + file << "\t<" << name << " value=\"" << (U32) control->get().asInteger() << "\"/>\n"; + break; + } + case TYPE_S32: + { + file << "\t<" << name << " value=\"" << (S32) control->get().asInteger() << "\"/>\n"; + break; + } + case TYPE_F32: + { + file << "\t<" << name << " value=\"" << (F32) control->get().asReal() << "\"/>\n"; + break; + } + case TYPE_VEC3: + { + LLVector3 vector(control->get()); + file << "\t<" << name << " value=\"" << vector.mV[VX] << " " << vector.mV[VY] << " " << vector.mV[VZ] << "\"/>\n"; + break; + } + case TYPE_VEC3D: + { + LLVector3d vector(control->get()); + file << "\t<" << name << " value=\"" << vector.mdV[VX] << " " << vector.mdV[VY] << " " << vector.mdV[VZ] << "\"/>\n"; + break; + } + case TYPE_RECT: + { + LLRect rect(control->get()); + file << "\t<" << name << " value=\"" << rect.mLeft << " " << rect.mBottom << " " << rect.getWidth() << " " << rect.getHeight() << "\"/>\n"; + break; + } + case TYPE_COL4: + { + LLColor4 color(control->get()); + file << "\t<" << name << " value=\"" << color.mV[VRED] << ", " << color.mV[VGREEN] << ", " << color.mV[VBLUE] << ", " << color.mV[VALPHA] << "\"/>\n"; + break; + } + case TYPE_COL3: + { + LLColor3 color(control->get()); + file << "\t<" << name << " value=\"" << color.mV[VRED] << ", " << color.mV[VGREEN] << ", " << color.mV[VBLUE] << "\"/>\n"; + break; + } + case TYPE_BOOLEAN: + { + file << "\t<" << name << " value=\"" << (control->get().asBoolean() ? "TRUE" : "FALSE") << "\"/>\n"; + break; + } + case TYPE_STRING: + { + file << "\t<" << name << " value=\"" << LLSDXMLFormatter::escapeString(control->get().asString()) << "\"/>\n"; + break; + } + default: + { + CONTROL_ERRS << "LLControlGroup::saveToFile - unknown control type!" << llendl; + break; + } + } + + // Debug spam + // llinfos << name << " " << control->getValue().asString() << llendl; + }// next + + file << "</settings>\n"; + file.close(); + + return controls.getLength(); +} + +void LLControlGroup::applyOverrides(const std::map<std::string, std::string>& overrides) +{ + for (std::map<std::string, std::string>::const_iterator iter = overrides.begin(); + iter != overrides.end(); ++iter) + { + const std::string& command = iter->first; + const std::string& value = iter->second; + LLControlBase* control = (LLControlBase *)mNameTable[command]; + if (control) + { + switch(control->mType) + { + case TYPE_U32: + control->set((LLSD::Integer)atof(value.c_str())); + break; + case TYPE_S32: + control->set((S32)atof(value.c_str())); + break; + case TYPE_F32: + control->set((F32)atof(value.c_str())); + break; + case TYPE_BOOLEAN: + if (!LLString::compareInsensitive(value.c_str(), "TRUE")) + { + control->set(TRUE); + } + else if (!LLString::compareInsensitive(value.c_str(), "FALSE")) + { + control->set(FALSE); + } + else + { + control->set((BOOL)atof(value.c_str())); + } + break; + case TYPE_STRING: + control->set(value); + break; +// // *FIX: implement this given time and need. +// case TYPE_UUID: +// break; + // we don't support command line overrides of vec3 or col4 + // yet - requires parsing of multiple values + case TYPE_VEC3: + case TYPE_VEC3D: + case TYPE_COL4: + case TYPE_COL3: + default: + break; + } + } + else + { + llinfos << "There is no control variable " << command << llendl; + } + } +} + +void LLControlGroup::resetToDefaults() +{ + ctrl_name_table_t::iterator control_iter; + for (control_iter = mNameTable.begin(); + control_iter != mNameTable.end(); + ++control_iter) + { + LLControlBase* control = (*control_iter).second; + control->resetToDefault(); + } +} + +//============================================================================ +// FIrst-use + + +void LLControlGroup::addWarning(const LLString& name) +{ + LLString warnname = "Warn" + name; + if(!mNameTable[warnname]) + { + LLString comment = LLString("Enables ") + name + LLString(" warning dialog"); + declareBOOL(warnname, TRUE, comment); + mWarnings.insert(warnname); + } +} + +BOOL LLControlGroup::getWarning(const LLString& name) +{ + LLString warnname = "Warn" + name; + return getBOOL(warnname); +} + +void LLControlGroup::setWarning(const LLString& name, BOOL val) +{ + LLString warnname = "Warn" + name; + setBOOL(warnname, val); +} + +void LLControlGroup::resetWarnings() +{ + for (std::set<LLString>::iterator iter = mWarnings.begin(); + iter != mWarnings.end(); ++iter) + { + setBOOL(*iter, TRUE); + } +} + + + +//============================================================================= +// Listener ID generator/management + +void LLControlBase::releaseListenerID(S32 id) +{ + mFreeIDs.push_back(id); +} + +S32 LLControlBase::allocateListenerID() +{ + if(mFreeIDs.size() == 0) + { //Out of IDs so generate some new ones. + for(int t=0;t<32;t++) + { + mFreeIDs.push_back(mTopID++); + } + } + S32 rtn = mFreeIDs.front(); + mFreeIDs.pop_front(); + mUsedIDs.push_back(rtn); + return rtn; +} + +bool LLControlBase::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) +{ + if (event->desc() == "value_changed") + { + setValue(((LLValueChangedEvent*)(LLEvent*)event)->mValue); + return TRUE; + } + return TRUE; +} + +void LLControlBase::firePropertyChanged() +{ + LLValueChangedEvent *evt = new LLValueChangedEvent(this, getValue()); + fireEvent(evt, ""); +} + +//============================================================================ +// Used to add a listener callback that will be called on the frame that the controls value changes + +S32 LLControl::addListener(LLControl::tListenerCallback* cbfn) +{ + S32 id = allocateListenerID(); + mListeners.push_back(cbfn); + mListenerIDs.push_back( id ); + return id; +} + +void LLControl::updateListeners() { + LLControl::tPropertyChangedListIter iter = mChangeEvents.begin(); + while(iter!=mChangeEvents.end()){ + LLControl::tPropertyChangedEvent& evt = *iter; + (*evt.mCBFN)(evt.mNewValue,evt.mID,*this); + iter++; + } + mChangeEvents.clear(); +} + +//static +void LLControlBase::updateAllListeners() +{ + std::set< LLControlBase* >::iterator iter = mChangedControls.begin(); + while(iter != mChangedControls.end()){ + (*iter)->updateListeners(); + iter++; + } + mChangedControls.clear(); +} + +LLControl::LLControl( + const LLString& name, + eControlType type, + LLSD initial, + const LLString& comment, + BOOL persist) : + LLControlBase(name, type, comment, persist), + mCurrent(initial), + mDefault(initial) +{ +} + +//============================================================================ + +#ifdef TEST_HARNESS +void main() +{ + F32_CONTROL foo, getfoo; + + S32_CONTROL bar, getbar; + + BOOL_CONTROL baz; + + U32 count = gGlobals.loadFromFile("controls.ini"); + llinfos << "Loaded " << count << " controls" << llendl; + + // test insertion + foo = new LLControl<F32>("gFoo", 5.f, 1.f, 20.f); + gGlobals.addEntry("gFoo", foo); + + bar = new LLControl<S32>("gBar", 10, 2, 22); + gGlobals.addEntry("gBar", bar); + + baz = new LLControl<BOOL>("gBaz", FALSE); + gGlobals.addEntry("gBaz", baz); + + // test retrieval + getfoo = (LLControl<F32>*) gGlobals.resolveName("gFoo"); + getfoo->dump(); + + getbar = (S32_CONTROL) gGlobals.resolveName("gBar"); + getbar->dump(); + + // change data + getfoo->set(10.f); + getfoo->dump(); + + // Failure modes + + // ...min > max + // badfoo = new LLControl<F32>("gFoo2", 100.f, 20.f, 5.f); + + // ...initial > max + // badbar = new LLControl<S32>("gBar2", 10, 20, 100000); + + // ...misspelled name + // getfoo = (F32_CONTROL) gGlobals.resolveName("fooMisspelled"); + // getfoo->dump(); + + // ...invalid data type + getfoo = (F32_CONTROL) gGlobals.resolveName("gFoo"); + getfoo->set(TRUE); + getfoo->dump(); + + // ...out of range data + // getfoo->set(100000000.f); + // getfoo->dump(); + + // Clean Up + delete foo; + delete bar; + delete baz; +} +#endif + +BOOL control_insert_before( LLControlBase* first, LLControlBase* second ) +{ + return ( first->getName().compare(second->getName()) < 0 ); +} + diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h new file mode 100644 index 0000000000..b88f388a0b --- /dev/null +++ b/indra/llxml/llcontrol.h @@ -0,0 +1,255 @@ +/** + * @file llcontrol.h + * @brief A mechanism for storing "control state" for a program + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLCONTROL_H +#define LL_LLCONTROL_H + +#include "llevent.h" +#include "llnametable.h" +#include "llmap.h" +#include "llstring.h" +#include "llrect.h" + +class LLVector3; +class LLVector3d; +class LLColor4; +class LLColor3; +class LLColor4U; + +const BOOL NO_PERSIST = FALSE; + +typedef enum e_control_type +{ + TYPE_U32, + TYPE_S32, + TYPE_F32, + TYPE_BOOLEAN, + TYPE_STRING, + TYPE_VEC3, + TYPE_VEC3D, + TYPE_RECT, + TYPE_COL4, + TYPE_COL3, + TYPE_COL4U +} eControlType; + +class LLControlBase : public LLSimpleListenerObservable +{ +friend class LLControlGroup; +protected: + LLString mName; + LLString mComment; + eControlType mType; + BOOL mHasRange; + BOOL mPersist; + BOOL mIsDefault; + static std::set<LLControlBase*> mChangedControls; + static std::list<S32> mFreeIDs;//These lists are used to store the ID's of registered event listeners. + static std::list<S32> mUsedIDs; + static S32 mTopID;//This is the index of the highest ID event listener ID. When the free pool is exhausted, new IDs are allocated from here. + +public: + static void releaseListenerID(S32 id); + static S32 allocateListenerID(); + static void updateAllListeners(); + virtual void updateListeners() = 0; + + LLControlBase(const LLString& name, eControlType type, const LLString& comment, BOOL persist) + : mName(name), + mComment(comment), + mType(type), + mHasRange(FALSE), + mPersist(persist), + mIsDefault(TRUE) + { + if (mPersist && mComment.empty()) + { + llerrs << "Must supply a comment for control " << mName << llendl; + } + sMaxControlNameLength = llmax((U32)mName.size(), sMaxControlNameLength); + } + + virtual ~LLControlBase(); + + const LLString& getName() const { return mName; } + const LLString& getComment() const { return mComment; } + + eControlType type() { return mType; } + BOOL isType(eControlType tp) { return tp == mType; } + + // Defaults to no-op + virtual void resetToDefault(); + + LLSD registerListener(LLSimpleListenerObservable *listener, LLSD userdata = ""); + + virtual LLSD get() const = 0; + virtual LLSD getValue() const = 0; + virtual void setValue(LLSD value) = 0; + virtual void set(LLSD value) = 0; + + // From LLSimpleListener + virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata); + + void firePropertyChanged(); + + static U32 sMaxControlNameLength; + +protected: + const char* name() { return mName.c_str(); } + const char* comment() { return mComment.c_str(); } +}; + +class LLControl +: public LLControlBase +{ +friend class LLControlGroup; +protected: + LLSD mCurrent; + LLSD mDefault; + +public: + + typedef void tListenerCallback(const LLSD& newValue,S32 listenerID, LLControl& control); + typedef struct{ + S32 mID; + LLSD mNewValue; + tListenerCallback* mCBFN; + }tPropertyChangedEvent; + + typedef std::list<tPropertyChangedEvent>::iterator tPropertyChangedListIter; + std::list<tPropertyChangedEvent> mChangeEvents; + std::list< tListenerCallback* > mListeners; + std::list< S32 > mListenerIDs; + + virtual void updateListeners(); + S32 addListener(tListenerCallback* cbfn); + + LLControl( + const LLString& name, + eControlType type, + LLSD initial, const + LLString& comment, + BOOL persist = TRUE); + + void set(LLSD val) { setValue(val); } + LLSD get() const { return getValue(); } + LLSD getdefault() const { return mDefault; } + LLSD getValue() const { return mCurrent; } + BOOL llsd_compare(const LLSD& a, const LLSD& b); + + void setValue(LLSD value) + { + if (llsd_compare(mCurrent, value) == FALSE) + { + mCurrent = value; + mIsDefault = llsd_compare(mCurrent, mDefault); + firePropertyChanged(); + } + } + + /*virtual*/ void resetToDefault() { mCurrent = mDefault; mIsDefault = TRUE;} + + virtual ~LLControl() + { + //Remove and deregister all listeners.. + while(mListenerIDs.size()) + { + S32 id = mListenerIDs.front(); + mListenerIDs.pop_front(); + releaseListenerID(id); + } + } +}; + +//const U32 STRING_CACHE_SIZE = 10000; +class LLControlGroup +{ +public: + typedef std::map<LLString, LLPointer<LLControlBase> > ctrl_name_table_t; + ctrl_name_table_t mNameTable; + std::set<LLString> mWarnings; + +public: + LLControlGroup(); + ~LLControlGroup(); + void cleanup(); + + LLControlBase* getControl(const LLString& name); + LLSD registerListener(const LLString& name, LLSimpleListenerObservable *listener); + + BOOL declareControl(const LLString& name, eControlType type, const LLSD initial_val, const LLString& comment, BOOL persist); + BOOL declareU32(const LLString& name, U32 initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareS32(const LLString& name, S32 initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareF32(const LLString& name, F32 initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareBOOL(const LLString& name, BOOL initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareString(const LLString& name, const LLString &initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareVec3(const LLString& name, const LLVector3 &initial_val,const LLString& comment, BOOL persist = TRUE); + BOOL declareVec3d(const LLString& name, const LLVector3d &initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareRect(const LLString& name, const LLRect &initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareColor4U(const LLString& name, const LLColor4U &initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareColor4(const LLString& name, const LLColor4 &initial_val, const LLString& comment, BOOL persist = TRUE); + BOOL declareColor3(const LLString& name, const LLColor3 &initial_val, const LLString& comment, BOOL persist = TRUE); + + LLString findString(const LLString& name); + + LLString getString(const LLString& name); + LLWString getWString(const LLString& name); + LLString getText(const LLString& name); + LLVector3 getVector3(const LLString& name); + LLVector3d getVector3d(const LLString& name); + LLRect getRect(const LLString& name); + BOOL getBOOL(const LLString& name); + S32 getS32(const LLString& name); + F32 getF32(const LLString& name); + U32 getU32(const LLString& name); + LLSD getValue(const LLString& name); + + + // Note: If an LLColor4U control exists, it will cast it to the correct + // LLColor4 for you. + LLColor4 getColor(const LLString& name); + LLColor4U getColor4U(const LLString& name); + LLColor4 getColor4(const LLString& name); + LLColor3 getColor3(const LLString& name); + + void setBOOL(const LLString& name, BOOL val); + void setS32(const LLString& name, S32 val); + void setF32(const LLString& name, F32 val); + void setU32(const LLString& name, U32 val); + void setString(const LLString& name, const LLString& val); + void setVector3(const LLString& name, const LLVector3 &val); + void setVector3d(const LLString& name, const LLVector3d &val); + void setRect(const LLString& name, const LLRect &val); + void setColor4U(const LLString& name, const LLColor4U &val); + void setColor4(const LLString& name, const LLColor4 &val); + void setColor3(const LLString& name, const LLColor3 &val); + void setValue(const LLString& name, const LLSD& val); + + BOOL controlExists(const LLString& name); + + // Returns number of controls loaded, 0 if failed + // If require_declaration is false, will auto-declare controls it finds + // as the given type. + U32 loadFromFileLegacy(const LLString& filename, BOOL require_declaration = TRUE, eControlType declare_as = TYPE_STRING); + U32 loadFromFile(const LLString& filename, BOOL require_declaration = TRUE, eControlType declare_as = TYPE_STRING); + U32 saveToFile(const LLString& filename, BOOL skip_if_default); + void applyOverrides(const std::map<std::string, std::string>& overrides); + void resetToDefaults(); + + // Ignorable Warnings + + // Add a config variable to be reset on resetWarnings() + void addWarning(const LLString& name); + BOOL getWarning(const LLString& name); + void setWarning(const LLString& name, BOOL val); + + // Resets all ignorables + void resetWarnings(); +}; + +#endif diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp new file mode 100644 index 0000000000..7d77fa8be7 --- /dev/null +++ b/indra/llxml/llxmlnode.cpp @@ -0,0 +1,3008 @@ +/** + * @file llxmlnode.cpp + * @author Tom Yedwab + * @brief LLXMLNode implementation + * + * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include <iostream> +#include <map> + +#include "llxmlnode.h" + +#include "v3color.h" +#include "v4color.h" +#include "v4coloru.h" +#include "v3math.h" +#include "v3dmath.h" +#include "v4math.h" +#include "llquaternion.h" +#include "lluuid.h" + +const S32 MAX_COLUMN_WIDTH = 80; + +// static +BOOL LLXMLNode::sStripEscapedStrings = TRUE; +BOOL LLXMLNode::sStripWhitespaceValues = FALSE; + +LLXMLNode::LLXMLNode() : + mID(""), + mIsAttribute(FALSE), + mVersionMajor(0), + mVersionMinor(0), + mLength(0), + mPrecision(64), + mType(TYPE_CONTAINER), + mEncoding(ENCODING_DEFAULT), + mParent(NULL), + mChildren(NULL), + mName(NULL), + mValue(""), + mDefault(NULL) +{ +} + +LLXMLNode::LLXMLNode(const LLString& name, BOOL is_attribute) : + mID(""), + mIsAttribute(is_attribute), + mVersionMajor(0), + mVersionMinor(0), + mLength(0), + mPrecision(64), + mType(TYPE_CONTAINER), + mEncoding(ENCODING_DEFAULT), + mParent(NULL), + mChildren(NULL), + mValue(""), + mDefault(NULL) +{ + mName = gStringTable.addStringEntry(name); +} + +LLXMLNode::LLXMLNode(LLStringTableEntry* name, BOOL is_attribute) : + mID(""), + mIsAttribute(is_attribute), + mVersionMajor(0), + mVersionMinor(0), + mLength(0), + mPrecision(64), + mType(TYPE_CONTAINER), + mEncoding(ENCODING_DEFAULT), + mParent(NULL), + mChildren(NULL), + mName(name), + mValue(""), + mDefault(NULL) +{ +} + +// virtual +LLXMLNode::~LLXMLNode() +{ + // Strictly speaking none of this should be required execept 'delete mChildren'... + if (mChildren) + { + for (LLXMLChildList::iterator iter = mChildren->map.begin(); + iter != mChildren->map.end(); ++iter) + { + LLXMLNodePtr child = iter->second; + child->mParent = NULL; + child->mNext = NULL; + child->mPrev = NULL; + } + mChildren->map.clear(); + mChildren->head = NULL; + mChildren->tail = NULL; + delete mChildren; + } + for (LLXMLAttribList::iterator iter = mAttributes.begin(); + iter != mAttributes.end(); ++iter) + { + LLXMLNodePtr attr = iter->second; + attr->mParent = NULL; + attr->mNext = NULL; + attr->mPrev = NULL; + } + llassert(mParent == NULL); + mDefault = NULL; +} + +BOOL LLXMLNode::isNull() +{ + return (mName == NULL); +} + +// protected +BOOL LLXMLNode::removeChild(LLXMLNode *target_child) +{ + if (!target_child) + { + return FALSE; + } + if (target_child->mIsAttribute) + { + LLXMLAttribList::iterator children_itr = mAttributes.find(target_child->mName); + if (children_itr != mAttributes.end()) + { + target_child->mParent = NULL; + mAttributes.erase(children_itr); + return TRUE; + } + } + else if (mChildren) + { + LLXMLChildList::iterator children_itr = mChildren->map.find(target_child->mName); + while (children_itr != mChildren->map.end()) + { + if (target_child == children_itr->second) + { + if (target_child == mChildren->head) + { + mChildren->head = target_child->mNext; + } + + LLXMLNodePtr prev = target_child->mPrev; + LLXMLNodePtr next = target_child->mNext; + if (prev.notNull()) prev->mNext = next; + if (next.notNull()) next->mPrev = prev; + + target_child->mPrev = NULL; + target_child->mNext = NULL; + target_child->mParent = NULL; + mChildren->map.erase(children_itr); + if (mChildren->map.empty()) + { + delete mChildren; + mChildren = NULL; + } + return TRUE; + } + else if (children_itr->first != target_child->mName) + { + break; + } + else + { + ++children_itr; + } + } + } + return FALSE; +} + +void LLXMLNode::addChild(LLXMLNodePtr new_child) +{ + if (new_child->mParent != NULL) + { + if (new_child->mParent == this) + { + return; + } + new_child->mParent->removeChild(new_child); + } + + new_child->mParent = this; + if (new_child->mIsAttribute) + { + mAttributes.insert(std::pair<LLStringTableEntry*, LLXMLNodePtr>(new_child->mName, new_child)); + } + else + { + if (!mChildren) + { + mChildren = new LLXMLChildren(); + mChildren->head = new_child; + mChildren->tail = new_child; + } + mChildren->map.insert(std::pair<LLStringTableEntry*, LLXMLNodePtr>(new_child->mName, new_child)); + + if (mChildren->tail != new_child) + { + mChildren->tail->mNext = new_child; + new_child->mPrev = mChildren->tail; + mChildren->tail = new_child; + } + } + + new_child->updateDefault(); +} + +// virtual +LLXMLNodePtr LLXMLNode::createChild(const LLString& name, BOOL is_attribute) +{ + return createChild(gStringTable.addStringEntry(name), is_attribute); +} + +// virtual +LLXMLNodePtr LLXMLNode::createChild(LLStringTableEntry* name, BOOL is_attribute) +{ + LLXMLNode* ret = new LLXMLNode(name, is_attribute); + ret->mID = ""; + addChild(ret); + return ret; +} + +BOOL LLXMLNode::deleteChild(LLXMLNode *child) +{ + if (removeChild(child)) + { + return TRUE; + } + return FALSE; +} + +void LLXMLNode::setParent(LLXMLNodePtr new_parent) +{ + if (new_parent.notNull()) + { + new_parent->addChild(this); + } + else + { + if (mParent != NULL) + { + LLXMLNodePtr old_parent = mParent; + mParent = NULL; + old_parent->removeChild(this); + } + } +} + + +void LLXMLNode::updateDefault() +{ + if (mParent != NULL && !mParent->mDefault.isNull()) + { + mDefault = NULL; + + // Find default value in parent's default tree + if (!mParent->mDefault.isNull()) + { + findDefault(mParent->mDefault); + } + } + + if (mChildren) + { + LLXMLChildList::const_iterator children_itr; + LLXMLChildList::const_iterator children_end = mChildren->map.end(); + for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr) + { + LLXMLNodePtr child = (*children_itr).second; + child->updateDefault(); + } + } +} + +void XMLCALL StartXMLNode(void *userData, + const XML_Char *name, + const XML_Char **atts) +{ + // Create a new node + LLXMLNode *new_node_ptr = new LLXMLNode(name, FALSE); + LLXMLNodePtr new_node = new_node_ptr; + new_node->mID = ""; + LLXMLNodePtr ptr_new_node = new_node; + + // Set the parent-child relationship with the current active node + LLXMLNode* parent = (LLXMLNode *)userData; + + new_node_ptr->mParser = parent->mParser; + + // Set the current active node to the new node + XML_Parser *parser = parent->mParser; + XML_SetUserData(*parser, (void *)new_node_ptr); + + // Parse attributes + U32 pos = 0; + while (atts[pos] != NULL) + { + LLString attr_name = atts[pos]; + LLString attr_value = atts[pos+1]; + + // Special cases + if ('i' == attr_name[0] && "id" == attr_name) + { + new_node->mID = attr_value; + } + else if ('v' == attr_name[0] && "version" == attr_name) + { + U32 version_major = 0; + U32 version_minor = 0; + if (sscanf(attr_value.c_str(), "%d.%d", &version_major, &version_minor) > 0) + { + new_node->mVersionMajor = version_major; + new_node->mVersionMinor = version_minor; + } + } + else if (('s' == attr_name[0] && "size" == attr_name) || ('l' == attr_name[0] && "length" == attr_name)) + { + U32 length; + if (sscanf(attr_value.c_str(), "%d", &length) > 0) + { + new_node->mLength = length; + } + } + else if ('p' == attr_name[0] && "precision" == attr_name) + { + U32 precision; + if (sscanf(attr_value.c_str(), "%d", &precision) > 0) + { + new_node->mPrecision = precision; + } + } + else if ('t' == attr_name[0] && "type" == attr_name) + { + if ("boolean" == attr_value) + { + new_node->mType = LLXMLNode::TYPE_BOOLEAN; + } + else if ("integer" == attr_value) + { + new_node->mType = LLXMLNode::TYPE_INTEGER; + } + else if ("float" == attr_value) + { + new_node->mType = LLXMLNode::TYPE_FLOAT; + } + else if ("string" == attr_value) + { + new_node->mType = LLXMLNode::TYPE_STRING; + } + else if ("uuid" == attr_value) + { + new_node->mType = LLXMLNode::TYPE_UUID; + } + else if ("noderef" == attr_value) + { + new_node->mType = LLXMLNode::TYPE_NODEREF; + } + } + else if ('e' == attr_name[0] && "encoding" == attr_name) + { + if ("decimal" == attr_value) + { + new_node->mEncoding = LLXMLNode::ENCODING_DECIMAL; + } + else if ("hex" == attr_value) + { + new_node->mEncoding = LLXMLNode::ENCODING_HEX; + } + /*else if (attr_value == "base32") + { + new_node->mEncoding = LLXMLNode::ENCODING_BASE32; + }*/ + } + + // only one attribute child per description + LLXMLNodePtr attr_node; + if (!new_node->getAttribute(attr_name, attr_node, FALSE)) + { + attr_node = new LLXMLNode(attr_name, TRUE); + } + attr_node->setValue(attr_value); + new_node->addChild(attr_node); + + pos += 2; + } + + if (parent) + { + parent->addChild(new_node); + } +} + +void XMLCALL EndXMLNode(void *userData, + const XML_Char *name) +{ + // [FUGLY] Set the current active node to the current node's parent + LLXMLNode *node = (LLXMLNode *)userData; + XML_Parser *parser = node->mParser; + XML_SetUserData(*parser, (void *)node->mParent); + // SJB: total hack: + if (LLXMLNode::sStripWhitespaceValues) + { + LLString value = node->getValue(); + BOOL is_empty = TRUE; + for (std::string::size_type s = 0; s < value.length(); s++) + { + char c = value[s]; + if (c != ' ' && c != '\t' && c != '\n') + { + is_empty = FALSE; + break; + } + } + if (is_empty) + { + value.clear(); + node->setValue(value); + } + } +} + +void XMLCALL XMLData(void *userData, + const XML_Char *s, + int len) +{ + LLXMLNode* current_node = (LLXMLNode *)userData; + LLString value = current_node->getValue(); + if (LLXMLNode::sStripEscapedStrings) + { + if (s[0] == '\"' && s[len-1] == '\"') + { + // Special-case: Escaped string. + LLString unescaped_string; + for (S32 pos=1; pos<len-1; ++pos) + { + if (s[pos] == '\\' && s[pos+1] == '\\') + { + unescaped_string.append("\\"); + ++pos; + } + else if (s[pos] == '\\' && s[pos+1] == '\"') + { + unescaped_string.append("\""); + ++pos; + } + else + { + unescaped_string.append(&s[pos], 1); + } + } + value.append(unescaped_string); + current_node->setValue(value); + return; + } + } + value.append(LLString(s, 0, len)); + current_node->setValue(value); +} + + + +// static +bool LLXMLNode::updateNode( + LLXMLNodePtr& node, + LLXMLNodePtr& update_node) +{ + + if (!node || !update_node) + { + llwarns << "Node invalid" << llendl; + return FALSE; + } + + //update the node value + node->mValue = update_node->mValue; + + //update all attribute values + LLXMLAttribList::const_iterator itor; + + for(itor = update_node->mAttributes.begin(); itor != update_node->mAttributes.end(); ++itor) + { + const LLStringTableEntry* attribNameEntry = (*itor).first; + LLXMLNodePtr updateAttribNode = (*itor).second; + + LLXMLNodePtr attribNode; + + node->getAttribute(attribNameEntry, attribNode, 0); + + if (attribNode) + { + attribNode->mValue = updateAttribNode->mValue; + } + } + + //update all of node's children with updateNodes children that match name + LLXMLNodePtr child; + LLXMLNodePtr updateChild; + + for (updateChild = update_node->getFirstChild(); updateChild.notNull(); + updateChild = updateChild->getNextSibling()) + { + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + LLString nodeName; + LLString updateName; + + updateChild->getAttributeString("name", updateName); + child->getAttributeString("name", nodeName); + + + //if it's a combobox there's no name, but there is a value + if (updateName.empty()) + { + updateChild->getAttributeString("value", updateName); + child->getAttributeString("value", nodeName); + } + + if ((nodeName != "") && (updateName == nodeName)) + { + updateNode(child, updateChild); + break; + } + } + } + + return TRUE; +} + + + + +// static +bool LLXMLNode::parseFile( + LLString filename, + LLXMLNodePtr& node, + LLXMLNode* defaults_tree) +{ + // Read file + FILE* fp = LLFile::fopen(filename.c_str(), "rb"); + if (fp == NULL) + { + node = new LLXMLNode(); + return false; + } + fseek(fp, 0, SEEK_END); + U32 length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + U8* buffer = new U8[length+1]; + fread(buffer, 1, length, fp); + buffer[length] = 0; + fclose(fp); + + bool rv = parseBuffer(buffer, length, node, defaults_tree); + delete [] buffer; + return rv; +} + +// static +bool LLXMLNode::parseBuffer( + U8* buffer, + U32 length, + LLXMLNodePtr& node, + LLXMLNode* defaults) +{ + // Init + XML_Parser my_parser = XML_ParserCreate(NULL); + XML_SetElementHandler(my_parser, StartXMLNode, EndXMLNode); + XML_SetCharacterDataHandler(my_parser, XMLData); + + // Create a root node + LLXMLNode *file_node_ptr = new LLXMLNode("XML", FALSE); + LLXMLNodePtr file_node = file_node_ptr; + + file_node->mParser = &my_parser; + + XML_SetUserData(my_parser, (void *)file_node_ptr); + + // Do the parsing + if (XML_Parse(my_parser, (const char *)buffer, length, TRUE) != XML_STATUS_OK) + { + llwarns << "Error parsing xml error code: " + << XML_ErrorString(XML_GetErrorCode(my_parser)) + << " on lne " << XML_GetCurrentLineNumber(my_parser) + << llendl; + } + + // Deinit + XML_ParserFree(my_parser); + + if (!file_node->mChildren || file_node->mChildren->map.size() != 1) + { + llwarns << "Parse failure - wrong number of top-level nodes xml." + << llendl; + node = new LLXMLNode(); + return false; + } + + LLXMLNode *return_node = file_node->mChildren->map.begin()->second; + + return_node->setDefault(defaults); + return_node->updateDefault(); + + node = return_node; + return true; +} + +BOOL LLXMLNode::isFullyDefault() +{ + if (mDefault.isNull()) + { + return FALSE; + } + BOOL has_default_value = (mValue == mDefault->mValue); + BOOL has_default_attribute = (mIsAttribute == mDefault->mIsAttribute); + BOOL has_default_type = mIsAttribute || (mType == mDefault->mType); + BOOL has_default_encoding = mIsAttribute || (mEncoding == mDefault->mEncoding); + BOOL has_default_precision = mIsAttribute || (mPrecision == mDefault->mPrecision); + BOOL has_default_length = mIsAttribute || (mLength == mDefault->mLength); + + if (has_default_value + && has_default_type + && has_default_encoding + && has_default_precision + && has_default_length + && has_default_attribute) + { + if (mChildren) + { + LLXMLChildList::const_iterator children_itr; + LLXMLChildList::const_iterator children_end = mChildren->map.end(); + for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr) + { + LLXMLNodePtr child = (*children_itr).second; + if (!child->isFullyDefault()) + { + return FALSE; + } + } + } + return TRUE; + } + + return FALSE; +} + +// static +void LLXMLNode::writeHeaderToFile(FILE *fOut) +{ + fprintf(fOut, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n"); +} + +void LLXMLNode::writeToFile(FILE *fOut, LLString indent) +{ + if (isFullyDefault()) + { + // Don't write out nodes that are an exact match to defaults + return; + } + + std::ostringstream ostream; + writeToOstream(ostream, indent); + LLString outstring = ostream.str(); + fwrite(outstring.c_str(), 1, outstring.length(), fOut); +} + +void LLXMLNode::writeToOstream(std::ostream& output_stream, const LLString& indent) +{ + if (isFullyDefault()) + { + // Don't write out nodes that are an exact match to defaults + return; + } + + BOOL has_default_type = mDefault.isNull()?FALSE:(mType == mDefault->mType); + BOOL has_default_encoding = mDefault.isNull()?FALSE:(mEncoding == mDefault->mEncoding); + BOOL has_default_precision = mDefault.isNull()?FALSE:(mPrecision == mDefault->mPrecision); + BOOL has_default_length = mDefault.isNull()?FALSE:(mLength == mDefault->mLength); + + // stream the name + output_stream << indent.c_str() << "<" << mName->mString; + + // ID + if (mID != "") + { + output_stream << " id=\"" << mID.c_str() << "\""; + } + + // Type + if (!has_default_type) + { + switch (mType) + { + case TYPE_BOOLEAN: + output_stream << " type=\"boolean\""; + break; + case TYPE_INTEGER: + output_stream << " type=\"integer\""; + break; + case TYPE_FLOAT: + output_stream << " type=\"float\""; + break; + case TYPE_STRING: + output_stream << " type=\"string\""; + break; + case TYPE_UUID: + output_stream << " type=\"uuid\""; + break; + case TYPE_NODEREF: + output_stream << " type=\"noderef\""; + break; + default: + // default on switch(enum) eliminates a warning on linux + break; + }; + } + + // Encoding + if (!has_default_encoding) + { + switch (mEncoding) + { + case ENCODING_DECIMAL: + output_stream << " encoding=\"decimal\""; + break; + case ENCODING_HEX: + output_stream << " encoding=\"hex\""; + break; + /*case ENCODING_BASE32: + output_stream << " encoding=\"base32\""; + break;*/ + default: + // default on switch(enum) eliminates a warning on linux + break; + }; + } + + // Precision + if (!has_default_precision && (mType == TYPE_INTEGER || mType == TYPE_FLOAT)) + { + output_stream << " precision=\"" << mPrecision << "\""; + } + + // Version + if (mVersionMajor > 0 || mVersionMinor > 0) + { + output_stream << " version=\"" << mVersionMajor << "." << mVersionMinor << "\""; + } + + // Array length + if (!has_default_length && mLength > 0) + { + output_stream << " length=\"" << mLength << "\""; + } + + { + // Write out attributes + S32 col_pos = 0; + LLXMLAttribList::const_iterator attr_itr; + LLXMLAttribList::const_iterator attr_end = mAttributes.end(); + for (attr_itr = mAttributes.begin(); attr_itr != attr_end; ++attr_itr) + { + LLXMLNodePtr child = (*attr_itr).second; + if (child->mDefault.isNull() || child->mDefault->mValue != child->mValue) + { + LLString attr = child->mName->mString; + if (attr == "id" || + attr == "type" || + attr == "encoding" || + attr == "precision" || + attr == "version" || + attr == "length") + { + continue; // skip built-in attributes + } + + LLString attr_str = llformat(" %s=\"%s\"", + attr.c_str(), + escapeXML(child->mValue).c_str()); + if (col_pos + (S32)attr_str.length() > MAX_COLUMN_WIDTH) + { + output_stream << "\n" << indent << " "; + col_pos = 4; + } + col_pos += attr_str.length(); + output_stream << attr_str; + } + } + } + + if (!mChildren && mValue == "") + { + output_stream << " />\n"; + return; + } + else + { + output_stream << ">\n"; + if (mChildren) + { + // stream non-attributes + LLString next_indent = indent + "\t"; + for (LLXMLNode* child = getFirstChild(); child; child = child->getNextSibling()) + { + child->writeToOstream(output_stream, next_indent); + } + } + if (!mValue.empty()) + { + LLString contents = getTextContents(); + output_stream << indent.c_str() << "\t" << escapeXML(contents) << "\n"; + } + output_stream << indent.c_str() << "</" << mName->mString << ">\n"; + } +} + +void LLXMLNode::findName(const LLString& name, LLXMLNodeList &results) +{ + LLStringTableEntry* name_entry = gStringTable.checkStringEntry(name); + if (name_entry == mName) + { + results.insert(std::pair<LLString, LLXMLNode*>(this->mName->mString, this)); + return; + } + if (mChildren) + { + LLXMLChildList::const_iterator children_itr; + LLXMLChildList::const_iterator children_end = mChildren->map.end(); + for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr) + { + LLXMLNodePtr child = (*children_itr).second; + child->findName(name_entry, results); + } + } +} + +void LLXMLNode::findName(LLStringTableEntry* name, LLXMLNodeList &results) +{ + if (name == mName) + { + results.insert(std::pair<LLString, LLXMLNode*>(this->mName->mString, this)); + return; + } + if (mChildren) + { + LLXMLChildList::const_iterator children_itr; + LLXMLChildList::const_iterator children_end = mChildren->map.end(); + for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr) + { + LLXMLNodePtr child = (*children_itr).second; + child->findName(name, results); + } + } +} + +void LLXMLNode::findID(const LLString& id, LLXMLNodeList &results) +{ + if (id == mID) + { + results.insert(std::pair<LLString, LLXMLNode*>(this->mName->mString, this)); + return; + } + if (mChildren) + { + LLXMLChildList::const_iterator children_itr; + LLXMLChildList::const_iterator children_end = mChildren->map.end(); + for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr) + { + LLXMLNodePtr child = (*children_itr).second; + child->findID(id, results); + } + } +} + +void LLXMLNode::scrubToTree(LLXMLNode *tree) +{ + if (!tree || !tree->mChildren) + { + return; + } + if (mChildren) + { + std::vector<LLXMLNodePtr> to_delete_list; + LLXMLChildList::iterator itor = mChildren->map.begin(); + while (itor != mChildren->map.end()) + { + LLXMLNodePtr child = itor->second; + LLXMLNodePtr child_tree = NULL; + // Look for this child in the default's children + bool found = false; + LLXMLChildList::iterator itor2 = tree->mChildren->map.begin(); + while (itor2 != tree->mChildren->map.end()) + { + if (child->mName == itor2->second->mName) + { + child_tree = itor2->second; + found = true; + } + ++itor2; + } + if (!found) + { + to_delete_list.push_back(child); + } + else + { + child->scrubToTree(child_tree); + } + ++itor; + } + std::vector<LLXMLNodePtr>::iterator itor3; + for (itor3=to_delete_list.begin(); itor3!=to_delete_list.end(); ++itor3) + { + (*itor3)->setParent(NULL); + } + } +} + +bool LLXMLNode::getChild(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing) +{ + return getChild(gStringTable.checkStringEntry(name), node, use_default_if_missing); +} + +bool LLXMLNode::getChild(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing) +{ + if (mChildren) + { + LLXMLChildList::const_iterator child_itr = mChildren->map.find(name); + if (child_itr != mChildren->map.end()) + { + node = (*child_itr).second; + return true; + } + } + if (use_default_if_missing && !mDefault.isNull()) + { + return mDefault->getChild(name, node, FALSE); + } + node = new LLXMLNode(); + return false; +} + +void LLXMLNode::getChildren(const LLString& name, LLXMLNodeList &children, BOOL use_default_if_missing) const +{ + getChildren(gStringTable.checkStringEntry(name), children, use_default_if_missing); +} + +void LLXMLNode::getChildren(const LLStringTableEntry* name, LLXMLNodeList &children, BOOL use_default_if_missing) const +{ + if (mChildren) + { + LLXMLChildList::const_iterator child_itr = mChildren->map.find(name); + if (child_itr != mChildren->map.end()) + { + LLXMLChildList::const_iterator children_end = mChildren->map.end(); + while (child_itr != children_end) + { + LLXMLNodePtr child = (*child_itr).second; + if (name != child->mName) + { + break; + } + children.insert(std::pair<LLString, LLXMLNodePtr>(child->mName->mString, child)); + child_itr++; + } + } + } + if (children.size() == 0 && use_default_if_missing && !mDefault.isNull()) + { + mDefault->getChildren(name, children, FALSE); + } +} + +bool LLXMLNode::getAttribute(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing) +{ + return getAttribute(gStringTable.checkStringEntry(name), node, use_default_if_missing); +} + +bool LLXMLNode::getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing) +{ + LLXMLAttribList::const_iterator child_itr = mAttributes.find(name); + if (child_itr != mAttributes.end()) + { + node = (*child_itr).second; + return true; + } + if (use_default_if_missing && !mDefault.isNull()) + { + return mDefault->getAttribute(name, node, FALSE); + } + node = new LLXMLNode(); + return false; +} + +bool LLXMLNode::setAttributeString(const LLString& attr, const LLString& value) +{ + LLStringTableEntry* name = gStringTable.checkStringEntry(attr); + LLXMLAttribList::const_iterator child_itr = mAttributes.find(name); + if (child_itr != mAttributes.end()) + { + LLXMLNodePtr node = (*child_itr).second; + node->setValue(value); + return true; + } + return false; +} + +BOOL LLXMLNode::hasAttribute(const LLString& name ) +{ + LLXMLNodePtr node; + return getAttribute(name, node); +} + +BOOL LLXMLNode::getAttributeBOOL(const LLString& name, BOOL& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getBoolValue(1, &value)); +} + +BOOL LLXMLNode::getAttributeU8(const LLString& name, U8& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getByteValue(1, &value)); +} + +BOOL LLXMLNode::getAttributeS8(const LLString& name, S8& value ) +{ + LLXMLNodePtr node; + S32 val; + if (!(getAttribute(name, node) && node->getIntValue(1, &val))) + { + return false; + } + value = val; + return true; +} + +BOOL LLXMLNode::getAttributeU16(const LLString& name, U16& value ) +{ + LLXMLNodePtr node; + U32 val; + if (!(getAttribute(name, node) && node->getUnsignedValue(1, &val))) + { + return false; + } + value = val; + return true; +} + +BOOL LLXMLNode::getAttributeS16(const LLString& name, S16& value ) +{ + LLXMLNodePtr node; + S32 val; + if (!(getAttribute(name, node) && node->getIntValue(1, &val))) + { + return false; + } + value = val; + return true; +} + +BOOL LLXMLNode::getAttributeU32(const LLString& name, U32& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getUnsignedValue(1, &value)); +} + +BOOL LLXMLNode::getAttributeS32(const LLString& name, S32& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getIntValue(1, &value)); +} + +BOOL LLXMLNode::getAttributeF32(const LLString& name, F32& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getFloatValue(1, &value)); +} + +BOOL LLXMLNode::getAttributeF64(const LLString& name, F64& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getDoubleValue(1, &value)); +} + +BOOL LLXMLNode::getAttributeColor(const LLString& name, LLColor4& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getFloatValue(4, value.mV)); +} + +BOOL LLXMLNode::getAttributeColor4(const LLString& name, LLColor4& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getFloatValue(4, value.mV)); +} + +BOOL LLXMLNode::getAttributeColor4U(const LLString& name, LLColor4U& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getByteValue(4, value.mV)); +} + +BOOL LLXMLNode::getAttributeVector3(const LLString& name, LLVector3& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getFloatValue(3, value.mV)); +} + +BOOL LLXMLNode::getAttributeVector3d(const LLString& name, LLVector3d& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getDoubleValue(3, value.mdV)); +} + +BOOL LLXMLNode::getAttributeQuat(const LLString& name, LLQuaternion& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getFloatValue(4, value.mQ)); +} + +BOOL LLXMLNode::getAttributeUUID(const LLString& name, LLUUID& value ) +{ + LLXMLNodePtr node; + return (getAttribute(name, node) && node->getUUIDValue(1, &value)); +} + +BOOL LLXMLNode::getAttributeString(const LLString& name, LLString& value ) +{ + LLXMLNodePtr node; + if (!getAttribute(name, node)) + { + return false; + } + value = node->getValue(); + return true; +} + +LLXMLNodePtr LLXMLNode::getRoot() +{ + if (mParent == NULL) + { + return this; + } + return mParent->getRoot(); +} + +/*static */ +const char *LLXMLNode::skipWhitespace(const char *str) +{ + // skip whitespace characters + while (str[0] == ' ' || str[0] == '\t' || str[0] == '\n') ++str; + return str; +} + +/*static */ +const char *LLXMLNode::skipNonWhitespace(const char *str) +{ + // skip non-whitespace characters + while (str[0] != ' ' && str[0] != '\t' && str[0] != '\n' && str[0] != 0) ++str; + return str; +} + +/*static */ +const char *LLXMLNode::parseInteger(const char *str, U64 *dest, BOOL *is_negative, U32 precision, Encoding encoding) +{ + *dest = 0; + *is_negative = FALSE; + + str = skipWhitespace(str); + + if (str[0] == 0) return NULL; + + if (encoding == ENCODING_DECIMAL || encoding == ENCODING_DEFAULT) + { + if (str[0] == '+') + { + ++str; + } + if (str[0] == '-') + { + *is_negative = TRUE; + ++str; + } + + str = skipWhitespace(str); + + U64 ret = 0; + while (str[0] >= '0' && str[0] <= '9') + { + ret *= 10; + ret += str[0] - '0'; + ++str; + } + + if (str[0] == '.') + { + // If there is a fractional part, skip it + str = skipNonWhitespace(str); + } + + *dest = ret; + return str; + } + if (encoding == ENCODING_HEX) + { + U64 ret = 0; + str = skipWhitespace(str); + for (U32 pos=0; pos<(precision/4); ++pos) + { + ret <<= 4; + str = skipWhitespace(str); + if (str[0] >= '0' && str[0] <= '9') + { + ret += str[0] - '0'; + } + else if (str[0] >= 'a' && str[0] <= 'f') + { + ret += str[0] - 'a' + 10; + } + else if (str[0] >= 'A' && str[0] <= 'F') + { + ret += str[0] - 'A' + 10; + } + else + { + return NULL; + } + ++str; + } + + *dest = ret; + return str; + } + return NULL; +} + +// 25 elements - decimal expansions of 1/(2^n), multiplied by 10 each iteration +const U64 float_coeff_table[] = + { 5, 25, 125, 625, 3125, + 15625, 78125, 390625, 1953125, 9765625, + 48828125, 244140625, 1220703125, 6103515625LL, 30517578125LL, + 152587890625LL, 762939453125LL, 3814697265625LL, 19073486328125LL, 95367431640625LL, + 476837158203125LL, 2384185791015625LL, 11920928955078125LL, 59604644775390625LL, 298023223876953125LL }; + +// 36 elements - decimal expansions of 1/(2^n) after the last 28, truncated, no multiply each iteration +const U64 float_coeff_table_2[] = + { 149011611938476562LL,74505805969238281LL, + 37252902984619140LL, 18626451492309570LL, 9313225746154785LL, 4656612873077392LL, + 2328306436538696LL, 1164153218269348LL, 582076609134674LL, 291038304567337LL, + 145519152283668LL, 72759576141834LL, 36379788070917LL, 18189894035458LL, + 9094947017729LL, 4547473508864LL, 2273736754432LL, 1136868377216LL, + 568434188608LL, 284217094304LL, 142108547152LL, 71054273576LL, + 35527136788LL, 17763568394LL, 8881784197LL, 4440892098LL, + 2220446049LL, 1110223024LL, 555111512LL, 277555756LL, + 138777878, 69388939, 34694469, 17347234, + 8673617, 4336808, 2168404, 1084202, + 542101, 271050, 135525, 67762, + }; + +/*static */ +const char *LLXMLNode::parseFloat(const char *str, F64 *dest, U32 precision, Encoding encoding) +{ + str = skipWhitespace(str); + + if (str[0] == 0) return NULL; + + if (encoding == ENCODING_DECIMAL || encoding == ENCODING_DEFAULT) + { + str = skipWhitespace(str); + + if (memcmp(str, "inf", 3) == 0) + { + *(U64 *)dest = 0x7FF0000000000000ll; + return str + 3; + } + if (memcmp(str, "-inf", 4) == 0) + { + *(U64 *)dest = 0xFFF0000000000000ll; + return str + 4; + } + if (memcmp(str, "1.#INF", 6) == 0) + { + *(U64 *)dest = 0x7FF0000000000000ll; + return str + 6; + } + if (memcmp(str, "-1.#INF", 7) == 0) + { + *(U64 *)dest = 0xFFF0000000000000ll; + return str + 7; + } + + F64 negative = 1.0f; + if (str[0] == '+') + { + ++str; + } + if (str[0] == '-') + { + negative = -1.0f; + ++str; + } + + const char* base_str = str; + str = skipWhitespace(str); + + // Parse the integer part of the expression + U64 int_part = 0; + while (str[0] >= '0' && str[0] <= '9') + { + int_part *= 10; + int_part += U64(str[0] - '0'); + ++str; + } + + U64 f_part = 0;//, f_decimal = 1; + if (str[0] == '.') + { + ++str; + U64 remainder = 0; + U32 pos = 0; + // Parse the decimal part of the expression + while (str[0] >= '0' && str[0] <= '9' && pos < 25) + { + remainder = (remainder*10) + U64(str[0] - '0'); + f_part <<= 1; + //f_decimal <<= 1; + // Check the n'th bit + if (remainder >= float_coeff_table[pos]) + { + remainder -= float_coeff_table[pos]; + f_part |= 1; + } + ++pos; + ++str; + } + if (pos == 25) + { + // Drop any excessive digits + while (str[0] >= '0' && str[0] <= '9') + { + ++str; + } + } + else + { + while (pos < 25) + { + remainder *= 10; + f_part <<= 1; + //f_decimal <<= 1; + // Check the n'th bit + if (remainder >= float_coeff_table[pos]) + { + remainder -= float_coeff_table[pos]; + f_part |= 1; + } + ++pos; + } + } + pos = 0; + while (pos < 36) + { + f_part <<= 1; + //f_decimal <<= 1; + if (remainder >= float_coeff_table_2[pos]) + { + remainder -= float_coeff_table_2[pos]; + f_part |= 1; + } + ++pos; + } + } + + F64 ret = F64(int_part) + (F64(f_part)/F64(1LL<<61)); + + F64 exponent = 1.f; + if (str[0] == 'e') + { + // Scientific notation! + ++str; + U64 exp; + BOOL is_negative; + str = parseInteger(str, &exp, &is_negative, 64, ENCODING_DECIMAL); + if (str == NULL) + { + exp = 1; + } + F64 exp_d = F64(exp) * (is_negative?-1:1); + exponent = pow(10.0, exp_d); + } + + if (str == base_str) + { + // no digits parsed + return NULL; + } + else + { + *dest = ret*negative*exponent; + return str; + } + } + if (encoding == ENCODING_HEX) + { + U64 bytes_dest; + BOOL is_negative; + str = parseInteger(str, (U64 *)&bytes_dest, &is_negative, precision, ENCODING_HEX); + // Upcast to F64 + switch (precision) + { + case 32: + { + U32 short_dest = (U32)bytes_dest; + F32 ret_val = *(F32 *)&short_dest; + *dest = ret_val; + } + break; + case 64: + *dest = *(F64 *)&bytes_dest; + break; + default: + return NULL; + } + return str; + } + return NULL; +} + +U32 LLXMLNode::getBoolValue(U32 expected_length, BOOL *array) +{ + llassert(array); + + // Check type - accept booleans or strings + if (mType != TYPE_BOOLEAN && mType != TYPE_STRING && mType != TYPE_UNKNOWN) + { + return 0; + } + + LLString *str_array = new LLString[expected_length]; + + U32 length = getStringValue(expected_length, str_array); + + U32 ret_length = 0; + for (U32 i=0; i<length; ++i) + { + LLString::toLower(str_array[i]); + if (str_array[i] == "false") + { + array[ret_length++] = FALSE; + } + else if (str_array[i] == "true") + { + array[ret_length++] = TRUE; + } + } + + delete[] str_array; + +#if LL_DEBUG + if (ret_length != expected_length) + { + lldebugs << "LLXMLNode::getBoolValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << ret_length << llendl; + } +#endif + return ret_length; +} + +U32 LLXMLNode::getByteValue(U32 expected_length, U8 *array, Encoding encoding) +{ + llassert(array); + + // Check type - accept bytes or integers (below 256 only) + if (mType != TYPE_INTEGER + && mType != TYPE_UNKNOWN) + { + return 0; + } + + if (mLength > 0 && mLength != expected_length) + { + llwarns << "XMLNode::getByteValue asked for " << expected_length + << " elements, while node has " << mLength << llendl; + return 0; + } + + if (encoding == ENCODING_DEFAULT) + { + encoding = mEncoding; + } + + const char *value_string = mValue.c_str(); + + U32 i; + for (i=0; i<expected_length; ++i) + { + U64 value; + BOOL is_negative; + value_string = parseInteger(value_string, &value, &is_negative, 8, encoding); + if (value_string == NULL) + { + break; + } + if (value > 255 || is_negative) + { + llwarns << "getByteValue: Value outside of valid range." << llendl; + break; + } + array[i] = U8(value); + } +#if LL_DEBUG + if (i != expected_length) + { + lldebugs << "LLXMLNode::getByteValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << i << llendl; + } +#endif + return i; +} + +U32 LLXMLNode::getIntValue(U32 expected_length, S32 *array, Encoding encoding) +{ + llassert(array); + + // Check type - accept bytes or integers + if (mType != TYPE_INTEGER && mType != TYPE_UNKNOWN) + { + return 0; + } + + if (mLength > 0 && mLength != expected_length) + { + llwarns << "XMLNode::getIntValue asked for " << expected_length + << " elements, while node has " << mLength << llendl; + return 0; + } + + if (encoding == ENCODING_DEFAULT) + { + encoding = mEncoding; + } + + const char *value_string = mValue.c_str(); + + U32 i = 0; + for (i=0; i<expected_length; ++i) + { + U64 value; + BOOL is_negative; + value_string = parseInteger(value_string, &value, &is_negative, 32, encoding); + if (value_string == NULL) + { + break; + } + if (value > 0x7fffffff) + { + llwarns << "getIntValue: Value outside of valid range." << llendl; + break; + } + array[i] = S32(value) * (is_negative?-1:1); + } + +#if LL_DEBUG + if (i != expected_length) + { + lldebugs << "LLXMLNode::getIntValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << i << llendl; + } +#endif + return i; +} + +U32 LLXMLNode::getUnsignedValue(U32 expected_length, U32 *array, Encoding encoding) +{ + llassert(array); + + // Check type - accept bytes or integers + if (mType != TYPE_INTEGER && mType != TYPE_UNKNOWN) + { + return 0; + } + + if (mLength > 0 && mLength != expected_length) + { + llwarns << "XMLNode::getUnsignedValue asked for " << expected_length + << " elements, while node has " << mLength << llendl; + return 0; + } + + if (encoding == ENCODING_DEFAULT) + { + encoding = mEncoding; + } + + const char *value_string = mValue.c_str(); + + U32 i = 0; + // Int type + for (i=0; i<expected_length; ++i) + { + U64 value; + BOOL is_negative; + value_string = parseInteger(value_string, &value, &is_negative, 32, encoding); + if (value_string == NULL) + { + break; + } + if (is_negative || value > 0xffffffff) + { + llwarns << "getUnsignedValue: Value outside of valid range." << llendl; + break; + } + array[i] = U32(value); + } + +#if LL_DEBUG + if (i != expected_length) + { + lldebugs << "LLXMLNode::getUnsignedValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << i << llendl; + } +#endif + + return i; +} + +U32 LLXMLNode::getLongValue(U32 expected_length, U64 *array, Encoding encoding) +{ + llassert(array); + + // Check type - accept bytes or integers + if (mType != TYPE_INTEGER && mType != TYPE_UNKNOWN) + { + return 0; + } + + if (mLength > 0 && mLength != expected_length) + { + llwarns << "XMLNode::getLongValue asked for " << expected_length << " elements, while node has " << mLength << llendl; + return 0; + } + + if (encoding == ENCODING_DEFAULT) + { + encoding = mEncoding; + } + + const char *value_string = mValue.c_str(); + + U32 i = 0; + // Int type + for (i=0; i<expected_length; ++i) + { + U64 value; + BOOL is_negative; + value_string = parseInteger(value_string, &value, &is_negative, 64, encoding); + if (value_string == NULL) + { + break; + } + if (is_negative) + { + llwarns << "getLongValue: Value outside of valid range." << llendl; + break; + } + array[i] = value; + } + +#if LL_DEBUG + if (i != expected_length) + { + lldebugs << "LLXMLNode::getLongValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << i << llendl; + } +#endif + + return i; +} + +U32 LLXMLNode::getFloatValue(U32 expected_length, F32 *array, Encoding encoding) +{ + llassert(array); + + // Check type - accept only floats or doubles + if (mType != TYPE_FLOAT && mType != TYPE_UNKNOWN) + { + return 0; + } + + if (mLength > 0 && mLength != expected_length) + { + llwarns << "XMLNode::getFloatValue asked for " << expected_length << " elements, while node has " << mLength << llendl; + return 0; + } + + if (encoding == ENCODING_DEFAULT) + { + encoding = mEncoding; + } + + const char *value_string = mValue.c_str(); + + U32 i; + for (i=0; i<expected_length; ++i) + { + F64 value; + value_string = parseFloat(value_string, &value, 32, encoding); + if (value_string == NULL) + { + break; + } + array[i] = F32(value); + } +#if LL_DEBUG + if (i != expected_length) + { + lldebugs << "LLXMLNode::getFloatValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << i << llendl; + } +#endif + return i; +} + +U32 LLXMLNode::getDoubleValue(U32 expected_length, F64 *array, Encoding encoding) +{ + llassert(array); + + // Check type - accept only floats or doubles + if (mType != TYPE_FLOAT && mType != TYPE_UNKNOWN) + { + return 0; + } + + if (mLength > 0 && mLength != expected_length) + { + llwarns << "XMLNode::getDoubleValue asked for " << expected_length << " elements, while node has " << mLength << llendl; + return 0; + } + + if (encoding == ENCODING_DEFAULT) + { + encoding = mEncoding; + } + + const char *value_string = mValue.c_str(); + + U32 i; + for (i=0; i<expected_length; ++i) + { + F64 value; + value_string = parseFloat(value_string, &value, 64, encoding); + if (value_string == NULL) + { + break; + } + array[i] = value; + } +#if LL_DEBUG + if (i != expected_length) + { + lldebugs << "LLXMLNode::getDoubleValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << i << llendl; + } +#endif + return i; +} + +U32 LLXMLNode::getStringValue(U32 expected_length, LLString *array) +{ + llassert(array); + + // Can always return any value as a string + + if (mLength > 0 && mLength != expected_length) + { + llwarns << "XMLNode::getStringValue asked for " << expected_length << " elements, while node has " << mLength << llendl; + return 0; + } + + U32 num_returned_strings = 0; + + // Array of strings is whitespace-separated + const std::string sep(" \n\t"); + + std::string::size_type n = 0; + std::string::size_type m = 0; + while(1) + { + if (num_returned_strings >= expected_length) + { + break; + } + n = mValue.find_first_not_of(sep, m); + m = mValue.find_first_of(sep, n); + if (m == std::string::npos) + { + break; + } + array[num_returned_strings++] = mValue.substr(n,m-n); + } + if (n != std::string::npos && num_returned_strings < expected_length) + { + array[num_returned_strings++] = mValue.substr(n); + } +#if LL_DEBUG + if (num_returned_strings != expected_length) + { + lldebugs << "LLXMLNode::getStringValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << num_returned_strings << llendl; + } +#endif + + return num_returned_strings; +} + +U32 LLXMLNode::getUUIDValue(U32 expected_length, LLUUID *array) +{ + llassert(array); + + // Check type + if (mType != TYPE_UUID && mType != TYPE_UNKNOWN) + { + return 0; + } + + const char *value_string = mValue.c_str(); + + U32 i; + for (i=0; i<expected_length; ++i) + { + LLUUID uuid_value; + value_string = skipWhitespace(value_string); + + if (strlen(value_string) < (UUID_STR_LENGTH-1)) + { + break; + } + char uuid_string[UUID_STR_LENGTH]; + memcpy(uuid_string, value_string, (UUID_STR_LENGTH-1)); + uuid_string[(UUID_STR_LENGTH-1)] = 0; + + if (!LLUUID::parseUUID(uuid_string, &uuid_value)) + { + break; + } + value_string = &value_string[(UUID_STR_LENGTH-1)]; + array[i] = uuid_value; + } +#if LL_DEBUG + if (i != expected_length) + { + lldebugs << "LLXMLNode::getUUIDValue() failed for node named '" + << mName->mString << "' -- expected " << expected_length << " but " + << "only found " << i << llendl; + } +#endif + return i; +} + +U32 LLXMLNode::getNodeRefValue(U32 expected_length, LLXMLNode **array) +{ + llassert(array); + + // Check type + if (mType != TYPE_NODEREF && mType != TYPE_UNKNOWN) + { + return 0; + } + + LLString *string_array = new LLString[expected_length]; + + U32 num_strings = getStringValue(expected_length, string_array); + + U32 num_returned_refs = 0; + + LLXMLNodePtr root = getRoot(); + for (U32 strnum=0; strnum<num_strings; ++strnum) + { + LLXMLNodeList node_list; + root->findID(string_array[strnum], node_list); + if (node_list.empty()) + { + llwarns << "XML: Could not find node ID: " << string_array[strnum] << llendl; + } + else if (node_list.size() > 1) + { + llwarns << "XML: Node ID not unique: " << string_array[strnum] << llendl; + } + else + { + LLXMLNodeList::const_iterator list_itr = node_list.begin(); + if (list_itr != node_list.end()) + { + LLXMLNode* child = (*list_itr).second; + + array[num_returned_refs++] = child; + } + } + } + + delete[] string_array; + + return num_returned_refs; +} + +void LLXMLNode::setBoolValue(U32 length, const BOOL *array) +{ + if (length == 0) return; + + LLString new_value; + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0) + { + new_value = llformat("%s %s", new_value.c_str(), array[pos]?"true":"false"); + } + else + { + new_value = array[pos]?"true":"false"; + } + } + + mValue = new_value; + mEncoding = ENCODING_DEFAULT; + mLength = length; + mType = TYPE_BOOLEAN; +} + +void LLXMLNode::setByteValue(U32 length, const U8* const array, Encoding encoding) +{ + if (length == 0) return; + + LLString new_value; + if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) + { + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0) + { + new_value.append(llformat(" %u", array[pos])); + } + else + { + new_value = llformat("%u", array[pos]); + } + } + } + if (encoding == ENCODING_HEX) + { + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0 && pos % 16 == 0) + { + new_value.append(llformat(" %02X", array[pos])); + } + else + { + new_value.append(llformat("%02X", array[pos])); + } + } + } + // TODO -- Handle Base32 + + mValue = new_value; + mEncoding = encoding; + mLength = length; + mType = TYPE_INTEGER; + mPrecision = 8; +} + + +void LLXMLNode::setIntValue(U32 length, const S32 *array, Encoding encoding) +{ + if (length == 0) return; + + LLString new_value; + if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) + { + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0) + { + new_value.append(llformat(" %d", array[pos])); + } + else + { + new_value = llformat("%d", array[pos]); + } + } + mValue = new_value; + } + else if (encoding == ENCODING_HEX) + { + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0 && pos % 16 == 0) + { + new_value.append(llformat(" %08X", ((U32 *)array)[pos])); + } + else + { + new_value.append(llformat("%08X", ((U32 *)array)[pos])); + } + } + mValue = new_value; + } + else + { + mValue = new_value; + } + // TODO -- Handle Base32 + + mEncoding = encoding; + mLength = length; + mType = TYPE_INTEGER; + mPrecision = 32; +} + +void LLXMLNode::setUnsignedValue(U32 length, const U32* array, Encoding encoding) +{ + if (length == 0) return; + + LLString new_value; + if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) + { + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0) + { + new_value.append(llformat(" %u", array[pos])); + } + else + { + new_value = llformat("%u", array[pos]); + } + } + } + if (encoding == ENCODING_HEX) + { + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0 && pos % 16 == 0) + { + new_value.append(llformat(" %08X", array[pos])); + } + else + { + new_value.append(llformat("%08X", array[pos])); + } + } + mValue = new_value; + } + // TODO -- Handle Base32 + + mValue = new_value; + mEncoding = encoding; + mLength = length; + mType = TYPE_INTEGER; + mPrecision = 32; +} + +#if LL_WINDOWS +#define PU64 "I64u" +#else +#define PU64 "llu" +#endif + +void LLXMLNode::setLongValue(U32 length, const U64* array, Encoding encoding) +{ + if (length == 0) return; + + LLString new_value; + if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) + { + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0) + { + new_value.append(llformat(" %" PU64, array[pos])); + } + else + { + new_value = llformat("%" PU64, array[pos]); + } + } + mValue = new_value; + } + if (encoding == ENCODING_HEX) + { + for (U32 pos=0; pos<length; ++pos) + { + U32 upper_32 = U32(array[pos]>>32); + U32 lower_32 = U32(array[pos]&0xffffffff); + if (pos > 0 && pos % 8 == 0) + { + new_value.append(llformat(" %08X%08X", upper_32, lower_32)); + } + else + { + new_value.append(llformat("%08X%08X", upper_32, lower_32)); + } + } + mValue = new_value; + } + else + { + mValue = new_value; + } + // TODO -- Handle Base32 + + mEncoding = encoding; + mLength = length; + mType = TYPE_INTEGER; + mPrecision = 64; +} + +void LLXMLNode::setFloatValue(U32 length, const F32 *array, Encoding encoding, U32 precision) +{ + if (length == 0) return; + + LLString new_value; + if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) + { + char format_string[10]; + if (precision > 0) + { + if (precision > 25) + { + precision = 25; + } + sprintf(format_string, "%%.%dg", precision); + } + else + { + sprintf(format_string, "%%g"); + } + + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0) + { + new_value.append(" "); + new_value.append(llformat(format_string, array[pos])); + } + else + { + new_value.assign(llformat(format_string, array[pos])); + } + } + mValue = new_value; + } + else if (encoding == ENCODING_HEX) + { + U32 *byte_array = (U32 *)array; + setUnsignedValue(length, byte_array, ENCODING_HEX); + } + else + { + mValue = new_value; + } + + mEncoding = encoding; + mLength = length; + mType = TYPE_FLOAT; + mPrecision = 32; +} + +void LLXMLNode::setDoubleValue(U32 length, const F64 *array, Encoding encoding, U32 precision) +{ + if (length == 0) return; + + LLString new_value; + if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) + { + char format_string[10]; + if (precision > 0) + { + if (precision > 25) + { + precision = 25; + } + sprintf(format_string, "%%.%dg", precision); + } + else + { + sprintf(format_string, "%%g"); + } + for (U32 pos=0; pos<length; ++pos) + { + if (pos > 0) + { + new_value.append(" "); + new_value.append(llformat(format_string, array[pos])); + } + else + { + new_value.assign(llformat(format_string, array[pos])); + } + } + mValue = new_value; + } + if (encoding == ENCODING_HEX) + { + U64 *byte_array = (U64 *)array; + setLongValue(length, byte_array, ENCODING_HEX); + } + else + { + mValue = new_value; + } + // TODO -- Handle Base32 + + mEncoding = encoding; + mLength = length; + mType = TYPE_FLOAT; + mPrecision = 64; +} + +// static +LLString LLXMLNode::escapeXML(const LLString& xml) +{ + LLString out; + for (LLString::size_type i = 0; i < xml.size(); ++i) + { + char c = xml[i]; + switch(c) + { + case '"': out.append("""); break; + case '\'': out.append("'"); break; + case '&': out.append("&"); break; + case '<': out.append("<"); break; + case '>': out.append(">"); break; + default: out.push_back(c); break; + } + } + return out; +} + +void LLXMLNode::setStringValue(U32 length, const LLString *array) +{ + if (length == 0) return; + + LLString new_value; + for (U32 pos=0; pos<length; ++pos) + { + new_value.append(escapeXML(array[pos])); + if (pos < length-1) new_value.append(" "); + } + + mValue = new_value; + mEncoding = ENCODING_DEFAULT; + mLength = length; + mType = TYPE_STRING; +} + +void LLXMLNode::setUUIDValue(U32 length, const LLUUID *array) +{ + if (length == 0) return; + + LLString new_value; + for (U32 pos=0; pos<length; ++pos) + { + new_value.append(array[pos].getString()); + if (pos < length-1) new_value.append(" "); + } + + mValue = new_value; + mEncoding = ENCODING_DEFAULT; + mLength = length; + mType = TYPE_UUID; +} + +void LLXMLNode::setNodeRefValue(U32 length, const LLXMLNode **array) +{ + if (length == 0) return; + + LLString new_value; + for (U32 pos=0; pos<length; ++pos) + { + if (array[pos]->mID != "") + { + new_value.append(array[pos]->mID); + } + else + { + new_value.append("(null)"); + } + if (pos < length-1) new_value.append(" "); + } + + mValue = new_value; + mEncoding = ENCODING_DEFAULT; + mLength = length; + mType = TYPE_NODEREF; +} + +void LLXMLNode::setValue(const LLString& value) +{ + if (TYPE_CONTAINER == mType) + { + mType = TYPE_UNKNOWN; + } + mValue = value; +} + +void LLXMLNode::setDefault(LLXMLNode *default_node) +{ + mDefault = default_node; +} + +void LLXMLNode::findDefault(LLXMLNode *defaults_list) +{ + if (defaults_list) + { + LLXMLNodeList children; + defaults_list->getChildren(mName->mString, children); + + LLXMLNodeList::const_iterator children_itr; + LLXMLNodeList::const_iterator children_end = children.end(); + for (children_itr = children.begin(); children_itr != children_end; ++children_itr) + { + LLXMLNode* child = (*children_itr).second; + if (child->mVersionMajor == mVersionMajor && + child->mVersionMinor == mVersionMinor) + { + mDefault = child; + return; + } + } + } + mDefault = NULL; +} + +BOOL LLXMLNode::deleteChildren(const LLString& name) +{ + U32 removed_count = 0; + LLXMLNodeList node_list; + findName(name, node_list); + if (!node_list.empty()) + { + // TODO -- use multimap::find() + // TODO -- need to watch out for invalid iterators + LLXMLNodeList::iterator children_itr; + for (children_itr = node_list.begin(); children_itr != node_list.end(); ++children_itr) + { + LLXMLNode* child = (*children_itr).second; + if (deleteChild(child)) + { + removed_count++; + } + } + } + return removed_count > 0 ? TRUE : FALSE; +} + +BOOL LLXMLNode::deleteChildren(LLStringTableEntry* name) +{ + U32 removed_count = 0; + LLXMLNodeList node_list; + findName(name, node_list); + if (!node_list.empty()) + { + // TODO -- use multimap::find() + // TODO -- need to watch out for invalid iterators + LLXMLNodeList::iterator children_itr; + for (children_itr = node_list.begin(); children_itr != node_list.end(); ++children_itr) + { + LLXMLNode* child = (*children_itr).second; + if (deleteChild(child)) + { + removed_count++; + } + } + } + return removed_count > 0 ? TRUE : FALSE; +} + +void LLXMLNode::setAttributes(LLXMLNode::ValueType type, U32 precision, LLXMLNode::Encoding encoding, U32 length) +{ + mType = type; + mEncoding = encoding; + mPrecision = precision; + mLength = length; +} + +void LLXMLNode::setName(const LLString& name) +{ + setName(gStringTable.addStringEntry(name)); +} + +void LLXMLNode::setName(LLStringTableEntry* name) +{ + LLXMLNode* old_parent = mParent; + if (mParent) + { + // we need to remove and re-add to the parent so that + // the multimap key agrees with this node's name + mParent->removeChild(this); + } + mName = name; + if (old_parent) + { + old_parent->addChild(this); + } +} + +void LLXMLNode::appendValue(const LLString& value) +{ + mValue.append(value); +} + +U32 LLXMLNode::getChildCount() const +{ + if (mChildren) + { + return mChildren->map.size(); + } + return 0; +} + +//*************************************************** +// UNIT TESTING +//*************************************************** + +U32 get_rand(U32 max_value) +{ + U32 random_num = rand() + ((U32)rand() << 16); + return (random_num % max_value); +} + +LLXMLNode *get_rand_node(LLXMLNode *node) +{ + if (node->mChildren) + { + U32 num_children = node->mChildren->map.size(); + if (get_rand(2) == 0) + { + while (true) + { + S32 child_num = S32(get_rand(num_children*2)) - num_children; + LLXMLChildList::iterator itor = node->mChildren->map.begin(); + while (child_num > 0) + { + --child_num; + ++itor; + } + if (!itor->second->mIsAttribute) + { + return get_rand_node(itor->second); + } + } + } + } + return node; +} + +void LLXMLNode::createUnitTest(S32 max_num_children) +{ + // Random ID + char rand_id[20]; + U32 rand_id_len = get_rand(10)+5; + U32 pos = 0; + for (; pos<rand_id_len; ++pos) + { + rand_id[pos] = get_rand(26)+'a'; + } + rand_id[pos] = 0; + mID = rand_id; + + if (max_num_children < 2) + { + setStringValue(1, &mID); + return; + } + + // Checksums + U32 integer_checksum = 0; + U64 long_checksum = 0; + U32 bool_true_count = 0; + LLUUID uuid_checksum; + U32 noderef_checksum = 0; + U32 float_checksum = 0; + + // Create a random number of children + U32 num_children = get_rand(max_num_children)+1; + for (U32 child_num=0; child_num<num_children; ++child_num) + { + // Random Name + char child_name[20]; + U32 child_name_len = get_rand(10)+5; + pos = 0; + for (; pos<child_name_len; ++pos) + { + child_name[pos] = get_rand(26)+'a'; + } + child_name[pos] = 0; + + LLXMLNode *new_child = createChild(child_name, FALSE); + + // Random ID + char child_id[20]; + U32 child_id_len = get_rand(10)+5; + pos = 0; + for (; pos<child_id_len; ++pos) + { + child_id[pos] = get_rand(26)+'a'; + } + child_id[pos] = 0; + new_child->mID = child_id; + + // Random Length + U32 array_size = get_rand(28)+1; + + // Random Encoding + Encoding new_encoding = get_rand(2)?ENCODING_DECIMAL:ENCODING_HEX; + + // Random Type + int type = get_rand(8); + switch (type) + { + case 0: // TYPE_CONTAINER + new_child->createUnitTest(max_num_children/2); + break; + case 1: // TYPE_BOOLEAN + { + BOOL random_bool_values[30]; + for (U32 value=0; value<array_size; ++value) + { + random_bool_values[value] = get_rand(2); + if (random_bool_values[value]) + { + ++bool_true_count; + } + } + new_child->setBoolValue(array_size, random_bool_values); + } + break; + case 2: // TYPE_INTEGER (32-bit) + { + U32 random_int_values[30]; + for (U32 value=0; value<array_size; ++value) + { + random_int_values[value] = get_rand(0xffffffff); + integer_checksum ^= random_int_values[value]; + } + new_child->setUnsignedValue(array_size, random_int_values, new_encoding); + } + break; + case 3: // TYPE_INTEGER (64-bit) + { + U64 random_int_values[30]; + for (U64 value=0; value<array_size; ++value) + { + random_int_values[value] = (U64(get_rand(0xffffffff)) << 32) + get_rand(0xffffffff); + long_checksum ^= random_int_values[value]; + } + new_child->setLongValue(array_size, random_int_values, new_encoding); + } + break; + case 4: // TYPE_FLOAT (32-bit) + { + F32 random_float_values[30]; + for (U32 value=0; value<array_size; ++value) + { + S32 exponent = get_rand(256) - 128; + S32 fractional_part = get_rand(0xffffffff); + S32 sign = get_rand(2) * 2 - 1; + random_float_values[value] = F32(fractional_part) / F32(0xffffffff) * exp(F32(exponent)) * F32(sign); + + U32 *float_bits = &((U32 *)random_float_values)[value]; + if (*float_bits == 0x80000000) + { + *float_bits = 0x00000000; + } + float_checksum ^= (*float_bits & 0xfffff000); + } + new_child->setFloatValue(array_size, random_float_values, new_encoding, 12); + } + break; + case 5: // TYPE_FLOAT (64-bit) + { + F64 random_float_values[30]; + for (U32 value=0; value<array_size; ++value) + { + S32 exponent = get_rand(2048) - 1024; + S32 fractional_part = get_rand(0xffffffff); + S32 sign = get_rand(2) * 2 - 1; + random_float_values[value] = F64(fractional_part) / F64(0xffffffff) * exp(F64(exponent)) * F64(sign); + + U64 *float_bits = &((U64 *)random_float_values)[value]; + if (*float_bits == 0x8000000000000000ll) + { + *float_bits = 0x0000000000000000ll; + } + float_checksum ^= ((*float_bits & 0xfffffff000000000ll) >> 32); + } + new_child->setDoubleValue(array_size, random_float_values, new_encoding, 12); + } + break; + case 6: // TYPE_UUID + { + LLUUID random_uuid_values[30]; + for (U32 value=0; value<array_size; ++value) + { + random_uuid_values[value].generate(); + for (S32 byte=0; byte<UUID_BYTES; ++byte) + { + uuid_checksum.mData[byte] ^= random_uuid_values[value].mData[byte]; + } + } + new_child->setUUIDValue(array_size, random_uuid_values); + } + break; + case 7: // TYPE_NODEREF + { + LLXMLNode *random_node_array[30]; + LLXMLNode *root = getRoot(); + for (U32 value=0; value<array_size; ++value) + { + random_node_array[value] = get_rand_node(root); + const char *node_name = random_node_array[value]->mName->mString; + for (U32 pos=0; pos<strlen(node_name); ++pos) + { + U32 hash_contrib = U32(node_name[pos]) << ((pos % 4) * 8); + noderef_checksum ^= hash_contrib; + } + } + new_child->setNodeRefValue(array_size, (const LLXMLNode **)random_node_array); + } + break; + } + } + + createChild("integer_checksum", TRUE)->setUnsignedValue(1, &integer_checksum, LLXMLNode::ENCODING_HEX); + createChild("long_checksum", TRUE)->setLongValue(1, &long_checksum, LLXMLNode::ENCODING_HEX); + createChild("bool_true_count", TRUE)->setUnsignedValue(1, &bool_true_count, LLXMLNode::ENCODING_HEX); + createChild("uuid_checksum", TRUE)->setUUIDValue(1, &uuid_checksum); + createChild("noderef_checksum", TRUE)->setUnsignedValue(1, &noderef_checksum, LLXMLNode::ENCODING_HEX); + createChild("float_checksum", TRUE)->setUnsignedValue(1, &float_checksum, LLXMLNode::ENCODING_HEX); +} + +BOOL LLXMLNode::performUnitTest(LLString &error_buffer) +{ + if (!mChildren) + { + error_buffer.append(llformat("ERROR Node %s: No children found.\n", mName->mString)); + return FALSE; + } + + // Checksums + U32 integer_checksum = 0; + U32 bool_true_count = 0; + LLUUID uuid_checksum; + U32 noderef_checksum = 0; + U32 float_checksum = 0; + U64 long_checksum = 0; + + LLXMLChildList::iterator itor; + for (itor=mChildren->map.begin(); itor!=mChildren->map.end(); ++itor) + { + LLXMLNode *node = itor->second; + if (node->mIsAttribute) + { + continue; + } + if (node->mType == TYPE_CONTAINER) + { + if (!node->performUnitTest(error_buffer)) + { + error_buffer.append(llformat("Child test failed for %s.\n", mName->mString)); + //return FALSE; + } + continue; + } + if (node->mLength < 1 || node->mLength > 30) + { + error_buffer.append(llformat("ERROR Node %s: Invalid array length %d, child %s.\n", mName->mString, node->mLength, node->mName->mString)); + return FALSE; + } + switch (node->mType) + { + case TYPE_CONTAINER: + case TYPE_UNKNOWN: + break; + case TYPE_BOOLEAN: + { + BOOL bool_array[30]; + if (node->getBoolValue(node->mLength, bool_array) < node->mLength) + { + error_buffer.append(llformat("ERROR Node %s: Could not read boolean array, child %s.\n", mName->mString, node->mName->mString)); + return FALSE; + } + for (U32 pos=0; pos<(U32)node->mLength; ++pos) + { + if (bool_array[pos]) + { + ++bool_true_count; + } + } + } + break; + case TYPE_INTEGER: + { + if (node->mPrecision == 32) + { + U32 integer_array[30]; + if (node->getUnsignedValue(node->mLength, integer_array, node->mEncoding) < node->mLength) + { + error_buffer.append(llformat("ERROR Node %s: Could not read integer array, child %s.\n", mName->mString, node->mName->mString)); + return FALSE; + } + for (U32 pos=0; pos<(U32)node->mLength; ++pos) + { + integer_checksum ^= integer_array[pos]; + } + } + else + { + U64 integer_array[30]; + if (node->getLongValue(node->mLength, integer_array, node->mEncoding) < node->mLength) + { + error_buffer.append(llformat("ERROR Node %s: Could not read long integer array, child %s.\n", mName->mString, node->mName->mString)); + return FALSE; + } + for (U32 pos=0; pos<(U32)node->mLength; ++pos) + { + long_checksum ^= integer_array[pos]; + } + } + } + break; + case TYPE_FLOAT: + { + if (node->mPrecision == 32) + { + F32 float_array[30]; + if (node->getFloatValue(node->mLength, float_array, node->mEncoding) < node->mLength) + { + error_buffer.append(llformat("ERROR Node %s: Could not read float array, child %s.\n", mName->mString, node->mName->mString)); + return FALSE; + } + for (U32 pos=0; pos<(U32)node->mLength; ++pos) + { + U32 float_bits = ((U32 *)float_array)[pos]; + float_checksum ^= (float_bits & 0xfffff000); + } + } + else + { + F64 float_array[30]; + if (node->getDoubleValue(node->mLength, float_array, node->mEncoding) < node->mLength) + { + error_buffer.append(llformat("ERROR Node %s: Could not read float array, child %s.\n", mName->mString, node->mName->mString)); + return FALSE; + } + for (U32 pos=0; pos<(U32)node->mLength; ++pos) + { + U64 float_bits = ((U64 *)float_array)[pos]; + float_checksum ^= ((float_bits & 0xfffffff000000000ll) >> 32); + } + } + } + break; + case TYPE_STRING: + break; + case TYPE_UUID: + { + LLUUID uuid_array[30]; + if (node->getUUIDValue(node->mLength, uuid_array) < node->mLength) + { + error_buffer.append(llformat("ERROR Node %s: Could not read uuid array, child %s.\n", mName->mString, node->mName->mString)); + return FALSE; + } + for (U32 pos=0; pos<(U32)node->mLength; ++pos) + { + for (S32 byte=0; byte<UUID_BYTES; ++byte) + { + uuid_checksum.mData[byte] ^= uuid_array[pos].mData[byte]; + } + } + } + break; + case TYPE_NODEREF: + { + LLXMLNode *node_array[30]; + if (node->getNodeRefValue(node->mLength, node_array) < node->mLength) + { + error_buffer.append(llformat("ERROR Node %s: Could not read node ref array, child %s.\n", mName->mString, node->mName->mString)); + return FALSE; + } + for (U32 pos=0; pos<node->mLength; ++pos) + { + const char *node_name = node_array[pos]->mName->mString; + for (U32 pos2=0; pos2<strlen(node_name); ++pos2) + { + U32 hash_contrib = U32(node_name[pos2]) << ((pos2 % 4) * 8); + noderef_checksum ^= hash_contrib; + } + } + } + break; + } + } + + LLXMLNodePtr checksum_node; + + // Compare checksums + { + U32 node_integer_checksum = 0; + if (!getAttribute("integer_checksum", checksum_node, FALSE) || + checksum_node->getUnsignedValue(1, &node_integer_checksum, ENCODING_HEX) != 1) + { + error_buffer.append(llformat("ERROR Node %s: Integer checksum missing.\n", mName->mString)); + return FALSE; + } + if (node_integer_checksum != integer_checksum) + { + error_buffer.append(llformat("ERROR Node %s: Integer checksum mismatch: read %X / calc %X.\n", mName->mString, node_integer_checksum, integer_checksum)); + return FALSE; + } + } + + { + U64 node_long_checksum = 0; + if (!getAttribute("long_checksum", checksum_node, FALSE) || + checksum_node->getLongValue(1, &node_long_checksum, ENCODING_HEX) != 1) + { + error_buffer.append(llformat("ERROR Node %s: Long Integer checksum missing.\n", mName->mString)); + return FALSE; + } + if (node_long_checksum != long_checksum) + { + U32 *pp1 = (U32 *)&node_long_checksum; + U32 *pp2 = (U32 *)&long_checksum; + error_buffer.append(llformat("ERROR Node %s: Long Integer checksum mismatch: read %08X%08X / calc %08X%08X.\n", mName->mString, pp1[1], pp1[0], pp2[1], pp2[0])); + return FALSE; + } + } + + { + U32 node_bool_true_count = 0; + if (!getAttribute("bool_true_count", checksum_node, FALSE) || + checksum_node->getUnsignedValue(1, &node_bool_true_count, ENCODING_HEX) != 1) + { + error_buffer.append(llformat("ERROR Node %s: Boolean checksum missing.\n", mName->mString)); + return FALSE; + } + if (node_bool_true_count != bool_true_count) + { + error_buffer.append(llformat("ERROR Node %s: Boolean checksum mismatch: read %X / calc %X.\n", mName->mString, node_bool_true_count, bool_true_count)); + return FALSE; + } + } + + { + LLUUID node_uuid_checksum; + if (!getAttribute("uuid_checksum", checksum_node, FALSE) || + checksum_node->getUUIDValue(1, &node_uuid_checksum) != 1) + { + error_buffer.append(llformat("ERROR Node %s: UUID checksum missing.\n", mName->mString)); + return FALSE; + } + if (node_uuid_checksum != uuid_checksum) + { + error_buffer.append(llformat("ERROR Node %s: UUID checksum mismatch: read %s / calc %s.\n", mName->mString, node_uuid_checksum.getString().c_str(), uuid_checksum.getString().c_str())); + return FALSE; + } + } + + { + U32 node_noderef_checksum = 0; + if (!getAttribute("noderef_checksum", checksum_node, FALSE) || + checksum_node->getUnsignedValue(1, &node_noderef_checksum, ENCODING_HEX) != 1) + { + error_buffer.append(llformat("ERROR Node %s: Node Ref checksum missing.\n", mName->mString)); + return FALSE; + } + if (node_noderef_checksum != noderef_checksum) + { + error_buffer.append(llformat("ERROR Node %s: Node Ref checksum mismatch: read %X / calc %X.\n", mName->mString, node_noderef_checksum, noderef_checksum)); + return FALSE; + } + } + + { + U32 node_float_checksum = 0; + if (!getAttribute("float_checksum", checksum_node, FALSE) || + checksum_node->getUnsignedValue(1, &node_float_checksum, ENCODING_HEX) != 1) + { + error_buffer.append(llformat("ERROR Node %s: Float checksum missing.\n", mName->mString)); + return FALSE; + } + if (node_float_checksum != float_checksum) + { + error_buffer.append(llformat("ERROR Node %s: Float checksum mismatch: read %X / calc %X.\n", mName->mString, node_float_checksum, float_checksum)); + return FALSE; + } + } + + return TRUE; +} + +LLXMLNodePtr LLXMLNode::getFirstChild() +{ + if (!mChildren) return NULL; + LLXMLNodePtr ret = mChildren->head; + return ret; +} + +LLXMLNodePtr LLXMLNode::getNextSibling() +{ + LLXMLNodePtr ret = mNext; + return ret; +} + +LLString LLXMLNode::getTextContents() const +{ + std::string msg; + LLXMLNodeList p_children; + getChildren("p", p_children); + if (p_children.size() > 0) + { + // Case 1: node has <p>text</p> tags + LLXMLNodeList::iterator itor; + for (itor = p_children.begin(); itor != p_children.end(); ++itor) + { + LLXMLNodePtr p = itor->second; + msg += p->getValue() + "\n"; + } + } + else + { + LLString contents = mValue; + std::string::size_type n = contents.find_first_not_of(" \t\n"); + if (n != std::string::npos && contents[n] == '\"') + { + // Case 2: node has quoted text + S32 num_lines = 0; + while(1) + { + // mContents[n] == '"' + ++n; + std::string::size_type t = n; + std::string::size_type m = 0; + // fix-up escaped characters + while(1) + { + m = contents.find_first_of("\\\"", t); // find first \ or " + if ((m == std::string::npos) || (contents[m] == '\"')) + { + break; + } + contents.erase(m,1); + t = m+1; + } + if (m == std::string::npos) + { + break; + } + // mContents[m] == '"' + num_lines++; + msg += contents.substr(n,m-n) + "\n"; + n = contents.find_first_of("\"", m+1); + if (n == std::string::npos) + { + if (num_lines == 1) + { + msg.erase(msg.size()-1); // remove "\n" if only one line + } + break; + } + } + } + else + { + // Case 3: node has embedded text (beginning and trailing whitespace trimmed) + LLString::size_type start = mValue.find_first_not_of(" \t\n"); + if (start != mValue.npos) + { + LLString::size_type end = mValue.find_last_not_of(" \t\n"); + if (end != mValue.npos) + { + msg = mValue.substr(start, end+1-start); + } + else + { + msg = mValue.substr(start); + } + } + } + } + return msg; +} diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h new file mode 100644 index 0000000000..ca8c1d176f --- /dev/null +++ b/indra/llxml/llxmlnode.h @@ -0,0 +1,266 @@ +/** + * @file llxmlnode.h + * @brief LLXMLNode definition + * + * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLXMLNODE_H +#define LL_LLXMLNODE_H + +#define XML_STATIC +#include "expat/expat.h" +#include <map> + +#include "indra_constants.h" +#include "llmemory.h" +#include "llstring.h" +#include "llstringtable.h" + + + +struct CompareAttributes +{ + bool operator()(const LLStringTableEntry* const lhs, const LLStringTableEntry* const rhs) const + { + if (lhs == NULL) + return TRUE; + if (rhs == NULL) + return FALSE; + + return strcmp(lhs->mString, rhs->mString) < 0; + } +}; + + +// Defines a simple node hierarchy for reading and writing task objects + +class LLXMLNode; +typedef LLPointer<LLXMLNode> LLXMLNodePtr; +typedef std::multimap<LLString, LLXMLNodePtr > LLXMLNodeList; +typedef std::multimap<const LLStringTableEntry *, LLXMLNodePtr > LLXMLChildList; +typedef std::map<const LLStringTableEntry *, LLXMLNodePtr, CompareAttributes> LLXMLAttribList; + +struct LLXMLChildren +{ + LLXMLChildList map; // Map of children names->pointers + LLXMLNodePtr head; // Head of the double-linked list + LLXMLNodePtr tail; // Tail of the double-linked list +}; + +class LLXMLNode : public LLThreadSafeRefCount +{ +public: + enum ValueType + { + TYPE_CONTAINER, // A node which contains nodes + TYPE_UNKNOWN, // A node loaded from file without a specified type + TYPE_BOOLEAN, // "true" or "false" + TYPE_INTEGER, // any integer type: U8, U32, S32, U64, etc. + TYPE_FLOAT, // any floating point type: F32, F64 + TYPE_STRING, // a string + TYPE_UUID, // a UUID + TYPE_NODEREF, // the ID of another node in the hierarchy to reference + }; + + enum Encoding + { + ENCODING_DEFAULT = 0, + ENCODING_DECIMAL, + ENCODING_HEX, + // ENCODING_BASE32, // Not implemented yet + }; + +protected: + virtual ~LLXMLNode(); + +public: + LLXMLNode(); + LLXMLNode(const LLString& name, BOOL is_attribute); + LLXMLNode(LLStringTableEntry* name, BOOL is_attribute); + + BOOL isNull(); + + BOOL deleteChild(LLXMLNode* child); + void addChild(LLXMLNodePtr new_parent); + void setParent(LLXMLNodePtr new_parent); // reparent if necessary + + // Serialization + static bool parseFile( + LLString filename, + LLXMLNodePtr& node, + LLXMLNode* defaults_tree); + static bool parseBuffer( + U8* buffer, + U32 length, + LLXMLNodePtr& node, + LLXMLNode* defaults); + static bool updateNode( + LLXMLNodePtr& node, + LLXMLNodePtr& update_node); + static void writeHeaderToFile(FILE *fOut); + void writeToFile(FILE *fOut, LLString indent = ""); + void writeToOstream(std::ostream& output_stream, const LLString& indent = ""); + + // Utility + void findName(const LLString& name, LLXMLNodeList &results); + void findName(LLStringTableEntry* name, LLXMLNodeList &results); + void findID(const LLString& id, LLXMLNodeList &results); + + + virtual LLXMLNodePtr createChild(const LLString& name, BOOL is_attribute); + virtual LLXMLNodePtr createChild(LLStringTableEntry* name, BOOL is_attribute); + + + // Getters + U32 getBoolValue(U32 expected_length, BOOL *array); + U32 getByteValue(U32 expected_length, U8 *array, Encoding encoding = ENCODING_DEFAULT); + U32 getIntValue(U32 expected_length, S32 *array, Encoding encoding = ENCODING_DEFAULT); + U32 getUnsignedValue(U32 expected_length, U32 *array, Encoding encoding = ENCODING_DEFAULT); + U32 getLongValue(U32 expected_length, U64 *array, Encoding encoding = ENCODING_DEFAULT); + U32 getFloatValue(U32 expected_length, F32 *array, Encoding encoding = ENCODING_DEFAULT); + U32 getDoubleValue(U32 expected_length, F64 *array, Encoding encoding = ENCODING_DEFAULT); + U32 getStringValue(U32 expected_length, LLString *array); + U32 getUUIDValue(U32 expected_length, LLUUID *array); + U32 getNodeRefValue(U32 expected_length, LLXMLNode **array); + + BOOL hasAttribute(const LLString& name ); + + BOOL getAttributeBOOL(const LLString& name, BOOL& value ); + BOOL getAttributeU8(const LLString& name, U8& value ); + BOOL getAttributeS8(const LLString& name, S8& value ); + BOOL getAttributeU16(const LLString& name, U16& value ); + BOOL getAttributeS16(const LLString& name, S16& value ); + BOOL getAttributeU32(const LLString& name, U32& value ); + BOOL getAttributeS32(const LLString& name, S32& value ); + BOOL getAttributeF32(const LLString& name, F32& value ); + BOOL getAttributeF64(const LLString& name, F64& value ); + BOOL getAttributeColor(const LLString& name, LLColor4& value ); + BOOL getAttributeColor4(const LLString& name, LLColor4& value ); + BOOL getAttributeColor4U(const LLString& name, LLColor4U& value ); + BOOL getAttributeVector3(const LLString& name, LLVector3& value ); + BOOL getAttributeVector3d(const LLString& name, LLVector3d& value ); + BOOL getAttributeQuat(const LLString& name, LLQuaternion& value ); + BOOL getAttributeUUID(const LLString& name, LLUUID& value ); + BOOL getAttributeString(const LLString& name, LLString& value ); + + const ValueType& getType() const { return mType; } + const U32 getLength() const { return mLength; } + const U32 getPrecision() const { return mPrecision; } + const LLString& getValue() const { return mValue; } + LLString getTextContents() const; + const LLStringTableEntry* getName() const { return mName; } + const BOOL hasName(LLString name) const { return mName == gStringTable.checkStringEntry(name); } + const LLString& getID() const { return mID; } + + U32 getChildCount() const; + // getChild returns a Null LLXMLNode (not a NULL pointer) if there is no such child. + // This child has no value so any getTYPEValue() calls on it will return 0. + bool getChild(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); + bool getChild(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); + void getChildren(const LLString& name, LLXMLNodeList &children, BOOL use_default_if_missing = TRUE) const; + void getChildren(const LLStringTableEntry* name, LLXMLNodeList &children, BOOL use_default_if_missing = TRUE) const; + + bool getAttribute(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); + bool getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); + + // The following skip over attributes + LLXMLNodePtr getFirstChild(); + LLXMLNodePtr getNextSibling(); + + LLXMLNodePtr getRoot(); + + // Setters + + bool setAttributeString(const LLString& attr, const LLString& value); + + void setBoolValue(const BOOL value) { setBoolValue(1, &value); } + void setByteValue(const U8 value, Encoding encoding = ENCODING_DEFAULT) { setByteValue(1, &value, encoding); } + void setIntValue(const S32 value, Encoding encoding = ENCODING_DEFAULT) { setIntValue(1, &value, encoding); } + void setUnsignedValue(const U32 value, Encoding encoding = ENCODING_DEFAULT) { setUnsignedValue(1, &value, encoding); } + void setLongValue(const U64 value, Encoding encoding = ENCODING_DEFAULT) { setLongValue(1, &value, encoding); } + void setFloatValue(const F32 value, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0) { setFloatValue(1, &value, encoding); } + void setDoubleValue(const F64 value, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0) { setDoubleValue(1, &value, encoding); } + void setStringValue(const LLString value) { setStringValue(1, &value); } + void setUUIDValue(const LLUUID value) { setUUIDValue(1, &value); } + void setNodeRefValue(const LLXMLNode *value) { setNodeRefValue(1, &value); } + + void setBoolValue(U32 length, const BOOL *array); + void setByteValue(U32 length, const U8 *array, Encoding encoding = ENCODING_DEFAULT); + void setIntValue(U32 length, const S32 *array, Encoding encoding = ENCODING_DEFAULT); + void setUnsignedValue(U32 length, const U32* array, Encoding encoding = ENCODING_DEFAULT); + void setLongValue(U32 length, const U64 *array, Encoding encoding = ENCODING_DEFAULT); + void setFloatValue(U32 length, const F32 *array, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0); + void setDoubleValue(U32 length, const F64 *array, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0); + void setStringValue(U32 length, const LLString *array); + void setUUIDValue(U32 length, const LLUUID *array); + void setNodeRefValue(U32 length, const LLXMLNode **array); + void setValue(const LLString& value); + void setName(const LLString& name); + void setName(LLStringTableEntry* name); + + // Escapes " (quot) ' (apos) & (amp) < (lt) > (gt) + // TomY TODO: Make this private + static LLString escapeXML(const LLString& xml); + + // Set the default node corresponding to this default node + void setDefault(LLXMLNode *default_node); + + // Find the node within defaults_list which corresponds to this node + void findDefault(LLXMLNode *defaults_list); + + void updateDefault(); + + // Delete any child nodes that aren't among the tree's children, recursive + void scrubToTree(LLXMLNode *tree); + + BOOL deleteChildren(const LLString& name); + BOOL deleteChildren(LLStringTableEntry* name); + void setAttributes(ValueType type, U32 precision, Encoding encoding, U32 length); + void appendValue(const LLString& value); + + // Unit Testing + void createUnitTest(S32 max_num_children); + BOOL performUnitTest(LLString &error_buffer); + +protected: + BOOL removeChild(LLXMLNode* child); + +public: + LLString mID; // The ID attribute of this node + + XML_Parser *mParser; // Temporary pointer while loading + + BOOL mIsAttribute; // Flag is only used for output formatting + U32 mVersionMajor; // Version of this tag to use + U32 mVersionMinor; + U32 mLength; // If the length is nonzero, then only return arrays of this length + U32 mPrecision; // The number of BITS per array item + ValueType mType; // The value type + Encoding mEncoding; // The value encoding + + LLXMLNode* mParent; // The parent node + LLXMLChildren* mChildren; // The child nodes + LLXMLAttribList mAttributes; // The attribute nodes + LLXMLNodePtr mPrev; // Double-linked list previous node + LLXMLNodePtr mNext; // Double-linked list next node + + static BOOL sStripEscapedStrings; + static BOOL sStripWhitespaceValues; + +protected: + LLStringTableEntry *mName; // The name of this node + LLString mValue; // The value of this node (use getters/setters only) + + LLXMLNodePtr mDefault; // Mirror node in the default tree + + static const char *skipWhitespace(const char *str); + static const char *skipNonWhitespace(const char *str); + static const char *parseInteger(const char *str, U64 *dest, BOOL *is_negative, U32 precision, Encoding encoding); + static const char *parseFloat(const char *str, F64 *dest, U32 precision, Encoding encoding); + + BOOL isFullyDefault(); +}; + +#endif // LL_LLXMLNODE diff --git a/indra/llxml/llxmlparser.cpp b/indra/llxml/llxmlparser.cpp new file mode 100644 index 0000000000..baaeedf586 --- /dev/null +++ b/indra/llxml/llxmlparser.cpp @@ -0,0 +1,398 @@ +/** + * @file llxmlparser.cpp + * @brief LLXmlParser implementation + * + * Copyright (c) 2002-$CurrentYear$`, Linden Research, Inc. + * $License$ + */ + +// llxmlparser.cpp +// +// copyright 2002, linden research inc + + +#include "linden_common.h" + +#include "llxmlparser.h" +#include "llerror.h" + + +LLXmlParser::LLXmlParser() + : + mParser( NULL ), + mDepth( 0 ) +{ + strcpy( mAuxErrorString, "no error" ); + + // Override the document's declared encoding. + mParser = XML_ParserCreate(NULL); + + XML_SetUserData(mParser, this); + XML_SetElementHandler( mParser, startElementHandler, endElementHandler); + XML_SetCharacterDataHandler( mParser, characterDataHandler); + XML_SetProcessingInstructionHandler( mParser, processingInstructionHandler); + XML_SetCommentHandler( mParser, commentHandler); + + XML_SetCdataSectionHandler( mParser, startCdataSectionHandler, endCdataSectionHandler); + + // This sets the default handler but does not inhibit expansion of internal entities. + // The entity reference will not be passed to the default handler. + XML_SetDefaultHandlerExpand( mParser, defaultDataHandler); + + XML_SetUnparsedEntityDeclHandler( mParser, unparsedEntityDeclHandler); +} + +LLXmlParser::~LLXmlParser() +{ + XML_ParserFree( mParser ); +} + + +BOOL LLXmlParser::parseFile(const std::string &path) +{ + llassert( !mDepth ); + + BOOL success = TRUE; + + FILE *file = LLFile::fopen(path.c_str(), "rb"); + if( !file ) + { + sprintf( mAuxErrorString, "Couldn't open file %s", path.c_str()); + success = FALSE; + } + else + { + S32 bytes_read = 0; + + fseek(file, 0L, SEEK_END); + S32 buffer_size = ftell(file); + fseek(file, 0L, SEEK_SET); + + void* buffer = XML_GetBuffer(mParser, buffer_size); + if( !buffer ) + { + sprintf( mAuxErrorString, "Unable to allocate XML buffer while reading file %s", path.c_str() ); + success = FALSE; + goto exit_label; + } + + bytes_read = (S32)fread(buffer, 1, buffer_size, file); + if( bytes_read <= 0 ) + { + sprintf( mAuxErrorString, "Error while reading file %s", path.c_str() ); + success = FALSE; + goto exit_label; + } + + if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) ) + { + sprintf( mAuxErrorString, "Error while parsing file %s", path.c_str() ); + success = FALSE; + } + +exit_label: + fclose( file ); + } + + + if( success ) + { + llassert( !mDepth ); + } + mDepth = 0; + + if( !success ) + { + llwarns << mAuxErrorString << llendl; + } + + return success; +} + + +// Parses some input. Returns 0 if a fatal error is detected. +// The last call must have isFinal true; +// len may be zero for this call (or any other). +S32 LLXmlParser::parse( const char* buf, int len, int isFinal ) +{ + return XML_Parse(mParser, buf, len, isFinal); +} + +const char* LLXmlParser::getErrorString() +{ + const char* error_string = XML_ErrorString(XML_GetErrorCode( mParser )); + if( !error_string ) + { + error_string = mAuxErrorString; + } + return error_string; +} + +S32 LLXmlParser::getCurrentLineNumber() +{ + return XML_GetCurrentLineNumber( mParser ); +} + +S32 LLXmlParser::getCurrentColumnNumber() +{ + return XML_GetCurrentColumnNumber(mParser); +} + +/////////////////////////////////////////////////////////////////////////////// +// Pseudo-private methods. These are only used by internal callbacks. + +// static +void LLXmlParser::startElementHandler( + void *userData, + const XML_Char *name, + const XML_Char **atts) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->startElement( name, atts ); + self->mDepth++; +} + +// static +void LLXmlParser::endElementHandler( + void *userData, + const XML_Char *name) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->mDepth--; + self->endElement( name ); +} + +// s is not 0 terminated. +// static +void LLXmlParser::characterDataHandler( + void *userData, + const XML_Char *s, + int len) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->characterData( s, len ); +} + +// target and data are 0 terminated +// static +void LLXmlParser::processingInstructionHandler( + void *userData, + const XML_Char *target, + const XML_Char *data) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->processingInstruction( target, data ); +} + +// data is 0 terminated +// static +void LLXmlParser::commentHandler(void *userData, const XML_Char *data) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->comment( data ); +} + +// static +void LLXmlParser::startCdataSectionHandler(void *userData) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->mDepth++; + self->startCdataSection(); +} + +// static +void LLXmlParser::endCdataSectionHandler(void *userData) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->endCdataSection(); + self->mDepth++; +} + +// This is called for any characters in the XML document for +// which there is no applicable handler. This includes both +// characters that are part of markup which is of a kind that is +// not reported (comments, markup declarations), or characters +// that are part of a construct which could be reported but +// for which no handler has been supplied. The characters are passed +// exactly as they were in the XML document except that +// they will be encoded in UTF-8. Line boundaries are not normalized. +// Note that a byte order mark character is not passed to the default handler. +// There are no guarantees about how characters are divided between calls +// to the default handler: for example, a comment might be split between +// multiple calls. + +// static +void LLXmlParser::defaultDataHandler( + void *userData, + const XML_Char *s, + int len) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->defaultData( s, len ); +} + +// This is called for a declaration of an unparsed (NDATA) +// entity. The base argument is whatever was set by XML_SetBase. +// The entityName, systemId and notationName arguments will never be null. +// The other arguments may be. +// static +void LLXmlParser::unparsedEntityDeclHandler( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName) +{ + LLXmlParser* self = (LLXmlParser*) userData; + self->unparsedEntityDecl( entityName, base, systemId, publicId, notationName ); +} + + + + +//////////////////////////////////////////////////////////////////// +// Test code. + +/* +class LLXmlDOMParser : public LLXmlParser +{ +public: + + LLXmlDOMParser() {} + virtual ~LLXmlDOMParser() {} + + void tabs() + { + for ( int i = 0; i < getDepth(); i++) + { + putchar(' '); + } + } + + virtual void startElement(const char *name, const char **atts) + { + tabs(); + printf("startElement %s\n", name); + + S32 i = 0; + while( atts[i] && atts[i+1] ) + { + tabs(); + printf( "\t%s=%s\n", atts[i], atts[i+1] ); + i += 2; + } + + if( atts[i] ) + { + tabs(); + printf( "\ttrailing attribute: %s\n", atts[i] ); + } + } + + virtual void endElement(const char *name) + { + tabs(); + printf("endElement %s\n", name); + } + + virtual void characterData(const char *s, int len) + { + tabs(); + + char* str = new char[len+1]; + strncpy( str, s, len ); + str[len] = '\0'; + printf("CharacterData %s\n", str); + delete str; + } + + virtual void processingInstruction(const char *target, const char *data) + { + tabs(); + printf("processingInstruction %s\n", data); + } + virtual void comment(const char *data) + { + tabs(); + printf("comment %s\n", data); + } + + virtual void startCdataSection() + { + tabs(); + printf("startCdataSection\n"); + } + + virtual void endCdataSection() + { + tabs(); + printf("endCdataSection\n"); + } + + virtual void defaultData(const char *s, int len) + { + tabs(); + + char* str = new char[len+1]; + strncpy( str, s, len ); + str[len] = '\0'; + printf("defaultData %s\n", str); + delete str; + } + + virtual void unparsedEntityDecl( + const char *entityName, + const char *base, + const char *systemId, + const char *publicId, + const char *notationName) + { + tabs(); + + printf( + "unparsed entity:\n" + "\tentityName %s\n" + "\tbase %s\n" + "\tsystemId %s\n" + "\tpublicId %s\n" + "\tnotationName %s\n", + entityName, + base, + systemId, + publicId, + notationName ); + } +}; + + +int main() +{ + char buf[1024]; + + FILE* file = LLFile::fopen("test.xml", "rb"); + if( !file ) + { + return 1; + } + + LLXmlDOMParser parser; + int done; + do { + size_t len = fread(buf, 1, sizeof(buf), file); + done = len < sizeof(buf); + if( 0 == parser.parse( buf, len, done) ) + { + fprintf(stderr, + "%s at line %d\n", + parser.getErrorString(), + parser.getCurrentLineNumber() ); + return 1; + } + } while (!done); + + fclose( file ); + return 0; +} +*/ + diff --git a/indra/llxml/llxmlparser.h b/indra/llxml/llxmlparser.h new file mode 100644 index 0000000000..2cb75591fb --- /dev/null +++ b/indra/llxml/llxmlparser.h @@ -0,0 +1,109 @@ +/** + * @file llxmlparser.h + * @brief LLXmlParser class definition + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLXMLPARSER_H +#define LL_LLXMLPARSER_H + +#define XML_STATIC +#include "expat/expat.h" + +class LLXmlParser +{ +public: + LLXmlParser(); + virtual ~LLXmlParser(); + + // Parses entire file + BOOL parseFile(const std::string &path); + + // Parses some input. Returns 0 if a fatal error is detected. + // The last call must have isFinal true; + // len may be zero for this call (or any other). + S32 parse( const char* buf, int len, int isFinal ); + + const char* getErrorString(); + + S32 getCurrentLineNumber(); + + S32 getCurrentColumnNumber(); + + S32 getDepth() { return mDepth; } + +protected: + // atts is array of name/value pairs, terminated by 0; + // names and values are 0 terminated. + virtual void startElement(const char *name, const char **atts) {} + + virtual void endElement(const char *name) {} + + // s is not 0 terminated. + virtual void characterData(const char *s, int len) {} + + // target and data are 0 terminated + virtual void processingInstruction(const char *target, const char *data) {} + + // data is 0 terminated + virtual void comment(const char *data) {} + + virtual void startCdataSection() {} + + virtual void endCdataSection() {} + + // This is called for any characters in the XML document for + // which there is no applicable handler. This includes both + // characters that are part of markup which is of a kind that is + // not reported (comments, markup declarations), or characters + // that are part of a construct which could be reported but + // for which no handler has been supplied. The characters are passed + // exactly as they were in the XML document except that + // they will be encoded in UTF-8. Line boundaries are not normalized. + // Note that a byte order mark character is not passed to the default handler. + // There are no guarantees about how characters are divided between calls + // to the default handler: for example, a comment might be split between + // multiple calls. + virtual void defaultData(const char *s, int len) {} + + // This is called for a declaration of an unparsed (NDATA) + // entity. The base argument is whatever was set by XML_SetBase. + // The entityName, systemId and notationName arguments will never be null. + // The other arguments may be. + virtual void unparsedEntityDecl( + const char *entityName, + const char *base, + const char *systemId, + const char *publicId, + const char *notationName) {} + +public: + /////////////////////////////////////////////////////////////////////////////// + // Pseudo-private methods. These are only used by internal callbacks. + + static void startElementHandler(void *userData, const XML_Char *name, const XML_Char **atts); + static void endElementHandler(void *userData, const XML_Char *name); + static void characterDataHandler(void *userData, const XML_Char *s, int len); + static void processingInstructionHandler(void *userData, const XML_Char *target, const XML_Char *data); + static void commentHandler(void *userData, const XML_Char *data); + static void startCdataSectionHandler(void *userData); + static void endCdataSectionHandler(void *userData); + static void defaultDataHandler( void *userData, const XML_Char *s, int len); + static void unparsedEntityDeclHandler( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + + +protected: + XML_Parser mParser; + int mDepth; + char mAuxErrorString[1024]; +}; + +#endif // LL_LLXMLPARSER_H diff --git a/indra/llxml/llxmltree.cpp b/indra/llxml/llxmltree.cpp new file mode 100644 index 0000000000..3d0d1ba379 --- /dev/null +++ b/indra/llxml/llxmltree.cpp @@ -0,0 +1,671 @@ +/** + * @file llxmltree.cpp + * @brief LLXmlTree implementation + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llxmltree.h" +#include "v3color.h" +#include "v4color.h" +#include "v4coloru.h" +#include "v3math.h" +#include "v3dmath.h" +#include "v4math.h" +#include "llquaternion.h" +#include "lluuid.h" + +////////////////////////////////////////////////////////////// +// LLXmlTree + +// static +LLStdStringTable LLXmlTree::sAttributeKeys(1024); + +LLXmlTree::LLXmlTree() + : mRoot( NULL ), + mNodeNames(512) +{ +} + +LLXmlTree::~LLXmlTree() +{ + cleanup(); +} + +void LLXmlTree::cleanup() +{ + delete mRoot; + mRoot = NULL; + mNodeNames.cleanup(); +} + + +BOOL LLXmlTree::parseFile(const std::string &path, BOOL keep_contents) +{ + delete mRoot; + mRoot = NULL; + + LLXmlTreeParser parser(this); + BOOL success = parser.parseFile( path, &mRoot, keep_contents ); + if( !success ) + { + S32 line_number = parser.getCurrentLineNumber(); + const char* error = parser.getErrorString(); + llwarns << "LLXmlTree parse failed. Line " << line_number << ": " << error << llendl; + } + return success; +} + +void LLXmlTree::dump() +{ + if( mRoot ) + { + dumpNode( mRoot, " " ); + } +} + +void LLXmlTree::dumpNode( LLXmlTreeNode* node, const LLString& prefix ) +{ + node->dump( prefix ); + + LLString new_prefix = prefix + " "; + for( LLXmlTreeNode* child = node->getFirstChild(); child; child = node->getNextChild() ) + { + dumpNode( child, new_prefix ); + } +} + +////////////////////////////////////////////////////////////// +// LLXmlTreeNode + +LLXmlTreeNode::LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LLXmlTree* tree ) + : mName(name), + mParent(parent), + mTree(tree) +{ +} + +LLXmlTreeNode::~LLXmlTreeNode() +{ + attribute_map_t::iterator iter; + for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++) + delete iter->second; + child_list_t::iterator child_iter; + for (child_iter=mChildList.begin(); child_iter != mChildList.end(); child_iter++) + delete *child_iter; +} + +void LLXmlTreeNode::dump( const LLString& prefix ) +{ + llinfos << prefix << mName ; + if( !mContents.empty() ) + { + llcont << " contents = \"" << mContents << "\""; + } + attribute_map_t::iterator iter; + for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++) + { + LLStdStringHandle key = iter->first; + const LLString* value = iter->second; + llcont << prefix << " " << key << "=" << (value->empty() ? "NULL" : *value); + } + llcont << llendl; +} + +BOOL LLXmlTreeNode::hasAttribute(const std::string& name) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + attribute_map_t::iterator iter = mAttributes.find(canonical_name); + return (iter == mAttributes.end()) ? false : true; +} + +void LLXmlTreeNode::addAttribute(const std::string& name, const std::string& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + const LLString *newstr = new LLString(value); + mAttributes[canonical_name] = newstr; // insert + copy +} + +LLXmlTreeNode* LLXmlTreeNode::getFirstChild() +{ + mChildListIter = mChildList.begin(); + return getNextChild(); +} +LLXmlTreeNode* LLXmlTreeNode::getNextChild() +{ + if (mChildListIter == mChildList.end()) + return 0; + else + return *mChildListIter++; +} + +LLXmlTreeNode* LLXmlTreeNode::getChildByName(const std::string& name) +{ + LLStdStringHandle tableptr = mTree->mNodeNames.checkString(name); + mChildMapIter = mChildMap.lower_bound(tableptr); + mChildMapEndIter = mChildMap.upper_bound(tableptr); + return getNextNamedChild(); +} + +LLXmlTreeNode* LLXmlTreeNode::getNextNamedChild() +{ + if (mChildMapIter == mChildMapEndIter) + return NULL; + else + return (mChildMapIter++)->second; +} + +void LLXmlTreeNode::appendContents(const std::string& str) +{ + mContents.append( str ); +} + +void LLXmlTreeNode::addChild(LLXmlTreeNode* child) +{ + llassert( child ); + mChildList.push_back( child ); + + // Add a name mapping to this node + LLStdStringHandle tableptr = mTree->mNodeNames.insert(child->mName); + mChildMap.insert( child_map_t::value_type(tableptr, child)); + + child->mParent = this; +} + +////////////////////////////////////////////////////////////// + +// These functions assume that name is already in mAttritrubteKeys + +BOOL LLXmlTreeNode::getFastAttributeBOOL(LLStdStringHandle canonical_name, BOOL& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToBOOL( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeU8(LLStdStringHandle canonical_name, U8& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToU8( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeS8(LLStdStringHandle canonical_name, S8& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToS8( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeS16(LLStdStringHandle canonical_name, S16& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToS16( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeU16(LLStdStringHandle canonical_name, U16& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToU16( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeU32(LLStdStringHandle canonical_name, U32& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToU32( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeS32(LLStdStringHandle canonical_name, S32& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToS32( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeF32(LLStdStringHandle canonical_name, F32& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToF32( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeF64(LLStdStringHandle canonical_name, F64& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s && LLString::convertToF64( *s, value ); +} + +BOOL LLXmlTreeNode::getFastAttributeColor(LLStdStringHandle canonical_name, LLColor4& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s ? LLColor4::parseColor(s->c_str(), &value) : FALSE; +} + +BOOL LLXmlTreeNode::getFastAttributeColor4(LLStdStringHandle canonical_name, LLColor4& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s ? LLColor4::parseColor4(s->c_str(), &value) : FALSE; +} + +BOOL LLXmlTreeNode::getFastAttributeColor4U(LLStdStringHandle canonical_name, LLColor4U& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s ? LLColor4U::parseColor4U(s->c_str(), &value ) : FALSE; +} + +BOOL LLXmlTreeNode::getFastAttributeVector3(LLStdStringHandle canonical_name, LLVector3& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s ? LLVector3::parseVector3(s->c_str(), &value ) : FALSE; +} + +BOOL LLXmlTreeNode::getFastAttributeVector3d(LLStdStringHandle canonical_name, LLVector3d& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s ? LLVector3d::parseVector3d(s->c_str(), &value ) : FALSE; +} + +BOOL LLXmlTreeNode::getFastAttributeQuat(LLStdStringHandle canonical_name, LLQuaternion& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s ? LLQuaternion::parseQuat(s->c_str(), &value ) : FALSE; +} + +BOOL LLXmlTreeNode::getFastAttributeUUID(LLStdStringHandle canonical_name, LLUUID& value) +{ + const LLString *s = getAttribute( canonical_name ); + return s ? LLUUID::parseUUID(s->c_str(), &value ) : FALSE; +} + +BOOL LLXmlTreeNode::getFastAttributeString(LLStdStringHandle canonical_name, LLString& value) +{ + const LLString *s = getAttribute( canonical_name ); + if( !s ) + { + return FALSE; + } + + value = *s; + return TRUE; +} + + +////////////////////////////////////////////////////////////// + +BOOL LLXmlTreeNode::getAttributeBOOL(const std::string& name, BOOL& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeBOOL(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeU8(const std::string& name, U8& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeU8(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeS8(const std::string& name, S8& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeS8(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeS16(const std::string& name, S16& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeS16(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeU16(const std::string& name, U16& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeU16(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeU32(const std::string& name, U32& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeU32(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeS32(const std::string& name, S32& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeS32(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeF32(const std::string& name, F32& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeF32(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeF64(const std::string& name, F64& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeF64(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeColor(const std::string& name, LLColor4& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeColor(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeColor4(const std::string& name, LLColor4& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeColor4(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeColor4U(const std::string& name, LLColor4U& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeColor4U(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeVector3(const std::string& name, LLVector3& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeVector3(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeVector3d(const std::string& name, LLVector3d& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeVector3d(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeQuat(const std::string& name, LLQuaternion& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeQuat(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeUUID(const std::string& name, LLUUID& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeUUID(canonical_name, value); +} + +BOOL LLXmlTreeNode::getAttributeString(const std::string& name, LLString& value) +{ + LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name ); + return getFastAttributeString(canonical_name, value); +} + +/* + The following xml <message> nodes will all return the string from getTextContents(): + "The quick brown fox\n Jumps over the lazy dog" + + 1. HTML paragraph format: + <message> + <p>The quick brown fox</p> + <p> Jumps over the lazy dog</p> + </message> + 2. Each quoted section -> paragraph: + <message> + "The quick brown fox" + " Jumps over the lazy dog" + </message> + 3. Literal text with beginning and trailing whitespace removed: + <message> +The quick brown fox + Jumps over the lazy dog + </message> + +*/ + +LLString LLXmlTreeNode::getTextContents() +{ + std::string msg; + LLXmlTreeNode* p = getChildByName("p"); + if (p) + { + // Case 1: node has <p>text</p> tags + while (p) + { + msg += p->getContents() + "\n"; + p = getNextNamedChild(); + } + } + else + { + std::string::size_type n = mContents.find_first_not_of(" \t\n"); + if (n != std::string::npos && mContents[n] == '\"') + { + // Case 2: node has quoted text + S32 num_lines = 0; + while(1) + { + // mContents[n] == '"' + ++n; + std::string::size_type t = n; + std::string::size_type m = 0; + // fix-up escaped characters + while(1) + { + m = mContents.find_first_of("\\\"", t); // find first \ or " + if ((m == std::string::npos) || (mContents[m] == '\"')) + { + break; + } + mContents.erase(m,1); + t = m+1; + } + if (m == std::string::npos) + { + break; + } + // mContents[m] == '"' + num_lines++; + msg += mContents.substr(n,m-n) + "\n"; + n = mContents.find_first_of("\"", m+1); + if (n == std::string::npos) + { + if (num_lines == 1) + { + msg.erase(msg.size()-1); // remove "\n" if only one line + } + break; + } + } + } + else + { + // Case 3: node has embedded text (beginning and trailing whitespace trimmed) + msg = mContents; + } + } + return msg; +} + + +////////////////////////////////////////////////////////////// +// LLXmlTreeParser + +LLXmlTreeParser::LLXmlTreeParser(LLXmlTree* tree) + : mTree(tree), + mRoot( NULL ), + mCurrent( NULL ), + mDump( FALSE ) +{ +} + +LLXmlTreeParser::~LLXmlTreeParser() +{ +} + +BOOL LLXmlTreeParser::parseFile(const std::string &path, LLXmlTreeNode** root, BOOL keep_contents) +{ + llassert( !mRoot ); + llassert( !mCurrent ); + + mKeepContents = keep_contents; + + BOOL success = LLXmlParser::parseFile(path); + + *root = mRoot; + mRoot = NULL; + + if( success ) + { + llassert( !mCurrent ); + } + mCurrent = NULL; + + return success; +} + + +const std::string& LLXmlTreeParser::tabs() +{ + static LLString s; + s = ""; + S32 num_tabs = getDepth() - 1; + for( S32 i = 0; i < num_tabs; i++) + { + s += " "; + } + return s; +} + +void LLXmlTreeParser::startElement(const char* name, const char **atts) +{ + if( mDump ) + { + llinfos << tabs() << "startElement " << name << llendl; + + S32 i = 0; + while( atts[i] && atts[i+1] ) + { + llinfos << tabs() << "attribute: " << atts[i] << "=" << atts[i+1] << llendl; + i += 2; + } + } + + LLXmlTreeNode* child = CreateXmlTreeNode( std::string(name), mCurrent ); + + S32 i = 0; + while( atts[i] && atts[i+1] ) + { + child->addAttribute( atts[i], atts[i+1] ); + i += 2; + } + + if( mCurrent ) + { + mCurrent->addChild( child ); + + } + else + { + llassert( !mRoot ); + mRoot = child; + } + mCurrent = child; +} + +LLXmlTreeNode* LLXmlTreeParser::CreateXmlTreeNode(const std::string& name, LLXmlTreeNode* parent) +{ + return new LLXmlTreeNode(name, parent, mTree); +} + + +void LLXmlTreeParser::endElement(const char* name) +{ + if( mDump ) + { + llinfos << tabs() << "endElement " << name << llendl; + } + + if( !mCurrent->mContents.empty() ) + { + LLString::trim(mCurrent->mContents); + LLString::removeCRLF(mCurrent->mContents); + } + + mCurrent = mCurrent->getParent(); +} + +void LLXmlTreeParser::characterData(const char *s, int len) +{ + LLString str(s, len); + if( mDump ) + { + llinfos << tabs() << "CharacterData " << str << llendl; + } + + if (mKeepContents) + { + mCurrent->appendContents( str ); + } +} + +void LLXmlTreeParser::processingInstruction(const char *target, const char *data) +{ + if( mDump ) + { + llinfos << tabs() << "processingInstruction " << data << llendl; + } +} + +void LLXmlTreeParser::comment(const char *data) +{ + if( mDump ) + { + llinfos << tabs() << "comment " << data << llendl; + } +} + +void LLXmlTreeParser::startCdataSection() +{ + if( mDump ) + { + llinfos << tabs() << "startCdataSection" << llendl; + } +} + +void LLXmlTreeParser::endCdataSection() +{ + if( mDump ) + { + llinfos << tabs() << "endCdataSection" << llendl; + } +} + +void LLXmlTreeParser::defaultData(const char *s, int len) +{ + if( mDump ) + { + LLString str(s, len); + llinfos << tabs() << "defaultData " << str << llendl; + } +} + +void LLXmlTreeParser::unparsedEntityDecl( + const char* entity_name, + const char* base, + const char* system_id, + const char* public_id, + const char* notation_name) +{ + if( mDump ) + { + llinfos << tabs() << "unparsed entity:" << llendl; + llinfos << tabs() << " entityName " << entity_name << llendl; + llinfos << tabs() << " base " << base << llendl; + llinfos << tabs() << " systemId " << system_id << llendl; + llinfos << tabs() << " publicId " << public_id << llendl; + llinfos << tabs() << " notationName " << notation_name<< llendl; + } +} + +void test_llxmltree() +{ + LLXmlTree tree; + BOOL success = tree.parseFile( "test.xml" ); + if( success ) + { + tree.dump(); + } +} + diff --git a/indra/llxml/llxmltree.h b/indra/llxml/llxmltree.h new file mode 100644 index 0000000000..8cee425dd9 --- /dev/null +++ b/indra/llxml/llxmltree.h @@ -0,0 +1,216 @@ +/** + * @file llxmltree.h + * @author Aaron Yonas, Richard Nelson + * @brief LLXmlTree class definition + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLXMLTREE_H +#define LL_LLXMLTREE_H + +#include <map> +#include <list> +#include "llstring.h" +#include "llxmlparser.h" +#include "string_table.h" + +class LLColor4; +class LLColor4U; +class LLQuaternion; +class LLUUID; +class LLVector3; +class LLVector3d; +class LLXmlTreeNode; +class LLXmlTreeParser; + +////////////////////////////////////////////////////////////// +// LLXmlTree + +class LLXmlTree +{ + friend class LLXmlTreeNode; + +public: + LLXmlTree(); + virtual ~LLXmlTree(); + void cleanup(); + + virtual BOOL parseFile(const std::string &path, BOOL keep_contents = TRUE); + + LLXmlTreeNode* getRoot() { return mRoot; } + + void dump(); + void dumpNode( LLXmlTreeNode* node, const LLString &prefix ); + + static LLStdStringHandle addAttributeString( const std::string& name) + { + return sAttributeKeys.addString( name ); + } + +public: + // global + static LLStdStringTable sAttributeKeys; + +protected: + LLXmlTreeNode* mRoot; + + // local + LLStdStringTable mNodeNames; +}; + +////////////////////////////////////////////////////////////// +// LLXmlTreeNode + +class LLXmlTreeNode +{ + friend class LLXmlTree; + friend class LLXmlTreeParser; + +protected: + // Protected since nodes are only created and destroyed by friend classes and other LLXmlTreeNodes + LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LLXmlTree* tree ); + +public: + virtual ~LLXmlTreeNode(); + + const std::string& getName() + { + return mName; + } + BOOL hasName( const std::string& name ) + { + return mName == name; + } + + BOOL hasAttribute( const std::string& name ); + + // Fast versions use cannonical_name handlee to entru in LLXmlTree::sAttributeKeys string table + BOOL getFastAttributeBOOL( LLStdStringHandle cannonical_name, BOOL& value ); + BOOL getFastAttributeU8( LLStdStringHandle cannonical_name, U8& value ); + BOOL getFastAttributeS8( LLStdStringHandle cannonical_name, S8& value ); + BOOL getFastAttributeU16( LLStdStringHandle cannonical_name, U16& value ); + BOOL getFastAttributeS16( LLStdStringHandle cannonical_name, S16& value ); + BOOL getFastAttributeU32( LLStdStringHandle cannonical_name, U32& value ); + BOOL getFastAttributeS32( LLStdStringHandle cannonical_name, S32& value ); + BOOL getFastAttributeF32( LLStdStringHandle cannonical_name, F32& value ); + BOOL getFastAttributeF64( LLStdStringHandle cannonical_name, F64& value ); + BOOL getFastAttributeColor( LLStdStringHandle cannonical_name, LLColor4& value ); + BOOL getFastAttributeColor4( LLStdStringHandle cannonical_name, LLColor4& value ); + BOOL getFastAttributeColor4U( LLStdStringHandle cannonical_name, LLColor4U& value ); + BOOL getFastAttributeVector3( LLStdStringHandle cannonical_name, LLVector3& value ); + BOOL getFastAttributeVector3d( LLStdStringHandle cannonical_name, LLVector3d& value ); + BOOL getFastAttributeQuat( LLStdStringHandle cannonical_name, LLQuaternion& value ); + BOOL getFastAttributeUUID( LLStdStringHandle cannonical_name, LLUUID& value ); + BOOL getFastAttributeString( LLStdStringHandle cannonical_name, LLString& value ); + + // Normal versions find 'name' in LLXmlTree::sAttributeKeys then call fast versions + virtual BOOL getAttributeBOOL( const std::string& name, BOOL& value ); + virtual BOOL getAttributeU8( const std::string& name, U8& value ); + virtual BOOL getAttributeS8( const std::string& name, S8& value ); + virtual BOOL getAttributeU16( const std::string& name, U16& value ); + virtual BOOL getAttributeS16( const std::string& name, S16& value ); + virtual BOOL getAttributeU32( const std::string& name, U32& value ); + virtual BOOL getAttributeS32( const std::string& name, S32& value ); + virtual BOOL getAttributeF32( const std::string& name, F32& value ); + virtual BOOL getAttributeF64( const std::string& name, F64& value ); + virtual BOOL getAttributeColor( const std::string& name, LLColor4& value ); + virtual BOOL getAttributeColor4( const std::string& name, LLColor4& value ); + virtual BOOL getAttributeColor4U( const std::string& name, LLColor4U& value ); + virtual BOOL getAttributeVector3( const std::string& name, LLVector3& value ); + virtual BOOL getAttributeVector3d( const std::string& name, LLVector3d& value ); + virtual BOOL getAttributeQuat( const std::string& name, LLQuaternion& value ); + virtual BOOL getAttributeUUID( const std::string& name, LLUUID& value ); + virtual BOOL getAttributeString( const std::string& name, LLString& value ); + + const LLString& getContents() + { + return mContents; + } + LLString getTextContents(); + + LLXmlTreeNode* getParent() { return mParent; } + LLXmlTreeNode* getFirstChild(); + LLXmlTreeNode* getNextChild(); + S32 getChildCount() { return (S32)mChildList.size(); } + LLXmlTreeNode* getChildByName( const std::string& name ); // returns first child with name, NULL if none + LLXmlTreeNode* getNextNamedChild(); // returns next child with name, NULL if none + +protected: + const LLString* getAttribute( LLStdStringHandle name) + { + attribute_map_t::iterator iter = mAttributes.find(name); + return (iter == mAttributes.end()) ? 0 : iter->second; + } + +private: + void addAttribute( const std::string& name, const std::string& value ); + void appendContents( const std::string& str ); + void addChild( LLXmlTreeNode* child ); + + void dump( const LLString& prefix ); + +protected: + typedef std::map<LLStdStringHandle, const LLString*> attribute_map_t; + attribute_map_t mAttributes; + +private: + LLString mName; + LLString mContents; + + typedef std::list<class LLXmlTreeNode *> child_list_t; + child_list_t mChildList; + child_list_t::iterator mChildListIter; + + typedef std::multimap<LLStdStringHandle, LLXmlTreeNode *> child_map_t; + child_map_t mChildMap; // for fast name lookups + child_map_t::iterator mChildMapIter; + child_map_t::iterator mChildMapEndIter; + + LLXmlTreeNode* mParent; + LLXmlTree* mTree; +}; + +////////////////////////////////////////////////////////////// +// LLXmlTreeParser + +class LLXmlTreeParser : public LLXmlParser +{ +public: + LLXmlTreeParser(LLXmlTree* tree); + virtual ~LLXmlTreeParser(); + + BOOL parseFile(const std::string &path, LLXmlTreeNode** root, BOOL keep_contents ); + +protected: + const std::string& tabs(); + + // Overrides from LLXmlParser + virtual void startElement(const char *name, const char **attributes); + virtual void endElement(const char *name); + virtual void characterData(const char *s, int len); + virtual void processingInstruction(const char *target, const char *data); + virtual void comment(const char *data); + virtual void startCdataSection(); + virtual void endCdataSection(); + virtual void defaultData(const char *s, int len); + virtual void unparsedEntityDecl( + const char* entity_name, + const char* base, + const char* system_id, + const char* public_id, + const char* notation_name); + + //template method pattern + virtual LLXmlTreeNode* CreateXmlTreeNode(const std::string& name, LLXmlTreeNode* parent); + +protected: + LLXmlTree* mTree; + LLXmlTreeNode* mRoot; + LLXmlTreeNode* mCurrent; + BOOL mDump; // Dump parse tree to llinfos as it is read. + BOOL mKeepContents; +}; + +#endif // LL_LLXMLTREE_H |