diff options
Diffstat (limited to 'indra/llxml')
-rw-r--r-- | indra/llxml/llcontrol.cpp | 3026 | ||||
-rw-r--r-- | indra/llxml/llcontrol.h | 950 | ||||
-rw-r--r-- | indra/llxml/llxmlnode.cpp | 6553 | ||||
-rw-r--r-- | indra/llxml/llxmlnode.h | 674 | ||||
-rw-r--r-- | indra/llxml/llxmlparser.cpp | 832 | ||||
-rw-r--r-- | indra/llxml/llxmlparser.h | 266 | ||||
-rw-r--r-- | indra/llxml/llxmltree.cpp | 1388 | ||||
-rw-r--r-- | indra/llxml/llxmltree.h | 468 | ||||
-rw-r--r-- | indra/llxml/tests/llcontrol_test.cpp | 308 |
9 files changed, 7216 insertions, 7249 deletions
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index 5b6d197e52..beeca105bc 100644 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -1,1518 +1,1508 @@ -/** - * @file llcontrol.cpp - * @brief Holds global state for viewer. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include <iostream> -#include <fstream> -#include <algorithm> - -#include "llcontrol.h" - -#include "llstl.h" - -#include "llstring.h" -#include "v3math.h" -#include "v3dmath.h" -#include "v4coloru.h" -#include "v4color.h" -#include "v3color.h" -#include "llquaternion.h" -#include "llrect.h" -#include "llxmltree.h" -#include "llsdserialize.h" -#include "llfile.h" -#include "lltimer.h" -#include "lldir.h" - -#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG -#define CONTROL_ERRS LL_ERRS("ControlErrors") -#else -#define CONTROL_ERRS LL_WARNS("ControlErrors") -#endif - - -template <> eControlType get_control_type<U32>(); -template <> eControlType get_control_type<S32>(); -template <> eControlType get_control_type<F32>(); -template <> eControlType get_control_type<bool>(); -// Yay BOOL, its really an S32. -//template <> eControlType get_control_type<BOOL> () ; -template <> eControlType get_control_type<std::string>(); - -template <> eControlType get_control_type<LLVector3>(); -template <> eControlType get_control_type<LLVector3d>(); -template <> eControlType get_control_type<LLRect>(); -template <> eControlType get_control_type<LLColor4>(); -template <> eControlType get_control_type<LLColor3>(); -template <> eControlType get_control_type<LLColor4U>(); -template <> eControlType get_control_type<LLSD>(); - -template <> LLSD convert_to_llsd<U32>(const U32& in); -template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in); -template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in); -template <> LLSD convert_to_llsd<LLRect>(const LLRect& in); -template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in); -template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in); -template <> LLSD convert_to_llsd<LLColor4U>(const LLColor4U& in); - -template <> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLColor4U convert_from_llsd<LLColor4U>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, const std::string& control_name); -template <> LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, const std::string& control_name); - -//this defines the current version of the settings file -const S32 CURRENT_VERSION = 101; - -// If you define the environment variable LL_SETTINGS_PROFILE to any value this will activate -// the gSavedSettings profiling code. This code tracks the calls to get a saved (debug) setting. -// When the viewer exits the results are written to the log directory to the file specified -// by SETTINGS_PROFILE below. Only settings with an average access rate >= 2/second are output. -typedef std::pair<std::string, U32> settings_pair_t; -typedef std::vector<settings_pair_t> settings_vec_t; -LLSD getCount; -settings_vec_t getCount_v; -F64 start_time = 0; -std::string SETTINGS_PROFILE = "settings_profile.log"; - -bool LLControlVariable::llsd_compare(const LLSD& a, const LLSD & b) -{ - bool result = false; - switch (mType) - { - case TYPE_U32: - case TYPE_S32: - result = a.asInteger() == b.asInteger(); - break; - case TYPE_BOOLEAN: - result = a.asBoolean() == b.asBoolean(); - break; - case TYPE_F32: - result = a.asReal() == b.asReal(); - break; - case TYPE_VEC3: - case TYPE_VEC3D: - result = LLVector3d(a) == LLVector3d(b); - break; - case TYPE_QUAT: - result = LLQuaternion(a) == LLQuaternion(b); - break; - case TYPE_RECT: - result = LLRect(a) == LLRect(b); - break; - case TYPE_COL4: - result = LLColor4(a) == LLColor4(b); - break; - case TYPE_COL3: - result = LLColor3(a) == LLColor3(b); - break; - case TYPE_STRING: - result = a.asString() == b.asString(); - break; - default: - break; - } - - return result; -} - -LLControlVariable::LLControlVariable(const std::string& name, eControlType type, - LLSD initial, const std::string& comment, - ePersist persist, bool hidefromsettingseditor) - : mName(name), - mComment(comment), - mType(type), - mPersist(persist), - mHideFromSettingsEditor(hidefromsettingseditor) -{ - if ((persist != PERSIST_NO) && mComment.empty()) - { - LL_ERRS() << "Must supply a comment for control " << mName << LL_ENDL; - } - //Push back versus setValue'ing here, since we don't want to call a signal yet - mValues.push_back(initial); -} - - - -LLControlVariable::~LLControlVariable() -{ -} - -LLSD LLControlVariable::getComparableValue(const LLSD& value) -{ - // *FIX:MEP - The following is needed to make the LLSD::ImplString - // work with boolean controls... - LLSD storable_value; - if(TYPE_BOOLEAN == type() && value.isString()) - { - BOOL temp; - if(LLStringUtil::convertToBOOL(value.asString(), temp)) - { - storable_value = (bool)temp; - } - else - { - storable_value = false; - } - } - else if (TYPE_LLSD == type() && value.isString()) - { - LLPointer<LLSDNotationParser> parser = new LLSDNotationParser; - LLSD result; - std::stringstream value_stream(value.asString()); - if (parser->parse(value_stream, result, LLSDSerialize::SIZE_UNLIMITED) != LLSDParser::PARSE_FAILURE) - { - storable_value = result; - } - else - { - storable_value = value; - } - } - else - { - storable_value = value; - } - - return storable_value; -} - -void LLControlVariable::setValue(const LLSD& new_value, bool saved_value) -{ - if (mValidateSignal(this, new_value) == false) - { - // can not set new value, exit - return; - } - - LLSD storable_value = getComparableValue(new_value); - LLSD original_value = getValue(); - bool value_changed = llsd_compare(original_value, storable_value) == FALSE; - if(saved_value) - { - // If we're going to save this value, return to default but don't fire - resetToDefault(false); - if (llsd_compare(mValues.back(), storable_value) == FALSE) - { - mValues.push_back(storable_value); - } - } - else - { - // This is an unsaved value. Its needs to reside at - // mValues[2] (or greater). It must not affect - // the result of getSaveValue() - if (llsd_compare(mValues.back(), storable_value) == FALSE) - { - while(mValues.size() > 2) - { - // Remove any unsaved values. - mValues.pop_back(); - } - - if(mValues.size() < 2) - { - // Add the default to the 'save' value. - mValues.push_back(mValues[0]); - } - - // Add the 'un-save' value. - mValues.push_back(storable_value); - } - } - - if(value_changed) - { - firePropertyChanged(original_value); - } -} - -void LLControlVariable::setDefaultValue(const LLSD& value) -{ - // Set the control variables value and make it - // the default value. If the active value is changed, - // send the signal. - // *NOTE: Default values are not saved, only read. - - LLSD comparable_value = getComparableValue(value); - LLSD original_value = getValue(); - bool value_changed = (llsd_compare(original_value, comparable_value) == FALSE); - resetToDefault(false); - mValues[0] = comparable_value; - if(value_changed) - { - firePropertyChanged(original_value); - } -} - -void LLControlVariable::setPersist(ePersist state) -{ - mPersist = state; -} - -void LLControlVariable::setHiddenFromSettingsEditor(bool hide) -{ - mHideFromSettingsEditor = hide; -} - -void LLControlVariable::setComment(const std::string& comment) -{ - mComment = comment; -} - -void LLControlVariable::resetToDefault(bool fire_signal) -{ - //The first setting is always the default - //Pop to it and fire off the listener - LLSD originalValue = mValues.back(); - - while(mValues.size() > 1) - { - mValues.pop_back(); - } - - if(fire_signal) - { - firePropertyChanged(originalValue); - } -} - -bool LLControlVariable::shouldSave(bool nondefault_only) -{ - // This method is used to decide whether we should save a given - // variable. Two of the three values of mPersist are easy. - if (mPersist == PERSIST_NO) - return false; - - if (mPersist == PERSIST_ALWAYS) - return true; - - // PERSIST_NONDFT - // If caller doesn't need us to filter, just save. - if (! nondefault_only) - return true; - - // PERSIST_NONDFT: caller only wants us to save this variable if its value - // differs from default. - if (isDefault()) // never been altered - return false; - - // We've set at least one other value: compare it to default. Save only if - // they differ. - return ! llsd_compare(getSaveValue(), getDefault()); -} - -LLSD LLControlVariable::getSaveValue() const -{ - //The first level of the stack is default - //We assume that the second level is user preferences that should be saved - if(mValues.size() > 1) return mValues[1]; - return mValues[0]; -} - -LLPointer<LLControlVariable> LLControlGroup::getControl(const std::string& name) -{ - if (mSettingsProfile) - { - incrCount(name); - } - - ctrl_name_table_t::iterator iter = mNameTable.find(name); - return iter == mNameTable.end() ? LLPointer<LLControlVariable>() : iter->second; -} - - -//////////////////////////////////////////////////////////////////////////// - -// Must match the type definition in llcontrol.h -const std::string LLControlGroup::mTypeString[TYPE_COUNT] = { "U32" - ,"S32" - ,"F32" - ,"Boolean" - ,"String" - ,"Vector3" - ,"Vector3D" - ,"Quaternion" - ,"Rect" - ,"Color4" - ,"Color3" - ,"LLSD" - }; - -LLControlGroup::LLControlGroup(const std::string& name) -: LLInstanceTracker<LLControlGroup, std::string>(name), - mSettingsProfile(false) -{ - - if (NULL != getenv("LL_SETTINGS_PROFILE")) - { - mSettingsProfile = true; - } -} - -LLControlGroup::~LLControlGroup() -{ - cleanup(); -} - -static bool compareRoutine(settings_pair_t lhs, settings_pair_t rhs) -{ - return lhs.second > rhs.second; -}; - -void LLControlGroup::cleanup() -{ - if(mSettingsProfile && getCount.size() != 0) - { - std::string file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, SETTINGS_PROFILE); - LLFILE* out = LLFile::fopen(file, "w"); /* Flawfinder: ignore */ - if(!out) - { - LL_WARNS("SettingsProfile") << "Error opening " << SETTINGS_PROFILE << LL_ENDL; - } - else - { - F64 end_time = LLTimer::getTotalSeconds(); - U32 total_seconds = (U32)(end_time - start_time); - - std::string msg = llformat("Runtime (seconds): %d\n\n No. accesses Avg. accesses/sec Name\n", total_seconds); - std::ostringstream data_msg; - - data_msg << msg; - size_t data_size = data_msg.str().size(); - if (fwrite(data_msg.str().c_str(), 1, data_size, out) != data_size) - { - LL_WARNS("SettingsProfile") << "Failed to write settings profile header" << LL_ENDL; - } - - for (LLSD::map_const_iterator iter = getCount.beginMap(); iter != getCount.endMap(); ++iter) - { - getCount_v.push_back(settings_pair_t(iter->first, iter->second.asInteger())); - } - sort(getCount_v.begin(), getCount_v.end(), compareRoutine); - - for (settings_vec_t::iterator iter = getCount_v.begin(); iter != getCount_v.end(); ++iter) - { - U32 access_rate = 0; - if (total_seconds != 0) - { - access_rate = iter->second / total_seconds; - } - if (access_rate >= 2) - { - std::ostringstream data_msg; - msg = llformat("%13d %7d %s", iter->second, access_rate, iter->first.c_str()); - data_msg << msg << "\n"; - size_t data_size = data_msg.str().size(); - if (fwrite(data_msg.str().c_str(), 1, data_size, out) != data_size) - { - LL_WARNS("SettingsProfile") << "Failed to write settings profile" << LL_ENDL; - } - } - } - getCount = LLSD::emptyMap(); - fclose(out); - } - } - - mNameTable.clear(); -} - -eControlType LLControlGroup::typeStringToEnum(const std::string& typestr) -{ - for(int i = 0; i < (int)TYPE_COUNT; ++i) - { - if(mTypeString[i] == typestr) return (eControlType)i; - } - return (eControlType)-1; -} - -std::string LLControlGroup::typeEnumToString(eControlType typeenum) -{ - return mTypeString[typeenum]; -} - -LLControlVariable* LLControlGroup::declareControl(const std::string& name, eControlType type, const LLSD initial_val, const std::string& comment, LLControlVariable::ePersist persist, BOOL hidefromsettingseditor) -{ - LLControlVariable* existing_control = getControl(name); - if (existing_control) - { - if ((persist != LLControlVariable::PERSIST_NO) && existing_control->isType(type)) - { - if (!existing_control->llsd_compare(existing_control->getDefault(), initial_val)) - { - // Sometimes we need to declare a control *after* it has been loaded from a settings file. - LLSD cur_value = existing_control->getValue(); // get the current value - existing_control->setDefaultValue(initial_val); // set the default to the declared value - existing_control->setValue(cur_value); // now set to the loaded value - } - } - else - { - LL_WARNS("Settings") << "Control named " << name << " already exists, ignoring new declaration." << LL_ENDL; - } - return existing_control; - } - - // if not, create the control and add it to the name table - LLControlVariable* control = new LLControlVariable(name, type, initial_val, comment, persist, hidefromsettingseditor); - mNameTable[name] = control; - return control; -} - -LLControlVariable* LLControlGroup::declareU32(const std::string& name, const U32 initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_U32, (LLSD::Integer) initial_val, comment, persist); -} - -LLControlVariable* LLControlGroup::declareS32(const std::string& name, const S32 initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_S32, initial_val, comment, persist); -} - -LLControlVariable* LLControlGroup::declareF32(const std::string& name, const F32 initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_F32, initial_val, comment, persist); -} - -LLControlVariable* LLControlGroup::declareBOOL(const std::string& name, const BOOL initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_BOOLEAN, initial_val, comment, persist); -} - -LLControlVariable* LLControlGroup::declareString(const std::string& name, const std::string& initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_STRING, initial_val, comment, persist); -} - -LLControlVariable* LLControlGroup::declareVec3(const std::string& name, const LLVector3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_VEC3, initial_val.getValue(), comment, persist); -} - -LLControlVariable* LLControlGroup::declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist); -} - -LLControlVariable* LLControlGroup::declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_QUAT, initial_val.getValue(), comment, persist); -} - -LLControlVariable* LLControlGroup::declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist) -{ - return declareControl(name, TYPE_RECT, initial_val.getValue(), comment, persist); -} - -LLControlVariable* LLControlGroup::declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist ) -{ - return declareControl(name, TYPE_COL4, initial_val.getValue(), comment, persist); -} - -LLControlVariable* LLControlGroup::declareColor3(const std::string& name, const LLColor3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist ) -{ - return declareControl(name, TYPE_COL3, initial_val.getValue(), comment, persist); -} - -LLControlVariable* LLControlGroup::declareLLSD(const std::string& name, const LLSD &initial_val, const std::string& comment, LLControlVariable::ePersist persist ) -{ - return declareControl(name, TYPE_LLSD, initial_val, comment, persist); -} - -void LLControlGroup::incrCount(const std::string& name) -{ - if (0.0 == start_time) - { - start_time = LLTimer::getTotalSeconds(); - } - getCount[name] = getCount[name].asInteger() + 1; -} - -BOOL LLControlGroup::getBOOL(const std::string& name) -{ - return (BOOL)get<bool>(name); -} - -S32 LLControlGroup::getS32(const std::string& name) -{ - return get<S32>(name); -} - -U32 LLControlGroup::getU32(const std::string& name) -{ - return get<U32>(name); -} - -F32 LLControlGroup::getF32(const std::string& name) -{ - return get<F32>(name); -} - -std::string LLControlGroup::getString(const std::string& name) -{ - return get<std::string>(name); -} - -LLWString LLControlGroup::getWString(const std::string& name) -{ - return get<LLWString>(name); -} - -std::string LLControlGroup::getText(const std::string& name) -{ - std::string utf8_string = getString(name); - LLStringUtil::replaceChar(utf8_string, '^', '\n'); - LLStringUtil::replaceChar(utf8_string, '%', ' '); - return (utf8_string); -} - -LLVector3 LLControlGroup::getVector3(const std::string& name) -{ - return get<LLVector3>(name); -} - -LLVector3d LLControlGroup::getVector3d(const std::string& name) -{ - return get<LLVector3d>(name); -} - -LLQuaternion LLControlGroup::getQuaternion(const std::string& name) -{ - return get<LLQuaternion>(name); -} - -LLRect LLControlGroup::getRect(const std::string& name) -{ - return get<LLRect>(name); -} - - -LLColor4 LLControlGroup::getColor(const std::string& name) -{ - return get<LLColor4>(name); -} - -LLColor4 LLControlGroup::getColor4(const std::string& name) -{ - return get<LLColor4>(name); -} - -LLColor3 LLControlGroup::getColor3(const std::string& name) -{ - return get<LLColor3>(name); -} - -LLSD LLControlGroup::getLLSD(const std::string& name) -{ - return get<LLSD>(name); -} - -LLSD LLControlGroup::asLLSD(bool diffs_only) -{ - // Dump all stored values as LLSD - LLSD result = LLSD::emptyArray(); - for (ctrl_name_table_t::iterator iter = mNameTable.begin(); - iter != mNameTable.end(); iter++) - { - LLControlVariable *control = iter->second; - if (!control || control->isType(TYPE_STRING) || (diffs_only && control->isDefault())) - { - continue; - } - const std::string& name = iter->first; - result[name] = getLLSD(name); - } - return result; -} - -BOOL LLControlGroup::controlExists(const std::string& name) -{ - ctrl_name_table_t::iterator iter = mNameTable.find(name); - return iter != mNameTable.end(); -} - - -//------------------------------------------------------------------- -// Set functions -//------------------------------------------------------------------- - -void LLControlGroup::setBOOL(const std::string& name, BOOL val) -{ - set<bool>(name, val); -} - - -void LLControlGroup::setS32(const std::string& name, S32 val) -{ - set(name, val); -} - - -void LLControlGroup::setF32(const std::string& name, F32 val) -{ - set(name, val); -} - - -void LLControlGroup::setU32(const std::string& name, U32 val) -{ - set(name, val); -} - - -void LLControlGroup::setString(const std::string& name, const std::string &val) -{ - set(name, val); -} - - -void LLControlGroup::setVector3(const std::string& name, const LLVector3 &val) -{ - set(name, val); -} - -void LLControlGroup::setVector3d(const std::string& name, const LLVector3d &val) -{ - set(name, val); -} - -void LLControlGroup::setQuaternion(const std::string& name, const LLQuaternion &val) -{ - set(name, val); -} - -void LLControlGroup::setRect(const std::string& name, const LLRect &val) -{ - set(name, val); -} - -void LLControlGroup::setColor4(const std::string& name, const LLColor4 &val) -{ - set(name, val); -} - -void LLControlGroup::setLLSD(const std::string& name, const LLSD& val) -{ - set(name, val); -} - -void LLControlGroup::setUntypedValue(const std::string& name, const LLSD& val) -{ - if (name.empty()) - { - return; - } - - LLControlVariable* control = getControl(name); - - if (control) - { - control->setValue(val); - } - else - { - CONTROL_ERRS << "Invalid control " << name << LL_ENDL; - } -} - - -//--------------------------------------------------------------- -// Load and save -//--------------------------------------------------------------- - -// Returns number of controls loaded, so 0 if failure -U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, BOOL require_declaration, eControlType declare_as) -{ - std::string name; - - LLXmlTree xml_controls; - - if (!xml_controls.parseFile(filename)) - { - LL_WARNS("Settings") << "Unable to open control file " << filename << LL_ENDL; - return 0; - } - - LLXmlTreeNode* rootp = xml_controls.getRoot(); - if (!rootp || !rootp->hasAttribute("version")) - { - LL_WARNS("Settings") << "No valid settings header found in control file " << filename << LL_ENDL; - return 0; - } - - U32 validitems = 0; - S32 version; - - rootp->getAttributeS32("version", version); - - // Check file version - if (version != CURRENT_VERSION) - { - LL_INFOS("Settings") << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << LL_ENDL; - return 0; - } - - LLXmlTreeNode* child_nodep = rootp->getFirstChild(); - while(child_nodep) - { - name = child_nodep->getName(); - - BOOL declared = controlExists(name); - - 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 - LL_WARNS("Settings") << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << LL_ENDL; - } - child_nodep = rootp->getNextChild(); - continue; - } - - // Got an item. Load it up. - // If not declared, assume it's a string - if (!declared) - { - switch(declare_as) - { - case TYPE_COL4: - declareColor4(name, LLColor4::white, LLStringUtil::null, LLControlVariable::PERSIST_NO); - break; - case TYPE_STRING: - default: - declareString(name, LLStringUtil::null, LLStringUtil::null, LLControlVariable::PERSIST_NO); - break; - } - } - - // Control name has been declared in code. - LLControlVariable *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: - { - std::string string; - child_nodep->getAttributeString("value", 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_QUAT: - { - LLQuaternion quat; - - child_nodep->getAttributeQuat("value", quat); - - control->set(quat.getValue()); - validitems++; - } - break; - case TYPE_RECT: - { - //RN: hack to support reading rectangles from a string - std::string 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_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; - - default: - break; - - } - - child_nodep = rootp->getNextChild(); - } - - return validitems; -} - -U32 LLControlGroup::saveToFile(const std::string& filename, BOOL nondefault_only) -{ - LLSD settings; - int num_saved = 0; - for (ctrl_name_table_t::iterator iter = mNameTable.begin(); - iter != mNameTable.end(); iter++) - { - LLControlVariable* control = iter->second; - if (!control) - { - LL_WARNS("Settings") << "Tried to save invalid control: " << iter->first << LL_ENDL; - } - else if( control->shouldSave(nondefault_only) ) - { - settings[iter->first]["Type"] = typeEnumToString(control->type()); - settings[iter->first]["Comment"] = control->getComment(); - settings[iter->first]["Value"] = control->getSaveValue(); - ++num_saved; - } - } - llofstream file; - file.open(filename.c_str()); - if (file.is_open()) - { - LLSDSerialize::toPrettyXML(settings, file); - file.close(); - LL_INFOS("Settings") << "Saved to " << filename << LL_ENDL; - } - else - { - // This is a warning because sometime we want to use settings files which can't be written... - LL_WARNS("Settings") << "Unable to open settings file: " << filename << LL_ENDL; - return 0; - } - return num_saved; -} - -U32 LLControlGroup::loadFromFile(const std::string& filename, bool set_default_values, bool save_values) -{ - LLSD settings; - llifstream infile; - infile.open(filename.c_str()); - if(!infile.is_open()) - { - LL_WARNS("Settings") << "Cannot find file " << filename << " to load." << LL_ENDL; - return 0; - } - - if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(settings, infile)) - { - infile.close(); - LL_WARNS("Settings") << "Unable to parse LLSD control file " << filename << ". Trying Legacy Method." << LL_ENDL; - return loadFromFileLegacy(filename, TRUE, TYPE_STRING); - } - - U32 validitems = 0; - bool hidefromsettingseditor = false; - - for(LLSD::map_const_iterator itr = settings.beginMap(); itr != settings.endMap(); ++itr) - { - LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT; - std::string const & name = itr->first; - LLSD const & control_map = itr->second; - - if(control_map.has("Persist")) - { - persist = control_map["Persist"].asInteger()? - LLControlVariable::PERSIST_NONDFT : LLControlVariable::PERSIST_NO; - } - - // Sometimes we want to use the settings system to provide cheap persistence, but we - // don't want the settings themselves to be easily manipulated in the UI because - // doing so can cause support problems. So we have this option: - if(control_map.has("HideFromEditor")) - { - hidefromsettingseditor = control_map["HideFromEditor"].asInteger(); - } - else - { - hidefromsettingseditor = false; - } - - // If the control exists just set the value from the input file. - LLControlVariable* existing_control = getControl(name); - if(existing_control) - { - // set_default_values is true when we're loading the initial, - // immutable files from app_settings, e.g. settings.xml. - if(set_default_values) - { - // Override all previously set properties of this control. - // ... except for type. The types must match. - eControlType new_type = typeStringToEnum(control_map["Type"].asString()); - if(existing_control->isType(new_type)) - { - existing_control->setDefaultValue(control_map["Value"]); - existing_control->setPersist(persist); - existing_control->setHiddenFromSettingsEditor(hidefromsettingseditor); - existing_control->setComment(control_map["Comment"].asString()); - } - else - { - LL_ERRS() << "Mismatched type of control variable '" - << name << "' found while loading '" - << filename << "'." << LL_ENDL; - } - } - else if(existing_control->isPersisted()) - { - // save_values is specifically false for (e.g.) - // SessionSettingsFile and UserSessionSettingsFile -- in other - // words, for a file that's supposed to be transient. - existing_control->setValue(control_map["Value"], save_values); - } - // *NOTE: If not persisted and not setting defaults, - // the value should not get loaded. - } - else - { - // We've never seen this control before. Either we're loading up - // the initial set of default settings files (set_default_values) - // -- or we're loading user settings last saved by a viewer that - // supports a superset of the variables we know. - // CHOP-962: if we're loading an unrecognized user setting, make - // sure we save it later. If you try an experimental viewer, tweak - // a new setting, briefly revert to an old viewer, then return to - // the new one, we don't want the old viewer to discard the - // setting you changed. - if (! set_default_values) - { - // Using PERSIST_ALWAYS insists that saveToFile() (which calls - // LLControlVariable::shouldSave()) must save this control - // variable regardless of its value. We can safely set this - // LLControlVariable persistent because the 'persistent' flag - // is not itself persisted! - persist = LLControlVariable::PERSIST_ALWAYS; - // We want to mention unrecognized user settings variables - // (e.g. from a newer version of the viewer) in the log. But - // we also arrive here for Boolean variables generated by - // the notifications subsystem when the user checks "Don't - // show me this again." These aren't declared in settings.xml; - // they're actually named for the notification they suppress. - // We don't want to mention those. Apologies, this is a bit of - // a hack: we happen to know that user settings go into an - // LLControlGroup whose name is "Global". - if (getKey() == "Global") - { - LL_INFOS("LLControlGroup") << "preserving unrecognized " << getKey() - << " settings variable " << name << LL_ENDL; - } - } - - declareControl(name, - typeStringToEnum(control_map["Type"].asString()), - control_map["Value"], - control_map["Comment"].asString(), - persist, - hidefromsettingseditor - ); - } - - ++validitems; - } - - LL_DEBUGS("Settings") << "Loaded " << validitems << " settings from " << filename << LL_ENDL; - return validitems; -} - -void LLControlGroup::resetToDefaults() -{ - ctrl_name_table_t::iterator control_iter; - for (control_iter = mNameTable.begin(); - control_iter != mNameTable.end(); - ++control_iter) - { - LLControlVariable* control = (*control_iter).second; - control->resetToDefault(); - } -} - -void LLControlGroup::applyToAll(ApplyFunctor* func) -{ - for (ctrl_name_table_t::iterator iter = mNameTable.begin(); - iter != mNameTable.end(); iter++) - { - func->apply(iter->first, iter->second); - } -} - -//============================================================================ - -#ifdef TEST_HARNESS -void main() -{ - F32_CONTROL foo, getfoo; - - S32_CONTROL bar, getbar; - - BOOL_CONTROL baz; - - U32 count = gGlobals.loadFromFile("controls.ini"); - LL_INFOS("Settings") << "Loaded " << count << " controls" << LL_ENDL; - - // test insertion - foo = new LLControlVariable<F32>("gFoo", 5.f, 1.f, 20.f); - gGlobals.addEntry("gFoo", foo); - - bar = new LLControlVariable<S32>("gBar", 10, 2, 22); - gGlobals.addEntry("gBar", bar); - - baz = new LLControlVariable<BOOL>("gBaz", FALSE); - gGlobals.addEntry("gBaz", baz); - - // test retrieval - getfoo = (LLControlVariable<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 LLControlVariable<F32>("gFoo2", 100.f, 20.f, 5.f); - - // ...initial > max - // badbar = new LLControlVariable<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 - - -template <> eControlType get_control_type<U32>() -{ - return TYPE_U32; -} - -template <> eControlType get_control_type<S32>() -{ - return TYPE_S32; -} - -template <> eControlType get_control_type<F32>() -{ - return TYPE_F32; -} - -template <> eControlType get_control_type<bool> () -{ - return TYPE_BOOLEAN; -} -/* -// Yay BOOL, its really an S32. -template <> eControlType get_control_type<BOOL> () -{ - return TYPE_BOOLEAN; -} -*/ -template <> eControlType get_control_type<std::string>() -{ - return TYPE_STRING; -} - -template <> eControlType get_control_type<LLVector3>() -{ - return TYPE_VEC3; -} - -template <> eControlType get_control_type<LLVector3d>() -{ - return TYPE_VEC3D; -} - -template <> eControlType get_control_type<LLQuaternion>() -{ - return TYPE_QUAT; -} - -template <> eControlType get_control_type<LLRect>() -{ - return TYPE_RECT; -} - -template <> eControlType get_control_type<LLColor4>() -{ - return TYPE_COL4; -} - -template <> eControlType get_control_type<LLColor3>() -{ - return TYPE_COL3; -} - -template <> eControlType get_control_type<LLSD>() -{ - return TYPE_LLSD; -} - - -template <> LLSD convert_to_llsd<U32>(const U32& in) -{ - return (LLSD::Integer)in; -} - -template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in) -{ - return in.getValue(); -} - -template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in) -{ - return in.getValue(); -} -template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in) -{ - return in.getValue(); -} - -template <> LLSD convert_to_llsd<LLRect>(const LLRect& in) -{ - return in.getValue(); -} - -template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in) -{ - return in.getValue(); -} - -template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in) -{ - return in.getValue(); -} - -template <> LLSD convert_to_llsd<LLColor4U>(const LLColor4U& in) -{ - return in.getValue(); -} - - -template<> -bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_BOOLEAN) - return sd.asBoolean(); - else - { - CONTROL_ERRS << "Invalid BOOL value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return FALSE; - } -} - -template<> -S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_S32) - return sd.asInteger(); - else - { - CONTROL_ERRS << "Invalid S32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return 0; - } -} - -template<> -U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_U32) - return sd.asInteger(); - else - { - CONTROL_ERRS << "Invalid U32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return 0; - } -} - -template<> -F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_F32) - return (F32) sd.asReal(); - else - { - CONTROL_ERRS << "Invalid F32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return 0.0f; - } -} - -template<> -std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_STRING) - return sd.asString(); - else - { - CONTROL_ERRS << "Invalid string value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return LLStringUtil::null; - } -} - -template<> -LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - return utf8str_to_wstring(convert_from_llsd<std::string>(sd, type, control_name)); -} - -template<> -LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_VEC3) - return (LLVector3)sd; - else - { - CONTROL_ERRS << "Invalid LLVector3 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return LLVector3::zero; - } -} - -template<> -LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_VEC3D) - return (LLVector3d)sd; - else - { - CONTROL_ERRS << "Invalid LLVector3d value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return LLVector3d::zero; - } -} - -template<> -LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_QUAT) - return (LLQuaternion)sd; - else - { - CONTROL_ERRS << "Invalid LLQuaternion value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return LLQuaternion(); - } -} - -template<> -LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_RECT) - return LLRect(sd); - else - { - CONTROL_ERRS << "Invalid rect value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return LLRect::null; - } -} - - -template<> -LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_COL4) - { - LLColor4 color(sd); - if (color.mV[VRED] < 0.f || color.mV[VRED] > 1.f) - { - LL_WARNS("Settings") << "Color " << control_name << " red value out of range: " << color << LL_ENDL; - } - else if (color.mV[VGREEN] < 0.f || color.mV[VGREEN] > 1.f) - { - LL_WARNS("Settings") << "Color " << control_name << " green value out of range: " << color << LL_ENDL; - } - else if (color.mV[VBLUE] < 0.f || color.mV[VBLUE] > 1.f) - { - LL_WARNS("Settings") << "Color " << control_name << " blue value out of range: " << color << LL_ENDL; - } - else if (color.mV[VALPHA] < 0.f || color.mV[VALPHA] > 1.f) - { - LL_WARNS("Settings") << "Color " << control_name << " alpha value out of range: " << color << LL_ENDL; - } - - return LLColor4(sd); - } - else - { - CONTROL_ERRS << "Control " << control_name << " not a color" << LL_ENDL; - return LLColor4::white; - } -} - -template<> -LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - if (type == TYPE_COL3) - return sd; - else - { - CONTROL_ERRS << "Invalid LLColor3 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; - return LLColor3::white; - } -} - -template<> -LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, const std::string& control_name) -{ - return sd; -} - - -#if TEST_CACHED_CONTROL - -#define DECL_LLCC(T, V) static LLCachedControl<T> mySetting_##T("TestCachedControl"#T, V) -DECL_LLCC(U32, (U32)666); -DECL_LLCC(S32, (S32)-666); -DECL_LLCC(F32, (F32)-666.666); -DECL_LLCC(bool, true); -DECL_LLCC(BOOL, FALSE); -static LLCachedControl<std::string> mySetting_string("TestCachedControlstring", "Default String Value"); -DECL_LLCC(LLVector3, LLVector3(1.0f, 2.0f, 3.0f)); -DECL_LLCC(LLVector3d, LLVector3d(6.0f, 5.0f, 4.0f)); -DECL_LLCC(LLRect, LLRect(0, 0, 100, 500)); -DECL_LLCC(LLColor4, LLColor4(0.0f, 0.5f, 1.0f)); -DECL_LLCC(LLColor3, LLColor3(1.0f, 0.f, 0.5f)); -DECL_LLCC(LLColor4U, LLColor4U(255, 200, 100, 255)); - -LLSD test_llsd = LLSD()["testing1"] = LLSD()["testing2"]; -DECL_LLCC(LLSD, test_llsd); - -void test_cached_control() -{ -#define TEST_LLCC(T, V) if((T)mySetting_##T != V) LL_ERRS() << "Fail "#T << LL_ENDL - TEST_LLCC(U32, 666); - TEST_LLCC(S32, (S32)-666); - TEST_LLCC(F32, (F32)-666.666); - TEST_LLCC(bool, true); - TEST_LLCC(BOOL, FALSE); - if((std::string)mySetting_string != "Default String Value") LL_ERRS() << "Fail string" << LL_ENDL; - TEST_LLCC(LLVector3, LLVector3(1.0f, 2.0f, 3.0f)); - TEST_LLCC(LLVector3d, LLVector3d(6.0f, 5.0f, 4.0f)); - TEST_LLCC(LLRect, LLRect(0, 0, 100, 500)); - TEST_LLCC(LLColor4, LLColor4(0.0f, 0.5f, 1.0f)); - TEST_LLCC(LLColor3, LLColor3(1.0f, 0.f, 0.5f)); - TEST_LLCC(LLColor4U, LLColor4U(255, 200, 100, 255)); -//There's no LLSD comparsion for LLCC yet. TEST_LLCC(LLSD, test_llsd); -} -#endif // TEST_CACHED_CONTROL - +/**
+ * @file llcontrol.cpp
+ * @brief Holds global state for viewer.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+
+#include "llcontrol.h"
+
+#include "llstl.h"
+
+#include "llstring.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4coloru.h"
+#include "v4color.h"
+#include "v3color.h"
+#include "llquaternion.h"
+#include "llrect.h"
+#include "llxmltree.h"
+#include "llsdserialize.h"
+#include "llfile.h"
+#include "lltimer.h"
+#include "lldir.h"
+
+#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG
+#define CONTROL_ERRS LL_ERRS("ControlErrors")
+#else
+#define CONTROL_ERRS LL_WARNS("ControlErrors")
+#endif
+
+
+template <> eControlType get_control_type<U32>();
+template <> eControlType get_control_type<S32>();
+template <> eControlType get_control_type<F32>();
+template <> eControlType get_control_type<bool>();
+template <> eControlType get_control_type<std::string>();
+
+template <> eControlType get_control_type<LLVector3>();
+template <> eControlType get_control_type<LLVector3d>();
+template <> eControlType get_control_type<LLRect>();
+template <> eControlType get_control_type<LLColor4>();
+template <> eControlType get_control_type<LLColor3>();
+template <> eControlType get_control_type<LLColor4U>();
+template <> eControlType get_control_type<LLSD>();
+
+template <> LLSD convert_to_llsd<U32>(const U32& in);
+template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in);
+template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in);
+template <> LLSD convert_to_llsd<LLRect>(const LLRect& in);
+template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in);
+template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in);
+template <> LLSD convert_to_llsd<LLColor4U>(const LLColor4U& in);
+
+template <> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLColor4U convert_from_llsd<LLColor4U>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, std::string_view control_name);
+template <> LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, std::string_view control_name);
+
+//this defines the current version of the settings file
+const S32 CURRENT_VERSION = 101;
+
+// If you define the environment variable LL_SETTINGS_PROFILE to any value this will activate
+// the gSavedSettings profiling code. This code tracks the calls to get a saved (debug) setting.
+// When the viewer exits the results are written to the log directory to the file specified
+// by SETTINGS_PROFILE below. Only settings with an average access rate >= 2/second are output.
+typedef std::pair<std::string, U32> settings_pair_t;
+typedef std::vector<settings_pair_t> settings_vec_t;
+LLSD getCount;
+settings_vec_t getCount_v;
+F64 start_time = 0;
+std::string SETTINGS_PROFILE = "settings_profile.log";
+
+bool LLControlVariable::llsd_compare(const LLSD& a, const LLSD & b)
+{
+ bool result = false;
+ switch (mType)
+ {
+ case TYPE_U32:
+ case TYPE_S32:
+ result = a.asInteger() == b.asInteger();
+ break;
+ case TYPE_BOOLEAN:
+ result = a.asBoolean() == b.asBoolean();
+ break;
+ case TYPE_F32:
+ result = a.asReal() == b.asReal();
+ break;
+ case TYPE_VEC3:
+ case TYPE_VEC3D:
+ result = LLVector3d(a) == LLVector3d(b);
+ break;
+ case TYPE_QUAT:
+ result = LLQuaternion(a) == LLQuaternion(b);
+ break;
+ case TYPE_RECT:
+ result = LLRect(a) == LLRect(b);
+ break;
+ case TYPE_COL4:
+ result = LLColor4(a) == LLColor4(b);
+ break;
+ case TYPE_COL3:
+ result = LLColor3(a) == LLColor3(b);
+ break;
+ case TYPE_STRING:
+ result = a.asString() == b.asString();
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+LLControlVariable::LLControlVariable(const std::string& name, eControlType type,
+ LLSD initial, const std::string& comment,
+ ePersist persist, bool hidefromsettingseditor)
+ : mName(name),
+ mComment(comment),
+ mType(type),
+ mPersist(persist),
+ mHideFromSettingsEditor(hidefromsettingseditor)
+{
+ if ((persist != PERSIST_NO) && mComment.empty())
+ {
+ LL_ERRS() << "Must supply a comment for control " << mName << LL_ENDL;
+ }
+ //Push back versus setValue'ing here, since we don't want to call a signal yet
+ mValues.push_back(initial);
+}
+
+
+
+LLControlVariable::~LLControlVariable()
+{
+}
+
+LLSD LLControlVariable::getComparableValue(const LLSD& value)
+{
+ // *FIX:MEP - The following is needed to make the LLSD::ImplString
+ // work with boolean controls...
+ LLSD storable_value;
+ if(TYPE_BOOLEAN == type() && value.isString())
+ {
+ bool temp;
+ if(LLStringUtil::convertToBOOL(value.asString(), temp))
+ {
+ storable_value = temp;
+ }
+ else
+ {
+ storable_value = false;
+ }
+ }
+ else if (TYPE_LLSD == type() && value.isString())
+ {
+ LLPointer<LLSDNotationParser> parser = new LLSDNotationParser;
+ LLSD result;
+ std::stringstream value_stream(value.asString());
+ if (parser->parse(value_stream, result, LLSDSerialize::SIZE_UNLIMITED) != LLSDParser::PARSE_FAILURE)
+ {
+ storable_value = result;
+ }
+ else
+ {
+ storable_value = value;
+ }
+ }
+ else
+ {
+ storable_value = value;
+ }
+
+ return storable_value;
+}
+
+void LLControlVariable::setValue(const LLSD& new_value, bool saved_value)
+{
+ if (!mValidateSignal(this, new_value))
+ {
+ // can not set new value, exit
+ return;
+ }
+
+ LLSD storable_value = getComparableValue(new_value);
+ LLSD original_value = getValue();
+ bool value_changed = !llsd_compare(original_value, storable_value);
+ if(saved_value)
+ {
+ // If we're going to save this value, return to default but don't fire
+ resetToDefault(false);
+ if (!llsd_compare(mValues.back(), storable_value))
+ {
+ mValues.push_back(storable_value);
+ }
+ }
+ else
+ {
+ // This is an unsaved value. Its needs to reside at
+ // mValues[2] (or greater). It must not affect
+ // the result of getSaveValue()
+ if (!llsd_compare(mValues.back(), storable_value))
+ {
+ while(mValues.size() > 2)
+ {
+ // Remove any unsaved values.
+ mValues.pop_back();
+ }
+
+ if(mValues.size() < 2)
+ {
+ // Add the default to the 'save' value.
+ mValues.push_back(mValues[0]);
+ }
+
+ // Add the 'un-save' value.
+ mValues.push_back(storable_value);
+ }
+ }
+
+ if(value_changed)
+ {
+ firePropertyChanged(original_value);
+ }
+}
+
+void LLControlVariable::setDefaultValue(const LLSD& value)
+{
+ // Set the control variables value and make it
+ // the default value. If the active value is changed,
+ // send the signal.
+ // *NOTE: Default values are not saved, only read.
+
+ LLSD comparable_value = getComparableValue(value);
+ LLSD original_value = getValue();
+ bool value_changed = !llsd_compare(original_value, comparable_value);
+ resetToDefault(false);
+ mValues[0] = comparable_value;
+ if (value_changed)
+ {
+ firePropertyChanged(original_value);
+ }
+}
+
+void LLControlVariable::setPersist(ePersist state)
+{
+ mPersist = state;
+}
+
+void LLControlVariable::setHiddenFromSettingsEditor(bool hide)
+{
+ mHideFromSettingsEditor = hide;
+}
+
+void LLControlVariable::setComment(const std::string& comment)
+{
+ mComment = comment;
+}
+
+void LLControlVariable::resetToDefault(bool fire_signal)
+{
+ //The first setting is always the default
+ //Pop to it and fire off the listener
+ LLSD originalValue = mValues.back();
+
+ while(mValues.size() > 1)
+ {
+ mValues.pop_back();
+ }
+
+ if(fire_signal)
+ {
+ firePropertyChanged(originalValue);
+ }
+}
+
+bool LLControlVariable::shouldSave(bool nondefault_only)
+{
+ // This method is used to decide whether we should save a given
+ // variable. Two of the three values of mPersist are easy.
+ if (mPersist == PERSIST_NO)
+ return false;
+
+ if (mPersist == PERSIST_ALWAYS)
+ return true;
+
+ // PERSIST_NONDFT
+ // If caller doesn't need us to filter, just save.
+ if (! nondefault_only)
+ return true;
+
+ // PERSIST_NONDFT: caller only wants us to save this variable if its value
+ // differs from default.
+ if (isDefault()) // never been altered
+ return false;
+
+ // We've set at least one other value: compare it to default. Save only if
+ // they differ.
+ return ! llsd_compare(getSaveValue(), getDefault());
+}
+
+LLSD LLControlVariable::getSaveValue() const
+{
+ //The first level of the stack is default
+ //We assume that the second level is user preferences that should be saved
+ if(mValues.size() > 1) return mValues[1];
+ return mValues[0];
+}
+
+LLPointer<LLControlVariable> LLControlGroup::getControl(std::string_view name)
+{
+ if (mSettingsProfile)
+ {
+ incrCount(name);
+ }
+
+ ctrl_name_table_t::iterator iter = mNameTable.find(name.data());
+ return iter == mNameTable.end() ? LLPointer<LLControlVariable>() : iter->second;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+// Must match the type definition in llcontrol.h
+const std::string LLControlGroup::mTypeString[TYPE_COUNT] = { "U32"
+ ,"S32"
+ ,"F32"
+ ,"Boolean"
+ ,"String"
+ ,"Vector3"
+ ,"Vector3D"
+ ,"Quaternion"
+ ,"Rect"
+ ,"Color4"
+ ,"Color3"
+ ,"LLSD"
+ };
+
+LLControlGroup::LLControlGroup(const std::string& name)
+: LLInstanceTracker<LLControlGroup, std::string>(name),
+ mSettingsProfile(false)
+{
+
+ if (NULL != getenv("LL_SETTINGS_PROFILE"))
+ {
+ mSettingsProfile = true;
+ }
+}
+
+LLControlGroup::~LLControlGroup()
+{
+ cleanup();
+}
+
+static bool compareRoutine(settings_pair_t lhs, settings_pair_t rhs)
+{
+ return lhs.second > rhs.second;
+};
+
+void LLControlGroup::cleanup()
+{
+ if(mSettingsProfile && getCount.size() != 0)
+ {
+ std::string file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, SETTINGS_PROFILE);
+ LLFILE* out = LLFile::fopen(file, "w"); /* Flawfinder: ignore */
+ if(!out)
+ {
+ LL_WARNS("SettingsProfile") << "Error opening " << SETTINGS_PROFILE << LL_ENDL;
+ }
+ else
+ {
+ F64 end_time = LLTimer::getTotalSeconds();
+ U32 total_seconds = (U32)(end_time - start_time);
+
+ std::string msg = llformat("Runtime (seconds): %d\n\n No. accesses Avg. accesses/sec Name\n", total_seconds);
+ std::ostringstream data_msg;
+
+ data_msg << msg;
+ size_t data_size = data_msg.str().size();
+ if (fwrite(data_msg.str().c_str(), 1, data_size, out) != data_size)
+ {
+ LL_WARNS("SettingsProfile") << "Failed to write settings profile header" << LL_ENDL;
+ }
+
+ for (LLSD::map_const_iterator iter = getCount.beginMap(); iter != getCount.endMap(); ++iter)
+ {
+ getCount_v.push_back(settings_pair_t(iter->first, iter->second.asInteger()));
+ }
+ sort(getCount_v.begin(), getCount_v.end(), compareRoutine);
+
+ for (settings_vec_t::iterator iter = getCount_v.begin(); iter != getCount_v.end(); ++iter)
+ {
+ U32 access_rate = 0;
+ if (total_seconds != 0)
+ {
+ access_rate = iter->second / total_seconds;
+ }
+ if (access_rate >= 2)
+ {
+ std::ostringstream data_msg;
+ msg = llformat("%13d %7d %s", iter->second, access_rate, iter->first.c_str());
+ data_msg << msg << "\n";
+ size_t data_size = data_msg.str().size();
+ if (fwrite(data_msg.str().c_str(), 1, data_size, out) != data_size)
+ {
+ LL_WARNS("SettingsProfile") << "Failed to write settings profile" << LL_ENDL;
+ }
+ }
+ }
+ getCount = LLSD::emptyMap();
+ fclose(out);
+ }
+ }
+
+ mNameTable.clear();
+}
+
+eControlType LLControlGroup::typeStringToEnum(const std::string& typestr)
+{
+ for(int i = 0; i < (int)TYPE_COUNT; ++i)
+ {
+ if(mTypeString[i] == typestr) return (eControlType)i;
+ }
+ return (eControlType)-1;
+}
+
+std::string LLControlGroup::typeEnumToString(eControlType typeenum)
+{
+ return mTypeString[typeenum];
+}
+
+LLControlVariable* LLControlGroup::declareControl(const std::string& name, eControlType type, const LLSD initial_val, const std::string& comment, LLControlVariable::ePersist persist, bool hidefromsettingseditor)
+{
+ LLControlVariable* existing_control = getControl(name);
+ if (existing_control)
+ {
+ if ((persist != LLControlVariable::PERSIST_NO) && existing_control->isType(type))
+ {
+ if (!existing_control->llsd_compare(existing_control->getDefault(), initial_val))
+ {
+ // Sometimes we need to declare a control *after* it has been loaded from a settings file.
+ LLSD cur_value = existing_control->getValue(); // get the current value
+ existing_control->setDefaultValue(initial_val); // set the default to the declared value
+ existing_control->setValue(cur_value); // now set to the loaded value
+ }
+ }
+ else
+ {
+ LL_WARNS("Settings") << "Control named " << name << " already exists, ignoring new declaration." << LL_ENDL;
+ }
+ return existing_control;
+ }
+
+ // if not, create the control and add it to the name table
+ LLControlVariable* control = new LLControlVariable(name, type, initial_val, comment, persist, hidefromsettingseditor);
+ mNameTable[name] = control;
+ return control;
+}
+
+LLControlVariable* LLControlGroup::declareU32(const std::string& name, const U32 initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_U32, (LLSD::Integer) initial_val, comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareS32(const std::string& name, const S32 initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_S32, initial_val, comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareF32(const std::string& name, const F32 initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_F32, initial_val, comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareBOOL(const std::string& name, const bool initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_BOOLEAN, initial_val, comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareString(const std::string& name, const std::string& initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_STRING, initial_val, comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareVec3(const std::string& name, const LLVector3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_VEC3, initial_val.getValue(), comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_QUAT, initial_val.getValue(), comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
+{
+ return declareControl(name, TYPE_RECT, initial_val.getValue(), comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist )
+{
+ return declareControl(name, TYPE_COL4, initial_val.getValue(), comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareColor3(const std::string& name, const LLColor3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist )
+{
+ return declareControl(name, TYPE_COL3, initial_val.getValue(), comment, persist);
+}
+
+LLControlVariable* LLControlGroup::declareLLSD(const std::string& name, const LLSD &initial_val, const std::string& comment, LLControlVariable::ePersist persist )
+{
+ return declareControl(name, TYPE_LLSD, initial_val, comment, persist);
+}
+
+void LLControlGroup::incrCount(std::string_view name)
+{
+ if (0.0 == start_time)
+ {
+ start_time = LLTimer::getTotalSeconds();
+ }
+ getCount[name.data()] = getCount[name.data()].asInteger() + 1;
+}
+
+bool LLControlGroup::getBOOL(std::string_view name)
+{
+ return get<bool>(name);
+}
+
+S32 LLControlGroup::getS32(std::string_view name)
+{
+ return get<S32>(name);
+}
+
+U32 LLControlGroup::getU32(std::string_view name)
+{
+ return get<U32>(name);
+}
+
+F32 LLControlGroup::getF32(std::string_view name)
+{
+ return get<F32>(name);
+}
+
+std::string LLControlGroup::getString(std::string_view name)
+{
+ return get<std::string>(name);
+}
+
+LLWString LLControlGroup::getWString(std::string_view name)
+{
+ return get<LLWString>(name);
+}
+
+std::string LLControlGroup::getText(std::string_view name)
+{
+ std::string utf8_string = getString(name);
+ LLStringUtil::replaceChar(utf8_string, '^', '\n');
+ LLStringUtil::replaceChar(utf8_string, '%', ' ');
+ return (utf8_string);
+}
+
+LLVector3 LLControlGroup::getVector3(std::string_view name)
+{
+ return get<LLVector3>(name);
+}
+
+LLVector3d LLControlGroup::getVector3d(std::string_view name)
+{
+ return get<LLVector3d>(name);
+}
+
+LLQuaternion LLControlGroup::getQuaternion(std::string_view name)
+{
+ return get<LLQuaternion>(name);
+}
+
+LLRect LLControlGroup::getRect(std::string_view name)
+{
+ return get<LLRect>(name);
+}
+
+
+LLColor4 LLControlGroup::getColor(std::string_view name)
+{
+ return get<LLColor4>(name);
+}
+
+LLColor4 LLControlGroup::getColor4(std::string_view name)
+{
+ return get<LLColor4>(name);
+}
+
+LLColor3 LLControlGroup::getColor3(std::string_view name)
+{
+ return get<LLColor3>(name);
+}
+
+LLSD LLControlGroup::getLLSD(std::string_view name)
+{
+ return get<LLSD>(name);
+}
+
+LLSD LLControlGroup::asLLSD(bool diffs_only)
+{
+ // Dump all stored values as LLSD
+ LLSD result = LLSD::emptyArray();
+ for (ctrl_name_table_t::iterator iter = mNameTable.begin();
+ iter != mNameTable.end(); iter++)
+ {
+ LLControlVariable *control = iter->second;
+ if (!control || control->isType(TYPE_STRING) || (diffs_only && control->isDefault()))
+ {
+ continue;
+ }
+ const std::string& name = iter->first;
+ result[name] = getLLSD(name);
+ }
+ return result;
+}
+
+bool LLControlGroup::controlExists(const std::string& name)
+{
+ ctrl_name_table_t::iterator iter = mNameTable.find(name);
+ return iter != mNameTable.end();
+}
+
+
+//-------------------------------------------------------------------
+// Set functions
+//-------------------------------------------------------------------
+
+void LLControlGroup::setBOOL(std::string_view name, bool val)
+{
+ set<bool>(name, val);
+}
+
+
+void LLControlGroup::setS32(std::string_view name, S32 val)
+{
+ set(name, val);
+}
+
+
+void LLControlGroup::setF32(std::string_view name, F32 val)
+{
+ set(name, val);
+}
+
+
+void LLControlGroup::setU32(std::string_view name, U32 val)
+{
+ set(name, val);
+}
+
+
+void LLControlGroup::setString(std::string_view name, const std::string &val)
+{
+ set(name, val);
+}
+
+
+void LLControlGroup::setVector3(std::string_view name, const LLVector3 &val)
+{
+ set(name, val);
+}
+
+void LLControlGroup::setVector3d(std::string_view name, const LLVector3d &val)
+{
+ set(name, val);
+}
+
+void LLControlGroup::setQuaternion(std::string_view name, const LLQuaternion &val)
+{
+ set(name, val);
+}
+
+void LLControlGroup::setRect(std::string_view name, const LLRect &val)
+{
+ set(name, val);
+}
+
+void LLControlGroup::setColor4(std::string_view name, const LLColor4 &val)
+{
+ set(name, val);
+}
+
+void LLControlGroup::setLLSD(std::string_view name, const LLSD& val)
+{
+ set(name, val);
+}
+
+void LLControlGroup::setUntypedValue(std::string_view name, const LLSD& val)
+{
+ if (name.empty())
+ {
+ return;
+ }
+
+ LLControlVariable* control = getControl(name);
+
+ if (control)
+ {
+ control->setValue(val);
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << LL_ENDL;
+ }
+}
+
+
+//---------------------------------------------------------------
+// Load and save
+//---------------------------------------------------------------
+
+// Returns number of controls loaded, so 0 if failure
+U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, bool require_declaration, eControlType declare_as)
+{
+ std::string name;
+
+ LLXmlTree xml_controls;
+
+ if (!xml_controls.parseFile(filename))
+ {
+ LL_WARNS("Settings") << "Unable to open control file " << filename << LL_ENDL;
+ return 0;
+ }
+
+ LLXmlTreeNode* rootp = xml_controls.getRoot();
+ if (!rootp || !rootp->hasAttribute("version"))
+ {
+ LL_WARNS("Settings") << "No valid settings header found in control file " << filename << LL_ENDL;
+ return 0;
+ }
+
+ U32 validitems = 0;
+ S32 version;
+
+ rootp->getAttributeS32("version", version);
+
+ // Check file version
+ if (version != CURRENT_VERSION)
+ {
+ LL_INFOS("Settings") << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << LL_ENDL;
+ return 0;
+ }
+
+ LLXmlTreeNode* child_nodep = rootp->getFirstChild();
+ while(child_nodep)
+ {
+ name = child_nodep->getName();
+
+ bool declared = controlExists(name);
+
+ 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
+ LL_WARNS("Settings") << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << LL_ENDL;
+ }
+ child_nodep = rootp->getNextChild();
+ continue;
+ }
+
+ // Got an item. Load it up.
+ // If not declared, assume it's a string
+ if (!declared)
+ {
+ switch(declare_as)
+ {
+ case TYPE_COL4:
+ declareColor4(name, LLColor4::white, LLStringUtil::null, LLControlVariable::PERSIST_NO);
+ break;
+ case TYPE_STRING:
+ default:
+ declareString(name, LLStringUtil::null, LLStringUtil::null, LLControlVariable::PERSIST_NO);
+ break;
+ }
+ }
+
+ // Control name has been declared in code.
+ LLControlVariable *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:
+ {
+ std::string string;
+ child_nodep->getAttributeString("value", 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_QUAT:
+ {
+ LLQuaternion quat;
+
+ child_nodep->getAttributeQuat("value", quat);
+
+ control->set(quat.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_RECT:
+ {
+ //RN: hack to support reading rectangles from a string
+ std::string 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_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;
+
+ default:
+ break;
+
+ }
+
+ child_nodep = rootp->getNextChild();
+ }
+
+ return validitems;
+}
+
+U32 LLControlGroup::saveToFile(const std::string& filename, bool nondefault_only)
+{
+ LLSD settings;
+ int num_saved = 0;
+ for (ctrl_name_table_t::iterator iter = mNameTable.begin();
+ iter != mNameTable.end(); iter++)
+ {
+ LLControlVariable* control = iter->second;
+ if (!control)
+ {
+ LL_WARNS("Settings") << "Tried to save invalid control: " << iter->first << LL_ENDL;
+ }
+ else if( control->shouldSave(nondefault_only) )
+ {
+ settings[iter->first]["Type"] = typeEnumToString(control->type());
+ settings[iter->first]["Comment"] = control->getComment();
+ settings[iter->first]["Value"] = control->getSaveValue();
+ ++num_saved;
+ }
+ }
+ llofstream file;
+ file.open(filename.c_str());
+ if (file.is_open())
+ {
+ LLSDSerialize::toPrettyXML(settings, file);
+ file.close();
+ LL_INFOS("Settings") << "Saved to " << filename << LL_ENDL;
+ }
+ else
+ {
+ // This is a warning because sometime we want to use settings files which can't be written...
+ LL_WARNS("Settings") << "Unable to open settings file: " << filename << LL_ENDL;
+ return 0;
+ }
+ return num_saved;
+}
+
+U32 LLControlGroup::loadFromFile(const std::string& filename, bool set_default_values, bool save_values)
+{
+ LLSD settings;
+ llifstream infile;
+ infile.open(filename.c_str());
+ if(!infile.is_open())
+ {
+ LL_WARNS("Settings") << "Cannot find file " << filename << " to load." << LL_ENDL;
+ return 0;
+ }
+
+ if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(settings, infile))
+ {
+ infile.close();
+ LL_WARNS("Settings") << "Unable to parse LLSD control file " << filename << ". Trying Legacy Method." << LL_ENDL;
+ return loadFromFileLegacy(filename, true, TYPE_STRING);
+ }
+
+ U32 validitems = 0;
+ bool hidefromsettingseditor = false;
+
+ for(LLSD::map_const_iterator itr = settings.beginMap(); itr != settings.endMap(); ++itr)
+ {
+ LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT;
+ std::string const & name = itr->first;
+ LLSD const & control_map = itr->second;
+
+ if(control_map.has("Persist"))
+ {
+ persist = control_map["Persist"].asInteger()?
+ LLControlVariable::PERSIST_NONDFT : LLControlVariable::PERSIST_NO;
+ }
+
+ // Sometimes we want to use the settings system to provide cheap persistence, but we
+ // don't want the settings themselves to be easily manipulated in the UI because
+ // doing so can cause support problems. So we have this option:
+ if(control_map.has("HideFromEditor"))
+ {
+ hidefromsettingseditor = control_map["HideFromEditor"].asInteger();
+ }
+ else
+ {
+ hidefromsettingseditor = false;
+ }
+
+ // If the control exists just set the value from the input file.
+ LLControlVariable* existing_control = getControl(name);
+ if(existing_control)
+ {
+ // set_default_values is true when we're loading the initial,
+ // immutable files from app_settings, e.g. settings.xml.
+ if(set_default_values)
+ {
+ // Override all previously set properties of this control.
+ // ... except for type. The types must match.
+ eControlType new_type = typeStringToEnum(control_map["Type"].asString());
+ if(existing_control->isType(new_type))
+ {
+ existing_control->setDefaultValue(control_map["Value"]);
+ existing_control->setPersist(persist);
+ existing_control->setHiddenFromSettingsEditor(hidefromsettingseditor);
+ existing_control->setComment(control_map["Comment"].asString());
+ }
+ else
+ {
+ LL_ERRS() << "Mismatched type of control variable '"
+ << name << "' found while loading '"
+ << filename << "'." << LL_ENDL;
+ }
+ }
+ else if(existing_control->isPersisted())
+ {
+ // save_values is specifically false for (e.g.)
+ // SessionSettingsFile and UserSessionSettingsFile -- in other
+ // words, for a file that's supposed to be transient.
+ existing_control->setValue(control_map["Value"], save_values);
+ }
+ // *NOTE: If not persisted and not setting defaults,
+ // the value should not get loaded.
+ }
+ else
+ {
+ // We've never seen this control before. Either we're loading up
+ // the initial set of default settings files (set_default_values)
+ // -- or we're loading user settings last saved by a viewer that
+ // supports a superset of the variables we know.
+ // CHOP-962: if we're loading an unrecognized user setting, make
+ // sure we save it later. If you try an experimental viewer, tweak
+ // a new setting, briefly revert to an old viewer, then return to
+ // the new one, we don't want the old viewer to discard the
+ // setting you changed.
+ if (! set_default_values)
+ {
+ // Using PERSIST_ALWAYS insists that saveToFile() (which calls
+ // LLControlVariable::shouldSave()) must save this control
+ // variable regardless of its value. We can safely set this
+ // LLControlVariable persistent because the 'persistent' flag
+ // is not itself persisted!
+ persist = LLControlVariable::PERSIST_ALWAYS;
+ // We want to mention unrecognized user settings variables
+ // (e.g. from a newer version of the viewer) in the log. But
+ // we also arrive here for Boolean variables generated by
+ // the notifications subsystem when the user checks "Don't
+ // show me this again." These aren't declared in settings.xml;
+ // they're actually named for the notification they suppress.
+ // We don't want to mention those. Apologies, this is a bit of
+ // a hack: we happen to know that user settings go into an
+ // LLControlGroup whose name is "Global".
+ if (getKey() == "Global")
+ {
+ LL_INFOS("LLControlGroup") << "preserving unrecognized " << getKey()
+ << " settings variable " << name << LL_ENDL;
+ }
+ }
+
+ declareControl(name,
+ typeStringToEnum(control_map["Type"].asString()),
+ control_map["Value"],
+ control_map["Comment"].asString(),
+ persist,
+ hidefromsettingseditor
+ );
+ }
+
+ ++validitems;
+ }
+
+ LL_DEBUGS("Settings") << "Loaded " << validitems << " settings from " << filename << LL_ENDL;
+ return validitems;
+}
+
+void LLControlGroup::resetToDefaults()
+{
+ ctrl_name_table_t::iterator control_iter;
+ for (control_iter = mNameTable.begin();
+ control_iter != mNameTable.end();
+ ++control_iter)
+ {
+ LLControlVariable* control = (*control_iter).second;
+ control->resetToDefault();
+ }
+}
+
+void LLControlGroup::applyToAll(ApplyFunctor* func)
+{
+ for (ctrl_name_table_t::iterator iter = mNameTable.begin();
+ iter != mNameTable.end(); iter++)
+ {
+ func->apply(iter->first, iter->second);
+ }
+}
+
+//============================================================================
+
+#ifdef TEST_HARNESS
+void main()
+{
+ F32_CONTROL foo, getfoo;
+
+ S32_CONTROL bar, getbar;
+
+ BOOL_CONTROL baz;
+
+ U32 count = gGlobals.loadFromFile("controls.ini");
+ LL_INFOS("Settings") << "Loaded " << count << " controls" << LL_ENDL;
+
+ // test insertion
+ foo = new LLControlVariable<F32>("gFoo", 5.f, 1.f, 20.f);
+ gGlobals.addEntry("gFoo", foo);
+
+ bar = new LLControlVariable<S32>("gBar", 10, 2, 22);
+ gGlobals.addEntry("gBar", bar);
+
+ baz = new LLControlVariable<bool>("gBaz", false);
+ gGlobals.addEntry("gBaz", baz);
+
+ // test retrieval
+ getfoo = (LLControlVariable<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 LLControlVariable<F32>("gFoo2", 100.f, 20.f, 5.f);
+
+ // ...initial > max
+ // badbar = new LLControlVariable<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
+
+
+template <> eControlType get_control_type<U32>()
+{
+ return TYPE_U32;
+}
+
+template <> eControlType get_control_type<S32>()
+{
+ return TYPE_S32;
+}
+
+template <> eControlType get_control_type<F32>()
+{
+ return TYPE_F32;
+}
+
+template <> eControlType get_control_type<bool> ()
+{
+ return TYPE_BOOLEAN;
+}
+
+template <> eControlType get_control_type<std::string>()
+{
+ return TYPE_STRING;
+}
+
+template <> eControlType get_control_type<LLVector3>()
+{
+ return TYPE_VEC3;
+}
+
+template <> eControlType get_control_type<LLVector3d>()
+{
+ return TYPE_VEC3D;
+}
+
+template <> eControlType get_control_type<LLQuaternion>()
+{
+ return TYPE_QUAT;
+}
+
+template <> eControlType get_control_type<LLRect>()
+{
+ return TYPE_RECT;
+}
+
+template <> eControlType get_control_type<LLColor4>()
+{
+ return TYPE_COL4;
+}
+
+template <> eControlType get_control_type<LLColor3>()
+{
+ return TYPE_COL3;
+}
+
+template <> eControlType get_control_type<LLSD>()
+{
+ return TYPE_LLSD;
+}
+
+
+template <> LLSD convert_to_llsd<U32>(const U32& in)
+{
+ return (LLSD::Integer)in;
+}
+
+template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in)
+{
+ return in.getValue();
+}
+
+template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in)
+{
+ return in.getValue();
+}
+template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in)
+{
+ return in.getValue();
+}
+
+template <> LLSD convert_to_llsd<LLRect>(const LLRect& in)
+{
+ return in.getValue();
+}
+
+template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in)
+{
+ return in.getValue();
+}
+
+template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in)
+{
+ return in.getValue();
+}
+
+template <> LLSD convert_to_llsd<LLColor4U>(const LLColor4U& in)
+{
+ return in.getValue();
+}
+
+
+template<>
+bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_BOOLEAN)
+ return sd.asBoolean();
+ else
+ {
+ CONTROL_ERRS << "Invalid bool value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return false;
+ }
+}
+
+template<>
+S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_S32)
+ return sd.asInteger();
+ else
+ {
+ CONTROL_ERRS << "Invalid S32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return 0;
+ }
+}
+
+template<>
+U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_U32)
+ return sd.asInteger();
+ else
+ {
+ CONTROL_ERRS << "Invalid U32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return 0;
+ }
+}
+
+template<>
+F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_F32)
+ return (F32) sd.asReal();
+ else
+ {
+ CONTROL_ERRS << "Invalid F32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return 0.0f;
+ }
+}
+
+template<>
+std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_STRING)
+ return sd.asString();
+ else
+ {
+ CONTROL_ERRS << "Invalid string value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return LLStringUtil::null;
+ }
+}
+
+template<>
+LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ return utf8str_to_wstring(convert_from_llsd<std::string>(sd, type, control_name));
+}
+
+template<>
+LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_VEC3)
+ return (LLVector3)sd;
+ else
+ {
+ CONTROL_ERRS << "Invalid LLVector3 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return LLVector3::zero;
+ }
+}
+
+template<>
+LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_VEC3D)
+ return (LLVector3d)sd;
+ else
+ {
+ CONTROL_ERRS << "Invalid LLVector3d value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return LLVector3d::zero;
+ }
+}
+
+template<>
+LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_QUAT)
+ return (LLQuaternion)sd;
+ else
+ {
+ CONTROL_ERRS << "Invalid LLQuaternion value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return LLQuaternion();
+ }
+}
+
+template<>
+LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_RECT)
+ return LLRect(sd);
+ else
+ {
+ CONTROL_ERRS << "Invalid rect value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return LLRect::null;
+ }
+}
+
+
+template<>
+LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_COL4)
+ {
+ LLColor4 color(sd);
+ if (color.mV[VRED] < 0.f || color.mV[VRED] > 1.f)
+ {
+ LL_WARNS("Settings") << "Color " << control_name << " red value out of range: " << color << LL_ENDL;
+ }
+ else if (color.mV[VGREEN] < 0.f || color.mV[VGREEN] > 1.f)
+ {
+ LL_WARNS("Settings") << "Color " << control_name << " green value out of range: " << color << LL_ENDL;
+ }
+ else if (color.mV[VBLUE] < 0.f || color.mV[VBLUE] > 1.f)
+ {
+ LL_WARNS("Settings") << "Color " << control_name << " blue value out of range: " << color << LL_ENDL;
+ }
+ else if (color.mV[VALPHA] < 0.f || color.mV[VALPHA] > 1.f)
+ {
+ LL_WARNS("Settings") << "Color " << control_name << " alpha value out of range: " << color << LL_ENDL;
+ }
+
+ return LLColor4(sd);
+ }
+ else
+ {
+ CONTROL_ERRS << "Control " << control_name << " not a color" << LL_ENDL;
+ return LLColor4::white;
+ }
+}
+
+template<>
+LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ if (type == TYPE_COL3)
+ return sd;
+ else
+ {
+ CONTROL_ERRS << "Invalid LLColor3 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
+ return LLColor3::white;
+ }
+}
+
+template<>
+LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ return sd;
+}
+
+
+#if TEST_CACHED_CONTROL
+
+#define DECL_LLCC(T, V) static LLCachedControl<T> mySetting_##T("TestCachedControl"#T, V)
+DECL_LLCC(U32, (U32)666);
+DECL_LLCC(S32, (S32)-666);
+DECL_LLCC(F32, (F32)-666.666);
+DECL_LLCC(bool, true);
+static LLCachedControl<std::string> mySetting_string("TestCachedControlstring", "Default String Value");
+DECL_LLCC(LLVector3, LLVector3(1.0f, 2.0f, 3.0f));
+DECL_LLCC(LLVector3d, LLVector3d(6.0f, 5.0f, 4.0f));
+DECL_LLCC(LLRect, LLRect(0, 0, 100, 500));
+DECL_LLCC(LLColor4, LLColor4(0.0f, 0.5f, 1.0f));
+DECL_LLCC(LLColor3, LLColor3(1.0f, 0.f, 0.5f));
+DECL_LLCC(LLColor4U, LLColor4U(255, 200, 100, 255));
+
+LLSD test_llsd = LLSD()["testing1"] = LLSD()["testing2"];
+DECL_LLCC(LLSD, test_llsd);
+
+void test_cached_control()
+{
+#define TEST_LLCC(T, V) if((T)mySetting_##T != V) LL_ERRS() << "Fail "#T << LL_ENDL
+ TEST_LLCC(U32, 666);
+ TEST_LLCC(S32, (S32)-666);
+ TEST_LLCC(F32, (F32)-666.666);
+ TEST_LLCC(bool, true);
+ if((std::string)mySetting_string != "Default String Value") LL_ERRS() << "Fail string" << LL_ENDL;
+ TEST_LLCC(LLVector3, LLVector3(1.0f, 2.0f, 3.0f));
+ TEST_LLCC(LLVector3d, LLVector3d(6.0f, 5.0f, 4.0f));
+ TEST_LLCC(LLRect, LLRect(0, 0, 100, 500));
+ TEST_LLCC(LLColor4, LLColor4(0.0f, 0.5f, 1.0f));
+ TEST_LLCC(LLColor3, LLColor3(1.0f, 0.f, 0.5f));
+ TEST_LLCC(LLColor4U, LLColor4U(255, 200, 100, 255));
+//There's no LLSD comparsion for LLCC yet. TEST_LLCC(LLSD, test_llsd);
+}
+#endif // TEST_CACHED_CONTROL
+
diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 37663a6fb2..f542745b44 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -1,476 +1,474 @@ -/** - * @file llcontrol.h - * @brief A mechanism for storing "control state" for a program - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLCONTROL_H -#define LL_LLCONTROL_H - -#include "llboost.h" -#include "llevent.h" -#include "llstring.h" -#include "llrect.h" -#include "llrefcount.h" -#include "llinstancetracker.h" - -#include <vector> - -// *NOTE: boost::visit_each<> generates warning 4675 on .net 2003 -// Disable the warning for the boost includes. -#if LL_WINDOWS -# if (_MSC_VER >= 1300 && _MSC_VER < 1400) -# pragma warning(push) -# pragma warning( disable : 4675 ) -# endif -#endif - -#include <boost/bind.hpp> - -#if LL_WINDOWS - #pragma warning (push) - #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch - #pragma warning (disable : 4264) -#endif -#include <boost/signals2.hpp> -#if LL_WINDOWS - #pragma warning (pop) -#endif - -#if LL_WINDOWS -# if (_MSC_VER >= 1300 && _MSC_VER < 1400) -# pragma warning(pop) -# endif -#endif - -class LLVector3; -class LLVector3d; -class LLQuaternion; -class LLColor4; -class LLColor3; - -// if this is changed, also modify mTypeString in llcontrol.h -typedef enum e_control_type -{ - TYPE_U32 = 0, - TYPE_S32, - TYPE_F32, - TYPE_BOOLEAN, - TYPE_STRING, - TYPE_VEC3, - TYPE_VEC3D, - TYPE_QUAT, - TYPE_RECT, - TYPE_COL4, - TYPE_COL3, - TYPE_LLSD, - TYPE_COUNT -} eControlType; - -class LLControlVariable : public LLRefCount -{ - LOG_CLASS(LLControlVariable); - - friend class LLControlGroup; - -public: - typedef boost::signals2::signal<bool(LLControlVariable* control, const LLSD&), boost_boolean_combiner> validate_signal_t; - typedef boost::signals2::signal<void(LLControlVariable* control, const LLSD&, const LLSD&)> commit_signal_t; - - enum ePersist - { - PERSIST_NO, // don't save this var - PERSIST_NONDFT, // save this var if differs from default - PERSIST_ALWAYS // save this var even if has default value - }; - -private: - std::string mName; - std::string mComment; - eControlType mType; - ePersist mPersist; - bool mHideFromSettingsEditor; - std::vector<LLSD> mValues; - - commit_signal_t mCommitSignal; - validate_signal_t mValidateSignal; - -public: - LLControlVariable(const std::string& name, eControlType type, - LLSD initial, const std::string& comment, - ePersist persist = PERSIST_NONDFT, bool hidefromsettingseditor = false); - - virtual ~LLControlVariable(); - - const std::string& getName() const { return mName; } - const std::string& getComment() const { return mComment; } - - eControlType type() { return mType; } - bool isType(eControlType tp) { return tp == mType; } - - void resetToDefault(bool fire_signal = false); - - commit_signal_t* getSignal() { return &mCommitSignal; } // shorthand for commit signal - commit_signal_t* getCommitSignal() { return &mCommitSignal; } - validate_signal_t* getValidateSignal() { return &mValidateSignal; } - - bool isDefault() { return (mValues.size() == 1); } - bool shouldSave(bool nondefault_only); - bool isPersisted() { return mPersist != PERSIST_NO; } - bool isHiddenFromSettingsEditor() { return mHideFromSettingsEditor; } - LLSD get() const { return getValue(); } - LLSD getValue() const { return mValues.back(); } - LLSD getDefault() const { return mValues.front(); } - LLSD getSaveValue() const; - - void set(const LLSD& val) { setValue(val); } - void setValue(const LLSD& value, bool saved_value = TRUE); - void setDefaultValue(const LLSD& value); - void setPersist(ePersist); - void setHiddenFromSettingsEditor(bool hide); - void setComment(const std::string& comment); - -private: - void firePropertyChanged(const LLSD &pPreviousValue) - { - mCommitSignal(this, mValues.back(), pPreviousValue); - } - LLSD getComparableValue(const LLSD& value); - bool llsd_compare(const LLSD& a, const LLSD & b); -}; - -typedef LLPointer<LLControlVariable> LLControlVariablePtr; - -//! Helper functions for converting between static types and LLControl values -template <class T> -eControlType get_control_type() -{ - LL_WARNS() << "Usupported control type: " << typeid(T).name() << "." << LL_ENDL; - return TYPE_COUNT; -} - -template <class T> -LLSD convert_to_llsd(const T& in) -{ - // default implementation - return LLSD(in); -} - -template <class T> -T convert_from_llsd(const LLSD& sd, eControlType type, const std::string& control_name) -{ - // needs specialization - return T(sd); -} - -//const U32 STRING_CACHE_SIZE = 10000; -class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string> -{ - LOG_CLASS(LLControlGroup); - -protected: - typedef std::map<std::string, LLControlVariablePtr > ctrl_name_table_t; - ctrl_name_table_t mNameTable; - static const std::string mTypeString[TYPE_COUNT]; - -public: - static eControlType typeStringToEnum(const std::string& typestr); - static std::string typeEnumToString(eControlType typeenum); - - LLControlGroup(const std::string& name); - ~LLControlGroup(); - void cleanup(); - - LLControlVariablePtr getControl(const std::string& name); - - struct ApplyFunctor - { - virtual ~ApplyFunctor() {}; - virtual void apply(const std::string& name, LLControlVariable* control) = 0; - }; - void applyToAll(ApplyFunctor* func); - - LLControlVariable* declareControl(const std::string& name, eControlType type, const LLSD initial_val, const std::string& comment, LLControlVariable::ePersist persist, BOOL hidefromsettingseditor = FALSE); - LLControlVariable* declareU32(const std::string& name, U32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareS32(const std::string& name, S32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareF32(const std::string& name, F32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareBOOL(const std::string& name, BOOL initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareString(const std::string& name, const std::string &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareVec3(const std::string& name, const LLVector3 &initial_val,const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareColor3(const std::string& name, const LLColor3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - LLControlVariable* declareLLSD(const std::string& name, const LLSD &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); - - std::string getString(const std::string& name); - std::string getText(const std::string& name); - BOOL getBOOL(const std::string& name); - S32 getS32(const std::string& name); - F32 getF32(const std::string& name); - U32 getU32(const std::string& name); - - LLWString getWString(const std::string& name); - LLVector3 getVector3(const std::string& name); - LLVector3d getVector3d(const std::string& name); - LLRect getRect(const std::string& name); - LLSD getLLSD(const std::string& name); - LLQuaternion getQuaternion(const std::string& name); - - LLColor4 getColor(const std::string& name); - LLColor4 getColor4(const std::string& name); - LLColor3 getColor3(const std::string& name); - - LLSD asLLSD(bool diffs_only); - - // generic getter - template<typename T> T get(const std::string& name) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - LLControlVariable* control = getControl(name); - LLSD value; - eControlType type = TYPE_COUNT; - - if (control) - { - value = control->get(); - type = control->type(); - } - else - { - LL_WARNS() << "Control " << name << " not found." << LL_ENDL; - return T(); - } - return convert_from_llsd<T>(value, type, name); - } - - void setBOOL(const std::string& name, BOOL val); - void setS32(const std::string& name, S32 val); - void setF32(const std::string& name, F32 val); - void setU32(const std::string& name, U32 val); - void setString(const std::string& name, const std::string& val); - void setVector3(const std::string& name, const LLVector3 &val); - void setVector3d(const std::string& name, const LLVector3d &val); - void setQuaternion(const std::string& name, const LLQuaternion &val); - void setRect(const std::string& name, const LLRect &val); - void setColor4(const std::string& name, const LLColor4 &val); - void setLLSD(const std::string& name, const LLSD& val); - - // type agnostic setter that takes LLSD - void setUntypedValue(const std::string& name, const LLSD& val); - - // generic setter - template<typename T> void set(const std::string& name, const T& val) - { - LLControlVariable* control = getControl(name); - - if (control && control->isType(get_control_type<T>())) - { - control->set(convert_to_llsd(val)); - } - else - { - LL_WARNS() << "Invalid control " << name << LL_ENDL; - } - } - - BOOL controlExists(const std::string& 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 std::string& filename, BOOL require_declaration = TRUE, eControlType declare_as = TYPE_STRING); - U32 saveToFile(const std::string& filename, BOOL nondefault_only); - U32 loadFromFile(const std::string& filename, bool default_values = false, bool save_values = true); - void resetToDefaults(); - void incrCount(const std::string& name); - - bool mSettingsProfile; -}; - - -//! Publish/Subscribe object to interact with LLControlGroups. - -//! Use an LLCachedControl instance to connect to a LLControlVariable -//! without have to manually create and bind a listener to a local -//! object. -template <class T> -class LLControlCache : public LLRefCount, public LLInstanceTracker<LLControlCache<T>, std::string> -{ -public: - // This constructor will declare a control if it doesn't exist in the contol group - LLControlCache(LLControlGroup& group, - const std::string& name, - const T& default_value, - const std::string& comment) - : LLInstanceTracker<LLControlCache<T>, std::string >(name) - { - if(!group.controlExists(name)) - { - if(!declareTypedControl(group, name, default_value, comment)) - { - LL_ERRS() << "The control could not be created!!!" << LL_ENDL; - } - } - - bindToControl(group, name); - } - - LLControlCache(LLControlGroup& group, - const std::string& name) - : LLInstanceTracker<LLControlCache<T>, std::string >(name) - { - if(!group.controlExists(name)) - { - LL_ERRS() << "Control named " << name << "not found." << LL_ENDL; - } - - bindToControl(group, name); - } - - ~LLControlCache() - { - } - - const T& getValue() const { return mCachedValue; } - -private: - void bindToControl(LLControlGroup& group, const std::string& name) - { - LLControlVariablePtr controlp = group.getControl(name); - mType = controlp->type(); - mCachedValue = convert_from_llsd<T>(controlp->get(), mType, name); - - // Add a listener to the controls signal... - // NOTE: All listeners connected to 0 group, for guaranty that variable handlers (gSavedSettings) call last - mConnection = controlp->getSignal()->connect(0, - boost::bind(&LLControlCache<T>::handleValueChange, this, _2) - ); - mType = controlp->type(); - } - bool declareTypedControl(LLControlGroup& group, - const std::string& name, - const T& default_value, - const std::string& comment) - { - LLSD init_value; - eControlType type = get_control_type<T>(); - init_value = convert_to_llsd(default_value); - if(type < TYPE_COUNT) - { - group.declareControl(name, type, init_value, comment, LLControlVariable::PERSIST_NO); - return true; - } - return false; - } - - bool handleValueChange(const LLSD& newvalue) - { - mCachedValue = convert_from_llsd<T>(newvalue, mType, ""); - return true; - } - -private: - T mCachedValue; - eControlType mType; - boost::signals2::scoped_connection mConnection; -}; - -template <typename T> -class LLCachedControl -{ -public: - LLCachedControl(LLControlGroup& group, - const std::string& name, - const T& default_value, - const std::string& comment = "Declared In Code") - { - mCachedControlPtr = LLControlCache<T>::getInstance(name).get(); - if (! mCachedControlPtr) - { - mCachedControlPtr = new LLControlCache<T>(group, name, default_value, comment); - } - } - - LLCachedControl(LLControlGroup& group, - const std::string& name) - { - mCachedControlPtr = LLControlCache<T>::getInstance(name).get(); - if (! mCachedControlPtr) - { - mCachedControlPtr = new LLControlCache<T>(group, name); - } - } - - operator const T&() const { return mCachedControlPtr->getValue(); } - operator boost::function<const T&()> () const { return boost::function<const T&()>(*this); } - const T& operator()() { return mCachedControlPtr->getValue(); } - -private: - LLPointer<LLControlCache<T> > mCachedControlPtr; -}; - -template <> eControlType get_control_type<U32>(); -template <> eControlType get_control_type<S32>(); -template <> eControlType get_control_type<F32>(); -template <> eControlType get_control_type<bool>(); -// Yay BOOL, its really an S32. -//template <> eControlType get_control_type<BOOL> () -template <> eControlType get_control_type<std::string>(); -template <> eControlType get_control_type<LLVector3>(); -template <> eControlType get_control_type<LLVector3d>(); -template <> eControlType get_control_type<LLQuaternion>(); -template <> eControlType get_control_type<LLRect>(); -template <> eControlType get_control_type<LLColor4>(); -template <> eControlType get_control_type<LLColor3>(); -template <> eControlType get_control_type<LLSD>(); - -template <> LLSD convert_to_llsd<U32>(const U32& in); -template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in); -template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in); -template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in); -template <> LLSD convert_to_llsd<LLRect>(const LLRect& in); -template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in); -template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in); - -template<> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, const std::string& control_name); -template<> LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, const std::string& control_name); - -//#define TEST_CACHED_CONTROL 1 -#ifdef TEST_CACHED_CONTROL -void test_cached_control(); -#endif // TEST_CACHED_CONTROL - -#endif +/**
+ * @file llcontrol.h
+ * @brief A mechanism for storing "control state" for a program
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLCONTROL_H
+#define LL_LLCONTROL_H
+
+#include "llboost.h"
+#include "llevent.h"
+#include "llstring.h"
+#include "llrect.h"
+#include "llrefcount.h"
+#include "llinstancetracker.h"
+
+#include <vector>
+
+// *NOTE: boost::visit_each<> generates warning 4675 on .net 2003
+// Disable the warning for the boost includes.
+#if LL_WINDOWS
+# if (_MSC_VER >= 1300 && _MSC_VER < 1400)
+# pragma warning(push)
+# pragma warning( disable : 4675 )
+# endif
+#endif
+
+#include <boost/bind.hpp>
+
+#if LL_WINDOWS
+ #pragma warning (push)
+ #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
+ #pragma warning (disable : 4264)
+#endif
+#include <boost/signals2.hpp>
+#if LL_WINDOWS
+ #pragma warning (pop)
+#endif
+
+#if LL_WINDOWS
+# if (_MSC_VER >= 1300 && _MSC_VER < 1400)
+# pragma warning(pop)
+# endif
+#endif
+
+class LLVector3;
+class LLVector3d;
+class LLQuaternion;
+class LLColor4;
+class LLColor3;
+
+// if this is changed, also modify mTypeString in llcontrol.h
+typedef enum e_control_type
+{
+ TYPE_U32 = 0,
+ TYPE_S32,
+ TYPE_F32,
+ TYPE_BOOLEAN,
+ TYPE_STRING,
+ TYPE_VEC3,
+ TYPE_VEC3D,
+ TYPE_QUAT,
+ TYPE_RECT,
+ TYPE_COL4,
+ TYPE_COL3,
+ TYPE_LLSD,
+ TYPE_COUNT
+} eControlType;
+
+class LLControlVariable : public LLRefCount
+{
+ LOG_CLASS(LLControlVariable);
+
+ friend class LLControlGroup;
+
+public:
+ typedef boost::signals2::signal<bool(LLControlVariable* control, const LLSD&), boost_boolean_combiner> validate_signal_t;
+ typedef boost::signals2::signal<void(LLControlVariable* control, const LLSD&, const LLSD&)> commit_signal_t;
+
+ enum ePersist
+ {
+ PERSIST_NO, // don't save this var
+ PERSIST_NONDFT, // save this var if differs from default
+ PERSIST_ALWAYS // save this var even if has default value
+ };
+
+private:
+ std::string mName;
+ std::string mComment;
+ eControlType mType;
+ ePersist mPersist;
+ bool mHideFromSettingsEditor;
+ std::vector<LLSD> mValues;
+
+ commit_signal_t mCommitSignal;
+ validate_signal_t mValidateSignal;
+
+public:
+ LLControlVariable(const std::string& name, eControlType type,
+ LLSD initial, const std::string& comment,
+ ePersist persist = PERSIST_NONDFT, bool hidefromsettingseditor = false);
+
+ virtual ~LLControlVariable();
+
+ const std::string& getName() const { return mName; }
+ const std::string& getComment() const { return mComment; }
+
+ eControlType type() { return mType; }
+ bool isType(eControlType tp) { return tp == mType; }
+
+ void resetToDefault(bool fire_signal = false);
+
+ commit_signal_t* getSignal() { return &mCommitSignal; } // shorthand for commit signal
+ commit_signal_t* getCommitSignal() { return &mCommitSignal; }
+ validate_signal_t* getValidateSignal() { return &mValidateSignal; }
+
+ bool isDefault() { return (mValues.size() == 1); }
+ bool shouldSave(bool nondefault_only);
+ bool isPersisted() { return mPersist != PERSIST_NO; }
+ bool isHiddenFromSettingsEditor() { return mHideFromSettingsEditor; }
+ LLSD get() const { return getValue(); }
+ LLSD getValue() const { return mValues.back(); }
+ LLSD getDefault() const { return mValues.front(); }
+ LLSD getSaveValue() const;
+
+ void set(const LLSD& val) { setValue(val); }
+ void setValue(const LLSD& value, bool saved_value = true);
+ void setDefaultValue(const LLSD& value);
+ void setPersist(ePersist);
+ void setHiddenFromSettingsEditor(bool hide);
+ void setComment(const std::string& comment);
+
+private:
+ void firePropertyChanged(const LLSD &pPreviousValue)
+ {
+ mCommitSignal(this, mValues.back(), pPreviousValue);
+ }
+ LLSD getComparableValue(const LLSD& value);
+ bool llsd_compare(const LLSD& a, const LLSD & b);
+};
+
+typedef LLPointer<LLControlVariable> LLControlVariablePtr;
+
+//! Helper functions for converting between static types and LLControl values
+template <class T>
+eControlType get_control_type()
+{
+ LL_WARNS() << "Usupported control type: " << typeid(T).name() << "." << LL_ENDL;
+ return TYPE_COUNT;
+}
+
+template <class T>
+LLSD convert_to_llsd(const T& in)
+{
+ // default implementation
+ return LLSD(in);
+}
+
+template <class T>
+T convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name)
+{
+ // needs specialization
+ return T(sd);
+}
+
+//const U32 STRING_CACHE_SIZE = 10000;
+class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string>
+{
+ LOG_CLASS(LLControlGroup);
+
+protected:
+ typedef std::map<std::string, LLControlVariablePtr > ctrl_name_table_t;
+ ctrl_name_table_t mNameTable;
+ static const std::string mTypeString[TYPE_COUNT];
+
+public:
+ static eControlType typeStringToEnum(const std::string& typestr);
+ static std::string typeEnumToString(eControlType typeenum);
+
+ LLControlGroup(const std::string& name);
+ ~LLControlGroup();
+ void cleanup();
+
+ LLControlVariablePtr getControl(std::string_view name);
+
+ struct ApplyFunctor
+ {
+ virtual ~ApplyFunctor() {};
+ virtual void apply(const std::string& name, LLControlVariable* control) = 0;
+ };
+ void applyToAll(ApplyFunctor* func);
+
+ LLControlVariable* declareControl(const std::string& name, eControlType type, const LLSD initial_val, const std::string& comment, LLControlVariable::ePersist persist, bool hidefromsettingseditor = false);
+ LLControlVariable* declareU32(const std::string& name, U32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareS32(const std::string& name, S32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareF32(const std::string& name, F32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareBOOL(const std::string& name, bool initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareString(const std::string& name, const std::string &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareVec3(const std::string& name, const LLVector3 &initial_val,const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareColor3(const std::string& name, const LLColor3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+ LLControlVariable* declareLLSD(const std::string& name, const LLSD &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
+
+ std::string getString(std::string_view name);
+ std::string getText(std::string_view name);
+ bool getBOOL(std::string_view name);
+ S32 getS32(std::string_view name);
+ F32 getF32(std::string_view name);
+ U32 getU32(std::string_view name);
+
+ LLWString getWString(std::string_view name);
+ LLVector3 getVector3(std::string_view name);
+ LLVector3d getVector3d(std::string_view name);
+ LLRect getRect(std::string_view name);
+ LLSD getLLSD(std::string_view name);
+ LLQuaternion getQuaternion(std::string_view name);
+
+ LLColor4 getColor(std::string_view name);
+ LLColor4 getColor4(std::string_view name);
+ LLColor3 getColor3(std::string_view name);
+
+ LLSD asLLSD(bool diffs_only);
+
+ // generic getter
+ template<typename T> T get(std::string_view name)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
+ LLControlVariable* control = getControl(name);
+ LLSD value;
+ eControlType type = TYPE_COUNT;
+
+ if (control)
+ {
+ value = control->get();
+ type = control->type();
+ }
+ else
+ {
+ LL_WARNS() << "Control " << name << " not found." << LL_ENDL;
+ return T();
+ }
+ return convert_from_llsd<T>(value, type, name);
+ }
+
+ void setBOOL(std::string_view name, bool val);
+ void setS32(std::string_view name, S32 val);
+ void setF32(std::string_view name, F32 val);
+ void setU32(std::string_view name, U32 val);
+ void setString(std::string_view name, const std::string& val);
+ void setVector3(std::string_view name, const LLVector3 &val);
+ void setVector3d(std::string_view name, const LLVector3d &val);
+ void setQuaternion(std::string_view name, const LLQuaternion &val);
+ void setRect(std::string_view name, const LLRect &val);
+ void setColor4(std::string_view name, const LLColor4 &val);
+ void setLLSD(std::string_view name, const LLSD& val);
+
+ // type agnostic setter that takes LLSD
+ void setUntypedValue(std::string_view name, const LLSD& val);
+
+ // generic setter
+ template<typename T> void set(std::string_view name, const T& val)
+ {
+ LLControlVariable* control = getControl(name);
+
+ if (control && control->isType(get_control_type<T>()))
+ {
+ control->set(convert_to_llsd(val));
+ }
+ else
+ {
+ LL_WARNS() << "Invalid control " << name << LL_ENDL;
+ }
+ }
+
+ bool controlExists(const std::string& 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 std::string& filename, bool require_declaration = true, eControlType declare_as = TYPE_STRING);
+ U32 saveToFile(const std::string& filename, bool nondefault_only);
+ U32 loadFromFile(const std::string& filename, bool default_values = false, bool save_values = true);
+ void resetToDefaults();
+ void incrCount(std::string_view name);
+
+ bool mSettingsProfile;
+};
+
+
+//! Publish/Subscribe object to interact with LLControlGroups.
+
+//! Use an LLCachedControl instance to connect to a LLControlVariable
+//! without have to manually create and bind a listener to a local
+//! object.
+template <class T>
+class LLControlCache : public LLRefCount, public LLInstanceTracker<LLControlCache<T>, std::string>
+{
+public:
+ // This constructor will declare a control if it doesn't exist in the contol group
+ LLControlCache(LLControlGroup& group,
+ const std::string& name,
+ const T& default_value,
+ const std::string& comment)
+ : LLInstanceTracker<LLControlCache<T>, std::string >(name)
+ {
+ if(!group.controlExists(name))
+ {
+ if(!declareTypedControl(group, name, default_value, comment))
+ {
+ LL_ERRS() << "The control could not be created!!!" << LL_ENDL;
+ }
+ }
+
+ bindToControl(group, name);
+ }
+
+ LLControlCache(LLControlGroup& group,
+ const std::string& name)
+ : LLInstanceTracker<LLControlCache<T>, std::string >(name)
+ {
+ if(!group.controlExists(name))
+ {
+ LL_ERRS() << "Control named " << name << "not found." << LL_ENDL;
+ }
+
+ bindToControl(group, name);
+ }
+
+ ~LLControlCache()
+ {
+ }
+
+ const T& getValue() const { return mCachedValue; }
+
+private:
+ void bindToControl(LLControlGroup& group, const std::string& name)
+ {
+ LLControlVariablePtr controlp = group.getControl(name);
+ mType = controlp->type();
+ mCachedValue = convert_from_llsd<T>(controlp->get(), mType, name);
+
+ // Add a listener to the controls signal...
+ // NOTE: All listeners connected to 0 group, for guaranty that variable handlers (gSavedSettings) call last
+ mConnection = controlp->getSignal()->connect(0,
+ boost::bind(&LLControlCache<T>::handleValueChange, this, _2)
+ );
+ mType = controlp->type();
+ }
+ bool declareTypedControl(LLControlGroup& group,
+ const std::string& name,
+ const T& default_value,
+ const std::string& comment)
+ {
+ LLSD init_value;
+ eControlType type = get_control_type<T>();
+ init_value = convert_to_llsd(default_value);
+ if(type < TYPE_COUNT)
+ {
+ group.declareControl(name, type, init_value, comment, LLControlVariable::PERSIST_NO);
+ return true;
+ }
+ return false;
+ }
+
+ bool handleValueChange(const LLSD& newvalue)
+ {
+ mCachedValue = convert_from_llsd<T>(newvalue, mType, "");
+ return true;
+ }
+
+private:
+ T mCachedValue;
+ eControlType mType;
+ boost::signals2::scoped_connection mConnection;
+};
+
+template <typename T>
+class LLCachedControl
+{
+public:
+ LLCachedControl(LLControlGroup& group,
+ const std::string& name,
+ const T& default_value,
+ const std::string& comment = "Declared In Code")
+ {
+ mCachedControlPtr = LLControlCache<T>::getInstance(name).get();
+ if (! mCachedControlPtr)
+ {
+ mCachedControlPtr = new LLControlCache<T>(group, name, default_value, comment);
+ }
+ }
+
+ LLCachedControl(LLControlGroup& group,
+ const std::string& name)
+ {
+ mCachedControlPtr = LLControlCache<T>::getInstance(name).get();
+ if (! mCachedControlPtr)
+ {
+ mCachedControlPtr = new LLControlCache<T>(group, name);
+ }
+ }
+
+ operator const T&() const { return mCachedControlPtr->getValue(); }
+ operator boost::function<const T&()> () const { return boost::function<const T&()>(*this); }
+ const T& operator()() { return mCachedControlPtr->getValue(); }
+
+private:
+ LLPointer<LLControlCache<T> > mCachedControlPtr;
+};
+
+template <> eControlType get_control_type<U32>();
+template <> eControlType get_control_type<S32>();
+template <> eControlType get_control_type<F32>();
+template <> eControlType get_control_type<bool>();
+template <> eControlType get_control_type<std::string>();
+template <> eControlType get_control_type<LLVector3>();
+template <> eControlType get_control_type<LLVector3d>();
+template <> eControlType get_control_type<LLQuaternion>();
+template <> eControlType get_control_type<LLRect>();
+template <> eControlType get_control_type<LLColor4>();
+template <> eControlType get_control_type<LLColor3>();
+template <> eControlType get_control_type<LLSD>();
+
+template <> LLSD convert_to_llsd<U32>(const U32& in);
+template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in);
+template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in);
+template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in);
+template <> LLSD convert_to_llsd<LLRect>(const LLRect& in);
+template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in);
+template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in);
+
+template<> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, std::string_view control_name);
+template<> LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, std::string_view control_name);
+
+//#define TEST_CACHED_CONTROL 1
+#ifdef TEST_CACHED_CONTROL
+void test_cached_control();
+#endif // TEST_CACHED_CONTROL
+
+#endif
diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 2298dc22e7..e7ea5a9fad 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -1,3285 +1,3268 @@ -/** - * @file llxmlnode.cpp - * @author Tom Yedwab - * @brief LLXMLNode implementation - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#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 "llstring.h" -#include "lluuid.h" -#include "lldir.h" - -// static -BOOL LLXMLNode::sStripEscapedStrings = TRUE; -BOOL LLXMLNode::sStripWhitespaceValues = FALSE; - -LLXMLNode::LLXMLNode() : - mID(""), - mParser(NULL), - mIsAttribute(FALSE), - mVersionMajor(0), - mVersionMinor(0), - mLength(0), - mPrecision(64), - mType(TYPE_CONTAINER), - mEncoding(ENCODING_DEFAULT), - mLineNumber(-1), - mParent(NULL), - mChildren(NULL), - mAttributes(), - mPrev(NULL), - mNext(NULL), - mName(NULL), - mValue(""), - mDefault(NULL) -{ -} - -LLXMLNode::LLXMLNode(const char* name, BOOL is_attribute) : - mID(""), - mParser(NULL), - mIsAttribute(is_attribute), - mVersionMajor(0), - mVersionMinor(0), - mLength(0), - mPrecision(64), - mType(TYPE_CONTAINER), - mEncoding(ENCODING_DEFAULT), - mLineNumber(-1), - mParent(NULL), - mChildren(NULL), - mAttributes(), - mPrev(NULL), - mNext(NULL), - mValue(""), - mDefault(NULL) -{ - mName = gStringTable.addStringEntry(name); -} - -LLXMLNode::LLXMLNode(LLStringTableEntry* name, BOOL is_attribute) : - mID(""), - mParser(NULL), - mIsAttribute(is_attribute), - mVersionMajor(0), - mVersionMinor(0), - mLength(0), - mPrecision(64), - mType(TYPE_CONTAINER), - mEncoding(ENCODING_DEFAULT), - mLineNumber(-1), - mParent(NULL), - mChildren(NULL), - mAttributes(), - mPrev(NULL), - mNext(NULL), - mName(name), - mValue(""), - mDefault(NULL) -{ -} - -// copy constructor (except for the children) -LLXMLNode::LLXMLNode(const LLXMLNode& rhs) : - mID(rhs.mID), - mIsAttribute(rhs.mIsAttribute), - mVersionMajor(rhs.mVersionMajor), - mVersionMinor(rhs.mVersionMinor), - mLength(rhs.mLength), - mPrecision(rhs.mPrecision), - mType(rhs.mType), - mEncoding(rhs.mEncoding), - mLineNumber(0), - mParser(NULL), - mParent(NULL), - mChildren(NULL), - mAttributes(), - mPrev(NULL), - mNext(NULL), - mName(rhs.mName), - mValue(rhs.mValue), - mDefault(rhs.mDefault) -{ -} - -// returns a new copy of this node and all its children -LLXMLNodePtr LLXMLNode::deepCopy() -{ - LLXMLNodePtr newnode = LLXMLNodePtr(new LLXMLNode(*this)); - if (mChildren.notNull()) - { - for (LLXMLChildList::iterator iter = mChildren->map.begin(); - iter != mChildren->map.end(); ++iter) - { - LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy()); - newnode->addChild(temp_ptr_for_gcc); - } - } - for (LLXMLAttribList::iterator iter = mAttributes.begin(); - iter != mAttributes.end(); ++iter) - { - LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy()); - newnode->addChild(temp_ptr_for_gcc); - } - - return newnode; -} - -// virtual -LLXMLNode::~LLXMLNode() -{ - // Strictly speaking none of this should be required execept 'delete mChildren'... - // Sadly, that's only true if we hadn't had reference-counted smart pointers linked - // in three different directions. This entire class is a frightening, hard-to-maintain - // mess. - if (mChildren.notNull()) - { - 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; - mChildren = NULL; - } - 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.notNull()) - { - 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; - } - if (target_child == mChildren->tail) - { - mChildren->tail = target_child->mPrev; - } - - 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()) - { - 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) - { - LLXMLAttribList::iterator found_it = mAttributes.find(new_child->mName); - if (found_it != mAttributes.end()) - { - removeChild(found_it->second); - } - mAttributes.insert(std::make_pair(new_child->mName, new_child)); - } - else - { - if (mChildren.isNull()) - { - mChildren = new LLXMLChildren(); - mChildren->head = new_child; - mChildren->tail = new_child; - } - mChildren->map.insert(std::make_pair(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 char* name, BOOL is_attribute) -{ - return createChild(gStringTable.addStringEntry(name), is_attribute); -} - -// virtual -LLXMLNodePtr LLXMLNode::createChild(LLStringTableEntry* name, BOOL is_attribute) -{ - LLXMLNodePtr ret(new LLXMLNode(name, is_attribute)); - ret->mID.clear(); - - 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()) - { - LLXMLNodePtr this_ptr(this); - new_parent->addChild(this_ptr); - } - 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.notNull()) - { - 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.clear(); - LLXMLNodePtr ptr_new_node = new_node; - - // Set the parent-child relationship with the current active node - LLXMLNode* parent = (LLXMLNode *)userData; - - if (NULL == parent) - { - LL_WARNS() << "parent (userData) is NULL; aborting function" << LL_ENDL; - return; - } - - new_node_ptr->mParser = parent->mParser; - new_node_ptr->setLineNumber(XML_GetCurrentLineNumber(*new_node_ptr->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) - { - std::string attr_name = atts[pos]; - std::string 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.c_str(), attr_node, FALSE)) - { - attr_node = new LLXMLNode(attr_name.c_str(), TRUE); - attr_node->setLineNumber(XML_GetCurrentLineNumber(*new_node_ptr->mParser)); - } - 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) - { - std::string 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; - std::string value = current_node->getValue(); - if (LLXMLNode::sStripEscapedStrings) - { - if (s[0] == '\"' && s[len-1] == '\"') - { - // Special-case: Escaped string. - std::string 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(std::string(s, len)); - current_node->setValue(value); -} - - - -// static -bool LLXMLNode::updateNode( - LLXMLNodePtr& node, - LLXMLNodePtr& update_node) -{ - - if (!node || !update_node) - { - LL_WARNS() << "Node invalid" << LL_ENDL; - 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 = node->getFirstChild(); - LLXMLNodePtr last_child = child; - LLXMLNodePtr updateChild; - - for (updateChild = update_node->getFirstChild(); updateChild.notNull(); - updateChild = updateChild->getNextSibling()) - { - while(child.notNull()) - { - std::string nodeName; - std::string 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); - last_child = child; - child = child->getNextSibling(); - if (child.isNull()) - { - child = node->getFirstChild(); - } - break; - } - - child = child->getNextSibling(); - if (child.isNull()) - { - child = node->getFirstChild(); - } - if (child == last_child) - { - break; - } - } - } - - return TRUE; -} - -// static -bool LLXMLNode::parseFile(const std::string& filename, LLXMLNodePtr& node, LLXMLNode* defaults_tree) -{ - // Read file - LL_DEBUGS("XMLNode") << "parsing XML file: " << filename << LL_ENDL; - LLFILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ - if (fp == NULL) - { - node = NULL ; - return false; - } - fseek(fp, 0, SEEK_END); - U32 length = ftell(fp); - fseek(fp, 0, SEEK_SET); - - U8* buffer = new U8[length+1]; - size_t nread = fread(buffer, 1, length, fp); - buffer[nread] = 0; - fclose(fp); - - bool rv = parseBuffer(buffer, nread, 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) - { - LL_WARNS() << "Error parsing xml error code: " - << XML_ErrorString(XML_GetErrorCode(my_parser)) - << " on line " << XML_GetCurrentLineNumber(my_parser) - << LL_ENDL; - } - - // Deinit - XML_ParserFree(my_parser); - - if (!file_node->mChildren || file_node->mChildren->map.size() != 1) - { - LL_WARNS() << "Parse failure - wrong number of top-level nodes xml." - << LL_ENDL; - node = NULL ; - return false; - } - - LLXMLNode *return_node = file_node->mChildren->map.begin()->second; - - return_node->setDefault(defaults); - return_node->updateDefault(); - - node = return_node; - return true; -} - -// static -bool LLXMLNode::parseStream( - std::istream& str, - 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); - - const int BUFSIZE = 1024; - U8* buffer = new U8[BUFSIZE]; - - while(str.good()) - { - str.read((char*)buffer, BUFSIZE); - int count = (int)str.gcount(); - - if (XML_Parse(my_parser, (const char *)buffer, count, !str.good()) != XML_STATUS_OK) - { - LL_WARNS() << "Error parsing xml error code: " - << XML_ErrorString(XML_GetErrorCode(my_parser)) - << " on lne " << XML_GetCurrentLineNumber(my_parser) - << LL_ENDL; - break; - } - } - - delete [] buffer; - - // Deinit - XML_ParserFree(my_parser); - - if (!file_node->mChildren || file_node->mChildren->map.size() != 1) - { - LL_WARNS() << "Parse failure - wrong number of top-level nodes xml." - << LL_ENDL; - node = NULL; - 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.notNull()) - { - 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 -bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root, - const std::vector<std::string>& paths) -{ - if (paths.empty()) return false; - - std::string filename = paths.front(); - if (filename.empty()) - { - return false; - } - - if (!LLXMLNode::parseFile(filename, root, NULL)) - { - LL_WARNS() << "Problem reading UI description file: " << filename << " " << errno << LL_ENDL; - return false; - } - - LLXMLNodePtr updateRoot; - - std::vector<std::string>::const_iterator itor; - - // We've already dealt with the first item, skip that one - for (itor = paths.begin() + 1; itor != paths.end(); ++itor) - { - std::string layer_filename = *itor; - if(layer_filename.empty() || layer_filename == filename) - { - // no localized version of this file, that's ok, keep looking - continue; - } - - if (!LLXMLNode::parseFile(layer_filename, updateRoot, NULL)) - { - LL_WARNS() << "Problem reading localized UI description file: " << layer_filename << LL_ENDL; - return false; - } - - std::string nodeName; - std::string updateName; - - updateRoot->getAttributeString("name", updateName); - root->getAttributeString("name", nodeName); - - if (updateName == nodeName) - { - LLXMLNode::updateNode(root, updateRoot); - } - } - - return true; -} - -// static -void LLXMLNode::writeHeaderToFile(LLFILE *out_file) -{ - fprintf(out_file, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n"); -} - -void LLXMLNode::writeToFile(LLFILE *out_file, const std::string& indent, bool use_type_decorations) -{ - if (isFullyDefault()) - { - // Don't write out nodes that are an exact match to defaults - return; - } - - std::ostringstream ostream; - writeToOstream(ostream, indent, use_type_decorations); - std::string outstring = ostream.str(); - size_t written = fwrite(outstring.c_str(), 1, outstring.length(), out_file); - if (written != outstring.length()) - { - LL_WARNS() << "Short write" << LL_ENDL; - } -} - -void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& indent, bool use_type_decorations) -{ - 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 << "<" << mName->mString << "\n"; - - if (use_type_decorations) - { - // ID - if (mID != "") - { - output_stream << indent << " id=\"" << mID << "\"\n"; - } - - // Type - if (!has_default_type) - { - switch (mType) - { - case TYPE_BOOLEAN: - output_stream << indent << " type=\"boolean\"\n"; - break; - case TYPE_INTEGER: - output_stream << indent << " type=\"integer\"\n"; - break; - case TYPE_FLOAT: - output_stream << indent << " type=\"float\"\n"; - break; - case TYPE_STRING: - output_stream << indent << " type=\"string\"\n"; - break; - case TYPE_UUID: - output_stream << indent << " type=\"uuid\"\n"; - break; - case TYPE_NODEREF: - output_stream << indent << " type=\"noderef\"\n"; - break; - default: - // default on switch(enum) eliminates a warning on linux - break; - }; - } - - // Encoding - if (!has_default_encoding) - { - switch (mEncoding) - { - case ENCODING_DECIMAL: - output_stream << indent << " encoding=\"decimal\"\n"; - break; - case ENCODING_HEX: - output_stream << indent << " encoding=\"hex\"\n"; - break; - /*case ENCODING_BASE32: - output_stream << indent << " encoding=\"base32\"\n"; - 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 << indent << " precision=\"" << mPrecision << "\"\n"; - } - - // Version - if (mVersionMajor > 0 || mVersionMinor > 0) - { - output_stream << indent << " version=\"" << mVersionMajor << "." << mVersionMinor << "\"\n"; - } - - // Array length - if (!has_default_length && mLength > 0) - { - output_stream << indent << " length=\"" << mLength << "\"\n"; - } - } - - { - // Write out attributes - 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) - { - std::string attr = child->mName->mString; - if (use_type_decorations - && (attr == "id" || - attr == "type" || - attr == "encoding" || - attr == "precision" || - attr == "version" || - attr == "length")) - { - continue; // skip built-in attributes - } - - std::string attr_str = llformat(" %s=\"%s\"", - attr.c_str(), - escapeXML(child->mValue).c_str()); - output_stream << indent << attr_str << "\n"; - } - } - } - - // erase last \n before attaching final > or /> - output_stream.seekp(-1, std::ios::cur); - - if (mChildren.isNull() && mValue == "") - { - output_stream << " />\n"; - return; - } - else - { - output_stream << ">\n"; - if (mChildren.notNull()) - { - // stream non-attributes - std::string next_indent = indent + " "; - for (LLXMLNode* child = getFirstChild(); child; child = child->getNextSibling()) - { - child->writeToOstream(output_stream, next_indent, use_type_decorations); - } - } - if (!mValue.empty()) - { - std::string contents = getTextContents(); - output_stream << indent << " " << escapeXML(contents) << "\n"; - } - output_stream << indent << "</" << mName->mString << ">\n"; - } -} - -void LLXMLNode::findName(const std::string& name, LLXMLNodeList &results) -{ - LLStringTableEntry* name_entry = gStringTable.checkStringEntry(name); - if (name_entry == mName) - { - results.insert(std::make_pair(this->mName->mString, this)); - return; - } - if (mChildren.notNull()) - { - 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::make_pair(this->mName->mString, this)); - return; - } - if (mChildren.notNull()) - { - 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 std::string& id, LLXMLNodeList &results) -{ - if (id == mID) - { - results.insert(std::make_pair(this->mName->mString, this)); - return; - } - if (mChildren.notNull()) - { - 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.isNull()) - { - return; - } - if (mChildren.notNull()) - { - 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) - { - LLXMLNodePtr ptr; - (*itor3)->setParent(ptr); - } - } -} - -bool LLXMLNode::getChild(const char* 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.notNull()) - { - 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 = NULL; - return false; -} - -void LLXMLNode::getChildren(const char* 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.notNull()) - { - 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::make_pair(child->mName->mString, child)); - child_itr++; - } - } - } - if (children.size() == 0 && use_default_if_missing && !mDefault.isNull()) - { - mDefault->getChildren(name, children, FALSE); - } -} - -// recursively walks the tree and returns all children at all nesting levels matching the name -void LLXMLNode::getDescendants(const LLStringTableEntry* name, LLXMLNodeList &children) const -{ - if (mChildren.notNull()) - { - for (LLXMLChildList::const_iterator child_itr = mChildren->map.begin(); - child_itr != mChildren->map.end(); ++child_itr) - { - LLXMLNodePtr child = (*child_itr).second; - if (name == child->mName) - { - children.insert(std::make_pair(child->mName->mString, child)); - } - // and check each child as well - child->getDescendants(name, children); - } - } -} - -bool LLXMLNode::getAttribute(const char* 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); - } - - return false; -} - -bool LLXMLNode::setAttributeString(const char* attr, const std::string& 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 char* name ) -{ - LLXMLNodePtr node; - return getAttribute(name, node); -} - -// the structure of these getAttribute_ functions is ugly, but it's because the -// underlying system is based on BOOL and LLString; if we change -// so that they're based on more generic mechanisms, these will be -// simplified. -bool LLXMLNode::getAttribute_bool(const char* name, bool& value ) -{ - LLXMLNodePtr node; - if (!getAttribute(name, node)) - { - return false; - } - BOOL temp; - bool retval = node->getBoolValue(1, &temp); - value = temp; - return retval; -} - -BOOL LLXMLNode::getAttributeBOOL(const char* name, BOOL& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getBoolValue(1, &value)); -} - -BOOL LLXMLNode::getAttributeU8(const char* name, U8& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getByteValue(1, &value)); -} - -BOOL LLXMLNode::getAttributeS8(const char* 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 char* 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 char* 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 char* name, U32& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getUnsignedValue(1, &value)); -} - -BOOL LLXMLNode::getAttributeS32(const char* name, S32& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getIntValue(1, &value)); -} - -BOOL LLXMLNode::getAttributeF32(const char* name, F32& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getFloatValue(1, &value)); -} - -BOOL LLXMLNode::getAttributeF64(const char* name, F64& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getDoubleValue(1, &value)); -} - -BOOL LLXMLNode::getAttributeColor(const char* name, LLColor4& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getFloatValue(4, value.mV)); -} - -BOOL LLXMLNode::getAttributeColor4(const char* name, LLColor4& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getFloatValue(4, value.mV)); -} - -BOOL LLXMLNode::getAttributeColor4U(const char* name, LLColor4U& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getByteValue(4, value.mV)); -} - -BOOL LLXMLNode::getAttributeVector3(const char* name, LLVector3& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getFloatValue(3, value.mV)); -} - -BOOL LLXMLNode::getAttributeVector3d(const char* name, LLVector3d& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getDoubleValue(3, value.mdV)); -} - -BOOL LLXMLNode::getAttributeQuat(const char* name, LLQuaternion& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getFloatValue(4, value.mQ)); -} - -BOOL LLXMLNode::getAttributeUUID(const char* name, LLUUID& value ) -{ - LLXMLNodePtr node; - return (getAttribute(name, node) && node->getUUIDValue(1, &value)); -} - -BOOL LLXMLNode::getAttributeString(const char* name, std::string& 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; - } - - std::string *str_array = new std::string[expected_length]; - - U32 length = getStringValue(expected_length, str_array); - - U32 ret_length = 0; - for (U32 i=0; i<length; ++i) - { - LLStringUtil::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) - { - LL_DEBUGS() << "LLXMLNode::getBoolValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << ret_length << LL_ENDL; - } -#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) - { - LL_WARNS() << "XMLNode::getByteValue asked for " << expected_length - << " elements, while node has " << mLength << LL_ENDL; - 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) - { - LL_WARNS() << "getByteValue: Value outside of valid range." << LL_ENDL; - break; - } - array[i] = U8(value); - } -#if LL_DEBUG - if (i != expected_length) - { - LL_DEBUGS() << "LLXMLNode::getByteValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << i << LL_ENDL; - } -#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) - { - LL_WARNS() << "XMLNode::getIntValue asked for " << expected_length - << " elements, while node has " << mLength << LL_ENDL; - 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) - { - LL_WARNS() << "getIntValue: Value outside of valid range." << LL_ENDL; - break; - } - array[i] = S32(value) * (is_negative?-1:1); - } - -#if LL_DEBUG - if (i != expected_length) - { - LL_DEBUGS() << "LLXMLNode::getIntValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << i << LL_ENDL; - } -#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) - { - LL_WARNS() << "XMLNode::getUnsignedValue asked for " << expected_length - << " elements, while node has " << mLength << LL_ENDL; - 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) - { - LL_WARNS() << "getUnsignedValue: Value outside of valid range." << LL_ENDL; - break; - } - array[i] = U32(value); - } - -#if LL_DEBUG - if (i != expected_length) - { - LL_DEBUGS() << "LLXMLNode::getUnsignedValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << i << LL_ENDL; - } -#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) - { - LL_WARNS() << "XMLNode::getLongValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL; - 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) - { - LL_WARNS() << "getLongValue: Value outside of valid range." << LL_ENDL; - break; - } - array[i] = value; - } - -#if LL_DEBUG - if (i != expected_length) - { - LL_DEBUGS() << "LLXMLNode::getLongValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << i << LL_ENDL; - } -#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) - { - LL_WARNS() << "XMLNode::getFloatValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL; - 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) - { - LL_DEBUGS() << "LLXMLNode::getFloatValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << i << LL_ENDL; - } -#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) - { - LL_WARNS() << "XMLNode::getDoubleValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL; - 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) - { - LL_DEBUGS() << "LLXMLNode::getDoubleValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << i << LL_ENDL; - } -#endif - return i; -} - -U32 LLXMLNode::getStringValue(U32 expected_length, std::string *array) -{ - llassert(array); - - // Can always return any value as a string - - if (mLength > 0 && mLength != expected_length) - { - LL_WARNS() << "XMLNode::getStringValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL; - 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) - { - LL_DEBUGS() << "LLXMLNode::getStringValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << num_returned_strings << LL_ENDL; - } -#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)) /* Flawfinder: ignore */ - { - break; - } - char uuid_string[UUID_STR_LENGTH]; /* Flawfinder: ignore */ - memcpy(uuid_string, value_string, (UUID_STR_LENGTH-1)); /* Flawfinder: ignore */ - uuid_string[(UUID_STR_LENGTH-1)] = 0; - - if (!LLUUID::parseUUID(std::string(uuid_string), &uuid_value)) - { - break; - } - value_string = &value_string[(UUID_STR_LENGTH-1)]; - array[i] = uuid_value; - } -#if LL_DEBUG - if (i != expected_length) - { - LL_DEBUGS() << "LLXMLNode::getUUIDValue() failed for node named '" - << mName->mString << "' -- expected " << expected_length << " but " - << "only found " << i << LL_ENDL; - } -#endif - return i; -} - -U32 LLXMLNode::getNodeRefValue(U32 expected_length, LLXMLNode **array) -{ - llassert(array); - - // Check type - if (mType != TYPE_NODEREF && mType != TYPE_UNKNOWN) - { - return 0; - } - - std::string *string_array = new std::string[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()) - { - LL_WARNS() << "XML: Could not find node ID: " << string_array[strnum] << LL_ENDL; - } - else if (node_list.size() > 1) - { - LL_WARNS() << "XML: Node ID not unique: " << string_array[strnum] << LL_ENDL; - } - 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; - - std::string 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; - - std::string 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; - - std::string 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; - - std::string 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; - - std::string 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; - - std::string new_value; - if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) - { - std::string format_string; - if (precision > 0) - { - if (precision > 25) - { - precision = 25; - } - format_string = llformat( "%%.%dg", precision); - } - else - { - format_string = llformat( "%%g"); - } - - for (U32 pos=0; pos<length; ++pos) - { - if (pos > 0) - { - new_value.append(" "); - new_value.append(llformat(format_string.c_str(), array[pos])); - } - else - { - new_value.assign(llformat(format_string.c_str(), 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; - - std::string new_value; - if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL) - { - std::string format_string; - if (precision > 0) - { - if (precision > 25) - { - precision = 25; - } - format_string = llformat( "%%.%dg", precision); - } - else - { - format_string = llformat( "%%g"); - } - for (U32 pos=0; pos<length; ++pos) - { - if (pos > 0) - { - new_value.append(" "); - new_value.append(llformat(format_string.c_str(), array[pos])); - } - else - { - new_value.assign(llformat(format_string.c_str(), 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 -std::string LLXMLNode::escapeXML(const std::string& xml) -{ - std::string out; - for (std::string::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 std::string *strings) -{ - if (length == 0) return; - - std::string new_value; - for (U32 pos=0; pos<length; ++pos) - { - // *NOTE: Do not escape strings here - do it on output - new_value.append( strings[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; - - std::string new_value; - for (U32 pos=0; pos<length; ++pos) - { - new_value.append(array[pos].asString()); - 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; - - std::string 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 std::string& 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 std::string& 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 std::string& 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) - { - LLXMLNodePtr this_ptr(this); - old_parent->addChild(this_ptr); - } -} - -// Unused -// void LLXMLNode::appendValue(const std::string& value) -// { -// mValue.append(value); -// } - -U32 LLXMLNode::getChildCount() const -{ - if (mChildren.notNull()) - { - 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.notNull()) - { - 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 - std::string rand_id; - U32 rand_id_len = get_rand(10)+5; - for (U32 pos = 0; pos<rand_id_len; ++pos) - { - char c = 'a' + get_rand(26); - rand_id.append(1, c); - } - 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 - std::string child_name; - U32 child_name_len = get_rand(10)+5; - for (U32 pos = 0; pos<child_name_len; ++pos) - { - char c = 'a' + get_rand(26); - child_name.append(1, c); - } - - LLXMLNode *new_child = createChild(child_name.c_str(), FALSE); - - // Random ID - std::string child_id; - U32 child_id_len = get_rand(10)+5; - for (U32 pos=0; pos<child_id_len; ++pos) - { - char c = 'a' + get_rand(26); - child_id.append(1, c); - } - 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) /* Flawfinder: ignore */ - { - 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(std::string &error_buffer) -{ - if (mChildren.isNull()) - { - 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) /* Flawfinder: ignore */ - { - 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.asString().c_str(), uuid_checksum.asString().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() const -{ - if (mChildren.isNull()) return NULL; - LLXMLNodePtr ret = mChildren->head; - return ret; -} - -LLXMLNodePtr LLXMLNode::getNextSibling() const -{ - LLXMLNodePtr ret = mNext; - return ret; -} - -std::string LLXMLNode::getSanitizedValue() const -{ - if (mIsAttribute) - { - return getValue() ; - } - else - { - return getTextContents(); - } -} - - -std::string LLXMLNode::getTextContents() const -{ - std::string msg; - std::string contents = mValue; - std::string::size_type n = contents.find_first_not_of(" \t\n"); - if (n != std::string::npos && contents[n] == '\"') - { - // Case 1: 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 2: node has embedded text (beginning and trailing whitespace trimmed) - std::string::size_type start = mValue.find_first_not_of(" \t\n"); - if (start != mValue.npos) - { - std::string::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); - } - } - // Convert any internal CR to LF - msg = utf8str_removeCRLF(msg); - } - return msg; -} - -void LLXMLNode::setLineNumber(S32 line_number) -{ - mLineNumber = line_number; -} - -S32 LLXMLNode::getLineNumber() -{ - return mLineNumber; -} +/**
+ * @file llxmlnode.cpp
+ * @author Tom Yedwab
+ * @brief LLXMLNode implementation
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#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 "llstring.h"
+#include "lluuid.h"
+#include "lldir.h"
+
+// static
+bool LLXMLNode::sStripEscapedStrings = true;
+bool LLXMLNode::sStripWhitespaceValues = false;
+
+LLXMLNode::LLXMLNode() :
+ mID(""),
+ mParser(NULL),
+ mIsAttribute(false),
+ mVersionMajor(0),
+ mVersionMinor(0),
+ mLength(0),
+ mPrecision(64),
+ mType(TYPE_CONTAINER),
+ mEncoding(ENCODING_DEFAULT),
+ mLineNumber(-1),
+ mParent(NULL),
+ mChildren(NULL),
+ mAttributes(),
+ mPrev(NULL),
+ mNext(NULL),
+ mName(NULL),
+ mValue(""),
+ mDefault(NULL)
+{
+}
+
+LLXMLNode::LLXMLNode(const char* name, bool is_attribute) :
+ mID(""),
+ mParser(NULL),
+ mIsAttribute(is_attribute),
+ mVersionMajor(0),
+ mVersionMinor(0),
+ mLength(0),
+ mPrecision(64),
+ mType(TYPE_CONTAINER),
+ mEncoding(ENCODING_DEFAULT),
+ mLineNumber(-1),
+ mParent(NULL),
+ mChildren(NULL),
+ mAttributes(),
+ mPrev(NULL),
+ mNext(NULL),
+ mValue(""),
+ mDefault(NULL)
+{
+ mName = gStringTable.addStringEntry(name);
+}
+
+LLXMLNode::LLXMLNode(LLStringTableEntry* name, bool is_attribute) :
+ mID(""),
+ mParser(NULL),
+ mIsAttribute(is_attribute),
+ mVersionMajor(0),
+ mVersionMinor(0),
+ mLength(0),
+ mPrecision(64),
+ mType(TYPE_CONTAINER),
+ mEncoding(ENCODING_DEFAULT),
+ mLineNumber(-1),
+ mParent(NULL),
+ mChildren(NULL),
+ mAttributes(),
+ mPrev(NULL),
+ mNext(NULL),
+ mName(name),
+ mValue(""),
+ mDefault(NULL)
+{
+}
+
+// copy constructor (except for the children)
+LLXMLNode::LLXMLNode(const LLXMLNode& rhs) :
+ mID(rhs.mID),
+ mIsAttribute(rhs.mIsAttribute),
+ mVersionMajor(rhs.mVersionMajor),
+ mVersionMinor(rhs.mVersionMinor),
+ mLength(rhs.mLength),
+ mPrecision(rhs.mPrecision),
+ mType(rhs.mType),
+ mEncoding(rhs.mEncoding),
+ mLineNumber(0),
+ mParser(NULL),
+ mParent(NULL),
+ mChildren(NULL),
+ mAttributes(),
+ mPrev(NULL),
+ mNext(NULL),
+ mName(rhs.mName),
+ mValue(rhs.mValue),
+ mDefault(rhs.mDefault)
+{
+}
+
+// returns a new copy of this node and all its children
+LLXMLNodePtr LLXMLNode::deepCopy()
+{
+ LLXMLNodePtr newnode = LLXMLNodePtr(new LLXMLNode(*this));
+ if (mChildren.notNull())
+ {
+ for (LLXMLChildList::iterator iter = mChildren->map.begin();
+ iter != mChildren->map.end(); ++iter)
+ {
+ LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy());
+ newnode->addChild(temp_ptr_for_gcc);
+ }
+ }
+ for (LLXMLAttribList::iterator iter = mAttributes.begin();
+ iter != mAttributes.end(); ++iter)
+ {
+ LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy());
+ newnode->addChild(temp_ptr_for_gcc);
+ }
+
+ return newnode;
+}
+
+// virtual
+LLXMLNode::~LLXMLNode()
+{
+ // Strictly speaking none of this should be required execept 'delete mChildren'...
+ // Sadly, that's only true if we hadn't had reference-counted smart pointers linked
+ // in three different directions. This entire class is a frightening, hard-to-maintain
+ // mess.
+ if (mChildren.notNull())
+ {
+ 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;
+ mChildren = NULL;
+ }
+ 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.notNull())
+ {
+ 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;
+ }
+ if (target_child == mChildren->tail)
+ {
+ mChildren->tail = target_child->mPrev;
+ }
+
+ 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())
+ {
+ 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)
+ {
+ LLXMLAttribList::iterator found_it = mAttributes.find(new_child->mName);
+ if (found_it != mAttributes.end())
+ {
+ removeChild(found_it->second);
+ }
+ mAttributes.insert(std::make_pair(new_child->mName, new_child));
+ }
+ else
+ {
+ if (mChildren.isNull())
+ {
+ mChildren = new LLXMLChildren();
+ mChildren->head = new_child;
+ mChildren->tail = new_child;
+ }
+ mChildren->map.insert(std::make_pair(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 char* name, bool is_attribute)
+{
+ return createChild(gStringTable.addStringEntry(name), is_attribute);
+}
+
+// virtual
+LLXMLNodePtr LLXMLNode::createChild(LLStringTableEntry* name, bool is_attribute)
+{
+ LLXMLNodePtr ret(new LLXMLNode(name, is_attribute));
+ ret->mID.clear();
+
+ 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())
+ {
+ LLXMLNodePtr this_ptr(this);
+ new_parent->addChild(this_ptr);
+ }
+ 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.notNull())
+ {
+ 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.clear();
+ LLXMLNodePtr ptr_new_node = new_node;
+
+ // Set the parent-child relationship with the current active node
+ LLXMLNode* parent = (LLXMLNode *)userData;
+
+ if (NULL == parent)
+ {
+ LL_WARNS() << "parent (userData) is NULL; aborting function" << LL_ENDL;
+ return;
+ }
+
+ new_node_ptr->mParser = parent->mParser;
+ new_node_ptr->setLineNumber(XML_GetCurrentLineNumber(*new_node_ptr->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)
+ {
+ std::string attr_name = atts[pos];
+ std::string 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.c_str(), attr_node, false))
+ {
+ attr_node = new LLXMLNode(attr_name.c_str(), true);
+ attr_node->setLineNumber(XML_GetCurrentLineNumber(*new_node_ptr->mParser));
+ }
+ 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)
+ {
+ std::string 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;
+ std::string value = current_node->getValue();
+ if (LLXMLNode::sStripEscapedStrings)
+ {
+ if (s[0] == '\"' && s[len-1] == '\"')
+ {
+ // Special-case: Escaped string.
+ std::string 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(std::string(s, len));
+ current_node->setValue(value);
+}
+
+
+
+// static
+bool LLXMLNode::updateNode(
+ LLXMLNodePtr& node,
+ LLXMLNodePtr& update_node)
+{
+
+ if (!node || !update_node)
+ {
+ LL_WARNS() << "Node invalid" << LL_ENDL;
+ 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 = node->getFirstChild();
+ LLXMLNodePtr last_child = child;
+ LLXMLNodePtr updateChild;
+
+ for (updateChild = update_node->getFirstChild(); updateChild.notNull();
+ updateChild = updateChild->getNextSibling())
+ {
+ while(child.notNull())
+ {
+ std::string nodeName;
+ std::string 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);
+ last_child = child;
+ child = child->getNextSibling();
+ if (child.isNull())
+ {
+ child = node->getFirstChild();
+ }
+ break;
+ }
+
+ child = child->getNextSibling();
+ if (child.isNull())
+ {
+ child = node->getFirstChild();
+ }
+ if (child == last_child)
+ {
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+// static
+bool LLXMLNode::parseFile(const std::string& filename, LLXMLNodePtr& node, LLXMLNode* defaults_tree)
+{
+ // Read file
+ LL_DEBUGS("XMLNode") << "parsing XML file: " << filename << LL_ENDL;
+ LLFILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
+ if (fp == NULL)
+ {
+ node = NULL ;
+ return false;
+ }
+ fseek(fp, 0, SEEK_END);
+ U32 length = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ U8* buffer = new U8[length+1];
+ size_t nread = fread(buffer, 1, length, fp);
+ buffer[nread] = 0;
+ fclose(fp);
+
+ bool rv = parseBuffer(buffer, nread, 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)
+ {
+ LL_WARNS() << "Error parsing xml error code: "
+ << XML_ErrorString(XML_GetErrorCode(my_parser))
+ << " on line " << XML_GetCurrentLineNumber(my_parser)
+ << LL_ENDL;
+ }
+
+ // Deinit
+ XML_ParserFree(my_parser);
+
+ if (!file_node->mChildren || file_node->mChildren->map.size() != 1)
+ {
+ LL_WARNS() << "Parse failure - wrong number of top-level nodes xml."
+ << LL_ENDL;
+ node = NULL ;
+ return false;
+ }
+
+ LLXMLNode *return_node = file_node->mChildren->map.begin()->second;
+
+ return_node->setDefault(defaults);
+ return_node->updateDefault();
+
+ node = return_node;
+ return true;
+}
+
+// static
+bool LLXMLNode::parseStream(
+ std::istream& str,
+ 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);
+
+ const int BUFSIZE = 1024;
+ U8* buffer = new U8[BUFSIZE];
+
+ while(str.good())
+ {
+ str.read((char*)buffer, BUFSIZE);
+ int count = (int)str.gcount();
+
+ if (XML_Parse(my_parser, (const char *)buffer, count, !str.good()) != XML_STATUS_OK)
+ {
+ LL_WARNS() << "Error parsing xml error code: "
+ << XML_ErrorString(XML_GetErrorCode(my_parser))
+ << " on lne " << XML_GetCurrentLineNumber(my_parser)
+ << LL_ENDL;
+ break;
+ }
+ }
+
+ delete [] buffer;
+
+ // Deinit
+ XML_ParserFree(my_parser);
+
+ if (!file_node->mChildren || file_node->mChildren->map.size() != 1)
+ {
+ LL_WARNS() << "Parse failure - wrong number of top-level nodes xml."
+ << LL_ENDL;
+ node = NULL;
+ 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.notNull())
+ {
+ 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
+bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root,
+ const std::vector<std::string>& paths)
+{
+ if (paths.empty()) return false;
+
+ std::string filename = paths.front();
+ if (filename.empty())
+ {
+ return false;
+ }
+
+ if (!LLXMLNode::parseFile(filename, root, NULL))
+ {
+ LL_WARNS() << "Problem reading UI description file: " << filename << " " << errno << LL_ENDL;
+ return false;
+ }
+
+ LLXMLNodePtr updateRoot;
+
+ std::vector<std::string>::const_iterator itor;
+
+ // We've already dealt with the first item, skip that one
+ for (itor = paths.begin() + 1; itor != paths.end(); ++itor)
+ {
+ std::string layer_filename = *itor;
+ if(layer_filename.empty() || layer_filename == filename)
+ {
+ // no localized version of this file, that's ok, keep looking
+ continue;
+ }
+
+ if (!LLXMLNode::parseFile(layer_filename, updateRoot, NULL))
+ {
+ LL_WARNS() << "Problem reading localized UI description file: " << layer_filename << LL_ENDL;
+ return false;
+ }
+
+ std::string nodeName;
+ std::string updateName;
+
+ updateRoot->getAttributeString("name", updateName);
+ root->getAttributeString("name", nodeName);
+
+ if (updateName == nodeName)
+ {
+ LLXMLNode::updateNode(root, updateRoot);
+ }
+ }
+
+ return true;
+}
+
+// static
+void LLXMLNode::writeHeaderToFile(LLFILE *out_file)
+{
+ fprintf(out_file, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n");
+}
+
+void LLXMLNode::writeToFile(LLFILE *out_file, const std::string& indent, bool use_type_decorations)
+{
+ if (isFullyDefault())
+ {
+ // Don't write out nodes that are an exact match to defaults
+ return;
+ }
+
+ std::ostringstream ostream;
+ writeToOstream(ostream, indent, use_type_decorations);
+ std::string outstring = ostream.str();
+ size_t written = fwrite(outstring.c_str(), 1, outstring.length(), out_file);
+ if (written != outstring.length())
+ {
+ LL_WARNS() << "Short write" << LL_ENDL;
+ }
+}
+
+void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& indent, bool use_type_decorations)
+{
+ 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 << "<" << mName->mString << "\n";
+
+ if (use_type_decorations)
+ {
+ // ID
+ if (mID != "")
+ {
+ output_stream << indent << " id=\"" << mID << "\"\n";
+ }
+
+ // Type
+ if (!has_default_type)
+ {
+ switch (mType)
+ {
+ case TYPE_BOOLEAN:
+ output_stream << indent << " type=\"boolean\"\n";
+ break;
+ case TYPE_INTEGER:
+ output_stream << indent << " type=\"integer\"\n";
+ break;
+ case TYPE_FLOAT:
+ output_stream << indent << " type=\"float\"\n";
+ break;
+ case TYPE_STRING:
+ output_stream << indent << " type=\"string\"\n";
+ break;
+ case TYPE_UUID:
+ output_stream << indent << " type=\"uuid\"\n";
+ break;
+ case TYPE_NODEREF:
+ output_stream << indent << " type=\"noderef\"\n";
+ break;
+ default:
+ // default on switch(enum) eliminates a warning on linux
+ break;
+ };
+ }
+
+ // Encoding
+ if (!has_default_encoding)
+ {
+ switch (mEncoding)
+ {
+ case ENCODING_DECIMAL:
+ output_stream << indent << " encoding=\"decimal\"\n";
+ break;
+ case ENCODING_HEX:
+ output_stream << indent << " encoding=\"hex\"\n";
+ break;
+ /*case ENCODING_BASE32:
+ output_stream << indent << " encoding=\"base32\"\n";
+ 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 << indent << " precision=\"" << mPrecision << "\"\n";
+ }
+
+ // Version
+ if (mVersionMajor > 0 || mVersionMinor > 0)
+ {
+ output_stream << indent << " version=\"" << mVersionMajor << "." << mVersionMinor << "\"\n";
+ }
+
+ // Array length
+ if (!has_default_length && mLength > 0)
+ {
+ output_stream << indent << " length=\"" << mLength << "\"\n";
+ }
+ }
+
+ {
+ // Write out attributes
+ 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)
+ {
+ std::string attr = child->mName->mString;
+ if (use_type_decorations
+ && (attr == "id" ||
+ attr == "type" ||
+ attr == "encoding" ||
+ attr == "precision" ||
+ attr == "version" ||
+ attr == "length"))
+ {
+ continue; // skip built-in attributes
+ }
+
+ std::string attr_str = llformat(" %s=\"%s\"",
+ attr.c_str(),
+ escapeXML(child->mValue).c_str());
+ output_stream << indent << attr_str << "\n";
+ }
+ }
+ }
+
+ // erase last \n before attaching final > or />
+ output_stream.seekp(-1, std::ios::cur);
+
+ if (mChildren.isNull() && mValue == "")
+ {
+ output_stream << " />\n";
+ return;
+ }
+ else
+ {
+ output_stream << ">\n";
+ if (mChildren.notNull())
+ {
+ // stream non-attributes
+ std::string next_indent = indent + " ";
+ for (LLXMLNode* child = getFirstChild(); child; child = child->getNextSibling())
+ {
+ child->writeToOstream(output_stream, next_indent, use_type_decorations);
+ }
+ }
+ if (!mValue.empty())
+ {
+ std::string contents = getTextContents();
+ output_stream << indent << " " << escapeXML(contents) << "\n";
+ }
+ output_stream << indent << "</" << mName->mString << ">\n";
+ }
+}
+
+void LLXMLNode::findName(const std::string& name, LLXMLNodeList &results)
+{
+ LLStringTableEntry* name_entry = gStringTable.checkStringEntry(name);
+ if (name_entry == mName)
+ {
+ results.insert(std::make_pair(this->mName->mString, this));
+ return;
+ }
+ if (mChildren.notNull())
+ {
+ 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::make_pair(this->mName->mString, this));
+ return;
+ }
+ if (mChildren.notNull())
+ {
+ 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 std::string& id, LLXMLNodeList &results)
+{
+ if (id == mID)
+ {
+ results.insert(std::make_pair(this->mName->mString, this));
+ return;
+ }
+ if (mChildren.notNull())
+ {
+ 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.isNull())
+ {
+ return;
+ }
+ if (mChildren.notNull())
+ {
+ 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)
+ {
+ LLXMLNodePtr ptr;
+ (*itor3)->setParent(ptr);
+ }
+ }
+}
+
+bool LLXMLNode::getChild(const char* 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.notNull())
+ {
+ 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 = NULL;
+ return false;
+}
+
+void LLXMLNode::getChildren(const char* 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.notNull())
+ {
+ 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::make_pair(child->mName->mString, child));
+ child_itr++;
+ }
+ }
+ }
+ if (children.size() == 0 && use_default_if_missing && !mDefault.isNull())
+ {
+ mDefault->getChildren(name, children, false);
+ }
+}
+
+// recursively walks the tree and returns all children at all nesting levels matching the name
+void LLXMLNode::getDescendants(const LLStringTableEntry* name, LLXMLNodeList &children) const
+{
+ if (mChildren.notNull())
+ {
+ for (LLXMLChildList::const_iterator child_itr = mChildren->map.begin();
+ child_itr != mChildren->map.end(); ++child_itr)
+ {
+ LLXMLNodePtr child = (*child_itr).second;
+ if (name == child->mName)
+ {
+ children.insert(std::make_pair(child->mName->mString, child));
+ }
+ // and check each child as well
+ child->getDescendants(name, children);
+ }
+ }
+}
+
+bool LLXMLNode::getAttribute(const char* 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);
+ }
+
+ return false;
+}
+
+bool LLXMLNode::setAttributeString(const char* attr, const std::string& 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 char* name )
+{
+ LLXMLNodePtr node;
+ return getAttribute(name, node);
+}
+
+bool LLXMLNode::getAttributeBOOL(const char* name, bool& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getBoolValue(1, &value));
+}
+
+bool LLXMLNode::getAttributeU8(const char* name, U8& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getByteValue(1, &value));
+}
+
+bool LLXMLNode::getAttributeS8(const char* 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 char* 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 char* 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 char* name, U32& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getUnsignedValue(1, &value));
+}
+
+bool LLXMLNode::getAttributeS32(const char* name, S32& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getIntValue(1, &value));
+}
+
+bool LLXMLNode::getAttributeF32(const char* name, F32& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(1, &value));
+}
+
+bool LLXMLNode::getAttributeF64(const char* name, F64& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getDoubleValue(1, &value));
+}
+
+bool LLXMLNode::getAttributeColor(const char* name, LLColor4& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(4, value.mV));
+}
+
+bool LLXMLNode::getAttributeColor4(const char* name, LLColor4& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(4, value.mV));
+}
+
+bool LLXMLNode::getAttributeColor4U(const char* name, LLColor4U& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getByteValue(4, value.mV));
+}
+
+bool LLXMLNode::getAttributeVector3(const char* name, LLVector3& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(3, value.mV));
+}
+
+bool LLXMLNode::getAttributeVector3d(const char* name, LLVector3d& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getDoubleValue(3, value.mdV));
+}
+
+bool LLXMLNode::getAttributeQuat(const char* name, LLQuaternion& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(4, value.mQ));
+}
+
+bool LLXMLNode::getAttributeUUID(const char* name, LLUUID& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getUUIDValue(1, &value));
+}
+
+bool LLXMLNode::getAttributeString(const char* name, std::string& 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;
+ }
+
+ std::string *str_array = new std::string[expected_length];
+
+ U32 length = getStringValue(expected_length, str_array);
+
+ U32 ret_length = 0;
+ for (U32 i=0; i<length; ++i)
+ {
+ LLStringUtil::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)
+ {
+ LL_DEBUGS() << "LLXMLNode::getBoolValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << ret_length << LL_ENDL;
+ }
+#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)
+ {
+ LL_WARNS() << "XMLNode::getByteValue asked for " << expected_length
+ << " elements, while node has " << mLength << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "getByteValue: Value outside of valid range." << LL_ENDL;
+ break;
+ }
+ array[i] = U8(value);
+ }
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ LL_DEBUGS() << "LLXMLNode::getByteValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << LL_ENDL;
+ }
+#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)
+ {
+ LL_WARNS() << "XMLNode::getIntValue asked for " << expected_length
+ << " elements, while node has " << mLength << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "getIntValue: Value outside of valid range." << LL_ENDL;
+ break;
+ }
+ array[i] = S32(value) * (is_negative?-1:1);
+ }
+
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ LL_DEBUGS() << "LLXMLNode::getIntValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << LL_ENDL;
+ }
+#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)
+ {
+ LL_WARNS() << "XMLNode::getUnsignedValue asked for " << expected_length
+ << " elements, while node has " << mLength << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "getUnsignedValue: Value outside of valid range." << LL_ENDL;
+ break;
+ }
+ array[i] = U32(value);
+ }
+
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ LL_DEBUGS() << "LLXMLNode::getUnsignedValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << LL_ENDL;
+ }
+#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)
+ {
+ LL_WARNS() << "XMLNode::getLongValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "getLongValue: Value outside of valid range." << LL_ENDL;
+ break;
+ }
+ array[i] = value;
+ }
+
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ LL_DEBUGS() << "LLXMLNode::getLongValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << LL_ENDL;
+ }
+#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)
+ {
+ LL_WARNS() << "XMLNode::getFloatValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL;
+ 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)
+ {
+ LL_DEBUGS() << "LLXMLNode::getFloatValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << LL_ENDL;
+ }
+#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)
+ {
+ LL_WARNS() << "XMLNode::getDoubleValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL;
+ 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)
+ {
+ LL_DEBUGS() << "LLXMLNode::getDoubleValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << LL_ENDL;
+ }
+#endif
+ return i;
+}
+
+U32 LLXMLNode::getStringValue(U32 expected_length, std::string *array)
+{
+ llassert(array);
+
+ // Can always return any value as a string
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ LL_WARNS() << "XMLNode::getStringValue asked for " << expected_length << " elements, while node has " << mLength << LL_ENDL;
+ 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)
+ {
+ LL_DEBUGS() << "LLXMLNode::getStringValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << num_returned_strings << LL_ENDL;
+ }
+#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)) /* Flawfinder: ignore */
+ {
+ break;
+ }
+ char uuid_string[UUID_STR_LENGTH]; /* Flawfinder: ignore */
+ memcpy(uuid_string, value_string, (UUID_STR_LENGTH-1)); /* Flawfinder: ignore */
+ uuid_string[(UUID_STR_LENGTH-1)] = 0;
+
+ if (!LLUUID::parseUUID(std::string(uuid_string), &uuid_value))
+ {
+ break;
+ }
+ value_string = &value_string[(UUID_STR_LENGTH-1)];
+ array[i] = uuid_value;
+ }
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ LL_DEBUGS() << "LLXMLNode::getUUIDValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << LL_ENDL;
+ }
+#endif
+ return i;
+}
+
+U32 LLXMLNode::getNodeRefValue(U32 expected_length, LLXMLNode **array)
+{
+ llassert(array);
+
+ // Check type
+ if (mType != TYPE_NODEREF && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ std::string *string_array = new std::string[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())
+ {
+ LL_WARNS() << "XML: Could not find node ID: " << string_array[strnum] << LL_ENDL;
+ }
+ else if (node_list.size() > 1)
+ {
+ LL_WARNS() << "XML: Node ID not unique: " << string_array[strnum] << LL_ENDL;
+ }
+ 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;
+
+ std::string 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;
+
+ std::string 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;
+
+ std::string 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;
+
+ std::string 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;
+
+ std::string 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;
+
+ std::string new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ std::string format_string;
+ if (precision > 0)
+ {
+ if (precision > 25)
+ {
+ precision = 25;
+ }
+ format_string = llformat( "%%.%dg", precision);
+ }
+ else
+ {
+ format_string = llformat( "%%g");
+ }
+
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(" ");
+ new_value.append(llformat(format_string.c_str(), array[pos]));
+ }
+ else
+ {
+ new_value.assign(llformat(format_string.c_str(), 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;
+
+ std::string new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ std::string format_string;
+ if (precision > 0)
+ {
+ if (precision > 25)
+ {
+ precision = 25;
+ }
+ format_string = llformat( "%%.%dg", precision);
+ }
+ else
+ {
+ format_string = llformat( "%%g");
+ }
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(" ");
+ new_value.append(llformat(format_string.c_str(), array[pos]));
+ }
+ else
+ {
+ new_value.assign(llformat(format_string.c_str(), 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
+std::string LLXMLNode::escapeXML(const std::string& xml)
+{
+ std::string out;
+ for (std::string::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 std::string *strings)
+{
+ if (length == 0) return;
+
+ std::string new_value;
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ // *NOTE: Do not escape strings here - do it on output
+ new_value.append( strings[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;
+
+ std::string new_value;
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ new_value.append(array[pos].asString());
+ 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;
+
+ std::string 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 std::string& 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 std::string& 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;
+}
+
+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;
+}
+
+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 std::string& 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)
+ {
+ LLXMLNodePtr this_ptr(this);
+ old_parent->addChild(this_ptr);
+ }
+}
+
+// Unused
+// void LLXMLNode::appendValue(const std::string& value)
+// {
+// mValue.append(value);
+// }
+
+U32 LLXMLNode::getChildCount() const
+{
+ if (mChildren.notNull())
+ {
+ 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.notNull())
+ {
+ 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
+ std::string rand_id;
+ U32 rand_id_len = get_rand(10)+5;
+ for (U32 pos = 0; pos<rand_id_len; ++pos)
+ {
+ char c = 'a' + get_rand(26);
+ rand_id.append(1, c);
+ }
+ 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
+ std::string child_name;
+ U32 child_name_len = get_rand(10)+5;
+ for (U32 pos = 0; pos<child_name_len; ++pos)
+ {
+ char c = 'a' + get_rand(26);
+ child_name.append(1, c);
+ }
+
+ LLXMLNode *new_child = createChild(child_name.c_str(), false);
+
+ // Random ID
+ std::string child_id;
+ U32 child_id_len = get_rand(10)+5;
+ for (U32 pos=0; pos<child_id_len; ++pos)
+ {
+ char c = 'a' + get_rand(26);
+ child_id.append(1, c);
+ }
+ 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) /* Flawfinder: ignore */
+ {
+ 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(std::string &error_buffer)
+{
+ if (mChildren.isNull())
+ {
+ 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) /* Flawfinder: ignore */
+ {
+ 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.asString().c_str(), uuid_checksum.asString().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() const
+{
+ if (mChildren.isNull()) return NULL;
+ LLXMLNodePtr ret = mChildren->head;
+ return ret;
+}
+
+LLXMLNodePtr LLXMLNode::getNextSibling() const
+{
+ LLXMLNodePtr ret = mNext;
+ return ret;
+}
+
+std::string LLXMLNode::getSanitizedValue() const
+{
+ if (mIsAttribute)
+ {
+ return getValue() ;
+ }
+ else
+ {
+ return getTextContents();
+ }
+}
+
+
+std::string LLXMLNode::getTextContents() const
+{
+ std::string msg;
+ std::string contents = mValue;
+ std::string::size_type n = contents.find_first_not_of(" \t\n");
+ if (n != std::string::npos && contents[n] == '\"')
+ {
+ // Case 1: 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 2: node has embedded text (beginning and trailing whitespace trimmed)
+ std::string::size_type start = mValue.find_first_not_of(" \t\n");
+ if (start != mValue.npos)
+ {
+ std::string::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);
+ }
+ }
+ // Convert any internal CR to LF
+ msg = utf8str_removeCRLF(msg);
+ }
+ return msg;
+}
+
+void LLXMLNode::setLineNumber(S32 line_number)
+{
+ mLineNumber = line_number;
+}
+
+S32 LLXMLNode::getLineNumber()
+{
+ return mLineNumber;
+}
diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index 62ab51fa8c..dd362d838a 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -1,339 +1,335 @@ -/** - * @file llxmlnode.h - * @brief LLXMLNode definition - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLXMLNODE_H -#define LL_LLXMLNODE_H - -#ifndef XML_STATIC -#define XML_STATIC -#endif -#ifdef LL_USESYSTEMLIBS -#include <expat.h> -#else -#include "expat/expat.h" -#endif -#include <map> - -#include "indra_constants.h" -#include "llrefcount.h" -#include "llpointer.h" -#include "llstring.h" -#include "llstringtable.h" -#include "llfile.h" -#include "lluuid.h" - -class LLVector3; -class LLVector3d; -class LLQuaternion; -class LLColor4; -class LLColor4U; - - -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<std::string, LLXMLNodePtr > LLXMLNodeList; -typedef std::multimap<const LLStringTableEntry *, LLXMLNodePtr > LLXMLChildList; -typedef std::map<const LLStringTableEntry *, LLXMLNodePtr, CompareAttributes> LLXMLAttribList; - -class LLColor4; -class LLColor4U; -class LLQuaternion; -class LLVector3; -class LLVector3d; -class LLVector4; -class LLVector4U; - -struct LLXMLChildren : public LLThreadSafeRefCount -{ - LLXMLChildList map; // Map of children names->pointers - LLXMLNodePtr head; // Head of the double-linked list - LLXMLNodePtr tail; // Tail of the double-linked list -}; -typedef LLPointer<LLXMLChildren> LLXMLChildrenPtr; - -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: - ~LLXMLNode(); - -public: - LLXMLNode(); - LLXMLNode(const char* name, BOOL is_attribute); - LLXMLNode(LLStringTableEntry* name, BOOL is_attribute); - LLXMLNode(const LLXMLNode& rhs); - LLXMLNodePtr deepCopy(); - - BOOL isNull(); - - BOOL deleteChild(LLXMLNode* child); - void addChild(LLXMLNodePtr& new_child); - void setParent(LLXMLNodePtr& new_parent); // reparent if necessary - - // Serialization - static bool parseFile( - const std::string& filename, - LLXMLNodePtr& node, - LLXMLNode* defaults_tree); - static bool parseBuffer( - U8* buffer, - U32 length, - LLXMLNodePtr& node, - LLXMLNode* defaults); - static bool parseStream( - std::istream& str, - LLXMLNodePtr& node, - LLXMLNode* defaults); - static bool updateNode( - LLXMLNodePtr& node, - LLXMLNodePtr& update_node); - - static bool getLayeredXMLNode(LLXMLNodePtr& root, const std::vector<std::string>& paths); - - - // Write standard XML file header: - // <?xml version="1.0" encoding="utf-8" standalone="yes" ?> - static void writeHeaderToFile(LLFILE *out_file); - - // Write XML to file with one attribute per line. - // XML escapes values as they are written. - void writeToFile(LLFILE *out_file, const std::string& indent = std::string(), bool use_type_decorations=true); - void writeToOstream(std::ostream& output_stream, const std::string& indent = std::string(), bool use_type_decorations=true); - - // Utility - void findName(const std::string& name, LLXMLNodeList &results); - void findName(LLStringTableEntry* name, LLXMLNodeList &results); - void findID(const std::string& id, LLXMLNodeList &results); - - - virtual LLXMLNodePtr createChild(const char* 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, std::string *array); - U32 getUUIDValue(U32 expected_length, LLUUID *array); - U32 getNodeRefValue(U32 expected_length, LLXMLNode **array); - - BOOL hasAttribute(const char* name ); - - // these are designed to be more generic versions of the functions - // rather than relying on LL-types - bool getAttribute_bool(const char* name, bool& value ); - - BOOL getAttributeBOOL(const char* name, BOOL& value ); - BOOL getAttributeU8(const char* name, U8& value ); - BOOL getAttributeS8(const char* name, S8& value ); - BOOL getAttributeU16(const char* name, U16& value ); - BOOL getAttributeS16(const char* name, S16& value ); - BOOL getAttributeU32(const char* name, U32& value ); - BOOL getAttributeS32(const char* name, S32& value ); - BOOL getAttributeF32(const char* name, F32& value ); - BOOL getAttributeF64(const char* name, F64& value ); - BOOL getAttributeColor(const char* name, LLColor4& value ); - BOOL getAttributeColor4(const char* name, LLColor4& value ); - BOOL getAttributeColor4U(const char* name, LLColor4U& value ); - BOOL getAttributeVector3(const char* name, LLVector3& value ); - BOOL getAttributeVector3d(const char* name, LLVector3d& value ); - BOOL getAttributeQuat(const char* name, LLQuaternion& value ); - BOOL getAttributeUUID(const char* name, LLUUID& value ); - BOOL getAttributeString(const char* name, std::string& value ); - - const ValueType& getType() const { return mType; } - U32 getLength() const { return mLength; } - U32 getPrecision() const { return mPrecision; } - const std::string& getValue() const { return mValue; } - std::string getSanitizedValue() const; - std::string getTextContents() const; - const LLStringTableEntry* getName() const { return mName; } - BOOL hasName(const char* name) const { return mName == gStringTable.checkStringEntry(name); } - BOOL hasName(const std::string& name) const { return mName == gStringTable.checkStringEntry(name.c_str()); } - const std::string& 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 char* 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 char* name, LLXMLNodeList &children, BOOL use_default_if_missing = TRUE) const; - void getChildren(const LLStringTableEntry* name, LLXMLNodeList &children, BOOL use_default_if_missing = TRUE) const; - - // recursively finds all children at any level matching name - void getDescendants(const LLStringTableEntry* name, LLXMLNodeList &children) const; - - bool getAttribute(const char* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); - bool getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); - - S32 getLineNumber(); - - // The following skip over attributes - LLXMLNodePtr getFirstChild() const; - LLXMLNodePtr getNextSibling() const; - - LLXMLNodePtr getRoot(); - - // Setters - - bool setAttributeString(const char* attr, const std::string& 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 std::string& 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 std::string *array); - void setUUIDValue(U32 length, const LLUUID *array); - void setNodeRefValue(U32 length, const LLXMLNode **array); - void setValue(const std::string& value); - void setName(const std::string& name); - void setName(LLStringTableEntry* name); - - void setLineNumber(S32 line_number); - - // Escapes " (quot) ' (apos) & (amp) < (lt) > (gt) - static std::string escapeXML(const std::string& 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 std::string& name); - BOOL deleteChildren(LLStringTableEntry* name); - void setAttributes(ValueType type, U32 precision, Encoding encoding, U32 length); -// void appendValue(const std::string& value); // Unused - - // Unit Testing - void createUnitTest(S32 max_num_children); - BOOL performUnitTest(std::string &error_buffer); - -protected: - BOOL removeChild(LLXMLNode* child); - -public: - std::string 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 - S32 mLineNumber; // line number in source file, if applicable - - LLXMLNode* mParent; // The parent node - LLXMLChildrenPtr 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 - - // The value of this node (use getters/setters only) - // Values are not XML-escaped in memory - // They may contain " (quot) ' (apos) & (amp) < (lt) > (gt) - std::string mValue; - - 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 +/**
+ * @file llxmlnode.h
+ * @brief LLXMLNode definition
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLXMLNODE_H
+#define LL_LLXMLNODE_H
+
+#ifndef XML_STATIC
+#define XML_STATIC
+#endif
+#ifdef LL_USESYSTEMLIBS
+#include <expat.h>
+#else
+#include "expat/expat.h"
+#endif
+#include <map>
+
+#include "indra_constants.h"
+#include "llrefcount.h"
+#include "llpointer.h"
+#include "llstring.h"
+#include "llstringtable.h"
+#include "llfile.h"
+#include "lluuid.h"
+
+class LLVector3;
+class LLVector3d;
+class LLQuaternion;
+class LLColor4;
+class LLColor4U;
+
+
+struct CompareAttributes
+{
+ bool operator()(const LLStringTableEntry* const lhs, const LLStringTableEntry* const rhs) const
+ {
+ if (lhs == NULL)
+ return true;
+ if (rhs == NULL)
+ return true;
+
+ 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<std::string, LLXMLNodePtr > LLXMLNodeList;
+typedef std::multimap<const LLStringTableEntry *, LLXMLNodePtr > LLXMLChildList;
+typedef std::map<const LLStringTableEntry *, LLXMLNodePtr, CompareAttributes> LLXMLAttribList;
+
+class LLColor4;
+class LLColor4U;
+class LLQuaternion;
+class LLVector3;
+class LLVector3d;
+class LLVector4;
+class LLVector4U;
+
+struct LLXMLChildren : public LLThreadSafeRefCount
+{
+ LLXMLChildList map; // Map of children names->pointers
+ LLXMLNodePtr head; // Head of the double-linked list
+ LLXMLNodePtr tail; // Tail of the double-linked list
+};
+typedef LLPointer<LLXMLChildren> LLXMLChildrenPtr;
+
+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:
+ ~LLXMLNode();
+
+public:
+ LLXMLNode();
+ LLXMLNode(const char* name, bool is_attribute);
+ LLXMLNode(LLStringTableEntry* name, bool is_attribute);
+ LLXMLNode(const LLXMLNode& rhs);
+ LLXMLNodePtr deepCopy();
+
+ bool isNull();
+
+ bool deleteChild(LLXMLNode* child);
+ void addChild(LLXMLNodePtr& new_child);
+ void setParent(LLXMLNodePtr& new_parent); // reparent if necessary
+
+ // Serialization
+ static bool parseFile(
+ const std::string& filename,
+ LLXMLNodePtr& node,
+ LLXMLNode* defaults_tree);
+ static bool parseBuffer(
+ U8* buffer,
+ U32 length,
+ LLXMLNodePtr& node,
+ LLXMLNode* defaults);
+ static bool parseStream(
+ std::istream& str,
+ LLXMLNodePtr& node,
+ LLXMLNode* defaults);
+ static bool updateNode(
+ LLXMLNodePtr& node,
+ LLXMLNodePtr& update_node);
+
+ static bool getLayeredXMLNode(LLXMLNodePtr& root, const std::vector<std::string>& paths);
+
+
+ // Write standard XML file header:
+ // <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+ static void writeHeaderToFile(LLFILE *out_file);
+
+ // Write XML to file with one attribute per line.
+ // XML escapes values as they are written.
+ void writeToFile(LLFILE *out_file, const std::string& indent = std::string(), bool use_type_decorations=true);
+ void writeToOstream(std::ostream& output_stream, const std::string& indent = std::string(), bool use_type_decorations=true);
+
+ // Utility
+ void findName(const std::string& name, LLXMLNodeList &results);
+ void findName(LLStringTableEntry* name, LLXMLNodeList &results);
+ void findID(const std::string& id, LLXMLNodeList &results);
+
+
+ virtual LLXMLNodePtr createChild(const char* 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, std::string *array);
+ U32 getUUIDValue(U32 expected_length, LLUUID *array);
+ U32 getNodeRefValue(U32 expected_length, LLXMLNode **array);
+
+ bool hasAttribute(const char* name );
+
+ bool getAttributeBOOL(const char* name, bool& value );
+ bool getAttributeU8(const char* name, U8& value );
+ bool getAttributeS8(const char* name, S8& value );
+ bool getAttributeU16(const char* name, U16& value );
+ bool getAttributeS16(const char* name, S16& value );
+ bool getAttributeU32(const char* name, U32& value );
+ bool getAttributeS32(const char* name, S32& value );
+ bool getAttributeF32(const char* name, F32& value );
+ bool getAttributeF64(const char* name, F64& value );
+ bool getAttributeColor(const char* name, LLColor4& value );
+ bool getAttributeColor4(const char* name, LLColor4& value );
+ bool getAttributeColor4U(const char* name, LLColor4U& value );
+ bool getAttributeVector3(const char* name, LLVector3& value );
+ bool getAttributeVector3d(const char* name, LLVector3d& value );
+ bool getAttributeQuat(const char* name, LLQuaternion& value );
+ bool getAttributeUUID(const char* name, LLUUID& value );
+ bool getAttributeString(const char* name, std::string& value );
+
+ const ValueType& getType() const { return mType; }
+ U32 getLength() const { return mLength; }
+ U32 getPrecision() const { return mPrecision; }
+ const std::string& getValue() const { return mValue; }
+ std::string getSanitizedValue() const;
+ std::string getTextContents() const;
+ const LLStringTableEntry* getName() const { return mName; }
+ bool hasName(const char* name) const { return mName == gStringTable.checkStringEntry(name); }
+ bool hasName(const std::string& name) const { return mName == gStringTable.checkStringEntry(name.c_str()); }
+ const std::string& 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 char* 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 char* name, LLXMLNodeList &children, bool use_default_if_missing = true) const;
+ void getChildren(const LLStringTableEntry* name, LLXMLNodeList &children, bool use_default_if_missing = true) const;
+
+ // recursively finds all children at any level matching name
+ void getDescendants(const LLStringTableEntry* name, LLXMLNodeList &children) const;
+
+ bool getAttribute(const char* name, LLXMLNodePtr& node, bool use_default_if_missing = true);
+ bool getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, bool use_default_if_missing = true);
+
+ S32 getLineNumber();
+
+ // The following skip over attributes
+ LLXMLNodePtr getFirstChild() const;
+ LLXMLNodePtr getNextSibling() const;
+
+ LLXMLNodePtr getRoot();
+
+ // Setters
+
+ bool setAttributeString(const char* attr, const std::string& 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 std::string& 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 std::string *array);
+ void setUUIDValue(U32 length, const LLUUID *array);
+ void setNodeRefValue(U32 length, const LLXMLNode **array);
+ void setValue(const std::string& value);
+ void setName(const std::string& name);
+ void setName(LLStringTableEntry* name);
+
+ void setLineNumber(S32 line_number);
+
+ // Escapes " (quot) ' (apos) & (amp) < (lt) > (gt)
+ static std::string escapeXML(const std::string& 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 std::string& name);
+ bool deleteChildren(LLStringTableEntry* name);
+ void setAttributes(ValueType type, U32 precision, Encoding encoding, U32 length);
+// void appendValue(const std::string& value); // Unused
+
+ // Unit Testing
+ void createUnitTest(S32 max_num_children);
+ bool performUnitTest(std::string &error_buffer);
+
+protected:
+ bool removeChild(LLXMLNode* child);
+
+public:
+ std::string 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
+ S32 mLineNumber; // line number in source file, if applicable
+
+ LLXMLNode* mParent; // The parent node
+ LLXMLChildrenPtr 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
+
+ // The value of this node (use getters/setters only)
+ // Values are not XML-escaped in memory
+ // They may contain " (quot) ' (apos) & (amp) < (lt) > (gt)
+ std::string mValue;
+
+ 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 index ecd75bd410..2cae8a14a7 100644 --- a/indra/llxml/llxmlparser.cpp +++ b/indra/llxml/llxmlparser.cpp @@ -1,416 +1,416 @@ -/** - * @file llxmlparser.cpp - * @brief LLXmlParser implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -// llxmlparser.cpp -// -// copyright 2002, linden research inc - - -#include "linden_common.h" - -#include "llxmlparser.h" -#include "llerror.h" - - -LLXmlParser::LLXmlParser() - : - mParser( NULL ), - mDepth( 0 ) -{ - 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; - - LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */ - if( !file ) - { - mAuxErrorString = llformat( "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 ) - { - mAuxErrorString = llformat( "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 ) - { - mAuxErrorString = llformat( "Error while reading file %s", path.c_str() ); - success = FALSE; - goto exit_label; - } - - if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) ) - { - mAuxErrorString = llformat( "Error while parsing file %s", path.c_str() ); - success = FALSE; - } - -exit_label: - fclose( file ); - } - - - if( success ) - { - llassert( !mDepth ); - } - mDepth = 0; - - if( !success ) - { - LL_WARNS() << mAuxErrorString << LL_ENDL; - } - - 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.c_str(); - } - 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]; - - LLFILE* 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; -} -*/ - +/**
+ * @file llxmlparser.cpp
+ * @brief LLXmlParser implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// llxmlparser.cpp
+//
+// copyright 2002, linden research inc
+
+
+#include "linden_common.h"
+
+#include "llxmlparser.h"
+#include "llerror.h"
+
+
+LLXmlParser::LLXmlParser()
+ :
+ mParser( NULL ),
+ mDepth( 0 )
+{
+ 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;
+
+ LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */
+ if( !file )
+ {
+ mAuxErrorString = llformat( "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 )
+ {
+ mAuxErrorString = llformat( "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 )
+ {
+ mAuxErrorString = llformat( "Error while reading file %s", path.c_str() );
+ success = false;
+ goto exit_label;
+ }
+
+ if( !XML_ParseBuffer(mParser, bytes_read, true ) )
+ {
+ mAuxErrorString = llformat( "Error while parsing file %s", path.c_str() );
+ success = false;
+ }
+
+exit_label:
+ fclose( file );
+ }
+
+
+ if( success )
+ {
+ llassert( !mDepth );
+ }
+ mDepth = 0;
+
+ if( !success )
+ {
+ LL_WARNS() << mAuxErrorString << LL_ENDL;
+ }
+
+ 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.c_str();
+ }
+ 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];
+
+ LLFILE* 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 index 3a8a927350..dfee4b8070 100644 --- a/indra/llxml/llxmlparser.h +++ b/indra/llxml/llxmlparser.h @@ -1,133 +1,133 @@ -/** - * @file llxmlparser.h - * @brief LLXmlParser class definition - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLXMLPARSER_H -#define LL_LLXMLPARSER_H - -#ifndef XML_STATIC -#define XML_STATIC -#endif -#ifdef LL_USESYSTEMLIBS -#include <expat.h> -#else -#include "expat/expat.h" -#endif - -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; - std::string mAuxErrorString; -}; - -#endif // LL_LLXMLPARSER_H +/**
+ * @file llxmlparser.h
+ * @brief LLXmlParser class definition
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLXMLPARSER_H
+#define LL_LLXMLPARSER_H
+
+#ifndef XML_STATIC
+#define XML_STATIC
+#endif
+#ifdef LL_USESYSTEMLIBS
+#include <expat.h>
+#else
+#include "expat/expat.h"
+#endif
+
+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;
+ std::string mAuxErrorString;
+};
+
+#endif // LL_LLXMLPARSER_H
diff --git a/indra/llxml/llxmltree.cpp b/indra/llxml/llxmltree.cpp index baf2e6a951..626bbcabc3 100644 --- a/indra/llxml/llxmltree.cpp +++ b/indra/llxml/llxmltree.cpp @@ -1,694 +1,694 @@ -/** - * @file llxmltree.cpp - * @brief LLXmlTree implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#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(); - LL_WARNS() << "LLXmlTree parse failed. Line " << line_number << ": " << error << LL_ENDL; - } - return success; -} - -void LLXmlTree::dump() -{ - if( mRoot ) - { - dumpNode( mRoot, " " ); - } -} - -void LLXmlTree::dumpNode( LLXmlTreeNode* node, const std::string& prefix ) -{ - node->dump( prefix ); - - std::string 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; - for(LLXmlTreeNode* node : mChildren) - { - delete node; - } - mChildren.clear(); -} - -void LLXmlTreeNode::dump( const std::string& prefix ) -{ - LL_INFOS() << prefix << mName ; - if( !mContents.empty() ) - { - LL_CONT << " contents = \"" << mContents << "\""; - } - attribute_map_t::iterator iter; - for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++) - { - LLStdStringHandle key = iter->first; - const std::string* value = iter->second; - LL_CONT << prefix << " " << key << "=" << (value->empty() ? "NULL" : *value); - } - LL_CONT << LL_ENDL; -} - -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 std::string *newstr = new std::string(value); - mAttributes[canonical_name] = newstr; // insert + copy -} - -LLXmlTreeNode* LLXmlTreeNode::getFirstChild() -{ - mChildrenIter = mChildren.begin(); - return getNextChild(); -} -LLXmlTreeNode* LLXmlTreeNode::getNextChild() -{ - if (mChildrenIter == mChildren.end()) - return 0; - else - return *mChildrenIter++; -} - -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 ); - mChildren.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 std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToBOOL( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeU8(LLStdStringHandle canonical_name, U8& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToU8( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeS8(LLStdStringHandle canonical_name, S8& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToS8( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeS16(LLStdStringHandle canonical_name, S16& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToS16( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeU16(LLStdStringHandle canonical_name, U16& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToU16( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeU32(LLStdStringHandle canonical_name, U32& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToU32( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeS32(LLStdStringHandle canonical_name, S32& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToS32( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeF32(LLStdStringHandle canonical_name, F32& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToF32( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeF64(LLStdStringHandle canonical_name, F64& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s && LLStringUtil::convertToF64( *s, value ); -} - -BOOL LLXmlTreeNode::getFastAttributeColor(LLStdStringHandle canonical_name, LLColor4& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s ? LLColor4::parseColor(*s, &value) : FALSE; -} - -BOOL LLXmlTreeNode::getFastAttributeColor4(LLStdStringHandle canonical_name, LLColor4& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s ? LLColor4::parseColor4(*s, &value) : FALSE; -} - -BOOL LLXmlTreeNode::getFastAttributeColor4U(LLStdStringHandle canonical_name, LLColor4U& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s ? LLColor4U::parseColor4U(*s, &value ) : FALSE; -} - -BOOL LLXmlTreeNode::getFastAttributeVector3(LLStdStringHandle canonical_name, LLVector3& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s ? LLVector3::parseVector3(*s, &value ) : FALSE; -} - -BOOL LLXmlTreeNode::getFastAttributeVector3d(LLStdStringHandle canonical_name, LLVector3d& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s ? LLVector3d::parseVector3d(*s, &value ) : FALSE; -} - -BOOL LLXmlTreeNode::getFastAttributeQuat(LLStdStringHandle canonical_name, LLQuaternion& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s ? LLQuaternion::parseQuat(*s, &value ) : FALSE; -} - -BOOL LLXmlTreeNode::getFastAttributeUUID(LLStdStringHandle canonical_name, LLUUID& value) -{ - const std::string *s = getAttribute( canonical_name ); - return s ? LLUUID::parseUUID(*s, &value ) : FALSE; -} - -BOOL LLXmlTreeNode::getFastAttributeString(LLStdStringHandle canonical_name, std::string& value) -{ - const std::string *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, std::string& 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> - -*/ - -std::string 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 ), - mKeepContents(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 std::string 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 ) - { - LL_INFOS() << tabs() << "startElement " << name << LL_ENDL; - - S32 i = 0; - while( atts[i] && atts[i+1] ) - { - LL_INFOS() << tabs() << "attribute: " << atts[i] << "=" << atts[i+1] << LL_ENDL; - 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 ) - { - LL_INFOS() << tabs() << "endElement " << name << LL_ENDL; - } - - if( !mCurrent->mContents.empty() ) - { - LLStringUtil::trim(mCurrent->mContents); - LLStringUtil::removeCRLF(mCurrent->mContents); - } - - mCurrent = mCurrent->getParent(); -} - -void LLXmlTreeParser::characterData(const char *s, int len) -{ - std::string str; - if (s) str = std::string(s, len); - if( mDump ) - { - LL_INFOS() << tabs() << "CharacterData " << str << LL_ENDL; - } - - if (mKeepContents) - { - mCurrent->appendContents( str ); - } -} - -void LLXmlTreeParser::processingInstruction(const char *target, const char *data) -{ - if( mDump ) - { - LL_INFOS() << tabs() << "processingInstruction " << data << LL_ENDL; - } -} - -void LLXmlTreeParser::comment(const char *data) -{ - if( mDump ) - { - LL_INFOS() << tabs() << "comment " << data << LL_ENDL; - } -} - -void LLXmlTreeParser::startCdataSection() -{ - if( mDump ) - { - LL_INFOS() << tabs() << "startCdataSection" << LL_ENDL; - } -} - -void LLXmlTreeParser::endCdataSection() -{ - if( mDump ) - { - LL_INFOS() << tabs() << "endCdataSection" << LL_ENDL; - } -} - -void LLXmlTreeParser::defaultData(const char *s, int len) -{ - if( mDump ) - { - std::string str; - if (s) str = std::string(s, len); - LL_INFOS() << tabs() << "defaultData " << str << LL_ENDL; - } -} - -void LLXmlTreeParser::unparsedEntityDecl( - const char* entity_name, - const char* base, - const char* system_id, - const char* public_id, - const char* notation_name) -{ - if( mDump ) - { - LL_INFOS() << tabs() << "unparsed entity:" << LL_ENDL; - LL_INFOS() << tabs() << " entityName " << entity_name << LL_ENDL; - LL_INFOS() << tabs() << " base " << base << LL_ENDL; - LL_INFOS() << tabs() << " systemId " << system_id << LL_ENDL; - LL_INFOS() << tabs() << " publicId " << public_id << LL_ENDL; - LL_INFOS() << tabs() << " notationName " << notation_name<< LL_ENDL; - } -} - -void test_llxmltree() -{ - LLXmlTree tree; - BOOL success = tree.parseFile( "test.xml" ); - if( success ) - { - tree.dump(); - } -} - +/**
+ * @file llxmltree.cpp
+ * @brief LLXmlTree implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#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();
+ LL_WARNS() << "LLXmlTree parse failed. Line " << line_number << ": " << error << LL_ENDL;
+ }
+ return success;
+}
+
+void LLXmlTree::dump()
+{
+ if( mRoot )
+ {
+ dumpNode( mRoot, " " );
+ }
+}
+
+void LLXmlTree::dumpNode( LLXmlTreeNode* node, const std::string& prefix )
+{
+ node->dump( prefix );
+
+ std::string 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;
+ for(LLXmlTreeNode* node : mChildren)
+ {
+ delete node;
+ }
+ mChildren.clear();
+}
+
+void LLXmlTreeNode::dump( const std::string& prefix )
+{
+ LL_INFOS() << prefix << mName ;
+ if( !mContents.empty() )
+ {
+ LL_CONT << " contents = \"" << mContents << "\"";
+ }
+ attribute_map_t::iterator iter;
+ for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++)
+ {
+ LLStdStringHandle key = iter->first;
+ const std::string* value = iter->second;
+ LL_CONT << prefix << " " << key << "=" << (value->empty() ? "NULL" : *value);
+ }
+ LL_CONT << LL_ENDL;
+}
+
+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();
+}
+
+void LLXmlTreeNode::addAttribute(const std::string& name, const std::string& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString(name);
+ const std::string *newstr = new std::string(value);
+ mAttributes[canonical_name] = newstr; // insert + copy
+}
+
+LLXmlTreeNode* LLXmlTreeNode::getFirstChild()
+{
+ mChildrenIter = mChildren.begin();
+ return getNextChild();
+}
+LLXmlTreeNode* LLXmlTreeNode::getNextChild()
+{
+ if (mChildrenIter == mChildren.end())
+ return 0;
+ else
+ return *mChildrenIter++;
+}
+
+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 );
+ mChildren.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 std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToBOOL( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeU8(LLStdStringHandle canonical_name, U8& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToU8( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeS8(LLStdStringHandle canonical_name, S8& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToS8( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeS16(LLStdStringHandle canonical_name, S16& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToS16( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeU16(LLStdStringHandle canonical_name, U16& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToU16( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeU32(LLStdStringHandle canonical_name, U32& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToU32( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeS32(LLStdStringHandle canonical_name, S32& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToS32( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeF32(LLStdStringHandle canonical_name, F32& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToF32( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeF64(LLStdStringHandle canonical_name, F64& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s && LLStringUtil::convertToF64( *s, value );
+}
+
+bool LLXmlTreeNode::getFastAttributeColor(LLStdStringHandle canonical_name, LLColor4& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s ? LLColor4::parseColor(*s, &value) : false;
+}
+
+bool LLXmlTreeNode::getFastAttributeColor4(LLStdStringHandle canonical_name, LLColor4& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s ? LLColor4::parseColor4(*s, &value) : false;
+}
+
+bool LLXmlTreeNode::getFastAttributeColor4U(LLStdStringHandle canonical_name, LLColor4U& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s ? LLColor4U::parseColor4U(*s, &value ) : false;
+}
+
+bool LLXmlTreeNode::getFastAttributeVector3(LLStdStringHandle canonical_name, LLVector3& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s ? LLVector3::parseVector3(*s, &value ) : false;
+}
+
+bool LLXmlTreeNode::getFastAttributeVector3d(LLStdStringHandle canonical_name, LLVector3d& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s ? LLVector3d::parseVector3d(*s, &value ) : false;
+}
+
+bool LLXmlTreeNode::getFastAttributeQuat(LLStdStringHandle canonical_name, LLQuaternion& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s ? LLQuaternion::parseQuat(*s, &value ) : false;
+}
+
+bool LLXmlTreeNode::getFastAttributeUUID(LLStdStringHandle canonical_name, LLUUID& value)
+{
+ const std::string *s = getAttribute( canonical_name );
+ return s ? LLUUID::parseUUID(*s, &value ) : false;
+}
+
+bool LLXmlTreeNode::getFastAttributeString(LLStdStringHandle canonical_name, std::string& value)
+{
+ const std::string *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, std::string& 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>
+
+*/
+
+std::string 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 ),
+ mKeepContents(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 std::string 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 )
+ {
+ LL_INFOS() << tabs() << "startElement " << name << LL_ENDL;
+
+ S32 i = 0;
+ while( atts[i] && atts[i+1] )
+ {
+ LL_INFOS() << tabs() << "attribute: " << atts[i] << "=" << atts[i+1] << LL_ENDL;
+ 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 )
+ {
+ LL_INFOS() << tabs() << "endElement " << name << LL_ENDL;
+ }
+
+ if( !mCurrent->mContents.empty() )
+ {
+ LLStringUtil::trim(mCurrent->mContents);
+ LLStringUtil::removeCRLF(mCurrent->mContents);
+ }
+
+ mCurrent = mCurrent->getParent();
+}
+
+void LLXmlTreeParser::characterData(const char *s, int len)
+{
+ std::string str;
+ if (s) str = std::string(s, len);
+ if( mDump )
+ {
+ LL_INFOS() << tabs() << "CharacterData " << str << LL_ENDL;
+ }
+
+ if (mKeepContents)
+ {
+ mCurrent->appendContents( str );
+ }
+}
+
+void LLXmlTreeParser::processingInstruction(const char *target, const char *data)
+{
+ if( mDump )
+ {
+ LL_INFOS() << tabs() << "processingInstruction " << data << LL_ENDL;
+ }
+}
+
+void LLXmlTreeParser::comment(const char *data)
+{
+ if( mDump )
+ {
+ LL_INFOS() << tabs() << "comment " << data << LL_ENDL;
+ }
+}
+
+void LLXmlTreeParser::startCdataSection()
+{
+ if( mDump )
+ {
+ LL_INFOS() << tabs() << "startCdataSection" << LL_ENDL;
+ }
+}
+
+void LLXmlTreeParser::endCdataSection()
+{
+ if( mDump )
+ {
+ LL_INFOS() << tabs() << "endCdataSection" << LL_ENDL;
+ }
+}
+
+void LLXmlTreeParser::defaultData(const char *s, int len)
+{
+ if( mDump )
+ {
+ std::string str;
+ if (s) str = std::string(s, len);
+ LL_INFOS() << tabs() << "defaultData " << str << LL_ENDL;
+ }
+}
+
+void LLXmlTreeParser::unparsedEntityDecl(
+ const char* entity_name,
+ const char* base,
+ const char* system_id,
+ const char* public_id,
+ const char* notation_name)
+{
+ if( mDump )
+ {
+ LL_INFOS() << tabs() << "unparsed entity:" << LL_ENDL;
+ LL_INFOS() << tabs() << " entityName " << entity_name << LL_ENDL;
+ LL_INFOS() << tabs() << " base " << base << LL_ENDL;
+ LL_INFOS() << tabs() << " systemId " << system_id << LL_ENDL;
+ LL_INFOS() << tabs() << " publicId " << public_id << LL_ENDL;
+ LL_INFOS() << tabs() << " notationName " << notation_name<< LL_ENDL;
+ }
+}
+
+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 index 0f7361c554..570edee5ad 100644 --- a/indra/llxml/llxmltree.h +++ b/indra/llxml/llxmltree.h @@ -1,234 +1,234 @@ -/** - * @file llxmltree.h - * @author Aaron Yonas, Richard Nelson - * @brief LLXmlTree class definition - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLXMLTREE_H -#define LL_LLXMLTREE_H - -#include <map> -#include <list> -#include "llstring.h" -#include "llxmlparser.h" -#include "llstringtable.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 std::string& 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, std::string& 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, std::string& value ); - - const std::string& getContents() - { - return mContents; - } - std::string getTextContents(); - - LLXmlTreeNode* getParent() { return mParent; } - LLXmlTreeNode* getFirstChild(); - LLXmlTreeNode* getNextChild(); - S32 getChildCount() { return (S32)mChildren.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 std::string* 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 std::string& prefix ); - -protected: - typedef std::map<LLStdStringHandle, const std::string*> attribute_map_t; - attribute_map_t mAttributes; - -private: - std::string mName; - std::string mContents; - - typedef std::vector<class LLXmlTreeNode *> children_t; - children_t mChildren; - children_t::iterator mChildrenIter; - - 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 LL_INFOS() as it is read. - BOOL mKeepContents; -}; - -#endif // LL_LLXMLTREE_H +/**
+ * @file llxmltree.h
+ * @author Aaron Yonas, Richard Nelson
+ * @brief LLXmlTree class definition
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLXMLTREE_H
+#define LL_LLXMLTREE_H
+
+#include <map>
+#include <list>
+#include "llstring.h"
+#include "llxmlparser.h"
+#include "llstringtable.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 std::string& 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, std::string& 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, std::string& value );
+
+ const std::string& getContents()
+ {
+ return mContents;
+ }
+ std::string getTextContents();
+
+ LLXmlTreeNode* getParent() { return mParent; }
+ LLXmlTreeNode* getFirstChild();
+ LLXmlTreeNode* getNextChild();
+ S32 getChildCount() { return (S32)mChildren.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 std::string* 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 std::string& prefix );
+
+protected:
+ typedef std::map<LLStdStringHandle, const std::string*> attribute_map_t;
+ attribute_map_t mAttributes;
+
+private:
+ std::string mName;
+ std::string mContents;
+
+ typedef std::vector<class LLXmlTreeNode *> children_t;
+ children_t mChildren;
+ children_t::iterator mChildrenIter;
+
+ 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 LL_INFOS() as it is read.
+ bool mKeepContents;
+};
+
+#endif // LL_LLXMLTREE_H
diff --git a/indra/llxml/tests/llcontrol_test.cpp b/indra/llxml/tests/llcontrol_test.cpp index f5f8b285f7..2025111363 100644 --- a/indra/llxml/tests/llcontrol_test.cpp +++ b/indra/llxml/tests/llcontrol_test.cpp @@ -1,154 +1,154 @@ -/** - * @file llcontrol_tut.cpp - * @date February 2008 - * @brief control group unit tests - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llsdserialize.h" -#include "llfile.h" -#include "stringize.h" - -#include "../llcontrol.h" - -#include "../test/lltut.h" -#include <memory> -#include <vector> - -namespace tut -{ - struct control_group - { - std::unique_ptr<LLControlGroup> mCG; - std::string mTestConfigDir; - std::string mTestConfigFile; - std::vector<std::string> mCleanups; - static bool mListenerFired; - control_group() - { - mCG.reset(new LLControlGroup("foo")); - LLUUID random; - random.generate(); - // generate temp dir - mTestConfigDir = STRINGIZE(LLFile::tmpdir() << "llcontrol-test-" << random << "/"); - mTestConfigFile = mTestConfigDir + "settings.xml"; - LLFile::mkdir(mTestConfigDir); - LLSD config; - config["TestSetting"]["Comment"] = "Dummy setting used for testing"; - config["TestSetting"]["Persist"] = 1; - config["TestSetting"]["Type"] = "U32"; - config["TestSetting"]["Value"] = 12; - writeSettingsFile(config); - } - ~control_group() - { - //Remove test files - for (auto filename : mCleanups) - { - LLFile::remove(filename); - } - LLFile::remove(mTestConfigFile); - LLFile::rmdir(mTestConfigDir); - } - void writeSettingsFile(const LLSD& config) - { - llofstream file(mTestConfigFile.c_str()); - if (file.is_open()) - { - LLSDSerialize::toPrettyXML(config, file); - } - file.close(); - } - static bool handleListenerTest() - { - control_group::mListenerFired = true; - return true; - } - }; - - bool control_group::mListenerFired = false; - - typedef test_group<control_group> control_group_test; - typedef control_group_test::object control_group_t; - control_group_test tut_control_group("control_group"); - - //load settings from files - LLSD - template<> template<> - void control_group_t::test<1>() - { - int results = mCG->loadFromFile(mTestConfigFile.c_str()); - ensure("number of settings", (results == 1)); - ensure("value of setting", (mCG->getU32("TestSetting") == 12)); - } - - //save settings to files - template<> template<> - void control_group_t::test<2>() - { - int results = mCG->loadFromFile(mTestConfigFile.c_str()); - mCG->setU32("TestSetting", 13); - ensure_equals("value of changed setting", mCG->getU32("TestSetting"), 13); - LLControlGroup test_cg("foo2"); - std::string temp_test_file = (mTestConfigDir + "setting_llsd_temp.xml"); - mCleanups.push_back(temp_test_file); - mCG->saveToFile(temp_test_file.c_str(), TRUE); - results = test_cg.loadFromFile(temp_test_file.c_str()); - ensure("number of changed settings loaded", (results == 1)); - ensure("value of changed settings loaded", (test_cg.getU32("TestSetting") == 13)); - } - - //priorities - template<> template<> - void control_group_t::test<3>() - { - // Pass default_values = true. This tells loadFromFile() we're loading - // a default settings file that declares variables, rather than a user - // settings file. When loadFromFile() encounters an unrecognized user - // settings variable, it forcibly preserves it (CHOP-962). - int results = mCG->loadFromFile(mTestConfigFile.c_str(), true); - LLControlVariable* control = mCG->getControl("TestSetting"); - LLSD new_value = 13; - control->setValue(new_value, FALSE); - ensure_equals("value of changed setting", mCG->getU32("TestSetting"), 13); - LLControlGroup test_cg("foo3"); - std::string temp_test_file = (mTestConfigDir + "setting_llsd_persist_temp.xml"); - mCleanups.push_back(temp_test_file); - mCG->saveToFile(temp_test_file.c_str(), TRUE); - results = test_cg.loadFromFile(temp_test_file.c_str()); - //If we haven't changed any settings, then we shouldn't have any settings to load - ensure("number of non-persisted changed settings loaded", (results == 0)); - } - - //listeners - template<> template<> - void control_group_t::test<4>() - { - int results = mCG->loadFromFile(mTestConfigFile.c_str()); - ensure("number of settings", (results == 1)); - mCG->getControl("TestSetting")->getSignal()->connect(boost::bind(&this->handleListenerTest)); - mCG->setU32("TestSetting", 13); - ensure("listener fired on changed setting", mListenerFired); - } - -} +/**
+ * @file llcontrol_tut.cpp
+ * @date February 2008
+ * @brief control group unit tests
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llsdserialize.h"
+#include "llfile.h"
+#include "stringize.h"
+
+#include "../llcontrol.h"
+
+#include "../test/lltut.h"
+#include <memory>
+#include <vector>
+
+namespace tut
+{
+ struct control_group
+ {
+ std::unique_ptr<LLControlGroup> mCG;
+ std::string mTestConfigDir;
+ std::string mTestConfigFile;
+ std::vector<std::string> mCleanups;
+ static bool mListenerFired;
+ control_group()
+ {
+ mCG.reset(new LLControlGroup("foo"));
+ LLUUID random;
+ random.generate();
+ // generate temp dir
+ mTestConfigDir = STRINGIZE(LLFile::tmpdir() << "llcontrol-test-" << random << "/");
+ mTestConfigFile = mTestConfigDir + "settings.xml";
+ LLFile::mkdir(mTestConfigDir);
+ LLSD config;
+ config["TestSetting"]["Comment"] = "Dummy setting used for testing";
+ config["TestSetting"]["Persist"] = 1;
+ config["TestSetting"]["Type"] = "U32";
+ config["TestSetting"]["Value"] = 12;
+ writeSettingsFile(config);
+ }
+ ~control_group()
+ {
+ //Remove test files
+ for (auto filename : mCleanups)
+ {
+ LLFile::remove(filename);
+ }
+ LLFile::remove(mTestConfigFile);
+ LLFile::rmdir(mTestConfigDir);
+ }
+ void writeSettingsFile(const LLSD& config)
+ {
+ llofstream file(mTestConfigFile.c_str());
+ if (file.is_open())
+ {
+ LLSDSerialize::toPrettyXML(config, file);
+ }
+ file.close();
+ }
+ static bool handleListenerTest()
+ {
+ control_group::mListenerFired = true;
+ return true;
+ }
+ };
+
+ bool control_group::mListenerFired = false;
+
+ typedef test_group<control_group> control_group_test;
+ typedef control_group_test::object control_group_t;
+ control_group_test tut_control_group("control_group");
+
+ //load settings from files - LLSD
+ template<> template<>
+ void control_group_t::test<1>()
+ {
+ int results = mCG->loadFromFile(mTestConfigFile.c_str());
+ ensure("number of settings", (results == 1));
+ ensure("value of setting", (mCG->getU32("TestSetting") == 12));
+ }
+
+ //save settings to files
+ template<> template<>
+ void control_group_t::test<2>()
+ {
+ int results = mCG->loadFromFile(mTestConfigFile.c_str());
+ mCG->setU32("TestSetting", 13);
+ ensure_equals("value of changed setting", mCG->getU32("TestSetting"), 13);
+ LLControlGroup test_cg("foo2");
+ std::string temp_test_file = (mTestConfigDir + "setting_llsd_temp.xml");
+ mCleanups.push_back(temp_test_file);
+ mCG->saveToFile(temp_test_file.c_str(), true);
+ results = test_cg.loadFromFile(temp_test_file.c_str());
+ ensure("number of changed settings loaded", (results == 1));
+ ensure("value of changed settings loaded", (test_cg.getU32("TestSetting") == 13));
+ }
+
+ //priorities
+ template<> template<>
+ void control_group_t::test<3>()
+ {
+ // Pass default_values = true. This tells loadFromFile() we're loading
+ // a default settings file that declares variables, rather than a user
+ // settings file. When loadFromFile() encounters an unrecognized user
+ // settings variable, it forcibly preserves it (CHOP-962).
+ int results = mCG->loadFromFile(mTestConfigFile.c_str(), true);
+ LLControlVariable* control = mCG->getControl("TestSetting");
+ LLSD new_value = 13;
+ control->setValue(new_value, false);
+ ensure_equals("value of changed setting", mCG->getU32("TestSetting"), 13);
+ LLControlGroup test_cg("foo3");
+ std::string temp_test_file = (mTestConfigDir + "setting_llsd_persist_temp.xml");
+ mCleanups.push_back(temp_test_file);
+ mCG->saveToFile(temp_test_file.c_str(), true);
+ results = test_cg.loadFromFile(temp_test_file.c_str());
+ //If we haven't changed any settings, then we shouldn't have any settings to load
+ ensure("number of non-persisted changed settings loaded", (results == 0));
+ }
+
+ //listeners
+ template<> template<>
+ void control_group_t::test<4>()
+ {
+ int results = mCG->loadFromFile(mTestConfigFile.c_str());
+ ensure("number of settings", (results == 1));
+ mCG->getControl("TestSetting")->getSignal()->connect(boost::bind(&this->handleListenerTest));
+ mCG->setU32("TestSetting", 13);
+ ensure("listener fired on changed setting", mListenerFired);
+ }
+
+}
|