Add `features` parsing for the zapxml python parser (#33853)

* Update zapxml parsing to support features

* Restyle
diff --git a/scripts/py_matter_idl/matter_idl/test_zapxml.py b/scripts/py_matter_idl/matter_idl/test_zapxml.py
index 38ba9c8..6e6cd3d 100755
--- a/scripts/py_matter_idl/matter_idl/test_zapxml.py
+++ b/scripts/py_matter_idl/matter_idl/test_zapxml.py
@@ -274,6 +274,40 @@
                              Cluster(name='Test2', code=20, enums=[e3])],
                              ))
 
+    def testFeatures(self):
+        idl = XmlToIdl('''<?xml version="1.0"?>
+            <configurator>
+              <cluster>
+                  <name>TestFeatures</name>
+                  <code>20</code>
+
+                  <features>
+                    <feature bit="0" code="DEPONOFF" name="OnOff" summary="Test">
+                      <optionalConform/>
+                    </feature>
+                    <feature bit="1" code="TEST" name="TestFeature" summary="Test2">
+                      <optionalConform/>
+                    </feature>
+                    <feature bit="2" code="XYZ" name="AnotherTest" summary="Test2">
+                      <optionalConform/>
+                    </feature>
+                  </features>
+              </cluster>
+            </configurator>
+        ''')
+        bitmap = Bitmap(
+            name='Feature',
+            base_type='bitmap32',
+            entries=[
+                ConstantEntry(name='OnOff', code=1),
+                ConstantEntry(name='TestFeature', code=2),
+                ConstantEntry(name='AnotherTest', code=4),
+            ])
+        self.assertEqual(idl,
+                         Idl(clusters=[
+                             Cluster(name='TestFeatures', code=20, bitmaps=[bitmap])
+                         ])),
+
     def testStruct(self):
         idl = XmlToIdl('''<?xml version="1.0"?>
             <configurator>
diff --git a/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py b/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py
index ae943bf..866ce71 100644
--- a/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py
+++ b/scripts/py_matter_idl/matter_idl/zapxml/handlers/handlers.py
@@ -337,6 +337,33 @@
         self.context.AddIdlPostProcessor(self)
 
 
+class FeaturesHandler(BaseHandler):
+    """Handles .../features 
+
+       Attaches a "Feature" bitmap to the given structure
+    """
+
+    def __init__(self, context: Context, cluster: Cluster):
+        super().__init__(context)
+        self._features = Bitmap(name='Feature', base_type="bitmap32", entries=[])
+        self._cluster = cluster
+
+    def GetNextProcessor(self, name, attrs):
+        if name.lower() == 'feature':
+            self._features.entries.append(ConstantEntry(
+                name=attrs['name'],
+                code=1 << ParseInt(attrs['bit']),
+            ))
+
+            # Sub-elements are conformance which is not representable in IDL
+            return BaseHandler(self.context, handled=HandledDepth.ENTIRE_TREE)
+        return BaseHandler(self.context)
+
+    def EndProcessing(self):
+        if self._features.entries:
+            self._cluster.bitmaps.append(self._features)
+
+
 class DescriptionHandler(BaseHandler):
     """Handles .../description text elements
 
@@ -511,6 +538,8 @@
             return CommandHandler(self.context, self._cluster, attrs)
         elif name.lower() == 'description':
             return DescriptionHandler(self.context, self._cluster)
+        elif name.lower() == 'features':
+            return FeaturesHandler(self.context, self._cluster)
         elif name.lower() in ['define', 'domain', 'tag', 'client', 'server']:
             # NOTE: we COULD use client and server to create separate definitions
             #       of each, but the usefulness of this is unclear as the definitions are