blob: 2d82153d76d95975be7d31a0e93055e96cdceb96 [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 logging
import xml.sax.xmlreader
from idl.matter_idl_types import Idl, ParseMetaData, Attribute
from typing import Optional, List
class IdlPostProcessor:
"""Defines a callback that will apply after an entire parsing
is complete.
"""
def FinalizeProcessing(self, idl: Idl):
"""Update idl with any post-processing directives."""
pass
class ProcessingPath:
"""Maintains the current path of tags within xml processing.
As processing descents into an xml like `<configurator><cluster>....`
paths will have contents like ['configurator', 'cluster', ...].
The main purpose for this is to log and keep track of what was visited
and in general to report things like 'this path found but was not handled'.
"""
def __init__(self, paths: List[str] = None):
if paths is None:
paths = []
self.paths = paths
def push(self, name: str):
self.paths.append(name)
def pop(self):
self.paths.pop()
def __str__(self):
return '::'.join(self.paths)
def __repr__(self):
return 'ProcessingPath(%r)' % self.paths
class Context:
"""
Contains a processing state during XML reading.
The purpose of this is to allow elements to interact with each other, share
data and defer processing.
Usage:
- globally shared data:
> locator: parsing location, for error reporting
> path: current ProcessingPath for any logging of where we are located
- shared data:
> global attributes are parsed by one handler, but used by others
- post-processing support:
> can register AddIdlPostProcessor to perform some processing once
a full parsing pass has been done
More data may be added in time if it involves separate XML parse handlers
needing to interact with each other.
"""
def __init__(self, locator: Optional[xml.sax.xmlreader.Locator] = None):
self.path = ProcessingPath()
self.locator = locator
self._not_handled = set()
self._idl_post_processors = []
# Map of code -> attribute
self._global_attributes = {}
def GetCurrentLocationMeta(self) -> ParseMetaData:
if not self.locator:
return None
return ParseMetaData(line=self.locator.getLineNumber(), column=self.locator.getColumnNumber())
def GetGlobalAttribute(self, code):
if code in self._global_attributes:
return self._global_attributes[code]
raise Exception(
'Global attribute 0x%X (%d) not found. You probably need to load global-attributes.xml' % (code, code))
def AddGlobalAttribute(self, attribute: Attribute):
# NOTE: this may get added several times as both 'client' and 'server'
# however matter should not differentiate between the two
code = attribute.definition.code
logging.info('Adding global attribute 0x%X (%d): %s' % (code, code, attribute.definition.name))
self._global_attributes[code] = attribute
def MarkTagNotHandled(self):
path = str(self.path)
if path not in self._not_handled:
logging.warning("TAG %s was not handled/recognized" % path)
self._not_handled.add(path)
def AddIdlPostProcessor(self, processor: IdlPostProcessor):
self._idl_post_processors.append(processor)
def PostProcess(self, idl: Idl):
for p in self._idl_post_processors:
p.FinalizeProcessing(idl)
self._idl_post_processors = []