blob: 6bf3db20dcaba1096b2c49be02f52378b1c43e5f [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_METADATA_BASE_TYPE_CONVERSION_H_
#define UI_BASE_METADATA_BASE_TYPE_CONVERSION_H_
#include <stdint.h>
#include <algorithm> // Silence broken lint check
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/component_export.h"
#include "base/containers/fixed_flat_map.h"
#include "base/files/file_path.h"
#include "base/ranges/algorithm.h"
#include "base/ranges/ranges.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/models/menu_separator_types.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/shadow_value.h"
#include "ui/gfx/text_constants.h"
#include "url/gurl.h"
#include "url/third_party/mozilla/url_parse.h"
namespace ui {
namespace metadata {
using ValidStrings = std::vector<std::u16string>;
// Various metadata methods pass types either by value or const ref depending on
// whether the types are "small" (defined as "fundamental, enum, or pointer").
// ArgType<T> gives the appropriate type to use as an argument in such cases.
template <typename T>
using ArgType =
typename std::conditional<std::is_fundamental<T>::value ||
std::is_enum<T>::value ||
std::is_pointer<T>::value ||
(std::is_move_assignable<T>::value &&
std::is_move_constructible<T>::value &&
!std::is_copy_assignable<T>::value &&
!std::is_copy_constructible<T>::value),
T,
const T&>::type;
COMPONENT_EXPORT(UI_BASE_METADATA) extern const char kNoPrefix[];
COMPONENT_EXPORT(UI_BASE_METADATA) extern const char kSkColorPrefix[];
// General Type Conversion Template Functions ---------------------------------
template <bool serializable,
bool read_only = false,
const char* name_prefix = kNoPrefix>
struct BaseTypeConverter {
static constexpr bool is_serializable = serializable;
static constexpr bool is_read_only = read_only;
static bool IsSerializable() { return is_serializable; }
static bool IsReadOnly() { return is_read_only; }
static const char* PropertyNamePrefix() { return name_prefix; }
};
template <typename T>
struct TypeConverter : BaseTypeConverter<std::is_enum<T>::value> {
static std::u16string ToString(ArgType<T> source_value);
static absl::optional<T> FromString(const std::u16string& source_value);
static ValidStrings GetValidStrings();
};
// The following definitions and macros are needed only in cases where a type
// is a mere alias to a POD type AND a specialized type converter is also needed
// to handle different the string conversions different from the existing POD
// type converter. See SkColor below as an example of their use.
// NOTE: This should be a rare occurrence and if possible use a unique type and
// a TypeConverter specialization based on that unique type.
template <typename T, typename K>
struct Uniquifier {
using type = T;
using tag = K;
};
#define MAKE_TYPE_UNIQUE(type_name) \
struct type_name##Tag {}; \
using type_name##Unique = \
::ui::metadata::Uniquifier<type_name, type_name##Tag>
#define _UNIQUE_TYPE_NAME1(type_name) type_name##Unique
#define _UNIQUE_TYPE_NAME2(qualifier, type_name) qualifier::type_name##Unique
#define _GET_TYPE_MACRO(_1, _2, NAME, ...) NAME
#define UNIQUE_TYPE_NAME(name, ...) \
_GET_TYPE_MACRO(name, ##__VA_ARGS__, _UNIQUE_TYPE_NAME2, _UNIQUE_TYPE_NAME1) \
(name, ##__VA_ARGS__)
// Types and macros for generating enum converters ----------------------------
template <typename T>
struct EnumStringsMap;
// *****************************************************************
// * NOTE: The ENUM macros *must* be placed outside any namespace. *
// *****************************************************************
//
// Use this macro only if your enum converters need to be accessible across
// modules. Place this in the header file for the module and use the other macro
// below as described.
//
#define EXPORT_ENUM_CONVERTERS(T, EXPORT) \
template <> \
EXPORT std::u16string ui::metadata::TypeConverter<T>::ToString( \
ui::metadata::ArgType<T> source_value); \
\
template <> \
EXPORT absl::optional<T> ui::metadata::TypeConverter<T>::FromString( \
const std::u16string& str); \
\
template <> \
EXPORT ui::metadata::ValidStrings \
ui::metadata::TypeConverter<T>::GetValidStrings();
// Generate the code to define a enum type to and from std::u16string
// conversions. The first argument is the type T, and the rest of the argument
// should have the enum value and string pairs defined in a format like
// "{enum_value0, string16_value0}, {enum_value1, string16_value1} ...".
// Both enum_values and string16_values need to be compile time constants.
//
#define DEFINE_ENUM_CONVERTERS(T, ...) \
template <> \
struct ui::metadata::EnumStringsMap<T> { \
static_assert(std::is_enum<T>::value, "Error: " #T " is not an enum."); \
\
static const auto& Get() { \
static constexpr auto kMap = \
base::MakeFixedFlatMap<T, base::StringPiece16>({__VA_ARGS__}); \
return kMap; \
} \
}; \
\
template <> \
std::u16string ui::metadata::TypeConverter<T>::ToString( \
ui::metadata::ArgType<T> source_value) { \
const auto& map = EnumStringsMap<T>::Get(); \
auto* it = map.find(source_value); \
return it != map.end() ? std::u16string(it->second) : std::u16string(); \
} \
\
template <> \
absl::optional<T> ui::metadata::TypeConverter<T>::FromString( \
const std::u16string& str) { \
const auto& map = EnumStringsMap<T>::Get(); \
using Pair = base::ranges::range_value_t<decltype(map)>; \
auto* it = base::ranges::find(map, str, &Pair::second); \
return it != map.end() ? absl::make_optional(it->first) : absl::nullopt; \
} \
\
template <> \
ui::metadata::ValidStrings \
ui::metadata::TypeConverter<T>::GetValidStrings() { \
ValidStrings string_values; \
base::ranges::transform( \
EnumStringsMap<T>::Get(), std::back_inserter(string_values), \
[](const auto& pair) { return std::u16string(pair.second); }); \
return string_values; \
}
// String Conversions ---------------------------------------------------------
COMPONENT_EXPORT(UI_BASE_METADATA)
std::u16string PointerToString(const void* pointer_val);
#define DECLARE_CONVERSIONS(T) \
template <> \
struct COMPONENT_EXPORT(UI_BASE_METADATA) \
TypeConverter<T> : BaseTypeConverter<true> { \
static std::u16string ToString(ArgType<T> source_value); \
static absl::optional<T> FromString(const std::u16string& source_value); \
static ValidStrings GetValidStrings() { return {}; } \
};
DECLARE_CONVERSIONS(int8_t)
DECLARE_CONVERSIONS(int16_t)
DECLARE_CONVERSIONS(int32_t)
DECLARE_CONVERSIONS(int64_t)
DECLARE_CONVERSIONS(uint8_t)
DECLARE_CONVERSIONS(uint16_t)
DECLARE_CONVERSIONS(uint32_t)
DECLARE_CONVERSIONS(uint64_t)
DECLARE_CONVERSIONS(float)
DECLARE_CONVERSIONS(double)
DECLARE_CONVERSIONS(const char*)
DECLARE_CONVERSIONS(base::FilePath)
DECLARE_CONVERSIONS(std::u16string)
DECLARE_CONVERSIONS(base::TimeDelta)
DECLARE_CONVERSIONS(gfx::Insets)
DECLARE_CONVERSIONS(gfx::Point)
DECLARE_CONVERSIONS(gfx::PointF)
DECLARE_CONVERSIONS(gfx::Range)
DECLARE_CONVERSIONS(gfx::Rect)
DECLARE_CONVERSIONS(gfx::RectF)
DECLARE_CONVERSIONS(gfx::ShadowValues)
DECLARE_CONVERSIONS(gfx::Size)
DECLARE_CONVERSIONS(gfx::SizeF)
DECLARE_CONVERSIONS(std::string)
DECLARE_CONVERSIONS(url::Component)
#undef DECLARE_CONVERSIONS
template <>
struct COMPONENT_EXPORT(UI_BASE_METADATA) TypeConverter<bool>
: BaseTypeConverter<true> {
static std::u16string ToString(bool source_value);
static absl::optional<bool> FromString(const std::u16string& source_value);
static ValidStrings GetValidStrings();
};
// Special conversions for wrapper types --------------------------------------
COMPONENT_EXPORT(UI_BASE_METADATA) const std::u16string& GetNullOptStr();
template <typename T>
struct TypeConverter<absl::optional<T>>
: BaseTypeConverter<TypeConverter<T>::is_serializable> {
static std::u16string ToString(ArgType<absl::optional<T>> source_value) {
if (!source_value)
return GetNullOptStr();
return TypeConverter<T>::ToString(source_value.value());
}
static absl::optional<absl::optional<T>> FromString(
const std::u16string& source_value) {
if (source_value == GetNullOptStr())
return absl::make_optional<absl::optional<T>>(absl::nullopt);
auto ret = TypeConverter<T>::FromString(source_value);
return ret ? absl::make_optional(ret) : absl::nullopt;
}
static ValidStrings GetValidStrings() { return {}; }
};
// Special Conversions for std:unique_ptr<T> and T* types ----------------------
template <typename T>
struct TypeConverter<std::unique_ptr<T>> : BaseTypeConverter<false, true> {
static std::u16string ToString(const std::unique_ptr<T>& source_value) {
return PointerToString(source_value.get());
}
static std::u16string ToString(const T* source_value) {
return PointerToString(source_value);
}
static absl::optional<std::unique_ptr<T>> FromString(
const std::u16string& source_value) {
DCHECK(false) << "Type converter cannot convert from string.";
return absl::nullopt;
}
static ValidStrings GetValidStrings() { return {}; }
};
template <typename T>
struct TypeConverter<T*> : BaseTypeConverter<false, true> {
static std::u16string ToString(ArgType<T*> source_value) {
return PointerToString(source_value);
}
static absl::optional<T*> FromString(const std::u16string& source_value) {
DCHECK(false) << "Type converter cannot convert from string.";
return absl::nullopt;
}
static ValidStrings GetValidStrings() { return {}; }
};
template <typename T>
struct TypeConverter<std::vector<T>>
: BaseTypeConverter<TypeConverter<T>::is_serializable> {
static std::u16string ToString(ArgType<std::vector<T>> source_value) {
std::vector<std::u16string> serialized;
base::ranges::transform(source_value, std::back_inserter(serialized),
&TypeConverter<T>::ToString);
return u"{" + base::JoinString(serialized, u",") + u"}";
}
static absl::optional<std::vector<T>> FromString(
const std::u16string& source_value) {
if (source_value.empty() || source_value.front() != u'{' ||
source_value.back() != u'}')
return absl::nullopt;
const auto values =
base::SplitString(source_value.substr(1, source_value.length() - 2),
u",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
std::vector<T> output;
for (const auto& value : values) {
auto ret = TypeConverter<T>::FromString(value);
if (!ret)
return absl::nullopt;
output.push_back(*ret);
}
return absl::make_optional(output);
}
static ValidStrings GetValidStrings() { return {}; }
};
MAKE_TYPE_UNIQUE(SkColor);
template <>
struct COMPONENT_EXPORT(UI_BASE_METADATA)
TypeConverter<UNIQUE_TYPE_NAME(SkColor)>
: BaseTypeConverter<true, false, kSkColorPrefix> {
static std::u16string ToString(SkColor source_value);
static absl::optional<SkColor> FromString(const std::u16string& source_value);
static ValidStrings GetValidStrings();
// Parses a string within |start| and |end| for a color string in the forms
// rgb(r, g, b), rgba(r, g, b, a), hsl(h, s%, l%), hsla(h, s%, l%, a),
// 0xXXXXXX, 0xXXXXXXXX, <decimal number>
// Returns the full string in |color| and the position immediately following
// the last token in |next_token|.
// Returns false if the input string cannot be properly parsed. |color| and
// |next_token| will be undefined.
static bool GetNextColor(std::u16string::const_iterator start,
std::u16string::const_iterator end,
std::u16string& color,
std::u16string::const_iterator& next_token);
static bool GetNextColor(std::u16string::const_iterator start,
std::u16string::const_iterator end,
std::u16string& color);
// Same as above, except returns the color string converted into an |SkColor|.
// Returns absl::nullopt if the color string cannot be properly parsed or the
// string cannot be converted into a valid SkColor and |next_token| may be
// undefined.
static absl::optional<SkColor> GetNextColor(
std::u16string::const_iterator start,
std::u16string::const_iterator end,
std::u16string::const_iterator& next_token);
static absl::optional<SkColor> GetNextColor(
std::u16string::const_iterator start,
std::u16string::const_iterator end);
// Converts the four elements of |pieces| beginning at |start_piece| to an
// SkColor by assuming the pieces are split from a string like
// "rgba(r,g,b,a)". Returns nullopt if conversion was unsuccessful.
static absl::optional<SkColor> RgbaPiecesToSkColor(
const std::vector<base::StringPiece16>& pieces,
size_t start_piece);
private:
static absl::optional<SkColor> ParseHexString(
const std::u16string& hex_string);
static absl::optional<SkColor> ParseHslString(
const std::u16string& hsl_string);
static absl::optional<SkColor> ParseRgbString(
const std::u16string& rgb_string);
};
using SkColorConverter = TypeConverter<UNIQUE_TYPE_NAME(SkColor)>;
} // namespace metadata
} // namespace ui
EXPORT_ENUM_CONVERTERS(gfx::HorizontalAlignment,
COMPONENT_EXPORT(UI_BASE_METADATA))
EXPORT_ENUM_CONVERTERS(gfx::VerticalAlignment,
COMPONENT_EXPORT(UI_BASE_METADATA))
EXPORT_ENUM_CONVERTERS(gfx::ElideBehavior, COMPONENT_EXPORT(UI_BASE_METADATA))
EXPORT_ENUM_CONVERTERS(ui::MenuSeparatorType,
COMPONENT_EXPORT(UI_BASE_METADATA))
#endif // UI_BASE_METDATA_BASE_TYPE_CONVERSION_H_