Reimplemented the code that backed the "settings" in lldb. There were many issues with the previous implementation:
- no setting auto completion
- very manual and error prone way of getting/setting variables
- tons of code duplication
- useless instance names for processes, threads

Now settings can easily be defined like option values. The new settings makes use of the "OptionValue" classes so we can re-use the option value code that we use to set settings in command options. No more instances, just "does the right thing".



git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@162366 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Interpreter/OptionValueDictionary.cpp b/source/Interpreter/OptionValueDictionary.cpp
new file mode 100644
index 0000000..e291f7f
--- /dev/null
+++ b/source/Interpreter/OptionValueDictionary.cpp
@@ -0,0 +1,434 @@
+//===-- OptionValueDictionary.cpp -------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueDictionary.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "llvm/ADT/StringRef.h"
+// Project includes
+#include "lldb/Core/FormatManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Interpreter/Args.h"
+#include "lldb/Interpreter/OptionValueString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void
+OptionValueDictionary::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask)
+{
+    const Type dict_type = ConvertTypeMaskToType (m_type_mask);
+    if (dump_mask & eDumpOptionType)
+    {
+        if (m_type_mask != eTypeInvalid)
+            strm.Printf ("(%s of %ss)", GetTypeAsCString(), GetBuiltinTypeAsCString(dict_type));
+        else
+            strm.Printf ("(%s)", GetTypeAsCString());
+    }
+    if (dump_mask & eDumpOptionValue)
+    {
+        if (dump_mask & eDumpOptionType)
+            strm.PutCString (" =");
+
+        collection::iterator pos, end = m_values.end();
+
+        strm.IndentMore();
+        
+        for (pos = m_values.begin(); pos != end; ++pos)
+        {
+            OptionValue *option_value = pos->second.get();
+            strm.EOL();
+            strm.Indent(pos->first.GetCString());
+            
+            const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
+            switch (dict_type)
+            {
+                default:
+                case eTypeArray:
+                case eTypeDictionary:
+                case eTypeProperties:
+                case eTypeFileSpecList:
+                case eTypePathMap:
+                    strm.PutChar (' ');
+                    option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
+                    break;
+
+                case eTypeBoolean:
+                case eTypeEnum:
+                case eTypeFileSpec:
+                case eTypeFormat:
+                case eTypeSInt64:
+                case eTypeString:
+                case eTypeUInt64:
+                case eTypeUUID:
+                    // No need to show the type for dictionaries of simple items
+                    strm.PutCString("=");
+                    option_value->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | extra_dump_options);
+                    break;
+            }
+        }
+        strm.IndentLess();
+    }
+
+}
+
+size_t
+OptionValueDictionary::GetArgs (Args &args) const
+{
+    args.Clear();
+    collection::const_iterator pos, end = m_values.end();
+    for (pos = m_values.begin(); pos != end; ++pos)
+    {
+        StreamString strm;
+        strm.Printf("%s=", pos->first.GetCString());
+        pos->second->DumpValue(NULL, strm, eDumpOptionValue|eDumpOptionRaw);
+        args.AppendArgument(strm.GetString().c_str());
+    }
+    return args.GetArgumentCount();
+}
+
+Error
+OptionValueDictionary::SetArgs (const Args &args, VarSetOperationType op)
+{
+    Error error;
+    const size_t argc = args.GetArgumentCount();
+    switch (op)
+    {
+    case eVarSetOperationClear:
+        Clear();
+        break;
+        
+    case eVarSetOperationAppend:
+    case eVarSetOperationReplace:
+    case eVarSetOperationAssign:
+        if (argc > 0)
+        {
+            for (size_t i=0; i<argc; ++i)
+            {
+                llvm::StringRef key_and_value(args.GetArgumentAtIndex(i));
+                if (!key_and_value.empty())
+                {
+                    std::pair<llvm::StringRef, llvm::StringRef> kvp(key_and_value.split('='));
+                    llvm::StringRef key = kvp.first;
+                    bool key_valid = false;
+                    if (!key.empty())
+                    {
+                        if (key.front() == '[')
+                        {
+                            // Key name starts with '[', so the the key value must be in single or double quotes like:
+                            // ['<key>']
+                            // ["<key>"]
+                            if ((key.size() > 2) && (key.back() == ']'))
+                            {
+                                // Strip leading '[' and trailing ']'
+                                key = key.substr(1, key.size()-2);
+                                const char quote_char = key.front();
+                                if ((quote_char == '\'') || (quote_char == '"'))
+                                {
+                                    if ((key.size() > 2) && (key.back() == quote_char))
+                                    {
+                                        // Strip the quotes
+                                        key = key.substr(1, key.size()-2);
+                                        key_valid = true;
+                                    }
+                                }
+                                else
+                                {
+                                    // square brackets, no quotes
+                                    key_valid = true;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            // No square brackets or quotes
+                            key_valid = true;
+                        }
+                    }
+                    if (!key_valid)
+                    {
+                        error.SetErrorStringWithFormat("invalid key \"%s\", the key must be a bare string or surrounded by brackets with optional quotes: [<key>] or ['<key>'] or [\"<key>\"]", kvp.first.str().c_str());
+                        return error;
+                    }
+
+                    lldb::OptionValueSP value_sp (CreateValueFromCStringForTypeMask (kvp.second.data(),
+                                                                                     m_type_mask,
+                                                                                     error));
+                    if (value_sp)
+                    {
+                        if (error.Fail())
+                            return error;
+                        m_value_was_set = true;
+                        SetValueForKey (ConstString(key), value_sp, true);
+                    }
+                    else
+                    {
+                        error.SetErrorString("dictionaries that can contain multiple types must subclass OptionValueArray");
+                    }
+                }
+                else
+                {
+                    error.SetErrorString("empty argument");
+                }
+            }
+        }
+        else
+        {
+            error.SetErrorString("assign operation takes one or more key=value arguments");
+        }
+        break;
+        
+    case eVarSetOperationRemove:
+        if (argc > 0)
+        {
+            for (size_t i=0; i<argc; ++i)
+            {
+                ConstString key(args.GetArgumentAtIndex(i));
+                if (!DeleteValueForKey(key))
+                {
+                    error.SetErrorStringWithFormat("no value found named '%s', aborting remove operation", key.GetCString());
+                    break;
+                }
+            }
+        }
+        else
+        {
+            error.SetErrorString("remove operation takes one or more key arguments");
+        }
+        break;
+        
+    case eVarSetOperationInsertBefore:
+    case eVarSetOperationInsertAfter:
+    case eVarSetOperationInvalid:
+        error = OptionValue::SetValueFromCString (NULL, op);
+        break;
+    }
+    return error;
+}
+
+Error
+OptionValueDictionary::SetValueFromCString (const char *value_cstr, VarSetOperationType op)
+{
+    Args args(value_cstr);
+    return SetArgs (args, op);
+}
+
+lldb::OptionValueSP
+OptionValueDictionary::GetSubValue (const ExecutionContext *exe_ctx, const char *name, bool will_modify, Error &error) const
+{
+    lldb::OptionValueSP value_sp;
+
+    if (name && name[0])
+    {
+        const char *sub_name = NULL;
+        ConstString key;
+        const char *open_bracket = ::strchr (name, '[');
+
+        if (open_bracket)
+        {
+            const char *key_start = open_bracket + 1;
+            const char *key_end = NULL;
+            switch (open_bracket[1])
+            {
+                case '\'':
+                    ++key_start;
+                    key_end = strchr(key_start, '\'');
+                    if (key_end)
+                    {
+                        if (key_end[1] == ']')
+                        {
+                            if (key_end[2])
+                                sub_name = key_end + 2;
+                        }
+                        else
+                        {
+                            error.SetErrorStringWithFormat ("invalid value path '%s', single quoted key names must be formatted as ['<key>'] where <key> is a string that doesn't contain quotes", name);
+                            return value_sp;
+                        }
+                    }
+                    else
+                    {
+                        error.SetErrorString ("missing '] key name terminator, key name started with ['");
+                        return value_sp;
+                    }
+                    break;
+                case '"':
+                    ++key_start;
+                    key_end = strchr(key_start, '"');
+                    if (key_end)
+                    {
+                        if (key_end[1] == ']')
+                        {
+                            if (key_end[2])
+                                sub_name = key_end + 2;
+                            break;
+                        }
+                        error.SetErrorStringWithFormat ("invalid value path '%s', double quoted key names must be formatted as [\"<key>\"] where <key> is a string that doesn't contain quotes", name);
+                        return value_sp;
+                    }
+                    else
+                    {
+                        error.SetErrorString ("missing \"] key name terminator, key name started with [\"");
+                        return value_sp;
+                    }
+                    break;
+
+                default:
+                    key_end = strchr(key_start, ']');
+                    if (key_end)
+                    {
+                        if (key_end[1])
+                            sub_name = key_end + 1;
+                    }
+                    else
+                    {
+                        error.SetErrorString ("missing ] key name terminator, key name started with [");
+                        return value_sp;
+                    }
+                    break;
+            }
+            
+            if (key_start && key_end)
+            {
+                key.SetCStringWithLength (key_start, key_end - key_start);
+        
+                value_sp = GetValueForKey (key);
+                if (value_sp)
+                {
+                    if (sub_name)
+                        return value_sp->GetSubValue (exe_ctx, sub_name, will_modify, error);
+                }
+                else
+                {
+                    error.SetErrorStringWithFormat("dictionary does not contain a value for the key name '%s'", key.GetCString());
+                }
+            }
+        }
+        if (!value_sp && error.AsCString() == NULL)
+        {
+            error.SetErrorStringWithFormat ("invalid value path '%s', %s values only support '[<key>]' subvalues where <key> a string value optionally delimitted by single or double quotes",
+                                            name,
+                                            GetTypeAsCString());
+        }
+    }
+    return value_sp;
+}
+
+Error
+OptionValueDictionary::SetSubValue (const ExecutionContext *exe_ctx, VarSetOperationType op, const char *name, const char *value)
+{
+    Error error;
+    const bool will_modify = true;
+    lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, name, will_modify, error));
+    if (value_sp)
+        error = value_sp->SetValueFromCString(value, op);
+    else
+    {
+        if (error.AsCString() == NULL)
+            error.SetErrorStringWithFormat("invalid value path '%s'", name);
+    }
+    return error;
+}
+
+
+lldb::OptionValueSP
+OptionValueDictionary::GetValueForKey (const ConstString &key) const
+{
+    lldb::OptionValueSP value_sp;
+    collection::const_iterator pos = m_values.find (key);
+    if (pos != m_values.end())
+        value_sp = pos->second;
+    return value_sp;
+}
+
+const char *
+OptionValueDictionary::GetStringValueForKey (const ConstString &key)
+{
+    collection::const_iterator pos = m_values.find (key);
+    if (pos != m_values.end())
+    {
+        OptionValueString *string_value = pos->second->GetAsString();
+        if (string_value)
+            return string_value->GetCurrentValue();
+    }
+    return NULL;
+}
+
+
+bool
+OptionValueDictionary::SetStringValueForKey (const ConstString &key, 
+                                             const char *value, 
+                                             bool can_replace)
+{
+    collection::const_iterator pos = m_values.find (key);
+    if (pos != m_values.end())
+    {
+        if (!can_replace)
+            return false;
+        if (pos->second->GetType() == OptionValue::eTypeString)
+        {
+            pos->second->SetValueFromCString(value);
+            return true;
+        }
+    }
+    m_values[key] = OptionValueSP (new OptionValueString (value));
+    return true;
+
+}
+
+bool
+OptionValueDictionary::SetValueForKey (const ConstString &key, 
+                                       const lldb::OptionValueSP &value_sp, 
+                                       bool can_replace)
+{
+    // Make sure the value_sp object is allowed to contain
+    // values of the type passed in...
+    if (value_sp && (m_type_mask & value_sp->GetTypeAsMask()))
+    {
+        if (!can_replace)
+        {
+            collection::const_iterator pos = m_values.find (key);
+            if (pos != m_values.end())
+                return false;
+        }
+        m_values[key] = value_sp;
+        return true;
+    }
+    return false;
+}
+
+bool
+OptionValueDictionary::DeleteValueForKey (const ConstString &key)
+{
+    collection::iterator pos = m_values.find (key);
+    if (pos != m_values.end())
+    {
+        m_values.erase(pos);
+        return true;
+    }
+    return false;
+}
+
+lldb::OptionValueSP
+OptionValueDictionary::DeepCopy () const
+{
+    OptionValueDictionary *copied_dict = new OptionValueDictionary (m_type_mask, m_raw_value_dump);
+    lldb::OptionValueSP copied_value_sp(copied_dict);
+    collection::const_iterator pos, end = m_values.end();
+    for (pos = m_values.begin(); pos != end; ++pos)
+    {
+        StreamString strm;
+        strm.Printf("%s=", pos->first.GetCString());
+        copied_dict->SetValueForKey (pos->first, pos->second->DeepCopy(), true);
+    }
+    return copied_value_sp;
+}
+