blob: d0ea5c4086304ffb244463ae7a3c5b651833ab8a [file] [log] [blame]
// 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 <limits>
#include <string>
#include <vector>
#include "libipp/ipp_package.h"
namespace {
template <typename Type>
bool LoadValue(const std::vector<Type>& data, size_t index, Type* v) {
if (data.size() > index) {
*v = data[index];
return true;
}
return false;
}
template <typename Type>
bool SaveValue(std::vector<Type>* data,
size_t index,
const Type& v,
bool is_a_set,
ipp::AttrState* state) {
if (data->size() <= index) {
if (is_a_set)
data->resize(index + 1);
else
return false;
}
(*data)[index] = v;
*state = ipp::AttrState::set;
return true;
}
ipp::InternalType CppTypeForUnknownAttribute(ipp::AttrType type) {
switch (type) {
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::kRange;
case ipp::AttrType::name:
case ipp::AttrType::text:
return ipp::InternalType::kStringWithLanguage;
default:
return ipp::InternalType::kString;
}
}
ipp::AttrName ToAttrName(const std::string& s) {
ipp::AttrName r = ipp::AttrName::_unknown;
FromString(s, &r);
return r;
}
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 {
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.size1) + "x" + ToString(v.size2);
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));
}
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;
}
AttrState Attribute::GetState() {
if (this->state_ != AttrState::unset)
return this->state_;
for (size_t i = 0; true; ++i) {
Collection* c = this->GetCollection(i);
if (c == nullptr)
break;
std::vector<Attribute*> attrs = c->GetAllAttributes();
for (auto a : attrs)
if (a->GetState() != AttrState::unset)
return AttrState::set;
}
return AttrState::unset;
}
void Attribute::SetState(AttrState status) {
if (type_ != AttrType::collection) {
state_ = status;
return;
}
// special algorithm for collection
if (status == AttrState::unset) {
state_ = AttrState::unset;
for (size_t i = 0; true; ++i) {
Collection* coll = GetCollection(i);
if (coll == nullptr)
break;
auto attrs = coll->GetAllAttributes();
for (Attribute* a : attrs)
a->SetState(AttrState::unset);
}
} else if (status == AttrState::set) {
// for collections, the field state_ never equals AttrState::set
state_ = AttrState::unset;
} else {
state_ = status;
}
}
ValueAttribute::ValueAttribute(AttrName name,
bool is_a_set,
AttrType type,
InternalType cpp_type)
: Attribute(name, is_a_set, type), cpp_type_(cpp_type) {
const size_t n = (is_a_set_ ? 0 : 1);
switch (cpp_type_) {
case InternalType::kInteger:
new (&(data_.as_int)) std::vector<int>(n);
break;
case InternalType::kString:
new (&(data_.as_string)) std::vector<std::string>(n);
break;
case InternalType::kResolution:
new (&(data_.as_resolution)) std::vector<Resolution>(n);
break;
case InternalType::kRange:
new (&(data_.as_ranges)) std::vector<RangeOfInteger>(n);
break;
case InternalType::kDateTime:
new (&(data_.as_datetime)) std::vector<DateTime>(n);
break;
case InternalType::kStringWithLanguage:
new (&(data_.as_string_with_language)) std::vector<StringWithLanguage>(n);
break;
}
}
ValueAttribute::~ValueAttribute() {
switch (cpp_type_) {
case InternalType::kInteger:
data_.as_int.~vector();
break;
case InternalType::kString:
data_.as_string.~vector();
break;
case InternalType::kResolution:
data_.as_resolution.~vector();
break;
case InternalType::kRange:
data_.as_ranges.~vector();
break;
case InternalType::kDateTime:
data_.as_datetime.~vector();
break;
case InternalType::kStringWithLanguage:
data_.as_string_with_language.~vector();
break;
}
}
size_t ValueAttribute::GetSize() const {
switch (cpp_type_) {
case InternalType::kInteger:
return data_.as_int.size();
case InternalType::kString:
return data_.as_string.size();
case InternalType::kResolution:
return data_.as_resolution.size();
case InternalType::kRange:
return data_.as_ranges.size();
case InternalType::kDateTime:
return data_.as_datetime.size();
case InternalType::kStringWithLanguage:
return data_.as_string_with_language.size();
}
return 0;
}
void ValueAttribute::Resize(size_t new_size) {
if (!is_a_set_)
return;
switch (cpp_type_) {
case InternalType::kInteger:
data_.as_int.resize(new_size);
break;
case InternalType::kString:
data_.as_string.resize(new_size);
break;
case InternalType::kResolution:
data_.as_resolution.resize(new_size);
break;
case InternalType::kRange:
data_.as_ranges.resize(new_size);
break;
case InternalType::kDateTime:
data_.as_datetime.resize(new_size);
break;
case InternalType::kStringWithLanguage:
data_.as_string_with_language.resize(new_size);
break;
}
}
bool ValueAttribute::GetValue(std::string* v, size_t index) const {
if (v == nullptr)
return false;
if (cpp_type_ == InternalType::kString) {
return LoadValue(data_.as_string, index, v);
}
if (cpp_type_ == InternalType::kStringWithLanguage) {
StringWithLanguage s;
if (!LoadValue(data_.as_string_with_language, index, &s))
return false;
*v = s;
return true;
}
if (cpp_type_ == InternalType::kInteger) {
int x;
if (!LoadValue(data_.as_int, index, &x))
return false;
if (type_ == AttrType::enum_ || type_ == AttrType::keyword) {
const std::string x2 = ToString(name_, x);
if (x2.empty())
return false;
*v = x2;
} else if (type_ == AttrType::boolean) {
*v = ToString(static_cast<bool>(x));
} else {
*v = ToString(x);
}
return true;
}
if (cpp_type_ == InternalType::kDateTime) {
DateTime x;
if (!LoadValue(data_.as_datetime, index, &x))
return false;
*v = ToString(x);
return true;
}
if (cpp_type_ == InternalType::kRange) {
RangeOfInteger x;
if (!LoadValue(data_.as_ranges, index, &x))
return false;
*v = ToString(x);
return true;
}
if (cpp_type_ == InternalType::kResolution) {
Resolution x;
if (!LoadValue(data_.as_resolution, index, &x))
return false;
*v = ToString(x);
return true;
}
return false;
}
bool ValueAttribute::GetValue(StringWithLanguage* v, size_t index) const {
if (v == nullptr || cpp_type_ != InternalType::kStringWithLanguage)
return false;
return LoadValue(data_.as_string_with_language, index, v);
}
bool ValueAttribute::GetValue(int* v, size_t index) const {
if (v == nullptr || cpp_type_ != InternalType::kInteger)
return false;
return LoadValue(data_.as_int, index, v);
}
bool ValueAttribute::GetValue(Resolution* v, size_t index) const {
if (v == nullptr || cpp_type_ != InternalType::kResolution)
return false;
return LoadValue(data_.as_resolution, index, v);
}
bool ValueAttribute::GetValue(RangeOfInteger* v, size_t index) const {
if (v == nullptr || cpp_type_ != InternalType::kRange)
return false;
return LoadValue(data_.as_ranges, index, v);
}
bool ValueAttribute::GetValue(DateTime* v, size_t index) const {
if (v == nullptr || cpp_type_ != InternalType::kDateTime)
return false;
return LoadValue(data_.as_datetime, index, v);
}
bool ValueAttribute::SetValue(const std::string& v, size_t index) {
if (cpp_type_ == InternalType::kString) {
return SaveValue(&data_.as_string, index, v, is_a_set_, &state_);
}
if (cpp_type_ == InternalType::kStringWithLanguage) {
StringWithLanguage s(v);
return SaveValue(&data_.as_string_with_language, index, s, is_a_set_,
&state_);
}
if (cpp_type_ == InternalType::kInteger) {
bool result = false;
int x;
if (type_ == AttrType::enum_ || type_ == AttrType::keyword) {
result = FromString(v, name_, &x);
} else if (type_ == AttrType::boolean) {
bool b = false;
result = FromString(v, &b);
x = static_cast<int>(b);
} else {
result = FromString(v, &x);
}
if (result)
result = SaveValue(&data_.as_int, index, x, is_a_set_, &state_);
return result;
}
return false;
}
bool ValueAttribute::SetValue(const StringWithLanguage& v, size_t index) {
if (cpp_type_ == InternalType::kStringWithLanguage)
return SaveValue(&data_.as_string_with_language, index, v, is_a_set_,
&state_);
return false;
}
bool ValueAttribute::SetValue(const int& v, size_t index) {
if (cpp_type_ == InternalType::kInteger)
return SaveValue(&data_.as_int, index, v, is_a_set_, &state_);
return false;
}
bool ValueAttribute::SetValue(const Resolution& v, size_t index) {
if (cpp_type_ == InternalType::kResolution)
return SaveValue(&data_.as_resolution, index, v, is_a_set_, &state_);
return false;
}
bool ValueAttribute::SetValue(const RangeOfInteger& v, size_t index) {
if (cpp_type_ == InternalType::kRange)
return SaveValue(&data_.as_ranges, index, v, is_a_set_, &state_);
return false;
}
bool ValueAttribute::SetValue(const DateTime& v, size_t index) {
if (cpp_type_ == InternalType::kDateTime)
return SaveValue(&data_.as_datetime, index, v, is_a_set_, &state_);
return false;
}
UnknownValueAttribute::UnknownValueAttribute(const std::string& name,
bool is_a_set,
AttrType type)
: ValueAttribute(
ToAttrName(name), is_a_set, type, CppTypeForUnknownAttribute(type)),
str_name_(name) {}
UnknownCollectionAttribute::UnknownCollectionAttribute(const std::string& name,
bool is_a_set)
: Attribute(ToAttrName(name), is_a_set, AttrType::collection),
str_name_(name) {}
void UnknownCollectionAttribute::Resize(size_t new_size) {
if (!IsASet())
return;
while (collections_.size() < new_size)
collections_.push_back(std::make_unique<Collection>());
collections_.resize(new_size);
}
} // namespace ipp