# region Import
from __future__ import annotations
from typing import overload
from typing import Any, Tuple, Type, TypeVar
from com.sun.star.beans import XPropertySet
from com.sun.star.lang import XComponent
from com.sun.star.util import XNumberFormatsSupplier
from com.sun.star.util import XNumberFormatTypes
from ooo.dyn.i18n.number_format_index import NumberFormatIndexEnum
from ooo.dyn.lang.locale import Locale
from ooo.dyn.util.number_format import NumberFormatEnum
from ooodev.exceptions import ex as mEx
from ooodev.format.inner.kind.format_kind import FormatKind
from ooodev.format.inner.style_base import StyleBase
from ooodev.loader import lo as mLo
from ooodev.utils import props as mProps
# com.sun.star.i18n.NumberFormatIndex
# endregion Import
_TNumbers = TypeVar("_TNumbers", bound="Numbers")
# https://wiki.documentfoundation.org/Documentation/DevGuide/Office_Development#Number_Formats
[docs]class Numbers(StyleBase):
"""
Calc Numbers format.
.. seealso::
- :ref:`help_calc_format_direct_cell_numbers`
.. versionadded:: 0.9.4
"""
[docs] def __init__(
self,
num_format: NumberFormatEnum | int = 0,
num_format_index: NumberFormatIndexEnum | int = -1,
lang_locale: Locale | None = None,
component: XComponent | None = None,
) -> None:
"""
Constructor
Args:
num_format (NumberFormatEnum, int, optional): Type of a number format.
Use this to select a default format. Defaults to 0 (General Format).
Only used if ``num_format_index`` is ``-1`` (omitted).
num_format_index (NumberFormatIndexEnum, int, optional): Index of a number format.
The enumeration values represent the built-in number formats. Defaults to ``-1``.
lang_locale (Locale, optional): Locale of the number format. Defaults to ``None`` which used current Locale.
component (XComponent, optional): Document such as Spreadsheet or Chart. If Omitted, the current document is used. Defaults to ``None``.
Returns:
None:
See Also:
- :ref:`help_calc_format_direct_cell_numbers`
- `API NumberFormat <https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1util_1_1NumberFormat.html>`__
- `API NumberFormatIndex <https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1i18n_1_1NumberFormatIndex.html>`__
"""
super().__init__()
if lang_locale is None:
# note the empty locale for default locale
lang_locale = Locale()
if component is None:
component = mLo.Lo.this_component
if component is None:
# this is only likely if both headless and options dynamic is set for Lo class
component = mLo.Lo.lo_component
self._num_cat = int(num_format)
self._num_cat = max(self._num_cat, 0)
self._num_format_index = int(num_format_index)
self._lang_locale = lang_locale
self._component = component
# region internal methods
def _get_property_name(self) -> str:
try:
return self._property_name
except AttributeError:
self._property_name = "NumberFormat"
return self._property_name
def _get_format_supplier(self) -> XNumberFormatsSupplier:
return mLo.Lo.qi(XNumberFormatsSupplier, self._component, True)
def _query_key(self, nf_str: str) -> int:
xfs = mLo.Lo.qi(XNumberFormatsSupplier, self._component, True)
n_formats = xfs.getNumberFormats()
return int(n_formats.queryKey(nf_str, self._lang_locale, False))
def _get_by_key_props(self) -> XPropertySet:
xfs = mLo.Lo.qi(XNumberFormatsSupplier, self._component, True)
n_formats = xfs.getNumberFormats()
return n_formats.getByKey(self.prop_format_key)
def _get_format_index(self) -> int:
xfs = mLo.Lo.qi(XNumberFormatsSupplier, self._component, True)
n_formats = xfs.getNumberFormats()
nft = mLo.Lo.qi(XNumberFormatTypes, n_formats, True)
if self._num_format_index == -1:
return nft.getStandardFormat(self._num_cat, self._lang_locale)
else:
return nft.getFormatIndex(self._num_format_index, self._lang_locale)
# endregion internal methods
# region Overrides
def _supported_services(self) -> Tuple[str, ...]:
try:
return self._supported_services_values
except AttributeError:
self._supported_services_values = ("com.sun.star.style.CellStyle", "com.sun.star.table.CellProperties")
return self._supported_services_values
def _is_valid_obj(self, obj: Any) -> bool:
return hasattr(obj, self._get_property_name())
# region apply()
@overload
def apply(self, obj: Any) -> None: ...
@overload
def apply(self, obj: Any, **kwargs: Any) -> None: ...
[docs] def apply(self, obj: Any, **kwargs: Any) -> None:
# sourcery skip: hoist-if-from-if
"""
Applies styles to object
Args:
obj (object): UNO Object that styles are to be applied.
kwargs (Any, optional): Expandable list of key value pairs that may be used in child classes.
Keyword Args:
format_key (Any, optional): NumberFormat key, overrides ``prop_format_key`` property value.
Returns:
None:
"""
# key = self._get_by_key_props(self._num_cat)
key = kwargs.pop("format_key", -1)
# key may be none.
# This is fine for some child classes.
if key == -1:
key = self.prop_format_key
if key == -1:
mLo.Lo.print(f"{self.__class__.__name__}.apply(): Unable to set Property. NumberFormat not found")
return
props = kwargs.pop("override_dv", {})
props.update({self._get_property_name(): key})
super().apply(obj, override_dv=props, **kwargs)
# endregion apply()
# region Copy()
@overload
def copy(self: _TNumbers) -> _TNumbers: ...
@overload
def copy(self: _TNumbers, **kwargs) -> _TNumbers: ...
[docs] def copy(self: _TNumbers, **kwargs) -> _TNumbers:
"""
Creates a copy of the instance.
Returns:
Numbers: Copy of the instance.
"""
# pylint: disable=protected-access
inst = self.__class__(**kwargs)
inst._format_key_prop = self.prop_format_key
return inst
# endregion Copy()
# endregion Overrides
# region Static Methods
# region from string
[docs] @classmethod
def from_str(
cls: Type[_TNumbers], nf_str: str, lang_locale: Locale | None = None, auto_add: bool = False, **kwargs
) -> _TNumbers:
# sourcery skip: hoist-similar-statement-from-if, remove-unnecessary-else, swap-if-else-branches, swap-nested-ifs
"""
Gets instance from format string
Args:
nf_str (str): Format string.
lang_locale (Locale, optional): Locale. Defaults to ``None``.
auto_add (bool, optional): If True, format string will be added to document if not found. Defaults to ``False``.
Keyword Args:
component (XComponent): Calc document. Default is current document.
Returns:
Numbers: Instance that represents numbers format.
"""
# pylint: disable=protected-access
nu = cls(num_format=0, lang_locale=lang_locale, **kwargs)
key = nu._query_key(nf_str)
if key == -1:
if auto_add:
try:
xfs = nu._get_format_supplier()
n_formats = xfs.getNumberFormats()
key = n_formats.addNew(nf_str, nu._lang_locale)
except Exception as e:
raise ValueError(f"Unable to add format string: {nf_str}") from e
else:
raise ValueError(f"Format string not found: {nf_str}")
inst = cls(lang_locale=lang_locale, **kwargs)
inst._format_key_prop = key
return inst
# endregion from string
# region from index
[docs] @classmethod
def from_index(cls: Type[_TNumbers], index: int, lang_locale: Locale | None = None, **kwargs) -> _TNumbers:
"""
Gets instance from number format index. This is the index that is assigned to the ``NumberFormat`` property of an object such as a cell.
Args:
index (int): Format (``NumberFormat``) index.
lang_locale (Locale, optional): Locale. Defaults to ``None``.
Keyword Args:
component (XComponent): Calc document. Default is current document.
Returns:
Numbers: Instance that represents numbers format.
"""
# pylint: disable=protected-access
inst = cls(lang_locale=lang_locale, **kwargs)
inst._format_key_prop = index
return inst
# endregion from index
# region from_obj()
@overload
@classmethod
def from_obj(cls: Type[_TNumbers], obj: Any) -> _TNumbers: ...
@overload
@classmethod
def from_obj(cls: Type[_TNumbers], obj: Any, **kwargs) -> _TNumbers: ...
[docs] @classmethod
def from_obj(cls: Type[_TNumbers], obj: Any, **kwargs) -> _TNumbers:
"""
Gets instance from object
Args:
obj (object): UNO Object.
Keyword Args:
component (XComponent): Calc document. Default is current document.
Raises:
NotSupportedError: If ``obj`` is not supported.
Returns:
Numbers: Instance that represents numbers format.
"""
# pylint: disable=protected-access
nu = cls(**kwargs)
if not nu._is_valid_obj(obj):
raise mEx.NotSupportedError(f'Object is not supported for conversion to "{cls.__name__}"')
# Get the number format index key of the cell's properties
nf = int(mProps.Props.get(obj, nu._get_property_name(), -1))
if nf == -1:
raise mEx.NotSupportedError(f'Object is not supported for conversion to "{cls.__name__}"')
locale = mProps.Props.get(obj, "CharLocale", None)
inst = cls(lang_locale=locale, **kwargs)
inst._format_key_prop = nf
inst.set_update_obj(obj)
return inst
# endregion from_obj()
# endregion Static Methods
# region Instance Properties
@property
def general(self: _TNumbers) -> _TNumbers:
"""Gets general format"""
result = self.__class__(num_format=0, lang_locale=self._lang_locale, component=self._component)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def date(self: _TNumbers) -> _TNumbers:
"""Gets date format"""
result = self.__class__(
num_format=NumberFormatEnum.DATE, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def time(self: _TNumbers) -> _TNumbers:
"""Gets time format"""
result = self.__class__(
num_format=NumberFormatEnum.TIME, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def currency(self: _TNumbers) -> _TNumbers:
"""Gets currency format"""
result = self.__class__(
num_format=NumberFormatEnum.CURRENCY, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def number(self: _TNumbers) -> _TNumbers:
"""Gets number format"""
result = self.__class__(
num_format=NumberFormatEnum.NUMBER, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def scientific(self: _TNumbers) -> _TNumbers:
"""Gets scientific format"""
result = self.__class__(
num_format=NumberFormatEnum.SCIENTIFIC, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def fraction(self: _TNumbers) -> _TNumbers:
"""Gets fraction format"""
result = self.__class__(
num_format=NumberFormatEnum.FRACTION, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def percent(self: _TNumbers) -> _TNumbers:
"""Gets percent format"""
result = self.__class__(
num_format=NumberFormatEnum.PERCENT, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def text(self: _TNumbers) -> _TNumbers:
"""Gets text format"""
result = self.__class__(
num_format=NumberFormatEnum.TEXT, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def datetime(self: _TNumbers) -> _TNumbers:
"""Gets datetime format"""
result = self.__class__(
num_format=NumberFormatEnum.DATETIME, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
@property
def boolean(self: _TNumbers) -> _TNumbers:
"""Gets boolean format"""
result = self.__class__(
num_format=NumberFormatEnum.LOGICAL, lang_locale=self._lang_locale, component=self._component
)
if self.has_update_obj():
result.set_update_obj(self.get_update_obj())
return result
# endregion Instance Properties
# region Properties
@property
def prop_format_kind(self) -> FormatKind:
"""Gets the kind of style"""
try:
return self._format_kind_prop
except AttributeError:
self._format_kind_prop = FormatKind.CELL
return self._format_kind_prop
@property
def prop_format_key(self) -> int:
"""Gets the format key"""
try:
return self._format_key_prop
except AttributeError:
self._format_key_prop = self._get_format_index()
return self._format_key_prop
@property
def prop_format_str(self) -> str:
"""Gets the format string, e.g. ``#,##0.00``"""
try:
return self._format_str_prop
except AttributeError:
props = self._get_by_key_props()
self._format_str_prop = str(mProps.Props.get(props, "FormatString"))
return self._format_str_prop
# endregion Properties