ICU 76.1  76.1
messageformat2_formattable.h
1 // © 2024 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #ifndef MESSAGEFORMAT2_FORMATTABLE_H
7 #define MESSAGEFORMAT2_FORMATTABLE_H
8 
9 #if U_SHOW_CPLUSPLUS_API
10 
11 #if !UCONFIG_NO_FORMATTING
12 
13 #if !UCONFIG_NO_MF2
14 
15 #include "unicode/chariter.h"
17 #include "unicode/messageformat2_data_model_names.h"
18 
19 #ifndef U_HIDE_DEPRECATED_API
20 
21 #include <map>
22 #include <variant>
23 
24 U_NAMESPACE_BEGIN
25 
26 class Hashtable;
27 class UVector;
28 
29 namespace message2 {
30 
31  class Formatter;
32  class MessageContext;
33  class Selector;
34 
35  // Formattable
36  // ----------
37 
47  public:
57  virtual const UnicodeString& tag() const = 0;
64  virtual ~FormattableObject();
65  }; // class FormattableObject
66 
67  class Formattable;
68 } // namespace message2
69 
70 U_NAMESPACE_END
71 
73 // Export an explicit template instantiation of the std::variant that is used
74 // to represent the message2::Formattable class.
75 // (When building DLLs for Windows this is required.)
76 // (See measunit_impl.h, datefmt.h, collationiterator.h, erarules.h and others
77 // for similar examples.)
78 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
79 #if defined(U_REAL_MSVC) && defined(_MSVC_STL_VERSION)
80 template class U_I18N_API std::_Variant_storage_<false,
81  double,
82  int64_t,
86  std::pair<const icu::message2::Formattable *,int32_t>>;
87 #endif
88 typedef std::pair<const icu::message2::Formattable*, int32_t> P;
89 template class U_I18N_API std::variant<double,
90  int64_t,
94  P>;
95 #endif
97 
98 U_NAMESPACE_BEGIN
99 
100 namespace message2 {
116  class U_I18N_API Formattable : public UObject {
117  public:
118 
126 
136  double getDouble(UErrorCode& status) const {
137  if (U_SUCCESS(status)) {
138  if (isDecimal() && getType() == UFMT_DOUBLE) {
139  return (std::get_if<icu::Formattable>(&contents))->getDouble();
140  }
141  if (std::holds_alternative<double>(contents)) {
142  return *(std::get_if<double>(&contents));
143  }
144  status = U_ILLEGAL_ARGUMENT_ERROR;
145  }
146  return 0;
147  }
148 
158  int32_t getLong(UErrorCode& status) const {
159  if (U_SUCCESS(status)) {
160  if (isDecimal() && getType() == UFMT_LONG) {
161  return std::get_if<icu::Formattable>(&contents)->getLong();
162  }
163  if (std::holds_alternative<int64_t>(contents)) {
164  return static_cast<int32_t>(*(std::get_if<int64_t>(&contents)));
165  }
166  status = U_ILLEGAL_ARGUMENT_ERROR;
167  }
168  return 0;
169  }
170 
181  int64_t getInt64Value(UErrorCode& status) const {
182  if (U_SUCCESS(status)) {
183  if (isDecimal() && getType() == UFMT_INT64) {
184  return std::get_if<icu::Formattable>(&contents)->getInt64();
185  }
186  if (std::holds_alternative<int64_t>(contents)) {
187  return *(std::get_if<int64_t>(&contents));
188  }
189  status = U_ILLEGAL_ARGUMENT_ERROR;
190  }
191  return 0;
192  }
193 
208  int64_t getInt64(UErrorCode& status) const;
218  const UnicodeString& getString(UErrorCode& status) const {
219  if (U_SUCCESS(status)) {
220  if (std::holds_alternative<UnicodeString>(contents)) {
221  return *std::get_if<UnicodeString>(&contents);
222  }
223  status = U_ILLEGAL_ARGUMENT_ERROR;
224  }
225  return bogusString;
226  }
227 
237  UDate getDate(UErrorCode& status) const {
238  if (U_SUCCESS(status)) {
239  if (isDate()) {
240  return *std::get_if<double>(&contents);
241  }
242  status = U_ILLEGAL_ARGUMENT_ERROR;
243  }
244  return 0;
245  }
246 
254  UBool isNumeric() const { return (getType() == UFMT_DOUBLE || getType() == UFMT_LONG || getType() == UFMT_INT64); }
255 
266  const Formattable* getArray(int32_t& count, UErrorCode& status) const;
267 
278  const FormattableObject* getObject(UErrorCode& status) const {
279  if (U_SUCCESS(status)) {
280  // Can't return a reference since FormattableObject
281  // is an abstract class
282  if (getType() == UFMT_OBJECT) {
283  return *std::get_if<const FormattableObject*>(&contents);
284  // TODO: should assert that if type is object, object is non-null
285  }
286  status = U_ILLEGAL_ARGUMENT_ERROR;
287  }
288  return nullptr;
289  }
298  friend inline void swap(Formattable& f1, Formattable& f2) noexcept {
299  using std::swap;
300 
301  swap(f1.contents, f2.contents);
302  swap(f1.holdsDate, f2.holdsDate);
303  }
325  Formattable() : contents(0.0) {}
334  Formattable(const UnicodeString& s) : contents(s) {}
343  Formattable(double d) : contents(d) {}
352  Formattable(int64_t i) : contents(i) {}
361  Formattable f;
362  f.contents = d;
363  f.holdsDate = true;
364  return f;
365  }
380  static Formattable forDecimal(std::string_view number, UErrorCode& status);
390  Formattable(const Formattable* arr, int32_t len) : contents(std::pair(arr, len)) {}
399  Formattable(const FormattableObject* obj) : contents(obj) {}
406  virtual ~Formattable();
419  private:
420 
421  std::variant<double,
422  int64_t,
424  icu::Formattable, // represents a Decimal
425  const FormattableObject*,
426  std::pair<const Formattable*, int32_t>> contents;
427  bool holdsDate = false; // otherwise, we get type errors about UDate being a duplicate type
428  UnicodeString bogusString; // :((((
429 
430  UBool isDecimal() const {
431  return std::holds_alternative<icu::Formattable>(contents);
432  }
433  UBool isDate() const {
434  return std::holds_alternative<double>(contents) && holdsDate;
435  }
436  }; // class Formattable
437 
451 #ifndef U_IN_DOXYGEN
452 class U_I18N_API ResolvedFunctionOption : public UObject {
453  private:
454 
455  /* const */ UnicodeString name;
456  /* const */ Formattable value;
457 
458  public:
459  const UnicodeString& getName() const { return name; }
460  const Formattable& getValue() const { return value; }
461  ResolvedFunctionOption(const UnicodeString& n, const Formattable& f) : name(n), value(f) {}
462  ResolvedFunctionOption() {}
463  ResolvedFunctionOption(ResolvedFunctionOption&&);
464  ResolvedFunctionOption& operator=(ResolvedFunctionOption&& other) noexcept {
465  name = std::move(other.name);
466  value = std::move(other.value);
467  return *this;
468  }
469  virtual ~ResolvedFunctionOption();
470 }; // class ResolvedFunctionOption
471 #endif
472 
480 using FunctionOptionsMap = std::map<UnicodeString, message2::Formattable>;
481 
489  public:
503  FunctionOptionsMap getOptions() const {
504  int32_t len;
505  const ResolvedFunctionOption* resolvedOptions = getResolvedFunctionOptions(len);
506  FunctionOptionsMap result;
507  for (int32_t i = 0; i < len; i++) {
508  const ResolvedFunctionOption& opt = resolvedOptions[i];
509  result[opt.getName()] = opt.getValue();
510  }
511  return result;
512  }
520  FunctionOptions() { options = nullptr; }
527  virtual ~FunctionOptions();
550  FunctionOptions& operator=(const FunctionOptions&) = delete;
551  private:
552  friend class MessageFormatter;
553  friend class StandardFunctions;
554 
555  explicit FunctionOptions(UVector&&, UErrorCode&);
556 
557  const ResolvedFunctionOption* getResolvedFunctionOptions(int32_t& len) const;
558  UBool getFunctionOption(const UnicodeString&, Formattable&) const;
559  // Returns empty string if option doesn't exist
560  UnicodeString getStringFunctionOption(const UnicodeString&) const;
561  int32_t optionsCount() const { return functionOptionsLen; }
562 
563  // Named options passed to functions
564  // This is not a Hashtable in order to make it possible for code in a public header file
565  // to construct a std::map from it, on-the-fly. Otherwise, it would be impossible to put
566  // that code in the header because it would have to call internal Hashtable methods.
567  ResolvedFunctionOption* options;
568  int32_t functionOptionsLen = 0;
569 }; // class FunctionOptions
570 
571 
572  // TODO doc comments
573  // Encapsulates either a formatted string or formatted number;
574  // more output types could be added in the future.
575 
587  public:
593  explicit FormattedValue(const UnicodeString&);
606  FormattedValue() : type(kString) {}
615  bool isString() const { return type == kString; }
624  bool isNumber() const { return type == kNumber; }
632  const UnicodeString& getString() const { return stringOutput; }
640  const number::FormattedNumber& getNumber() const { return numberOutput; }
656  FormattedValue(FormattedValue&& other) { *this = std::move(other); }
663  virtual ~FormattedValue();
664  private:
665  enum Type {
666  kString,
667  kNumber
668  };
669  Type type;
670  UnicodeString stringOutput;
671  number::FormattedNumber numberOutput;
672  }; // class FormattedValue
673 
687  public:
698  explicit FormattedPlaceholder(const UnicodeString& s) : fallback(s), type(kFallback) {}
711  : fallback(input.fallback), source(input.source),
712  formatted(std::move(output)), previousOptions(FunctionOptions()), type(kEvaluated) {}
726  : fallback(input.fallback), source(input.source),
727  formatted(std::move(output)), previousOptions(std::move(opts)), type(kEvaluated) {}
738  : fallback(fb), source(input), type(kUnevaluated) {}
746  FormattedPlaceholder() : type(kNull) {}
766  bool isFallback() const { return type == kFallback; }
776  bool isNullOperand() const { return type == kNull; }
786  bool isEvaluated() const { return (type == kEvaluated); }
795  bool canFormat() const { return !(isFallback() || isNullOperand()); }
803  const UnicodeString& getFallback() const { return fallback; }
812  const FunctionOptions& options() const { return previousOptions; }
813 
820  const FormattedValue& output() const { return formatted; }
836  FormattedPlaceholder(FormattedPlaceholder&& other) { *this = std::move(other); }
853  UErrorCode& status) const;
854 
855  private:
856  friend class MessageFormatter;
857 
858  enum Type {
859  kFallback, // Represents the result of formatting that encountered an error
860  kNull, // Represents the absence of both an output and an input (not necessarily an error)
861  kUnevaluated, // `source` should be valid, but there's no result yet
862  kEvaluated, // `formatted` exists
863  };
864  UnicodeString fallback;
865  Formattable source;
866  FormattedValue formatted;
867  FunctionOptions previousOptions; // Ignored unless type is kEvaluated
868  Type type;
869  }; // class FormattedPlaceholder
870 
882  public:
890  if (U_SUCCESS(status)) {
891  status = U_UNSUPPORTED_ERROR;
892  }
893  }
900  int32_t length(UErrorCode& status) const {
901  if (U_SUCCESS(status)) {
902  status = U_UNSUPPORTED_ERROR;
903  }
904  return -1;
905  }
912  char16_t charAt(int32_t index, UErrorCode& status) const {
913  (void) index;
914  if (U_SUCCESS(status)) {
915  status = U_UNSUPPORTED_ERROR;
916  }
917  return 0;
918  }
925  StringPiece subSequence(int32_t start, int32_t end, UErrorCode& status) const {
926  (void) start;
927  (void) end;
928  if (U_SUCCESS(status)) {
929  status = U_UNSUPPORTED_ERROR;
930  }
931  return "";
932  }
939  UnicodeString toString(UErrorCode& status) const override {
940  if (U_SUCCESS(status)) {
941  status = U_UNSUPPORTED_ERROR;
942  }
943  return {};
944  }
951  UnicodeString toTempString(UErrorCode& status) const override {
952  if (U_SUCCESS(status)) {
953  status = U_UNSUPPORTED_ERROR;
954  }
955  return {};
956  }
963  Appendable& appendTo(Appendable& appendable, UErrorCode& status) const override {
964  if (U_SUCCESS(status)) {
965  status = U_UNSUPPORTED_ERROR;
966  }
967  return appendable;
968  }
975  UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const override {
976  (void) cfpos;
977  if (U_SUCCESS(status)) {
978  status = U_UNSUPPORTED_ERROR;
979  }
980  return false;
981  }
989  if (U_SUCCESS(status)) {
990  status = U_UNSUPPORTED_ERROR;
991  }
992  return nullptr;
993  }
1001  }; // class FormattedMessage
1002 
1003 } // namespace message2
1004 
1005 U_NAMESPACE_END
1006 
1007 #endif // U_HIDE_DEPRECATED_API
1008 
1009 #endif /* #if !UCONFIG_NO_MF2 */
1010 
1011 #endif /* #if !UCONFIG_NO_FORMATTING */
1012 
1013 #endif /* U_SHOW_CPLUSPLUS_API */
1014 
1015 #endif // MESSAGEFORMAT2_FORMATTABLE_H
1016 
1017 // eof
C++ API: Character Iterator.
Base class for objects to which Unicode characters and strings can be appended.
Definition: appendable.h:54
Abstract class that defines an API for iteration on text objects.
Definition: chariter.h:361
Represents a span of a string containing a given field.
Formattable objects can be passed to the Format class or its subclasses for formatting.
Definition: fmtable.h:63
An abstract formatted value: a string with associated field attributes.
A Locale object represents a specific geographical, political, or cultural region.
Definition: locid.h:195
A string-like object that points to a sized piece of memory.
Definition: stringpiece.h:61
UObject is the common ICU "boilerplate" class.
Definition: uobject.h:223
UnicodeString is a string class that stores Unicode characters directly and provides similar function...
Definition: unistr.h:296
FormattableObject is an abstract class that can be implemented in order to define an arbitrary class ...
virtual ~FormattableObject()
Destructor.
virtual const UnicodeString & tag() const =0
Returns an arbitrary string representing the type of this object.
The Formattable class represents a typed value that can be formatted, originating either from a messa...
Formattable(const FormattableObject *obj)
Object constructor.
Formattable(const UnicodeString &s)
String constructor.
const FormattableObject * getObject(UErrorCode &status) const
Returns a pointer to the FormattableObject contained within this formattable, or if this object does ...
Formattable(const Formattable *arr, int32_t len)
Array constructor.
Formattable(int64_t i)
Int64 constructor.
static Formattable forDecimal(std::string_view number, UErrorCode &status)
Creates a Formattable object of an appropriate numeric type from a a decimal number in string form.
double getDouble(UErrorCode &status) const
Gets the double value of this object.
icu::Formattable asICUFormattable(UErrorCode &status) const
Converts the Formattable object to an ICU Formattable object.
virtual ~Formattable()
Destructor.
const UnicodeString & getString(UErrorCode &status) const
Gets the string value of this object.
int64_t getInt64(UErrorCode &status) const
Gets the int64 value of this object.
int32_t getLong(UErrorCode &status) const
Gets the long value of this object.
int64_t getInt64Value(UErrorCode &status) const
Gets the int64 value of this object.
const Formattable * getArray(int32_t &count, UErrorCode &status) const
Gets the array value and count of this object.
UBool isNumeric() const
Returns true if the data type of this Formattable object is kDouble.
static Formattable forDate(UDate d)
Date factory method.
Formattable(double d)
Double constructor.
friend void swap(Formattable &f1, Formattable &f2) noexcept
Non-member swap function.
Formattable(const Formattable &)
Copy constructor.
UFormattableType getType() const
Gets the data type of this Formattable object.
UDate getDate(UErrorCode &status) const
Gets the Date value of this object.
Formattable & operator=(Formattable) noexcept
Assignment operator.
Not yet implemented: The result of a message formatting operation.
UBool nextPosition(ConstrainedFieldPosition &cfpos, UErrorCode &status) const override
Not yet implemented.
StringPiece subSequence(int32_t start, int32_t end, UErrorCode &status) const
Not yet implemented.
int32_t length(UErrorCode &status) const
Not yet implemented.
CharacterIterator * toCharacterIterator(UErrorCode &status)
Not yet implemented.
UnicodeString toTempString(UErrorCode &status) const override
Not yet implemented.
FormattedMessage(UErrorCode &status)
Not yet implemented.
UnicodeString toString(UErrorCode &status) const override
Not yet implemented.
Appendable & appendTo(Appendable &appendable, UErrorCode &status) const override
Not yet implemented.
virtual ~FormattedMessage()
Destructor.
char16_t charAt(int32_t index, UErrorCode &status) const
Not yet implemented.
A FormattablePlaceholder encapsulates an input value (a message2::Formattable) together with an optio...
const UnicodeString & getFallback() const
Gets the fallback value of this placeholder, to be used in its place if an error occurs while formatt...
FormattedPlaceholder(const UnicodeString &s)
Fallback constructor.
UnicodeString formatToString(const Locale &locale, UErrorCode &status) const
Formats this as a string, using defaults.
bool isEvaluated() const
Returns true iff this has formatting output.
bool isNullOperand() const
Returns true iff this is a null placeholder.
FormattedPlaceholder(const FormattedPlaceholder &input, FormattedValue &&output)
Constructor for fully formatted placeholders.
FormattedPlaceholder & operator=(FormattedPlaceholder &&) noexcept
Move assignment operator: The source FormattedPlaceholder will be left in a valid but undefined state...
bool isFallback() const
Returns true iff this is a fallback placeholder.
const FunctionOptions & options() const
Returns the options of this placeholder.
FormattedPlaceholder(const Formattable &input, const UnicodeString &fb)
Constructor for unformatted placeholders.
bool canFormat() const
Returns true iff this represents a valid argument to the formatter.
FormattedPlaceholder(const FormattedPlaceholder &input, FunctionOptions &&opts, FormattedValue &&output)
Constructor for fully formatted placeholders with options.
const FormattedValue & output() const
Returns the formatted output of this placeholder.
const message2::Formattable & asFormattable() const
Returns the source Formattable value for this placeholder.
A FormattedValue represents the result of formatting a message2::Formattable.
const UnicodeString & getString() const
Gets the string contents of this value.
virtual ~FormattedValue()
Destructor.
FormattedValue(number::FormattedNumber &&)
Formatted number constructor.
const number::FormattedNumber & getNumber() const
Gets the number contents of this value.
bool isNumber() const
Returns true iff this is a formatted number.
FormattedValue(const UnicodeString &)
Formatted string constructor.
FormattedValue & operator=(FormattedValue &&) noexcept
Move assignment operator: The source FormattedValue will be left in a valid but undefined state.
bool isString() const
Returns true iff this is a formatted string.
Structure encapsulating named options passed to a custom selector or formatter.
virtual ~FunctionOptions()
Destructor.
FunctionOptions & operator=(FunctionOptions &&) noexcept
Move assignment operator: The source FunctionOptions will be left in a valid but undefined state.
FunctionOptionsMap getOptions() const
Returns a map of all name-value pairs provided as options to this function.
The result of a number formatting operation.
C++ API: All-in-one formatter for localized numbers, currencies, and units.
UFormattableType
Enum designating the type of a UFormattable instance.
Definition: uformattable.h:48
@ UFMT_LONG
ufmt_getLong() will return without conversion.
Definition: uformattable.h:51
@ UFMT_OBJECT
ufmt_getObject() will return without conversion.
Definition: uformattable.h:55
@ UFMT_DOUBLE
ufmt_getDouble() will return without conversion.
Definition: uformattable.h:50
@ UFMT_INT64
ufmt_getInt64() will return without conversion.
Definition: uformattable.h:54
int8_t UBool
The ICU boolean type, a signed-byte integer.
Definition: umachine.h:247
Basic definitions for ICU, for both C and C++ APIs.
UErrorCode
Standard ICU4C error code type, a substitute for exceptions.
Definition: utypes.h:430
@ U_ILLEGAL_ARGUMENT_ERROR
Start of codes indicating failure.
Definition: utypes.h:467
@ U_UNSUPPORTED_ERROR
Requested operation not supported in current context.
Definition: utypes.h:482
#define U_SUCCESS(x)
Does the error code indicate success?
Definition: utypes.h:742
#define U_I18N_API
Set to export library symbols from inside the i18n library, and to import them from outside.
Definition: utypes.h:316
double UDate
Date and Time data type.
Definition: utypes.h:218