| //===-- OptionValue.cpp ---------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Interpreter/OptionValue.h" | |
| #include "lldb/Interpreter/OptionValues.h" | |
| #include "lldb/Utility/StringList.h" | |
| #include <memory> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| OptionValue::OptionValue(const OptionValue &other) { | |
| std::lock_guard<std::mutex> lock(other.m_mutex); | |
| m_parent_wp = other.m_parent_wp; | |
| m_callback = other.m_callback; | |
| m_value_was_set = other.m_value_was_set; | |
| } | |
| OptionValue &OptionValue::operator=(const OptionValue &other) { | |
| std::scoped_lock<std::mutex, std::mutex> lock(m_mutex, other.m_mutex); | |
| m_parent_wp = other.m_parent_wp; | |
| m_callback = other.m_callback; | |
| m_value_was_set = other.m_value_was_set; | |
| return *this; | |
| } | |
| Status OptionValue::SetSubValue(const ExecutionContext *exe_ctx, | |
| VarSetOperationType op, llvm::StringRef name, | |
| llvm::StringRef value) { | |
| return Status::FromErrorString("SetSubValue is not supported"); | |
| } | |
| OptionValueBoolean *OptionValue::GetAsBoolean() { | |
| if (GetType() == OptionValue::eTypeBoolean) | |
| return static_cast<OptionValueBoolean *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueBoolean *OptionValue::GetAsBoolean() const { | |
| if (GetType() == OptionValue::eTypeBoolean) | |
| return static_cast<const OptionValueBoolean *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueChar *OptionValue::GetAsChar() const { | |
| if (GetType() == OptionValue::eTypeChar) | |
| return static_cast<const OptionValueChar *>(this); | |
| return nullptr; | |
| } | |
| OptionValueChar *OptionValue::GetAsChar() { | |
| if (GetType() == OptionValue::eTypeChar) | |
| return static_cast<OptionValueChar *>(this); | |
| return nullptr; | |
| } | |
| OptionValueFileSpec *OptionValue::GetAsFileSpec() { | |
| if (GetType() == OptionValue::eTypeFileSpec) | |
| return static_cast<OptionValueFileSpec *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueFileSpec *OptionValue::GetAsFileSpec() const { | |
| if (GetType() == OptionValue::eTypeFileSpec) | |
| return static_cast<const OptionValueFileSpec *>(this); | |
| return nullptr; | |
| } | |
| OptionValueFileSpecList *OptionValue::GetAsFileSpecList() { | |
| if (GetType() == OptionValue::eTypeFileSpecList) | |
| return static_cast<OptionValueFileSpecList *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueFileSpecList *OptionValue::GetAsFileSpecList() const { | |
| if (GetType() == OptionValue::eTypeFileSpecList) | |
| return static_cast<const OptionValueFileSpecList *>(this); | |
| return nullptr; | |
| } | |
| OptionValueArch *OptionValue::GetAsArch() { | |
| if (GetType() == OptionValue::eTypeArch) | |
| return static_cast<OptionValueArch *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueArch *OptionValue::GetAsArch() const { | |
| if (GetType() == OptionValue::eTypeArch) | |
| return static_cast<const OptionValueArch *>(this); | |
| return nullptr; | |
| } | |
| OptionValueArray *OptionValue::GetAsArray() { | |
| if (GetType() == OptionValue::eTypeArray) | |
| return static_cast<OptionValueArray *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueArray *OptionValue::GetAsArray() const { | |
| if (GetType() == OptionValue::eTypeArray) | |
| return static_cast<const OptionValueArray *>(this); | |
| return nullptr; | |
| } | |
| OptionValueArgs *OptionValue::GetAsArgs() { | |
| if (GetType() == OptionValue::eTypeArgs) | |
| return static_cast<OptionValueArgs *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueArgs *OptionValue::GetAsArgs() const { | |
| if (GetType() == OptionValue::eTypeArgs) | |
| return static_cast<const OptionValueArgs *>(this); | |
| return nullptr; | |
| } | |
| OptionValueDictionary *OptionValue::GetAsDictionary() { | |
| if (GetType() == OptionValue::eTypeDictionary) | |
| return static_cast<OptionValueDictionary *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueDictionary *OptionValue::GetAsDictionary() const { | |
| if (GetType() == OptionValue::eTypeDictionary) | |
| return static_cast<const OptionValueDictionary *>(this); | |
| return nullptr; | |
| } | |
| OptionValueEnumeration *OptionValue::GetAsEnumeration() { | |
| if (GetType() == OptionValue::eTypeEnum) | |
| return static_cast<OptionValueEnumeration *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueEnumeration *OptionValue::GetAsEnumeration() const { | |
| if (GetType() == OptionValue::eTypeEnum) | |
| return static_cast<const OptionValueEnumeration *>(this); | |
| return nullptr; | |
| } | |
| OptionValueFormat *OptionValue::GetAsFormat() { | |
| if (GetType() == OptionValue::eTypeFormat) | |
| return static_cast<OptionValueFormat *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueFormat *OptionValue::GetAsFormat() const { | |
| if (GetType() == OptionValue::eTypeFormat) | |
| return static_cast<const OptionValueFormat *>(this); | |
| return nullptr; | |
| } | |
| OptionValueLanguage *OptionValue::GetAsLanguage() { | |
| if (GetType() == OptionValue::eTypeLanguage) | |
| return static_cast<OptionValueLanguage *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueLanguage *OptionValue::GetAsLanguage() const { | |
| if (GetType() == OptionValue::eTypeLanguage) | |
| return static_cast<const OptionValueLanguage *>(this); | |
| return nullptr; | |
| } | |
| OptionValueFormatEntity *OptionValue::GetAsFormatEntity() { | |
| if (GetType() == OptionValue::eTypeFormatEntity) | |
| return static_cast<OptionValueFormatEntity *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueFormatEntity *OptionValue::GetAsFormatEntity() const { | |
| if (GetType() == OptionValue::eTypeFormatEntity) | |
| return static_cast<const OptionValueFormatEntity *>(this); | |
| return nullptr; | |
| } | |
| OptionValuePathMappings *OptionValue::GetAsPathMappings() { | |
| if (GetType() == OptionValue::eTypePathMap) | |
| return static_cast<OptionValuePathMappings *>(this); | |
| return nullptr; | |
| } | |
| const OptionValuePathMappings *OptionValue::GetAsPathMappings() const { | |
| if (GetType() == OptionValue::eTypePathMap) | |
| return static_cast<const OptionValuePathMappings *>(this); | |
| return nullptr; | |
| } | |
| OptionValueProperties *OptionValue::GetAsProperties() { | |
| if (GetType() == OptionValue::eTypeProperties) | |
| return static_cast<OptionValueProperties *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueProperties *OptionValue::GetAsProperties() const { | |
| if (GetType() == OptionValue::eTypeProperties) | |
| return static_cast<const OptionValueProperties *>(this); | |
| return nullptr; | |
| } | |
| OptionValueRegex *OptionValue::GetAsRegex() { | |
| if (GetType() == OptionValue::eTypeRegex) | |
| return static_cast<OptionValueRegex *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueRegex *OptionValue::GetAsRegex() const { | |
| if (GetType() == OptionValue::eTypeRegex) | |
| return static_cast<const OptionValueRegex *>(this); | |
| return nullptr; | |
| } | |
| OptionValueSInt64 *OptionValue::GetAsSInt64() { | |
| if (GetType() == OptionValue::eTypeSInt64) | |
| return static_cast<OptionValueSInt64 *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueSInt64 *OptionValue::GetAsSInt64() const { | |
| if (GetType() == OptionValue::eTypeSInt64) | |
| return static_cast<const OptionValueSInt64 *>(this); | |
| return nullptr; | |
| } | |
| OptionValueString *OptionValue::GetAsString() { | |
| if (GetType() == OptionValue::eTypeString) | |
| return static_cast<OptionValueString *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueString *OptionValue::GetAsString() const { | |
| if (GetType() == OptionValue::eTypeString) | |
| return static_cast<const OptionValueString *>(this); | |
| return nullptr; | |
| } | |
| OptionValueUInt64 *OptionValue::GetAsUInt64() { | |
| if (GetType() == OptionValue::eTypeUInt64) | |
| return static_cast<OptionValueUInt64 *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueUInt64 *OptionValue::GetAsUInt64() const { | |
| if (GetType() == OptionValue::eTypeUInt64) | |
| return static_cast<const OptionValueUInt64 *>(this); | |
| return nullptr; | |
| } | |
| OptionValueUUID *OptionValue::GetAsUUID() { | |
| if (GetType() == OptionValue::eTypeUUID) | |
| return static_cast<OptionValueUUID *>(this); | |
| return nullptr; | |
| } | |
| const OptionValueUUID *OptionValue::GetAsUUID() const { | |
| if (GetType() == OptionValue::eTypeUUID) | |
| return static_cast<const OptionValueUUID *>(this); | |
| return nullptr; | |
| } | |
| std::optional<bool> OptionValue::GetBooleanValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueBoolean *option_value = GetAsBoolean()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetBooleanValue(bool new_value) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueBoolean *option_value = GetAsBoolean()) { | |
| option_value->SetCurrentValue(new_value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<char> OptionValue::GetCharValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueChar *option_value = GetAsChar()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetCharValue(char new_value) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueChar *option_value = GetAsChar()) { | |
| option_value->SetCurrentValue(new_value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<int64_t> OptionValue::GetEnumerationValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueEnumeration *option_value = GetAsEnumeration()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetEnumerationValue(int64_t value) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueEnumeration *option_value = GetAsEnumeration()) { | |
| option_value->SetCurrentValue(value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<FileSpec> OptionValue::GetFileSpecValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueFileSpec *option_value = GetAsFileSpec()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetFileSpecValue(FileSpec file_spec) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueFileSpec *option_value = GetAsFileSpec()) { | |
| option_value->SetCurrentValue(file_spec, false); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool OptionValue::AppendFileSpecValue(FileSpec file_spec) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueFileSpecList *option_value = GetAsFileSpecList()) { | |
| option_value->AppendCurrentValue(file_spec); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<FileSpecList> OptionValue::GetFileSpecListValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueFileSpecList *option_value = GetAsFileSpecList()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| std::optional<lldb::Format> OptionValue::GetFormatValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueFormat *option_value = GetAsFormat()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetFormatValue(lldb::Format new_value) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueFormat *option_value = GetAsFormat()) { | |
| option_value->SetCurrentValue(new_value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<lldb::LanguageType> OptionValue::GetLanguageValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueLanguage *option_value = GetAsLanguage()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetLanguageValue(lldb::LanguageType new_language) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueLanguage *option_value = GetAsLanguage()) { | |
| option_value->SetCurrentValue(new_language); | |
| return true; | |
| } | |
| return false; | |
| } | |
| FormatEntity::Entry OptionValue::GetFormatEntityValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueFormatEntity *option_value = GetAsFormatEntity()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| const RegularExpression *OptionValue::GetRegexValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueRegex *option_value = GetAsRegex()) | |
| return option_value->GetCurrentValue(); | |
| return nullptr; | |
| } | |
| std::optional<int64_t> OptionValue::GetSInt64Value() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueSInt64 *option_value = GetAsSInt64()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetSInt64Value(int64_t new_value) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueSInt64 *option_value = GetAsSInt64()) { | |
| option_value->SetCurrentValue(new_value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<llvm::StringRef> OptionValue::GetStringValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueString *option_value = GetAsString()) | |
| return option_value->GetCurrentValueAsRef(); | |
| return {}; | |
| } | |
| bool OptionValue::SetStringValue(llvm::StringRef new_value) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueString *option_value = GetAsString()) { | |
| option_value->SetCurrentValue(new_value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<uint64_t> OptionValue::GetUInt64Value() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueUInt64 *option_value = GetAsUInt64()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetUInt64Value(uint64_t new_value) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueUInt64 *option_value = GetAsUInt64()) { | |
| option_value->SetCurrentValue(new_value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<UUID> OptionValue::GetUUIDValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueUUID *option_value = GetAsUUID()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetUUIDValue(const UUID &uuid) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueUUID *option_value = GetAsUUID()) { | |
| option_value->SetCurrentValue(uuid); | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<ArchSpec> OptionValue::GetArchSpecValue() const { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (const OptionValueArch *option_value = GetAsArch()) | |
| return option_value->GetCurrentValue(); | |
| return {}; | |
| } | |
| bool OptionValue::SetArchSpecValue(ArchSpec arch_spec) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueArch *option_value = GetAsArch()) { | |
| option_value->SetCurrentValue(arch_spec, false); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool OptionValue::SetFormatEntityValue(const FormatEntity::Entry &entry) { | |
| std::lock_guard<std::mutex> lock(m_mutex); | |
| if (OptionValueFormatEntity *option_value = GetAsFormatEntity()) { | |
| option_value->SetCurrentValue(entry); | |
| return true; | |
| } | |
| return false; | |
| } | |
| const char *OptionValue::GetBuiltinTypeAsCString(Type t) { | |
| switch (t) { | |
| case eTypeInvalid: | |
| return "invalid"; | |
| case eTypeArch: | |
| return "arch"; | |
| case eTypeArgs: | |
| return "arguments"; | |
| case eTypeArray: | |
| return "array"; | |
| case eTypeBoolean: | |
| return "boolean"; | |
| case eTypeChar: | |
| return "char"; | |
| case eTypeDictionary: | |
| return "dictionary"; | |
| case eTypeEnum: | |
| return "enum"; | |
| case eTypeFileLineColumn: | |
| return "file:line:column specifier"; | |
| case eTypeFileSpec: | |
| return "file"; | |
| case eTypeFileSpecList: | |
| return "file-list"; | |
| case eTypeFormat: | |
| return "format"; | |
| case eTypeFormatEntity: | |
| return "format-string"; | |
| case eTypeLanguage: | |
| return "language"; | |
| case eTypePathMap: | |
| return "path-map"; | |
| case eTypeProperties: | |
| return "properties"; | |
| case eTypeRegex: | |
| return "regex"; | |
| case eTypeSInt64: | |
| return "int"; | |
| case eTypeString: | |
| return "string"; | |
| case eTypeUInt64: | |
| return "unsigned"; | |
| case eTypeUUID: | |
| return "uuid"; | |
| } | |
| return nullptr; | |
| } | |
| lldb::OptionValueSP OptionValue::CreateValueFromCStringForTypeMask( | |
| const char *value_cstr, uint32_t type_mask, Status &error) { | |
| // If only 1 bit is set in the type mask for a dictionary or array then we | |
| // know how to decode a value from a cstring | |
| lldb::OptionValueSP value_sp; | |
| switch (type_mask) { | |
| case 1u << eTypeArch: | |
| value_sp = std::make_shared<OptionValueArch>(); | |
| break; | |
| case 1u << eTypeBoolean: | |
| value_sp = std::make_shared<OptionValueBoolean>(false); | |
| break; | |
| case 1u << eTypeChar: | |
| value_sp = std::make_shared<OptionValueChar>('\0'); | |
| break; | |
| case 1u << eTypeFileSpec: | |
| value_sp = std::make_shared<OptionValueFileSpec>(); | |
| break; | |
| case 1u << eTypeFormat: | |
| value_sp = std::make_shared<OptionValueFormat>(eFormatInvalid); | |
| break; | |
| case 1u << eTypeFormatEntity: | |
| value_sp = std::make_shared<OptionValueFormatEntity>(nullptr); | |
| break; | |
| case 1u << eTypeLanguage: | |
| value_sp = std::make_shared<OptionValueLanguage>(eLanguageTypeUnknown); | |
| break; | |
| case 1u << eTypeSInt64: | |
| value_sp = std::make_shared<OptionValueSInt64>(); | |
| break; | |
| case 1u << eTypeString: | |
| value_sp = std::make_shared<OptionValueString>(); | |
| break; | |
| case 1u << eTypeUInt64: | |
| value_sp = std::make_shared<OptionValueUInt64>(); | |
| break; | |
| case 1u << eTypeUUID: | |
| value_sp = std::make_shared<OptionValueUUID>(); | |
| break; | |
| } | |
| if (value_sp) | |
| error = value_sp->SetValueFromString(value_cstr, eVarSetOperationAssign); | |
| else | |
| error = Status::FromErrorString("unsupported type mask"); | |
| return value_sp; | |
| } | |
| bool OptionValue::DumpQualifiedName( | |
| Stream &strm, std::optional<Stream::HighlightSettings> highlight) const { | |
| bool dumped_something = false; | |
| lldb::OptionValueSP m_parent_sp(m_parent_wp.lock()); | |
| if (m_parent_sp) { | |
| if (m_parent_sp->DumpQualifiedName(strm, highlight)) | |
| dumped_something = true; | |
| } | |
| llvm::StringRef name(GetName()); | |
| if (!name.empty()) { | |
| if (dumped_something) | |
| strm.PutChar('.'); | |
| else | |
| dumped_something = true; | |
| strm.PutCStringColorHighlighted(name, highlight); | |
| } | |
| return dumped_something; | |
| } | |
| OptionValueSP OptionValue::DeepCopy(const OptionValueSP &new_parent) const { | |
| auto clone = Clone(); | |
| clone->SetParent(new_parent); | |
| return clone; | |
| } | |
| void OptionValue::AutoComplete(CommandInterpreter &interpreter, | |
| CompletionRequest &request) {} | |
| Status OptionValue::SetValueFromString(llvm::StringRef value, | |
| VarSetOperationType op) { | |
| Status error; | |
| switch (op) { | |
| case eVarSetOperationReplace: | |
| error = Status::FromErrorStringWithFormat( | |
| "%s objects do not support the 'replace' operation", | |
| GetTypeAsCString()); | |
| break; | |
| case eVarSetOperationInsertBefore: | |
| error = Status::FromErrorStringWithFormat( | |
| "%s objects do not support the 'insert-before' operation", | |
| GetTypeAsCString()); | |
| break; | |
| case eVarSetOperationInsertAfter: | |
| error = Status::FromErrorStringWithFormat( | |
| "%s objects do not support the 'insert-after' operation", | |
| GetTypeAsCString()); | |
| break; | |
| case eVarSetOperationRemove: | |
| error = Status::FromErrorStringWithFormat( | |
| "%s objects do not support the 'remove' operation", GetTypeAsCString()); | |
| break; | |
| case eVarSetOperationAppend: | |
| error = Status::FromErrorStringWithFormat( | |
| "%s objects do not support the 'append' operation", GetTypeAsCString()); | |
| break; | |
| case eVarSetOperationClear: | |
| error = Status::FromErrorStringWithFormat( | |
| "%s objects do not support the 'clear' operation", GetTypeAsCString()); | |
| break; | |
| case eVarSetOperationAssign: | |
| error = Status::FromErrorStringWithFormat( | |
| "%s objects do not support the 'assign' operation", GetTypeAsCString()); | |
| break; | |
| case eVarSetOperationInvalid: | |
| error = Status::FromErrorStringWithFormat( | |
| "invalid operation performed on a %s object", GetTypeAsCString()); | |
| break; | |
| } | |
| return error; | |
| } |