# ******************************************************************************************************
# valueexpression.py - Gbtc
#
# Copyright © 2022, Grid Protection Alliance. All Rights Reserved.
#
# Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
# the NOTICE file distributed with this work for additional information regarding copyright ownership.
# The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
# file except in compliance with the License. You may obtain a copy of the License at:
#
# http://opensource.org/licenses/MIT
#
# Unless agreed to in writing, the subject software distributed under the License is distributed on an
# "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
# License for the specific language governing permissions and limitations.
#
# Code Modification History:
# ----------------------------------------------------------------------------------------------------
# 09/03/2022 - J. Ritchie Carroll
# Generated original version of source code.
#
# ******************************************************************************************************
from typing import Optional, Tuple, Union
from gsf import Convert, Empty, override, normalize_enumname
from .expression import Expression
from .dataset import xsdformat
from .constants import ExpressionType, ExpressionValueType
from decimal import Decimal
from datetime import datetime
from uuid import UUID
import numpy as np
[docs]
class ValueExpression(Expression):
"""
Represents a value expression.
"""
def __init__(self, valuetype: ExpressionValueType, value: object):
self._valuetype = valuetype
if value is None:
self._value = None
elif valuetype == ExpressionValueType.BOOLEAN:
self._value = bool(value)
elif valuetype == ExpressionValueType.INT32:
self._value = np.int32(value)
elif valuetype == ExpressionValueType.INT64:
self._value = np.int64(value)
elif valuetype == ExpressionValueType.DECIMAL:
self._value = Decimal(value)
elif valuetype == ExpressionValueType.DOUBLE:
self._value = np.float64(value)
elif valuetype in [ExpressionValueType.STRING, ExpressionValueType.GUID, ExpressionValueType.DATETIME]:
self._value = value
else:
raise TypeError(f"cannot create new value expression; unexpected expression value type: {normalize_enumname(valuetype)}")
@override
@property
def expressiontype(self) -> ExpressionType:
"""
Gets the type of this `ValueExpression`.
"""
return ExpressionType.VALUE
@property
def valuetype(self) -> ExpressionValueType:
"""
Gets the value type of this `ValueExpression`.
"""
return self._valuetype
@property
def value(self) -> object:
"""
Gets the value of this `ValueExpression`.
"""
return self._value
def __repr__(self) -> str:
if self._valuetype == ExpressionValueType.DATETIME:
return xsdformat(datetime(self._value))
return str(self._valuetype)
[docs]
def is_null(self) -> bool:
"""
Gets a flag that determines if this `ValueExpression` is null, i.e., `None`.
"""
return self._value is None
[docs]
def integervalue(self, defaultvalue: int = 0) -> int:
"""
Gets the `ValueExpression` as an integer value or specified default value if not possible.
"""
if self._valuetype == ExpressionValueType.BOOLEAN:
return self._booleanvalue_asint()
if self._valuetype == ExpressionValueType.INT32:
return int(self._int32value())
if self._valuetype == ExpressionValueType.INT64:
return int(self._int64value())
return defaultvalue
def _validate_valuetype(self, valuetype: ExpressionValueType) -> Optional[Exception]:
if valuetype != self._valuetype:
return TypeError(f"cannot read expression value expression as \"{normalize_enumname(valuetype)}\", type is \"{normalize_enumname(self._valuetype)}\"")
return None
[docs]
def booleanvalue(self) -> Tuple[bool, Optional[Exception]]:
"""
Gets the `ValueExpression` as a boolean value.
An error will be returned if value type is not `ExpressionValueType.BOOLEAN`.
"""
err = self._validate_valuetype(ExpressionValueType.BOOLEAN)
return (self._booleanvalue(), None) if err is None else (False, err)
def _booleanvalue(self) -> bool:
return False if self._value is None else bool(self._value)
def _booleanvalue_asint(self) -> int:
return 1 if self._booleanvalue() else 0
[docs]
def int32value(self) -> Tuple[np.int32, Optional[Exception]]:
"""
Gets the `ValueExpression` as a 32-bit integer value.
An error will be returned if value type is not `ExpressionValueType.INT32`.
"""
err = self._validate_valuetype(ExpressionValueType.INT32)
return (self._int32value(), None) if err is None else (Empty.INT32, err)
def _int32value(self) -> np.int32:
return Empty.INT32 if self._value is None else np.int32(self._value)
[docs]
def int64value(self) -> Tuple[np.int64, Optional[Exception]]:
"""
Gets the `ValueExpression` as a 64-bit integer value.
An error will be returned if value type is not `ExpressionValueType.INT64`.
"""
err = self._validate_valuetype(ExpressionValueType.INT64)
return (self._int64value(), None) if err is None else (Empty.INT64, err)
def _int64value(self) -> np.int64:
return Empty.INT64 if self._value is None else np.int64(self._value)
[docs]
def decimalvalue(self) -> Tuple[Decimal, Optional[Exception]]:
"""
Gets the `ValueExpression` as a Decimal value.
An error will be returned if value type is not `ExpressionValueType.DECIMAL`.
"""
err = self._validate_valuetype(ExpressionValueType.DECIMAL)
return (self._decimalvalue(), None) if err is None else (Empty.DECIMAL, err)
def _decimalvalue(self) -> Decimal:
return Empty.DECIMAL if self._value is None else Decimal(self._value)
[docs]
def doublevalue(self) -> Tuple[np.float64, Optional[Exception]]:
"""
Gets the `ValueExpression` as a double value.
An error will be returned if value type is not `ExpressionValueType.DOUBLE`.
"""
err = self._validate_valuetype(ExpressionValueType.DOUBLE)
return (self._doublevalue(), None) if err is None else (Empty.DOUBLE, err)
def _doublevalue(self) -> np.float64:
return Empty.DOUBLE if self._value is None else np.float64(self._value)
[docs]
def stringvalue(self) -> Tuple[str, Optional[Exception]]:
"""
Gets the `ValueExpression` as a string value.
An error will be returned if value type is not `ExpressionValueType.STRING`.
"""
err = self._validate_valuetype(ExpressionValueType.STRING)
return (self._stringvalue(), None) if err is None else (Empty.STRING, err)
def _stringvalue(self) -> str:
return Empty.STRING if self._value is None else self._value
[docs]
def guidvalue(self) -> Tuple[UUID, Optional[Exception]]:
"""
Gets the `ValueExpression` as a GUID value.
An error will be returned if value type is not `ExpressionValueType.GUID`.
"""
err = self._validate_valuetype(ExpressionValueType.GUID)
return (self._guidvalue(), None) if err is None else (Empty.GUID, err)
def _guidvalue(self) -> UUID:
return Empty.GUID if self._value is None else self._value
[docs]
def datetimevalue(self) -> Tuple[datetime, Optional[Exception]]:
"""
Gets the `ValueExpression` as a datetime value.
An error will be returned if value type is not `ExpressionValueType.DATETIME`.
"""
err = self._validate_valuetype(ExpressionValueType.DATETIME)
return (self._datetimevalue(), None) if err is None else (Empty.DATETIME, err)
def _datetimevalue(self) -> datetime:
return Empty.DATETIME if self._value is None else self._value
[docs]
def convert(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
"""
Attempts to convert the `ValueExpression` to the specified type.
"""
# If source value is Null, result is Null, regardless of target type
if self.is_null():
return ValueExpression.nullvalue(target_typevalue), None
valuetype = self._valuetype
if valuetype == ExpressionValueType.BOOLEAN:
return self._convert_fromboolean(target_typevalue)
if valuetype == ExpressionValueType.INT32:
return self._convert_fromint32(target_typevalue)
if valuetype == ExpressionValueType.INT64:
return self._convert_fromint64(target_typevalue)
if valuetype == ExpressionValueType.DECIMAL:
return self._convert_fromdecimal(target_typevalue)
if valuetype == ExpressionValueType.DOUBLE:
return self._convert_fromdouble(target_typevalue)
if valuetype == ExpressionValueType.STRING:
return self._convert_fromstring(target_typevalue)
if valuetype == ExpressionValueType.GUID:
return self._convert_fromguid(target_typevalue)
if valuetype == ExpressionValueType.DATETIME:
return self._convert_fromdatetime(target_typevalue)
return None, TypeError("unexpected expression value type encountered")
def _convert_fromnumeric(self, value: Union[int, float], from_typename: str, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
try:
if target_typevalue == ExpressionValueType.BOOLEAN:
return ValueExpression(target_typevalue, value != 0), None
if target_typevalue == ExpressionValueType.INT32:
return ValueExpression(target_typevalue, np.int32(value)), None
if target_typevalue == ExpressionValueType.INT64:
return ValueExpression(target_typevalue, np.int64(value)), None
if target_typevalue == ExpressionValueType.DECIMAL:
return ValueExpression(target_typevalue, Decimal(value)), None
if target_typevalue == ExpressionValueType.DOUBLE:
return ValueExpression(target_typevalue, np.float64(value)), None
if target_typevalue == ExpressionValueType.STRING:
return ValueExpression(target_typevalue, str(value)), None
except Exception as ex:
return None, ValueError(f"failed while attempting to convert from \"{from_typename}\" value ({value}) to \"{normalize_enumname(target_typevalue.name)}\": {ex}")
return None, TypeError(f"cannot convert \"{from_typename}\" value ({value}) to \"{normalize_enumname(target_typevalue)}\"")
def _convert_fromboolean(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
return self._convert_fromnumeric(self._booleanvalue_asint(), "Boolean", target_typevalue)
def _convert_fromint32(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
return self._convert_fromnumeric(self._int32value().item(), "Int32", target_typevalue)
def _convert_fromint64(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
return self._convert_fromnumeric(self._int64value().item(), "Int64", target_typevalue)
def _convert_fromdecimal(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
return self._convert_fromnumeric(self._decimalvalue(), "Decimal", target_typevalue)
def _convert_fromdouble(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
return self._convert_fromnumeric(self._doublevalue().item(), "Double", target_typevalue)
def _convert_fromstring(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
value = self._stringvalue()
try:
if target_typevalue == ExpressionValueType.BOOLEAN:
return ValueExpression(target_typevalue, bool(value)), None
if target_typevalue == ExpressionValueType.INT32:
return ValueExpression(target_typevalue, np.int32(Decimal(value))), None
if target_typevalue == ExpressionValueType.INT64:
return ValueExpression(target_typevalue, np.int64(Decimal(value))), None
if target_typevalue == ExpressionValueType.DECIMAL:
return ValueExpression(target_typevalue, Decimal(value)), None
if target_typevalue == ExpressionValueType.DOUBLE:
return ValueExpression(target_typevalue, np.float64(value)), None
if target_typevalue == ExpressionValueType.STRING:
return ValueExpression(target_typevalue, value), None
if target_typevalue == ExpressionValueType.GUID:
return ValueExpression(target_typevalue, UUID(value)), None
if target_typevalue == ExpressionValueType.DATETIME:
return ValueExpression(target_typevalue, Convert.from_str(value, datetime)), None
except Exception as ex:
return None, ValueError(f"failed while attempting to convert \"String\" value ('{value}') to \"{normalize_enumname(target_typevalue)}\": {ex}")
return None, TypeError(f"cannot convert \"String\" value ('{value}') to \"{normalize_enumname(target_typevalue)}\"")
def _convert_fromguid(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
value = self._guidvalue()
if target_typevalue == ExpressionValueType.STRING:
return ValueExpression(target_typevalue, str(value)), None
if target_typevalue == ExpressionValueType.GUID:
return ValueExpression(target_typevalue, value), None
return None, TypeError(f"cannot convert \"Guid\" to \"{normalize_enumname(target_typevalue)}\"")
def _convert_fromdatetime(self, target_typevalue: ExpressionValueType) -> Tuple[Optional["ValueExpression"], Optional[Exception]]:
value = self._datetimevalue()
if target_typevalue == ExpressionValueType.STRING:
return ValueExpression(target_typevalue, xsdformat(value)), None
if target_typevalue == ExpressionValueType.DATETIME:
return ValueExpression(target_typevalue, value), None
return self._convert_fromnumeric(value.timestamp(), "DateTime", target_typevalue)
[docs]
@staticmethod
def nullvalue(target_valuetype: ExpressionValueType) -> "ValueExpression":
"""
Gets a `ValueExpression` that represents a null, i.e., `None`, value of the specified `ExpressionValueType`.
"""
return ValueExpression(target_valuetype, None)
TRUEVALUE = ValueExpression(ExpressionValueType.BOOLEAN, True)
"""
Defines a `ValueExpression` that represents `True`.
"""
FALSEVALUE = ValueExpression(ExpressionValueType.BOOLEAN, False)
"""
Defines a `ValueExpression` that represents `False`.
"""
NULLVALUE = ValueExpression.nullvalue(ExpressionValueType.UNDEFINED)
"""
Defines a `ValueExpression` that represents a null, i.e., `None`, value, value type `Undefined`.
"""
NULLBOOLVALUE = ValueExpression.nullvalue(ExpressionValueType.BOOLEAN)
"""
Defines a `ValueExpression` that represents a null, i.e., `None`, value of type `Boolean`.
"""
NULLINT32VALUE = ValueExpression.nullvalue(ExpressionValueType.INT32)
"""
Defines a `ValueExpression` that represents a null, i.e., `None`, value of type `Int32`.
"""
NULLDATETIMEVALUE = ValueExpression.nullvalue(ExpressionValueType.DATETIME)
"""
Defines a `ValueExpression` that represents a null, i.e., `None`, value of type `DateTime`.
"""
NULLSTRINGVALUE = ValueExpression.nullvalue(ExpressionValueType.STRING)
"""
Defines a `ValueExpression` that represents a null, i.e., `None`, value of type `String`.
"""
EMPTYSTRINGVALUE = ValueExpression(ExpressionValueType.STRING, Empty.STRING)
"""
Defines a `ValueExpression` that represents an empty string.
"""