blob: 23995fae5dd408e99336978ebfe6caaf165c75e3 [file] [log] [blame]
#
# Copyright (c) 2022 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import enum
import glob
import io
from typing import List, Optional
from matter_idl.matter_idl_types import *
from matter_idl.zapxml import ParseSource, ParseXmls
from .pseudo_clusters.pseudo_clusters import PseudoClusters
class _ItemType(enum.Enum):
Cluster = 0
Request = 1
Response = 2
Attribute = 3
Event = 4
Bitmap = 5
Enum = 6
Struct = 7
class SpecDefinitions:
def __init__(self, sources: List[ParseSource]):
self.__clusters_by_id: dict[int, Cluster] = {}
self.__commands_by_id: dict[int, dict[int, Command]] = {}
self.__responses_by_id: dict[int, dict[int, Struct]] = {}
self.__attributes_by_id: dict[int, dict[int, Attribute]] = {}
self.__events_by_id: dict[int, dict[int, Event]] = {}
self.__clusters_by_name: dict[str, int] = {}
self.__commands_by_name: dict[str, int] = {}
self.__responses_by_name: dict[str, int] = {}
self.__attributes_by_name: dict[str, int] = {}
self.__events_by_name: dict[str, int] = {}
self.__bitmaps_by_name: dict[str, dict[str, Bitmap]] = {}
self.__enums_by_name: dict[str, dict[str, Enum]] = {}
self.__structs_by_name: dict[str, dict[str, Struct]] = {}
idl = ParseXmls(sources)
for cluster in idl.clusters:
code: int = cluster.code
name: str = cluster.name
self.__clusters_by_id[code] = cluster
self.__commands_by_id[code] = {c.code: c for c in cluster.commands}
self.__responses_by_id[code] = {}
self.__attributes_by_id[code] = {
a.definition.code: a for a in cluster.attributes}
self.__events_by_id[code] = {e.code: e for e in cluster.events}
self.__clusters_by_name[name] = cluster.code
self.__commands_by_name[name] = {
c.name: c.code for c in cluster.commands}
self.__responses_by_name[name] = {}
self.__attributes_by_name[name] = {
a.definition.name: a.definition.code for a in cluster.attributes}
self.__events_by_name[name] = {
e.name: e.code for e in cluster.events}
self.__bitmaps_by_name[name] = {
b.name: b for b in cluster.bitmaps}
self.__enums_by_name[name] = {
e.name: e for e in cluster.enums}
self.__structs_by_name[name] = {
s.name: s for s in cluster.structs}
for struct in cluster.structs:
if struct.tag == StructTag.RESPONSE:
self.__responses_by_id[code][struct.code] = struct
self.__responses_by_name[name][struct.name] = struct.code
def get_cluster_name(self, cluster_id: int) -> str:
cluster = self.__clusters_by_id.get(cluster_id)
return cluster.name if cluster else None
def get_command_name(self, cluster_id: int, command_id: int) -> str:
command = self.__get_by_id(cluster_id, command_id, _ItemType.Request)
return command.name if command else None
def get_response_name(self, cluster_id: int, response_id: int) -> str:
response = self.__get_by_id(
cluster_id, response_id, _ItemType.Response)
return response.name if response else None
def get_attribute_name(self, cluster_id: int, attribute_id: int) -> str:
attribute = self.__get_by_id(
cluster_id, attribute_id, _ItemType.Attribute)
return attribute.definition.name if attribute else None
def get_event_name(self, cluster_id: int, event_id: int) -> str:
event = self.__get_by_id(cluster_id, event_id, _ItemType.Event)
return event.name if event else None
def get_cluster_id_by_name(self, cluster_name: str) -> int:
if cluster_name is None:
return None
# The idl parser remove spaces
cluster_name = cluster_name.replace(' ', '')
return self.__clusters_by_name.get(cluster_name)
def has_cluster_by_name(self, cluster_name: str) -> bool:
cluster_id = self.get_cluster_id_by_name(cluster_name)
return cluster_id is not None
def get_command_by_name(self, cluster_name: str, command_name: str) -> Command:
return self.__get_by_name(cluster_name, command_name, _ItemType.Request)
def get_response_by_name(self, cluster_name: str, response_name: str) -> Struct:
return self.__get_by_name(cluster_name, response_name, _ItemType.Response)
def get_attribute_by_name(self, cluster_name: str, attribute_name: str) -> Attribute:
return self.__get_by_name(cluster_name, attribute_name, _ItemType.Attribute)
def get_event_by_name(self, cluster_name: str, event_name: str) -> Event:
return self.__get_by_name(cluster_name, event_name, _ItemType.Event)
def get_bitmap_by_name(self, cluster_name: str, bitmap_name: str) -> Bitmap:
return self.__get_by_name(cluster_name, bitmap_name, _ItemType.Bitmap)
def get_enum_by_name(self, cluster_name: str, enum_name: str) -> Bitmap:
return self.__get_by_name(cluster_name, enum_name, _ItemType.Enum)
def get_struct_by_name(self, cluster_name: str, struct_name: str) -> Struct:
return self.__get_by_name(cluster_name, struct_name, _ItemType.Struct)
def get_type_by_name(self, cluster_name: str, target_name: str):
bitmap = self.get_bitmap_by_name(cluster_name, target_name)
if bitmap:
return bitmap
enum = self.get_enum_by_name(cluster_name, target_name)
if enum:
return enum
struct = self.get_struct_by_name(cluster_name, target_name)
if struct:
return struct
event = self.get_event_by_name(cluster_name, target_name)
if event:
return event
return None
def get_command_names(self, cluster_name: str) -> list[str]:
targets = self.__get_targets_by_cluster_name(
cluster_name, _ItemType.Request)
return [] if targets is None else [name for name in targets]
def get_event_names(self, cluster_name: str) -> list[str]:
targets = self.__get_targets_by_cluster_name(
cluster_name, _ItemType.Event)
return [] if targets is None else [name for name in targets]
def get_attribute_names(self, cluster_name: str) -> list[str]:
targets = self.__get_targets_by_cluster_name(
cluster_name, _ItemType.Attribute)
return [] if targets is None else [name for name in targets]
def is_fabric_scoped(self, target) -> bool:
if isinstance(target, Event):
return bool(target.is_fabric_sensitive)
if isinstance(target, Struct) and hasattr(target, 'qualities'):
return bool(target.qualities & StructQuality.FABRIC_SCOPED)
return False
def is_nullable(self, target) -> bool:
if hasattr(target, 'qualities'):
return bool(target.qualities & FieldQuality.NULLABLE)
return False
def __get_by_name(self, cluster_name: str, target_name: str, target_type: _ItemType):
if target_name is None:
return None
cluster_id = self.get_cluster_id_by_name(cluster_name)
if cluster_id is None:
return None
targets = self.__get_targets_by_cluster_name(cluster_name, target_type)
if targets is None:
return None
target = None
if target_type == _ItemType.Request:
target_id = targets.get(target_name)
target = self.__get_by_id(cluster_id, target_id, target_type)
elif target_type == _ItemType.Response:
target_id = targets.get(target_name)
target = self.__get_by_id(cluster_id, target_id, target_type)
elif target_type == _ItemType.Event:
target_id = targets.get(target_name)
target = self.__get_by_id(cluster_id, target_id, target_type)
elif target_type == _ItemType.Attribute:
target_id = targets.get(target_name)
target = self.__get_by_id(cluster_id, target_id, target_type)
elif target_type == _ItemType.Bitmap:
target = targets.get(target_name)
elif target_type == _ItemType.Enum:
target = targets.get(target_name)
elif target_type == _ItemType.Struct:
target = targets.get(target_name)
return target
def __get_by_id(self, cluster_id: int, target_id: int, target_type: str):
target_mapping = {
_ItemType.Request: self.__commands_by_id,
_ItemType.Response: self.__responses_by_id,
_ItemType.Attribute: self.__attributes_by_id,
_ItemType.Event: self.__events_by_id,
}
targets = target_mapping[target_type].get(cluster_id)
if targets is None:
return None
return targets.get(target_id)
def __get_targets_by_cluster_name(self, cluster_name: str, target_type: _ItemType):
if not cluster_name:
return None
target_mapping = {
_ItemType.Request: self.__commands_by_name,
_ItemType.Response: self.__responses_by_name,
_ItemType.Attribute: self.__attributes_by_name,
_ItemType.Event: self.__events_by_name,
_ItemType.Bitmap: self.__bitmaps_by_name,
_ItemType.Enum: self.__enums_by_name,
_ItemType.Struct: self.__structs_by_name,
}
# The idl parser remove spaces
cluster_name = cluster_name.replace(' ', '')
return target_mapping[target_type].get(cluster_name)
def SpecDefinitionsFromPaths(paths: str, pseudo_clusters: Optional[PseudoClusters] = PseudoClusters([])):
filenames = []
for path in paths:
if '*' in path or '?' in path:
filenames.extend(glob.glob(path, recursive=False))
else:
filenames.append(path)
sources = [ParseSource(source=name) for name in filenames]
for pseudo_cluster in pseudo_clusters.clusters:
if pseudo_cluster.definition is not None:
name = pseudo_cluster.name
definition = pseudo_cluster.definition
sources = (
sources + [ParseSource(source=io.StringIO(definition), name=name)])
return SpecDefinitions(sources)