| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "libipp/ipp_attribute.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <limits> |
| #include <string> |
| #include <vector> |
| |
| namespace { |
| |
| ipp::InternalType InternalTypeForUnknownAttribute(ipp::AttrType type) { |
| switch (type) { |
| case ipp::AttrType::collection: |
| return ipp::InternalType::kCollection; |
| case ipp::AttrType::boolean: |
| case ipp::AttrType::integer: |
| case ipp::AttrType::enum_: |
| return ipp::InternalType::kInteger; |
| case ipp::AttrType::dateTime: |
| return ipp::InternalType::kDateTime; |
| case ipp::AttrType::resolution: |
| return ipp::InternalType::kResolution; |
| case ipp::AttrType::rangeOfInteger: |
| return ipp::InternalType::kRangeOfInteger; |
| case ipp::AttrType::name: |
| case ipp::AttrType::text: |
| return ipp::InternalType::kStringWithLanguage; |
| default: |
| return ipp::InternalType::kString; |
| } |
| } |
| |
| std::string UnsignedToString(size_t x) { |
| std::string s; |
| do { |
| s.push_back('0' + (x % 10)); |
| x /= 10; |
| } while (x > 0); |
| std::reverse(s.begin(), s.end()); |
| return s; |
| } |
| } // namespace |
| |
| namespace ipp { |
| |
| namespace { |
| |
| // This struct exposes single static method performing conversion between values |
| // of different types. Returns true if conversion succeeded and false otherwise. |
| // |out_val| cannot be nullptr. |
| template <typename InputType, typename OutputType> |
| struct Converter { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| const InputType& in_val, |
| OutputType* out_val) { |
| return false; |
| } |
| }; |
| template <typename Type> |
| struct Converter<Type, Type> { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| const Type& in_val, |
| Type* out_val) { |
| *out_val = in_val; |
| return true; |
| } |
| }; |
| template <> |
| struct Converter<std::string, std::string> { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| const std::string& in_val, |
| std::string* out_val) { |
| *out_val = in_val; |
| return true; |
| } |
| }; |
| template <typename InputType> |
| struct Converter<InputType, std::string> { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| const InputType& in_val, |
| std::string* out_val) { |
| *out_val = ToString(in_val); |
| return true; |
| } |
| }; |
| template <> |
| struct Converter<int32_t, std::string> { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| int32_t in_val, |
| std::string* out_val) { |
| if (def.ipp_type == AttrType::boolean) { |
| *out_val = ToString(static_cast<bool>(in_val)); |
| } else if (def.ipp_type == AttrType::enum_ || |
| def.ipp_type == AttrType::keyword) { |
| *out_val = ToString(name, in_val); |
| } else if (def.ipp_type == AttrType::integer) { |
| *out_val = ToString(in_val); |
| } else { |
| return false; |
| } |
| return true; |
| } |
| }; |
| template <> |
| struct Converter<std::string, bool> { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| const std::string& in_val, |
| bool* out_val) { |
| return FromString(in_val, out_val); |
| } |
| }; |
| template <> |
| struct Converter<std::string, int32_t> { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| const std::string& in_val, |
| int32_t* out_val) { |
| bool result = false; |
| if (def.ipp_type == AttrType::boolean) { |
| bool out; |
| result = FromString(in_val, &out); |
| if (result) |
| *out_val = out; |
| } else if (def.ipp_type == AttrType::enum_ || |
| def.ipp_type == AttrType::keyword) { |
| int out; |
| result = FromString(in_val, name, &out); |
| if (result) |
| *out_val = out; |
| } else if (def.ipp_type == AttrType::integer) { |
| int out; |
| result = FromString(in_val, &out); |
| if (result) |
| *out_val = out; |
| } |
| return result; |
| } |
| }; |
| template <> |
| struct Converter<std::string, StringWithLanguage> { |
| static bool Convert(AttrName name, |
| const AttrDef& def, |
| const std::string& in_val, |
| StringWithLanguage* out_val) { |
| out_val->language = ""; |
| out_val->value = in_val; |
| return true; |
| } |
| }; |
| |
| // Creates new value for attribute |def| and saves it as void*. |
| template <typename Type> |
| void* CreateValue(const AttrDef& def) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return 0; |
| return new Type(); |
| } |
| template <> |
| void* CreateValue<Collection*>(const AttrDef& def) { |
| return def.constructor(); |
| } |
| |
| // Deletes value saved as void*. |
| template <typename Type> |
| void DeleteValue(void* value) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return; |
| delete reinterpret_cast<Type*>(value); |
| } |
| template <> |
| void DeleteValue<Collection*>(void* value) { |
| delete reinterpret_cast<Collection*>(value); |
| } |
| |
| // Returns pointer to a value stored as void*. |
| template <typename Type> |
| Type* ReadValuePtr(void** value) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return reinterpret_cast<Type*>(value); |
| return reinterpret_cast<Type*>(*value); |
| } |
| |
| // Const version of the template function above. |
| template <typename Type> |
| const Type* ReadValueConstPtr(void* const* value) { |
| if (sizeof(Type) <= sizeof(void*) && alignof(Type) <= alignof(void*)) |
| return reinterpret_cast<const Type*>(value); |
| return reinterpret_cast<Type* const>(*value); |
| } |
| |
| // Resizes vector of values in an attribute |def|. |
| template <typename Type> |
| void ResizeVector(const AttrDef& def, std::vector<Type>* v, size_t new_size) { |
| v->resize(new_size); |
| } |
| template <> |
| void ResizeVector<Collection*>(const AttrDef& def, |
| std::vector<Collection*>* v, |
| size_t new_size) { |
| const size_t old_size = v->size(); |
| for (size_t i = new_size; i < old_size; ++i) |
| delete v->at(i); |
| v->resize(new_size); |
| for (size_t i = old_size; i < new_size; ++i) |
| (*v)[i] = def.constructor(); |
| } |
| |
| // Deletes whole attribute |name|,|def| from |values|. |
| template <typename Type> |
| void DeleteAttrTyped(std::map<AttrName, void*>* values, |
| AttrName name, |
| const AttrDef& def) { |
| auto it = values->find(name); |
| if (it == values->end()) |
| return; |
| if (def.is_a_set) { |
| auto pv = reinterpret_cast<std::vector<Type>*>(it->second); |
| ResizeVector<Type>(def, pv, 0); |
| delete pv; |
| } else { |
| DeleteValue<Type>(it->second); |
| } |
| values->erase(it); |
| } |
| |
| // The same as previous one, just chooses correct template instantiation. |
| void DeleteAttr(std::map<AttrName, void*>* values, |
| AttrName name, |
| const AttrDef& def) { |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| DeleteAttrTyped<int32_t>(values, name, def); |
| break; |
| case InternalType::kString: |
| DeleteAttrTyped<std::string>(values, name, def); |
| break; |
| case InternalType::kResolution: |
| DeleteAttrTyped<Resolution>(values, name, def); |
| break; |
| case InternalType::kRangeOfInteger: |
| DeleteAttrTyped<RangeOfInteger>(values, name, def); |
| break; |
| case InternalType::kDateTime: |
| DeleteAttrTyped<DateTime>(values, name, def); |
| break; |
| case InternalType::kStringWithLanguage: |
| DeleteAttrTyped<StringWithLanguage>(values, name, def); |
| break; |
| case InternalType::kCollection: |
| DeleteAttrTyped<Collection*>(values, name, def); |
| break; |
| } |
| } |
| |
| // Returns a pointer to a value at position |index| in an attribute |name|. |
| // If the attribute is too short, it is resized to (|index|+1) when possible. |
| // When |cut_if_longer| is set, the attribute is shrunk to (|index|+1) values if |
| // it is longer. If |cut_if_longer| equals false, the attribute is no |
| // downsized. Returns nullptr <=> the attribute is too short and cannot be |
| // resized to reach (|index|+1) values. |
| template <typename Type> |
| Type* ResizeAttrGetValuePtr(std::map<AttrName, void*>* values, |
| AttrName name, |
| const AttrDef& def, |
| size_t index, |
| bool cut_if_longer) { |
| if (!def.is_a_set && index > 0) |
| return nullptr; |
| // Get an entry from |values|, add new if not exists. |
| auto it_inserted = values->insert({name, nullptr}); |
| std::map<AttrName, void*>::iterator it = it_inserted.first; |
| if (it_inserted.second) { |
| if (def.is_a_set) { |
| it->second = new std::vector<Type>(); |
| } else { |
| it->second = CreateValue<Type>(def); |
| } |
| } |
| // Returns the pointer, resize the attribute when needed. |
| if (def.is_a_set) { |
| std::vector<Type>* v = reinterpret_cast<std::vector<Type>*>(it->second); |
| if (cut_if_longer || v->size() <= index) |
| ResizeVector<Type>(def, v, index + 1); |
| return (v->data() + index); |
| } else { |
| return ReadValuePtr<Type>(&it->second); |
| } |
| } |
| |
| // Resizes an attribute |name|,|def| to |new_size| values. The parameter |
| // |cut_if_longer| works in the same way as in the previous template function. |
| // Returns false when the attribute is not a set and |new_size| > 1. |
| bool ResizeAttr(std::map<AttrName, void*>* values, |
| AttrName name, |
| const AttrDef& def, |
| size_t new_size, |
| bool cut_if_longer) { |
| if (new_size == 0) { |
| DeleteAttr(values, name, def); |
| return true; |
| } |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| return ResizeAttrGetValuePtr<int32_t>(values, name, def, new_size - 1, |
| cut_if_longer) != nullptr; |
| case InternalType::kString: |
| return ResizeAttrGetValuePtr<std::string>(values, name, def, new_size - 1, |
| cut_if_longer) != nullptr; |
| case InternalType::kResolution: |
| return ResizeAttrGetValuePtr<Resolution>(values, name, def, new_size - 1, |
| cut_if_longer) != nullptr; |
| case InternalType::kRangeOfInteger: |
| return ResizeAttrGetValuePtr<RangeOfInteger>( |
| values, name, def, new_size - 1, cut_if_longer) != nullptr; |
| case InternalType::kDateTime: |
| return ResizeAttrGetValuePtr<DateTime>(values, name, def, new_size - 1, |
| cut_if_longer) != nullptr; |
| case InternalType::kStringWithLanguage: |
| return ResizeAttrGetValuePtr<StringWithLanguage>( |
| values, name, def, new_size - 1, cut_if_longer) != nullptr; |
| case InternalType::kCollection: |
| return ResizeAttrGetValuePtr<Collection*>(values, name, def, new_size - 1, |
| cut_if_longer) != nullptr; |
| } |
| return false; |
| } |
| |
| // Reads a value at position |index| in an attribute |name|,|def| and saves it |
| // to |value|. Proper conversion is applied when needed. The function returns |
| // true if succeeds and false when one of the following occurs: |
| // * |value| is nullptr |
| // * the attribute has less than |index|+1 values |
| // * required conversion is not possible |
| template <typename InternalType, typename ApiType> |
| bool ReadConvertValueTyped(const std::map<AttrName, void*>& values, |
| AttrName name, |
| const AttrDef& def, |
| size_t index, |
| ApiType* value) { |
| if (value == nullptr) |
| return false; |
| auto it = values.find(name); |
| if (it == values.end()) |
| return false; |
| const InternalType* internal_value = nullptr; |
| if (def.is_a_set) { |
| auto v = ReadValueConstPtr<std::vector<InternalType>>(&it->second); |
| if (v->size() <= index) |
| return false; |
| internal_value = v->data() + index; |
| } else { |
| if (index != 0) |
| return false; |
| internal_value = ReadValueConstPtr<InternalType>(&it->second); |
| } |
| return Converter<InternalType, ApiType>::Convert(name, def, *internal_value, |
| value); |
| } |
| |
| // The same as previous one, just chooses correct template instantiation. |
| template <typename ApiType> |
| bool ReadConvertValue(const std::map<AttrName, void*>& values, |
| AttrName name, |
| const AttrDef& def, |
| size_t index, |
| ApiType* value) { |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| return ReadConvertValueTyped<int32_t, ApiType>(values, name, def, index, |
| value); |
| case InternalType::kString: |
| return ReadConvertValueTyped<std::string, ApiType>(values, name, def, |
| index, value); |
| case InternalType::kResolution: |
| return ReadConvertValueTyped<Resolution, ApiType>(values, name, def, |
| index, value); |
| case InternalType::kRangeOfInteger: |
| return ReadConvertValueTyped<RangeOfInteger, ApiType>(values, name, def, |
| index, value); |
| case InternalType::kDateTime: |
| return ReadConvertValueTyped<DateTime, ApiType>(values, name, def, index, |
| value); |
| case InternalType::kStringWithLanguage: |
| return ReadConvertValueTyped<StringWithLanguage, ApiType>( |
| values, name, def, index, value); |
| case InternalType::kCollection: |
| return false; |
| } |
| return false; |
| } |
| |
| // Saves |value| to position |index| in an attribute |name|,|def|. Proper |
| // conversion is applied when needed. The attribute is also resized when |index| |
| // is greater than the attribute's size. The function returns true if succeeds |
| // and false when one of the following occurs: |
| // * the attribute is not a set and |index| > 0 |
| // * required conversion is not possible (|value| is incorrect) |
| template <typename InternalType, typename ApiType> |
| bool SaveValueTyped(std::map<AttrName, void*>* values, |
| AttrName name, |
| const AttrDef& def, |
| size_t index, |
| const ApiType& value) { |
| InternalType internal_value; |
| if (!Converter<ApiType, InternalType>::Convert(name, def, value, |
| &internal_value)) |
| return false; |
| InternalType* internal_ptr = |
| ResizeAttrGetValuePtr<InternalType>(values, name, def, index, false); |
| if (internal_ptr == nullptr) |
| return false; |
| *internal_ptr = internal_value; |
| return true; |
| } |
| |
| } // end of namespace |
| |
| std::string ToString(AttrState s) { |
| switch (s) { |
| case AttrState::unset: |
| return "unset"; |
| case AttrState::set: |
| return "set"; |
| case AttrState::unsupported: |
| return "unsupported"; |
| case AttrState::unknown: |
| return "unknown"; |
| case AttrState::novalue_: |
| return "novalue"; |
| case AttrState::not_settable: |
| return "not-settable"; |
| case AttrState::delete_attribute: |
| return "delete-attribute"; |
| case AttrState::admin_define: |
| return "admin-define"; |
| } |
| return ""; |
| } |
| |
| std::string ToString(AttrType at) { |
| switch (at) { |
| case AttrType::integer: |
| return "integer"; |
| case AttrType::boolean: |
| return "boolean"; |
| case AttrType::enum_: |
| return "enum"; |
| case AttrType::octetString: |
| return "octetString"; |
| case AttrType::dateTime: |
| return "dateTime"; |
| case AttrType::resolution: |
| return "resolution"; |
| case AttrType::rangeOfInteger: |
| return "rangeOfInteger"; |
| case AttrType::collection: |
| return "collection"; |
| case AttrType::text: |
| return "text"; |
| case AttrType::name: |
| return "name"; |
| case AttrType::keyword: |
| return "keyword"; |
| case AttrType::uri: |
| return "uri"; |
| case AttrType::uriScheme: |
| return "uriScheme"; |
| case AttrType::charset: |
| return "charset"; |
| case AttrType::naturalLanguage: |
| return "naturalLanguage"; |
| case AttrType::mimeMediaType: |
| return "mimeMediaType"; |
| } |
| return ""; |
| } |
| |
| std::string ToString(bool v) { |
| return (v ? "true" : "false"); |
| } |
| |
| std::string ToString(int v) { |
| if (v < 0) { |
| // 2 x incrementation in case of (v == numeric_limit<int>::min()) |
| const std::string s = UnsignedToString(static_cast<size_t>(-(++v)) + 1); |
| return "-" + s; |
| } |
| return UnsignedToString(v); |
| } |
| |
| std::string ToString(const Resolution& v) { |
| std::string s = ToString(v.xres) + "x" + ToString(v.yres); |
| if (v.units == Resolution::kDotsPerInch) |
| s += "dpi"; |
| else |
| s += "dpc"; |
| return s; |
| } |
| |
| std::string ToString(const RangeOfInteger& v) { |
| return ("(" + ToString(v.min_value) + ":" + ToString(v.max_value) + ")"); |
| } |
| |
| std::string ToString(const DateTime& v) { |
| return (ToString(v.year) + "-" + ToString(v.month) + "-" + ToString(v.day) + |
| "," + ToString(v.hour) + ":" + ToString(v.minutes) + ":" + |
| ToString(v.seconds) + "." + ToString(v.deci_seconds) + "," + |
| std::string(1, v.UTC_direction) + ToString(v.UTC_hours) + ":" + |
| ToString(v.UTC_minutes)); |
| } |
| |
| std::string ToString(const StringWithLanguage& value) { |
| return value.value; |
| } |
| |
| bool FromString(const std::string& s, bool* v) { |
| if (v == nullptr) |
| return false; |
| if (s == "false") { |
| *v = false; |
| return true; |
| } |
| if (s == "true") { |
| *v = true; |
| return true; |
| } |
| return false; |
| } |
| |
| // JSON-like integer format: first character may be '-', the rest must be |
| // digits. Leading zeroes allowed. |
| bool FromString(const std::string& s, int* out) { |
| if (out == nullptr) |
| return false; |
| if (s.empty()) |
| return false; |
| auto it = s.begin(); |
| int vv = 0; |
| if (*it == '-') { |
| ++it; |
| if (it == s.end()) |
| return false; |
| // negative number |
| for (; it != s.end(); ++it) { |
| if (std::numeric_limits<int>::min() / 10 > vv) |
| return false; |
| vv *= 10; |
| if (*it < '0' || *it > '9') |
| return false; |
| const int d = (*it - '0'); |
| if (std::numeric_limits<int>::min() + d > vv) |
| return false; |
| vv -= d; |
| } |
| } else { |
| // positive number |
| for (; it != s.end(); ++it) { |
| if (std::numeric_limits<int>::max() / 10 < vv) |
| return false; |
| vv *= 10; |
| if (*it < '0' || *it > '9') |
| return false; |
| const int d = (*it - '0'); |
| if (std::numeric_limits<int>::max() - d < vv) |
| return false; |
| vv += d; |
| } |
| } |
| *out = vv; |
| return true; |
| } |
| |
| // Final class for Attribute represents unknown attribute, i.e.: an attribute |
| // defined during runtime. |
| class UnknownAttribute : public Attribute { |
| public: |
| UnknownAttribute(Collection* owner, AttrName name) |
| : Attribute(nullptr, name), owner_(owner) {} |
| Collection* const owner_; |
| }; |
| |
| Collection* Attribute::GetOwner() const { |
| if (offset_ == std::numeric_limits<int16_t>::min()) |
| return static_cast<const UnknownAttribute*>(this)->owner_; |
| return reinterpret_cast<Collection*>(reinterpret_cast<std::intptr_t>(this) - |
| offset_); |
| } |
| |
| AttrType Attribute::GetType() const { |
| return GetOwner()->GetAttributeDefinition(name_).ipp_type; |
| } |
| |
| bool Attribute::IsASet() const { |
| return GetOwner()->GetAttributeDefinition(name_).is_a_set; |
| } |
| |
| AttrState Attribute::GetState() const { |
| Collection* coll = GetOwner(); |
| if (coll->values_.count(name_)) |
| return AttrState::set; |
| auto it = coll->states_.find(name_); |
| if (it != coll->states_.end()) |
| return it->second; |
| return AttrState::unset; |
| } |
| |
| void Attribute::SetState(AttrState status) { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| if (status == AttrState::set) { |
| ResizeAttr(&coll->values_, name_, def, 1, false); |
| return; |
| } |
| DeleteAttr(&coll->values_, name_, def); |
| if (status != AttrState::unset) { |
| coll->states_[name_] = status; |
| } |
| } |
| |
| Attribute::Attribute(Collection* owner, AttrName name) |
| : offset_((owner == nullptr) ? (std::numeric_limits<int16_t>::min()) |
| : (reinterpret_cast<std::intptr_t>(this) - |
| reinterpret_cast<std::intptr_t>(owner))), |
| name_(name) { |
| if (owner != nullptr) |
| assert(GetOwner() == owner); |
| } |
| |
| size_t Attribute::GetSize() const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| auto it = coll->values_.find(name_); |
| if (it == coll->values_.end()) |
| return 0; |
| if (!def.is_a_set) |
| return 1; |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| return ReadValueConstPtr<std::vector<int32_t>>(&it->second)->size(); |
| case InternalType::kString: |
| return ReadValueConstPtr<std::vector<std::string>>(&it->second)->size(); |
| case InternalType::kResolution: |
| return ReadValueConstPtr<std::vector<Resolution>>(&it->second)->size(); |
| case InternalType::kRangeOfInteger: |
| return ReadValueConstPtr<std::vector<RangeOfInteger>>(&it->second) |
| ->size(); |
| case InternalType::kDateTime: |
| return ReadValueConstPtr<std::vector<DateTime>>(&it->second)->size(); |
| case InternalType::kStringWithLanguage: |
| return ReadValueConstPtr<std::vector<StringWithLanguage>>(&it->second) |
| ->size(); |
| case InternalType::kCollection: |
| return ReadValueConstPtr<std::vector<Collection*>>(&it->second)->size(); |
| } |
| return 0; |
| } |
| |
| void Attribute::Resize(size_t new_size) { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| if (ResizeAttr(&coll->values_, name_, def, new_size, true)) { |
| if (new_size > 0) |
| coll->states_.erase(name_); |
| } |
| } |
| |
| bool Attribute::GetValue(std::string* val, size_t index) const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| return ReadConvertValue(coll->values_, name_, def, index, val); |
| } |
| |
| bool Attribute::GetValue(StringWithLanguage* val, size_t index) const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| return ReadConvertValue(coll->values_, name_, def, index, val); |
| } |
| |
| bool Attribute::GetValue(int* val, size_t index) const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| return ReadConvertValue(coll->values_, name_, def, index, val); |
| } |
| |
| bool Attribute::GetValue(Resolution* val, size_t index) const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| return ReadConvertValue(coll->values_, name_, def, index, val); |
| } |
| |
| bool Attribute::GetValue(RangeOfInteger* val, size_t index) const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| return ReadConvertValue(coll->values_, name_, def, index, val); |
| } |
| |
| bool Attribute::GetValue(DateTime* val, size_t index) const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| return ReadConvertValue(coll->values_, name_, def, index, val); |
| } |
| |
| bool Attribute::SetValue(const std::string& val, size_t index) { |
| Collection* coll = GetOwner(); |
| return coll->SaveValue(name_, index, val); |
| } |
| |
| bool Attribute::SetValue(const StringWithLanguage& val, size_t index) { |
| Collection* coll = GetOwner(); |
| return coll->SaveValue(name_, index, val); |
| } |
| |
| bool Attribute::SetValue(const int& val, size_t index) { |
| Collection* coll = GetOwner(); |
| return coll->SaveValue(name_, index, val); |
| } |
| |
| bool Attribute::SetValue(const Resolution& val, size_t index) { |
| Collection* coll = GetOwner(); |
| return coll->SaveValue(name_, index, val); |
| } |
| |
| bool Attribute::SetValue(const RangeOfInteger& val, size_t index) { |
| Collection* coll = GetOwner(); |
| return coll->SaveValue(name_, index, val); |
| } |
| |
| bool Attribute::SetValue(const DateTime& val, size_t index) { |
| Collection* coll = GetOwner(); |
| return coll->SaveValue(name_, index, val); |
| } |
| |
| Collection* Attribute::GetCollection(size_t index) { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| if (def.cc_type != InternalType::kCollection) |
| return nullptr; |
| auto it = coll->values_.find(name_); |
| if (it == coll->values_.end()) |
| return nullptr; |
| Collection* p = nullptr; |
| if (def.is_a_set) { |
| auto v = ReadValuePtr<std::vector<Collection*>>(&it->second); |
| if (v->size() > index) |
| p = *(v->data() + index); |
| } else { |
| p = *(ReadValuePtr<Collection*>(&it->second)); |
| } |
| return p; |
| } |
| |
| const Collection* Attribute::GetCollection(size_t index) const { |
| Collection* coll = GetOwner(); |
| const AttrDef def = coll->GetAttributeDefinition(name_); |
| if (def.cc_type != InternalType::kCollection) |
| return nullptr; |
| auto it = coll->values_.find(name_); |
| if (it == coll->values_.end()) |
| return nullptr; |
| const Collection* p = nullptr; |
| if (def.is_a_set) { |
| auto v = ReadValueConstPtr<std::vector<Collection*>>(&it->second); |
| if (v->size() > index) |
| p = *(v->data() + index); |
| } else { |
| p = *(ReadValueConstPtr<Collection*>(&it->second)); |
| } |
| return p; |
| } |
| |
| Collection::~Collection() { |
| std::vector<AttrName> names; |
| for (auto& name_value : values_) |
| names.push_back(name_value.first); |
| for (auto& name : names) { |
| const AttrDef def = GetAttributeDefinition(name); |
| DeleteAttr(&values_, name, def); |
| } |
| for (auto& name_attr : unknown_attributes) { |
| delete static_cast<UnknownAttribute*>(name_attr.second.object); |
| } |
| } |
| |
| AttrDef Collection::GetAttributeDefinition(AttrName name) const { |
| auto it2 = unknown_attributes.find(name); |
| if (it2 != unknown_attributes.end()) |
| return it2->second.def; |
| auto it = definitions_->find(name); |
| if (it != definitions_->end()) |
| return it->second; |
| return {AttrType::integer, InternalType::kInteger, false}; |
| } |
| |
| Attribute* Collection::GetAttribute(AttrName an) { |
| const std::vector<Attribute*> known_attr = GetKnownAttributes(); |
| for (auto a : known_attr) |
| if (a->GetNameAsEnum() == an) |
| return a; |
| auto it2 = unknown_attributes.find(an); |
| if (it2 != unknown_attributes.end()) |
| return it2->second.object; |
| return nullptr; |
| } |
| |
| const Attribute* Collection::GetAttribute(AttrName an) const { |
| const std::vector<const Attribute*> known_attr = GetKnownAttributes(); |
| for (auto a : known_attr) |
| if (a->GetNameAsEnum() == an) |
| return a; |
| auto it2 = unknown_attributes.find(an); |
| if (it2 != unknown_attributes.end()) |
| return it2->second.object; |
| return nullptr; |
| } |
| |
| Attribute* Collection::GetAttribute(const std::string& name) { |
| AttrName an = AttrName::_unknown; |
| if (!FromString(name, &an)) { |
| for (const auto& e : unknown_names) { |
| if (e.second == name) { |
| an = e.first; |
| break; |
| } |
| } |
| } |
| return GetAttribute(an); |
| } |
| |
| const Attribute* Collection::GetAttribute(const std::string& name) const { |
| AttrName an = AttrName::_unknown; |
| if (!FromString(name, &an)) { |
| for (const auto& e : unknown_names) { |
| if (e.second == name) { |
| an = e.first; |
| break; |
| } |
| } |
| } |
| return GetAttribute(an); |
| } |
| |
| Attribute* Collection::AddUnknownAttribute(const std::string& name, |
| bool is_a_set, |
| AttrType type) { |
| // name cannot be empty |
| if (name.empty()) |
| return nullptr; |
| // type must be correct |
| if (ToString(type) == "") |
| return nullptr; |
| // |
| AttrName an = AttrName::_unknown; |
| if (!FromString(name, &an)) { |
| for (const auto& e : unknown_names) { |
| if (e.second == name) |
| return nullptr; |
| } |
| if (unknown_names.empty()) { |
| an = static_cast<AttrName>(std::numeric_limits<uint16_t>::max()); |
| } else { |
| an = static_cast<AttrName>( |
| static_cast<uint16_t>(unknown_names.begin()->first) - 1); |
| } |
| unknown_names[an] = name; |
| } else if (GetAttribute(an) != nullptr) { |
| return nullptr; |
| } |
| unknown_attributes[an].def.ipp_type = type; |
| unknown_attributes[an].def.is_a_set = is_a_set; |
| unknown_attributes[an].def.cc_type = InternalTypeForUnknownAttribute(type); |
| unknown_attributes[an].object = new UnknownAttribute(this, an); |
| if (type == AttrType::collection) { |
| unknown_attributes[an].def.constructor = []() -> Collection* { |
| return new EmptyCollection(); |
| }; |
| } |
| return unknown_attributes[an].object; |
| } |
| |
| std::vector<Attribute*> Collection::GetAllAttributes() { |
| const std::vector<Attribute*> known_attr = GetKnownAttributes(); |
| std::vector<Attribute*> v; |
| v.reserve(known_attr.size() + unknown_attributes.size()); |
| v.insert(v.end(), known_attr.begin(), known_attr.end()); |
| for (const auto& e : unknown_attributes) |
| v.push_back(e.second.object); |
| return v; |
| } |
| |
| std::vector<const Attribute*> Collection::GetAllAttributes() const { |
| const std::vector<const Attribute*> known_attr = GetKnownAttributes(); |
| std::vector<const Attribute*> v; |
| v.reserve(known_attr.size() + unknown_attributes.size()); |
| v.insert(v.end(), known_attr.begin(), known_attr.end()); |
| for (const auto& e : unknown_attributes) |
| v.push_back(e.second.object); |
| return v; |
| } |
| |
| // Saves |value| to attribute |name| at position |index| in collection |coll|. |
| // Proper conversion is applied when needed. The attribute is also resized when |
| // |index| is greater than the attribute's size. |coll| cannot be nullptr. The |
| // function returns true if succeeds and false when one of the following occurs: |
| // * the attribute is not a set and |index| > 0 |
| // * required conversion is not possible (|value| is incorrect) |
| template <typename ApiType> |
| bool Collection::SaveValue(AttrName name, size_t index, const ApiType& value) { |
| const AttrDef def = GetAttributeDefinition(name); |
| bool result = false; |
| switch (def.cc_type) { |
| case InternalType::kInteger: |
| result = |
| SaveValueTyped<int32_t, ApiType>(&values_, name, def, index, value); |
| break; |
| case InternalType::kString: |
| result = SaveValueTyped<std::string, ApiType>(&values_, name, def, index, |
| value); |
| break; |
| case InternalType::kResolution: |
| result = SaveValueTyped<Resolution, ApiType>(&values_, name, def, index, |
| value); |
| break; |
| case InternalType::kRangeOfInteger: |
| result = SaveValueTyped<RangeOfInteger, ApiType>(&values_, name, def, |
| index, value); |
| break; |
| case InternalType::kDateTime: |
| result = |
| SaveValueTyped<DateTime, ApiType>(&values_, name, def, index, value); |
| break; |
| case InternalType::kStringWithLanguage: |
| result = SaveValueTyped<StringWithLanguage, ApiType>(&values_, name, def, |
| index, value); |
| break; |
| case InternalType::kCollection: |
| return false; |
| } |
| if (result) |
| states_.erase(name); |
| return result; |
| } |
| |
| const std::map<AttrName, AttrDef> EmptyCollection::defs_; |
| |
| } // namespace ipp |