OSDN Git Service

Camera2: Deprecate LENS_RADIAL_DISTORTION, add LENS_DISTORTION
[android-x86/system-media.git] / camera / docs / metadata_model.py
1 #!/usr/bin/python
2
3 #
4 # Copyright (C) 2012 The Android Open Source Project
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #      http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18
19 """
20 A set of classes (models) each closely representing an XML node in the
21 metadata_definitions.xml file.
22
23   Node: Base class for most nodes.
24   Entry: A node corresponding to <entry> elements.
25   Clone: A node corresponding to <clone> elements.
26   MergedEntry: A node corresponding to either <entry> or <clone> elements.
27   Kind: A node corresponding to <dynamic>, <static>, <controls> elements.
28   InnerNamespace: A node corresponding to a <namespace> nested under a <kind>.
29   OuterNamespace: A node corresponding to a <namespace> with <kind> children.
30   Section: A node corresponding to a <section> element.
31   Enum: A class corresponding an <enum> element within an <entry>
32   EnumValue: A class corresponding to a <value> element within an Enum
33   Metadata: Root node that also provides tree construction functionality.
34   Tag: A node corresponding to a top level <tag> element.
35   Typedef: A node corresponding to a <typedef> element under <types>.
36 """
37
38 import sys
39 import itertools
40 from collections import OrderedDict
41
42 class Node(object):
43   """
44   Base class for most nodes that are part of the Metadata graph.
45
46   Attributes (Read-Only):
47     parent: An edge to a parent Node.
48     name: A string describing the name, usually but not always the 'name'
49           attribute of the corresponding XML node.
50   """
51
52   def __init__(self):
53     self._parent = None
54     self._name = None
55
56   @property
57   def parent(self):
58     return self._parent
59
60   @property
61   def name(self):
62     return self._name
63
64   def find_all(self, pred):
65     """
66     Find all descendants that match the predicate.
67
68     Args:
69       pred: a predicate function that acts as a filter for a Node
70
71     Yields:
72       A sequence of all descendants for which pred(node) is true,
73       in a pre-order visit order.
74     """
75     if pred(self):
76       yield self
77
78     if self._get_children() is None:
79       return
80
81     for i in self._get_children():
82       for j in i.find_all(pred):
83         yield j
84
85   def find_first(self, pred):
86     """
87     Find the first descendant that matches the predicate.
88
89     Args:
90       pred: a predicate function that acts as a filter for a Node
91
92     Returns:
93       The first Node from find_all(pred), or None if there were no results.
94     """
95     for i in self.find_all(pred):
96       return i
97
98     return None
99
100   def find_parent_first(self, pred):
101     """
102     Find the first ancestor that matches the predicate.
103
104     Args:
105       pred: A predicate function that acts as a filter for a Node
106
107     Returns:
108       The first ancestor closest to the node for which pred(node) is true.
109     """
110     for i in self.find_parents(pred):
111       return i
112
113     return None
114
115   def find_parents(self, pred):
116     """
117     Find all ancestors that match the predicate.
118
119     Args:
120       pred: A predicate function that acts as a filter for a Node
121
122     Yields:
123       A sequence of all ancestors (closest to furthest) from the node,
124       where pred(node) is true.
125     """
126     parent = self.parent
127
128     while parent is not None:
129       if pred(parent):
130         yield parent
131       parent = parent.parent
132
133   def sort_children(self):
134     """
135     Sorts the immediate children in-place.
136     """
137     self._sort_by_name(self._children)
138
139   def _sort_by_name(self, what):
140     what.sort(key=lambda x: x.name)
141
142   def _get_name(self):
143     return lambda x: x.name
144
145   # Iterate over all children nodes. None when node doesn't support children.
146   def _get_children(self):
147     return (i for i in self._children)
148
149   def _children_name_map_matching(self, match=lambda x: True):
150     d = {}
151     for i in self._get_children():
152       if match(i):
153         d[i.name] = i
154     return d
155
156   @staticmethod
157   def _dictionary_by_name(values):
158     d = OrderedDict()
159     for i in values:
160       d[i.name] = i
161
162     return d
163
164   def validate_tree(self):
165     """
166     Sanity check the tree recursively, ensuring for a node n, all children's
167     parents are also n.
168
169     Returns:
170       True if validation succeeds, False otherwise.
171     """
172     succ = True
173     children = self._get_children()
174     if children is None:
175       return True
176
177     for child in self._get_children():
178       if child.parent != self:
179         print >> sys.stderr, ("ERROR: Node '%s' doesn't match the parent" +    \
180                              "(expected: %s, actual %s)")                      \
181                              %(child, self, child.parent)
182         succ = False
183
184       succ = child.validate_tree() and succ
185
186     return succ
187
188   def __str__(self):
189     return "<%s name='%s'>" %(self.__class__, self.name)
190
191 class Metadata(Node):
192   """
193   A node corresponding to a <metadata> entry.
194
195   Attributes (Read-Only):
196     parent: An edge to the parent Node. This is always None for Metadata.
197     outer_namespaces: A sequence of immediate OuterNamespace children.
198     tags: A sequence of all Tag instances available in the graph.
199     types: An iterable of all Typedef instances available in the graph.
200   """
201
202   def __init__(self):
203     """
204     Initialize with no children. Use insert_* functions and then
205     construct_graph() to build up the Metadata from some source.
206     """
207 # Private
208     self._entries = []
209     # kind => { name => entry }
210     self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} }
211     self._entries_ordered = [] # list of ordered Entry/Clone instances
212     self._clones = []
213
214 # Public (Read Only)
215     self._name = None
216     self._parent = None
217     self._outer_namespaces = None
218     self._tags = []
219     self._types = []
220
221   @property
222   def outer_namespaces(self):
223     if self._outer_namespaces is None:
224       return None
225     else:
226       return (i for i in self._outer_namespaces)
227
228   @property
229   def tags(self):
230     return (i for i in self._tags)
231
232   @property
233   def types(self):
234     return (i for i in self._types)
235
236   def _get_properties(self):
237
238     for i in self._entries:
239       yield i
240
241     for i in self._clones:
242       yield i
243
244   def insert_tag(self, tag, description=""):
245     """
246     Insert a tag into the metadata.
247
248     Args:
249       tag: A string identifier for a tag.
250       description: A string description for a tag.
251
252     Example:
253       metadata.insert_tag("BC", "Backwards Compatibility for old API")
254
255     Remarks:
256       Subsequent calls to insert_tag with the same tag are safe (they will
257       be ignored).
258     """
259     tag_ids = [tg.name for tg in self.tags if tg.name == tag]
260     if not tag_ids:
261       self._tags.append(Tag(tag, self, description))
262
263   def insert_type(self, type_name, type_selector="typedef", **kwargs):
264     """
265     Insert a type into the metadata.
266
267     Args:
268       type_name: A type's name
269       type_selector: The selector for the type, e.g. 'typedef'
270
271     Args (if type_selector == 'typedef'):
272       languages: A map of 'language name' -> 'fully qualified class path'
273
274     Example:
275       metadata.insert_type('rectangle', 'typedef',
276                            { 'java': 'android.graphics.Rect' })
277
278     Remarks:
279       Subsequent calls to insert_type with the same type name are safe (they
280       will be ignored)
281     """
282
283     if type_selector != 'typedef':
284       raise ValueError("Unsupported type_selector given " + type_selector)
285
286     type_names = [tp.name for tp in self.types if tp.name == tp]
287     if not type_names:
288       self._types.append(Typedef(type_name, self, kwargs.get('languages')))
289
290   def insert_entry(self, entry):
291     """
292     Insert an entry into the metadata.
293
294     Args:
295       entry: A key-value dictionary describing an entry. Refer to
296              Entry#__init__ for the keys required/optional.
297
298     Remarks:
299       Subsequent calls to insert_entry with the same entry+kind name are safe
300       (they will be ignored).
301     """
302     e = Entry(**entry)
303     self._entries.append(e)
304     self._entry_map[e.kind][e.name] = e
305     self._entries_ordered.append(e)
306
307   def insert_clone(self, clone):
308     """
309     Insert a clone into the metadata.
310
311     Args:
312       clone: A key-value dictionary describing a clone. Refer to
313             Clone#__init__ for the keys required/optional.
314
315     Remarks:
316       Subsequent calls to insert_clone with the same clone+kind name are safe
317       (they will be ignored). Also the target entry need not be inserted
318       ahead of the clone entry.
319     """
320     # figure out corresponding entry later. allow clone insert, entry insert
321     entry = None
322     c = Clone(entry, **clone)
323     self._entry_map[c.kind][c.name] = c
324     self._clones.append(c)
325     self._entries_ordered.append(c)
326
327   def prune_clones(self):
328     """
329     Remove all clones that don't point to an existing entry.
330
331     Remarks:
332       This should be called after all insert_entry/insert_clone calls have
333       finished.
334     """
335     remove_list = []
336     for p in self._clones:
337       if p.entry is None:
338         remove_list.append(p)
339
340     for p in remove_list:
341
342       # remove from parent's entries list
343       if p.parent is not None:
344         p.parent._entries.remove(p)
345       # remove from parents' _leafs list
346       for ancestor in p.find_parents(lambda x: not isinstance(x, Metadata)):
347         ancestor._leafs.remove(p)
348
349       # remove from global list
350       self._clones.remove(p)
351       self._entry_map[p.kind].pop(p.name)
352       self._entries_ordered.remove(p)
353
354   def is_entry_this_kind(self, entry, kind):
355     """
356     Check if input entry if of input kind
357
358     Args:
359       entry: an Entry object
360       kind: a string. Possible values are "static", "dynamic", "controls"
361
362     Returns:
363       A boolean indicating whether input entry is of input kind.
364     """
365     if kind not in ("static", "dynamic", "controls"):
366       assert(False), "Unknown kind value " + kind
367
368     return entry.name in self._entry_map[kind]
369
370   # After all entries/clones are inserted,
371   # invoke this to generate the parent/child node graph all these objects
372   def construct_graph(self):
373     """
374     Generate the graph recursively, after which all Entry nodes will be
375     accessible recursively by crawling through the outer_namespaces sequence.
376
377     Remarks:
378       This is safe to be called multiple times at any time. It should be done at
379       least once or there will be no graph.
380     """
381     self.validate_tree()
382     self._construct_tags()
383     self.validate_tree()
384     self._construct_types()
385     self.validate_tree()
386     self._construct_clones()
387     self.validate_tree()
388     self._construct_outer_namespaces()
389     self.validate_tree()
390
391   def _construct_tags(self):
392     tag_dict = self._dictionary_by_name(self.tags)
393     for p in self._get_properties():
394       p._tags = []
395       for tag_id in p._tag_ids:
396         tag = tag_dict.get(tag_id)
397
398         if tag not in p._tags:
399           p._tags.append(tag)
400
401         if p not in tag.entries:
402           tag._entries.append(p)
403
404   def _construct_types(self):
405     type_dict = self._dictionary_by_name(self.types)
406     for p in self._get_properties():
407       if p._type_name:
408         type_node = type_dict.get(p._type_name)
409         p._typedef = type_node
410
411         if p not in type_node.entries:
412           type_node._entries.append(p)
413
414   def _construct_clones(self):
415     for p in self._clones:
416       target_kind = p.target_kind
417       target_entry = self._entry_map[target_kind].get(p.name)
418       p._entry = target_entry
419
420       # should not throw if we pass validation
421       # but can happen when importing obsolete CSV entries
422       if target_entry is None:
423         print >> sys.stderr, ("WARNING: Clone entry '%s' target kind '%s'" +   \
424                               " has no corresponding entry")                   \
425                              %(p.name, p.target_kind)
426
427   def _construct_outer_namespaces(self):
428
429     if self._outer_namespaces is None: #the first time this runs
430       self._outer_namespaces = []
431
432     root = self._dictionary_by_name(self._outer_namespaces)
433     for ons_name, ons in root.iteritems():
434       ons._leafs = []
435
436     for p in self._entries_ordered:
437       ons_name = p.get_outer_namespace()
438       ons = root.get(ons_name, OuterNamespace(ons_name, self))
439       root[ons_name] = ons
440
441       if p not in ons._leafs:
442         ons._leafs.append(p)
443
444     for ons_name, ons in root.iteritems():
445
446       ons.validate_tree()
447
448       self._construct_sections(ons)
449
450       if ons not in self._outer_namespaces:
451         self._outer_namespaces.append(ons)
452
453       ons.validate_tree()
454
455   def _construct_sections(self, outer_namespace):
456
457     sections_dict = self._dictionary_by_name(outer_namespace.sections)
458     for sec_name, sec in sections_dict.iteritems():
459       sec._leafs = []
460       sec.validate_tree()
461
462     for p in outer_namespace._leafs:
463       does_exist = sections_dict.get(p.get_section())
464
465       sec = sections_dict.get(p.get_section(), \
466           Section(p.get_section(), outer_namespace))
467       sections_dict[p.get_section()] = sec
468
469       sec.validate_tree()
470
471       if p not in sec._leafs:
472         sec._leafs.append(p)
473
474     for sec_name, sec in sections_dict.iteritems():
475
476       if not sec.validate_tree():
477         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
478                              "construct_sections (start), with section = '%s'")\
479                              %(sec)
480
481       self._construct_kinds(sec)
482
483       if sec not in outer_namespace.sections:
484         outer_namespace._sections.append(sec)
485
486       if not sec.validate_tree():
487         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
488                               "construct_sections (end), with section = '%s'") \
489                              %(sec)
490
491   # 'controls', 'static' 'dynamic'. etc
492   def _construct_kinds(self, section):
493     for kind in section.kinds:
494       kind._leafs = []
495       section.validate_tree()
496
497     group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
498     leaf_it = ((k, g) for k, g in group_entry_by_kind)
499
500     # allow multiple kinds with the same name. merge if adjacent
501     # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
502     # this helps maintain ABI compatibility when adding an entry in a new kind
503     for idx, (kind_name, entry_it) in enumerate(leaf_it):
504       if idx >= len(section._kinds):
505         kind = Kind(kind_name, section)
506         section._kinds.append(kind)
507         section.validate_tree()
508
509       kind = section._kinds[idx]
510
511       for p in entry_it:
512         if p not in kind._leafs:
513           kind._leafs.append(p)
514
515     for kind in section._kinds:
516       kind.validate_tree()
517       self._construct_inner_namespaces(kind)
518       kind.validate_tree()
519       self._construct_entries(kind)
520       kind.validate_tree()
521
522       if not section.validate_tree():
523         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
524                              "construct_kinds, with kind = '%s'") %(kind)
525
526       if not kind.validate_tree():
527         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
528                               "construct_kinds, with kind = '%s'") %(kind)
529
530   def _construct_inner_namespaces(self, parent, depth=0):
531     #parent is InnerNamespace or Kind
532     ins_dict = self._dictionary_by_name(parent.namespaces)
533     for name, ins in ins_dict.iteritems():
534       ins._leafs = []
535
536     for p in parent._leafs:
537       ins_list = p.get_inner_namespace_list()
538
539       if len(ins_list) > depth:
540         ins_str = ins_list[depth]
541         ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
542         ins_dict[ins_str] = ins
543
544         if p not in ins._leafs:
545           ins._leafs.append(p)
546
547     for name, ins in ins_dict.iteritems():
548       ins.validate_tree()
549       # construct children INS
550       self._construct_inner_namespaces(ins, depth + 1)
551       ins.validate_tree()
552       # construct children entries
553       self._construct_entries(ins, depth + 1)
554
555       if ins not in parent.namespaces:
556         parent._namespaces.append(ins)
557
558       if not ins.validate_tree():
559         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
560                               "construct_inner_namespaces, with ins = '%s'")   \
561                              %(ins)
562
563   # doesnt construct the entries, so much as links them
564   def _construct_entries(self, parent, depth=0):
565     #parent is InnerNamespace or Kind
566     entry_dict = self._dictionary_by_name(parent.entries)
567     for p in parent._leafs:
568       ins_list = p.get_inner_namespace_list()
569
570       if len(ins_list) == depth:
571         entry = entry_dict.get(p.name, p)
572         entry_dict[p.name] = entry
573
574     for name, entry in entry_dict.iteritems():
575
576       old_parent = entry.parent
577       entry._parent = parent
578
579       if entry not in parent.entries:
580         parent._entries.append(entry)
581
582       if old_parent is not None and old_parent != parent:
583         print >> sys.stderr, ("ERROR: Parent changed from '%s' to '%s' for " + \
584                               "entry '%s'")                                    \
585                              %(old_parent.name, parent.name, entry.name)
586
587   def _get_children(self):
588     if self.outer_namespaces is not None:
589       for i in self.outer_namespaces:
590         yield i
591
592     if self.tags is not None:
593       for i in self.tags:
594         yield i
595
596 class Tag(Node):
597   """
598   A tag Node corresponding to a top-level <tag> element.
599
600   Attributes (Read-Only):
601     name: alias for id
602     id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
603     description: The description of the tag, the contents of the <tag> element.
604     parent: An edge to the parent, which is always the Metadata root node.
605     entries: A sequence of edges to entries/clones that are using this Tag.
606   """
607   def __init__(self, name, parent, description=""):
608     self._name        = name  # 'id' attribute in XML
609     self._id          = name
610     self._description = description
611     self._parent      = parent
612
613     # all entries that have this tag, including clones
614     self._entries     = []  # filled in by Metadata#construct_tags
615
616   @property
617   def id(self):
618     return self._id
619
620   @property
621   def description(self):
622     return self._description
623
624   @property
625   def entries(self):
626     return (i for i in self._entries)
627
628   def _get_children(self):
629     return None
630
631 class Typedef(Node):
632   """
633   A typedef Node corresponding to a <typedef> element under a top-level <types>.
634
635   Attributes (Read-Only):
636     name: The name of this typedef as a string.
637     languages: A dictionary of 'language name' -> 'fully qualified class'.
638     parent: An edge to the parent, which is always the Metadata root node.
639     entries: An iterable over all entries which reference this typedef.
640   """
641   def __init__(self, name, parent, languages=None):
642     self._name        = name
643     self._parent      = parent
644
645     # all entries that have this typedef
646     self._entries     = []  # filled in by Metadata#construct_types
647
648     self._languages   = languages or {}
649
650   @property
651   def languages(self):
652     return self._languages
653
654   @property
655   def entries(self):
656     return (i for i in self._entries)
657
658   def _get_children(self):
659     return None
660
661 class OuterNamespace(Node):
662   """
663   A node corresponding to a <namespace> element under <metadata>
664
665   Attributes (Read-Only):
666     name: The name attribute of the <namespace name="foo"> element.
667     parent: An edge to the parent, which is always the Metadata root node.
668     sections: A sequence of Section children.
669   """
670   def __init__(self, name, parent, sections=[]):
671     self._name = name
672     self._parent = parent # MetadataSet
673     self._sections = sections[:]
674     self._leafs = []
675
676     self._children = self._sections
677
678   @property
679   def sections(self):
680     return (i for i in self._sections)
681
682 class Section(Node):
683   """
684   A node corresponding to a <section> element under <namespace>
685
686   Attributes (Read-Only):
687     name: The name attribute of the <section name="foo"> element.
688     parent: An edge to the parent, which is always an OuterNamespace instance.
689     description: A string description of the section, or None.
690     kinds: A sequence of Kind children.
691     merged_kinds: A sequence of virtual Kind children,
692                   with each Kind's children merged by the kind.name
693     hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
694   """
695   def __init__(self, name, parent, description=None, kinds=[]):
696     self._name = name
697     self._parent = parent
698     self._description = description
699     self._kinds = kinds[:]
700
701     self._leafs = []
702
703   @property
704   def description(self):
705     return self._description
706
707   @property
708   def kinds(self):
709     return (i for i in self._kinds)
710
711   @property
712   def hal_versions(self):
713     hal_versions = set()
714     for i in self._kinds:
715       for entry in i.entries:
716         hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
717       for namespace in i.namespaces:
718         hal_versions.update(namespace.hal_versions)
719     return hal_versions
720
721   def sort_children(self):
722     self.validate_tree()
723     # order is always controls,static,dynamic
724     find_child = lambda x: [i for i in self._get_children() if i.name == x]
725     new_lst = find_child('controls') \
726             + find_child('static')   \
727             + find_child('dynamic')
728     self._kinds = new_lst
729     self.validate_tree()
730
731   def _get_children(self):
732     return (i for i in self.kinds)
733
734   @property
735   def merged_kinds(self):
736
737     def aggregate_by_name(acc, el):
738       existing = [i for i in acc if i.name == el.name]
739       if existing:
740         k = existing[0]
741       else:
742         k = Kind(el.name, el.parent)
743         acc.append(k)
744
745       k._namespaces.extend(el._namespaces)
746       k._entries.extend(el._entries)
747
748       return acc
749
750     new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
751
752     for k in new_kinds_lst:
753       yield k
754
755   def combine_kinds_into_single_node(self):
756     r"""
757     Combines the section's Kinds into a single node.
758
759     Combines all the children (kinds) of this section into a single
760     virtual Kind node.
761
762     Returns:
763       A new Kind node that collapses all Kind siblings into one, combining
764       all their children together.
765
766       For example, given self.kinds == [ x, y ]
767
768         x  y               z
769       / |  | \    -->   / | | \
770       a b  c d          a b c d
771
772       a new instance z is returned in this example.
773
774     Remarks:
775       The children of the kinds are the same references as before, that is
776       their parents will point to the old parents and not to the new parent.
777     """
778     combined = Kind(name="combined", parent=self)
779
780     for k in self._get_children():
781       combined._namespaces.extend(k.namespaces)
782       combined._entries.extend(k.entries)
783
784     return combined
785
786 class Kind(Node):
787   """
788   A node corresponding to one of: <static>,<dynamic>,<controls> under a
789   <section> element.
790
791   Attributes (Read-Only):
792     name: A string which is one of 'static', 'dynamic, or 'controls'.
793     parent: An edge to the parent, which is always a Section  instance.
794     namespaces: A sequence of InnerNamespace children.
795     entries: A sequence of Entry/Clone children.
796     merged_entries: A sequence of MergedEntry virtual nodes from entries
797   """
798   def __init__(self, name, parent):
799     self._name = name
800     self._parent = parent
801     self._namespaces = []
802     self._entries = []
803
804     self._leafs = []
805
806   @property
807   def namespaces(self):
808     return self._namespaces
809
810   @property
811   def entries(self):
812     return self._entries
813
814   @property
815   def merged_entries(self):
816     for i in self.entries:
817       yield i.merge()
818
819   def sort_children(self):
820     self._namespaces.sort(key=self._get_name())
821     self._entries.sort(key=self._get_name())
822
823   def _get_children(self):
824     for i in self.namespaces:
825       yield i
826     for i in self.entries:
827       yield i
828
829   def combine_children_by_name(self):
830     r"""
831     Combine multiple children with the same name into a single node.
832
833     Returns:
834       A new Kind where all of the children with the same name were combined.
835
836       For example:
837
838       Given a Kind k:
839
840               k
841             / | \
842             a b c
843             | | |
844             d e f
845
846       a.name == "foo"
847       b.name == "foo"
848       c.name == "bar"
849
850       The returned Kind will look like this:
851
852              k'
853             /  \
854             a' c'
855           / |  |
856           d e  f
857
858     Remarks:
859       This operation is not recursive. To combine the grandchildren and other
860       ancestors, call this method on the ancestor nodes.
861     """
862     return Kind._combine_children_by_name(self, new_type=type(self))
863
864   # new_type is either Kind or InnerNamespace
865   @staticmethod
866   def _combine_children_by_name(self, new_type):
867     new_ins_dict = OrderedDict()
868     new_ent_dict = OrderedDict()
869
870     for ins in self.namespaces:
871       new_ins = new_ins_dict.setdefault(ins.name,
872                                         InnerNamespace(ins.name, parent=self))
873       new_ins._namespaces.extend(ins.namespaces)
874       new_ins._entries.extend(ins.entries)
875
876     for ent in self.entries:
877       new_ent = new_ent_dict.setdefault(ent.name,
878                                         ent.merge())
879
880     kind = new_type(self.name, self.parent)
881     kind._namespaces = new_ins_dict.values()
882     kind._entries = new_ent_dict.values()
883
884     return kind
885
886 class InnerNamespace(Node):
887   """
888   A node corresponding to a <namespace> which is an ancestor of a Kind.
889   These namespaces may have other namespaces recursively, or entries as leafs.
890
891   Attributes (Read-Only):
892     name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
893     parent: An edge to the parent, which is an InnerNamespace or a Kind.
894     namespaces: A sequence of InnerNamespace children.
895     entries: A sequence of Entry/Clone children.
896     merged_entries: A sequence of MergedEntry virtual nodes from entries
897     hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
898   """
899   def __init__(self, name, parent):
900     self._name        = name
901     self._parent      = parent
902     self._namespaces  = []
903     self._entries     = []
904     self._leafs       = []
905
906   @property
907   def namespaces(self):
908     return self._namespaces
909
910   @property
911   def entries(self):
912     return self._entries
913
914   @property
915   def hal_versions(self):
916     hal_versions = set()
917     for entry in self.entries:
918       hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
919     for namespace in self.namespaces:
920       hal_versions.update(namespace.hal_versions)
921     return hal_versions
922
923   @property
924   def merged_entries(self):
925     for i in self.entries:
926       yield i.merge()
927
928   def sort_children(self):
929     self._namespaces.sort(key=self._get_name())
930     self._entries.sort(key=self._get_name())
931
932   def _get_children(self):
933     for i in self.namespaces:
934       yield i
935     for i in self.entries:
936       yield i
937
938   def combine_children_by_name(self):
939     r"""
940     Combine multiple children with the same name into a single node.
941
942     Returns:
943       A new InnerNamespace where all of the children with the same name were
944       combined.
945
946       For example:
947
948       Given an InnerNamespace i:
949
950               i
951             / | \
952             a b c
953             | | |
954             d e f
955
956       a.name == "foo"
957       b.name == "foo"
958       c.name == "bar"
959
960       The returned InnerNamespace will look like this:
961
962              i'
963             /  \
964             a' c'
965           / |  |
966           d e  f
967
968     Remarks:
969       This operation is not recursive. To combine the grandchildren and other
970       ancestors, call this method on the ancestor nodes.
971     """
972     return Kind._combine_children_by_name(self, new_type=type(self))
973
974 class EnumValue(Node):
975   """
976   A class corresponding to a <value> element within an <enum> within an <entry>.
977
978   Attributes (Read-Only):
979     name: A string,                 e.g. 'ON' or 'OFF'
980     id: An optional numeric string, e.g. '0' or '0xFF'
981     deprecated: A boolean, True if the enum should be deprecated.
982     optional: A boolean
983     hidden: A boolean, True if the enum should be hidden.
984     ndk_hidden: A boolean, True if the enum should be hidden in NDK
985     notes: A string describing the notes, or None.
986     sdk_notes: A string describing extra notes for public SDK only
987     ndk_notes: A string describing extra notes for public NDK only
988     parent: An edge to the parent, always an Enum instance.
989     hal_major_version: The major HIDL HAL version this value was first added in
990     hal_minor_version: The minor HIDL HAL version this value was first added in
991   """
992   def __init__(self, name, parent,
993       id=None, deprecated=False, optional=False, hidden=False, notes=None, sdk_notes=None, ndk_notes=None, ndk_hidden=False, hal_version='3.2'):
994     self._name = name                    # str, e.g. 'ON' or 'OFF'
995     self._id = id                        # int, e.g. '0'
996     self._deprecated = deprecated        # bool
997     self._optional = optional            # bool
998     self._hidden = hidden                # bool
999     self._ndk_hidden = ndk_hidden        # bool
1000     self._notes = notes                  # None or str
1001     self._sdk_notes = sdk_notes          # None or str
1002     self._ndk_notes = ndk_notes          # None or str
1003     self._parent = parent
1004     if hal_version is None:
1005       if parent is not None and parent.parent is not None:
1006         self._hal_major_version = parent.parent.hal_major_version
1007         self._hal_minor_version = parent.parent.hal_minor_version
1008       else:
1009         self._hal_major_version = 3
1010         self._hal_minor_version = 2
1011     else:
1012       self._hal_major_version = int(hal_version.partition('.')[0])
1013       self._hal_minor_version = int(hal_version.partition('.')[2])
1014
1015   @property
1016   def id(self):
1017     return self._id
1018
1019   @property
1020   def deprecated(self):
1021     return self._deprecated
1022
1023   @property
1024   def optional(self):
1025     return self._optional
1026
1027   @property
1028   def hidden(self):
1029     return self._hidden
1030
1031   @property
1032   def ndk_hidden(self):
1033     return self._ndk_hidden
1034
1035   @property
1036   def notes(self):
1037     return self._notes
1038
1039   @property
1040   def sdk_notes(self):
1041     return self._sdk_notes
1042
1043   @property
1044   def ndk_notes(self):
1045     return self._ndk_notes
1046
1047   @property
1048   def hal_major_version(self):
1049     return self._hal_major_version
1050
1051   @property
1052   def hal_minor_version(self):
1053     return self._hal_minor_version
1054
1055   def _get_children(self):
1056     return None
1057
1058 class Enum(Node):
1059   """
1060   A class corresponding to an <enum> element within an <entry>.
1061
1062   Attributes (Read-Only):
1063     parent: An edge to the parent, always an Entry instance.
1064     values: A sequence of EnumValue children.
1065     has_values_with_id: A boolean representing if any of the children have a
1066         non-empty id property.
1067   """
1068   def __init__(self, parent, values, ids={}, deprecateds=[],
1069       optionals=[], hiddens=[], notes={}, sdk_notes={}, ndk_notes={}, ndk_hiddens=[], hal_versions={}):
1070     self._parent = parent
1071     self._name = None
1072     self._values =                                                             \
1073       [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens,  \
1074                   notes.get(val), sdk_notes.get(val), ndk_notes.get(val), val in ndk_hiddens, hal_versions.get(val))     \
1075         for val in values ]
1076
1077   @property
1078   def values(self):
1079     return (i for i in self._values)
1080
1081   @property
1082   def has_values_with_id(self):
1083     return bool(any(i for i in self.values if i.id))
1084
1085   def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
1086     return bool(any(i for i in self.values if i.hal_major_version == hal_major_version and i.hal_minor_version == hal_minor_version))
1087
1088   def _get_children(self):
1089     return (i for i in self._values)
1090
1091 class Entry(Node):
1092   """
1093   A node corresponding to an <entry> element.
1094
1095   Attributes (Read-Only):
1096     parent: An edge to the parent node, which is an InnerNamespace or Kind.
1097     name: The fully qualified name string, e.g. 'android.shading.mode'
1098     name_short: The name attribute from <entry name="mode">, e.g. mode
1099     type: The type attribute from <entry type="bar">
1100     kind: A string ('static', 'dynamic', 'controls') corresponding to the
1101           ancestor Kind#name
1102     container: The container attribute from <entry container="array">, or None.
1103     container_sizes: A sequence of size strings or None if container is None.
1104     enum: An Enum instance if the enum attribute is true, None otherwise.
1105     visibility: The visibility of this entry ('system', 'hidden', 'public')
1106                 across the system. System entries are only visible in native code
1107                 headers. Hidden entries are marked @hide in managed code, while
1108                 public entries are visible in the Android SDK.
1109     applied_visibility: As visibility, but always valid, defaulting to 'system'
1110                         if no visibility is given for an entry.
1111     applied_ndk_visible: Always valid. Default is 'false'.
1112                          Set to 'true' when the visibility implied entry is visible
1113                          in NDK.
1114     synthetic: The C-level visibility of this entry ('false', 'true').
1115                Synthetic entries will not be generated into the native metadata
1116                list of entries (in C code). In general a synthetic entry is
1117                glued together at the Java layer from multiple visibiltity=hidden
1118                entries.
1119     hwlevel: The lowest hardware level at which the entry is guaranteed
1120              to be supported by the camera device. All devices with higher
1121              hwlevels will also include this entry. None means that the
1122              entry is optional on any hardware level.
1123     deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1124                unreleased version this needs to be removed altogether. If applied
1125                to an entry from an older release, then this means the entry
1126                should be ignored by newer code.
1127     optional: a bool representing the optional attribute, which denotes the entry
1128               is required for hardware level full devices, but optional for other
1129               hardware levels.  None if not present.
1130     applied_optional: As optional but always valid, defaulting to False if no
1131                       optional attribute is present.
1132     tuple_values: A sequence of strings describing the tuple values,
1133                   None if container is not 'tuple'.
1134     description: A string description, or None.
1135     deprecation_description: A string describing the reason for deprecation. Must be present
1136                  if deprecated is true, otherwise may be None.
1137     range: A string range, or None.
1138     units: A string units, or None.
1139     tags: A sequence of Tag nodes associated with this Entry.
1140     type_notes: A string describing notes for the type, or None.
1141     typedef: A Typedef associated with this Entry, or None.
1142
1143   Remarks:
1144     Subclass Clone can be used interchangeable with an Entry,
1145     for when we don't care about the underlying type.
1146
1147     parent and tags edges are invalid until after Metadata#construct_graph
1148     has been invoked.
1149   """
1150   def __init__(self, **kwargs):
1151     """
1152     Instantiate a new Entry node.
1153
1154     Args:
1155       name: A string with the fully qualified name, e.g. 'android.shading.mode'
1156       type: A string describing the type, e.g. 'int32'
1157       kind: A string describing the kind, e.g. 'static'
1158       hal_version: A string for the initial HIDL HAL metadata version this entry
1159                    was added in
1160
1161     Args (if container):
1162       container: A string describing the container, e.g. 'array' or 'tuple'
1163       container_sizes: A list of string sizes if a container, or None otherwise
1164
1165     Args (if container is 'tuple'):
1166       tuple_values: A list of tuple values, e.g. ['width', 'height']
1167
1168     Args (if the 'enum' attribute is true):
1169       enum: A boolean, True if this is an enum, False otherwise
1170       enum_values: A list of value strings, e.g. ['ON', 'OFF']
1171       enum_optionals: A list of optional enum values, e.g. ['OFF']
1172       enum_notes: A dictionary of value->notes strings.
1173       enum_ids: A dictionary of value->id strings.
1174       enum_hal_versions: A dictionary of value->hal version strings
1175
1176     Args (if the 'deprecated' attribute is true):
1177       deprecation_description: A string explaining the deprecation, to be added
1178                                to the Java-layer @deprecated tag
1179
1180     Args (optional):
1181       description: A string with a description of the entry.
1182       range: A string with the range of the values of the entry, e.g. '>= 0'
1183       units: A string with the units of the values, e.g. 'inches'
1184       details: A string with the detailed documentation for the entry
1185       hal_details: A string with the HAL implementation details for the entry
1186       ndk_details: A string with the extra NDK API documentation for the entry=
1187       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1188       type_notes: A string with the notes for the type
1189       visibility: A string describing the visibility, eg 'system', 'hidden',
1190                   'public'
1191       synthetic: A bool to mark whether this entry is visible only at the Java
1192                  layer (True), or at both layers (False = default).
1193       hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1194       deprecated: A bool to mark whether this is @Deprecated at the Java layer
1195                  (default = False).
1196       optional: A bool to mark whether optional for non-full hardware devices
1197       typedef: A string corresponding to a typedef's name attribute.
1198     """
1199
1200     if kwargs.get('type') is None:
1201       print >> sys.stderr, "ERROR: Missing type for entry '%s' kind  '%s'"     \
1202       %(kwargs.get('name'), kwargs.get('kind'))
1203
1204     # Attributes are Read-Only, but edges may be mutated by
1205     # Metadata, particularly during construct_graph
1206
1207     self._name = kwargs['name']
1208     self._type = kwargs['type']
1209     self._kind = kwargs['kind'] # static, dynamic, or controls
1210
1211     self._init_common(**kwargs)
1212
1213   @property
1214   def type(self):
1215     return self._type
1216
1217   @property
1218   def kind(self):
1219     return self._kind
1220
1221   @property
1222   def hal_major_version(self):
1223     return self._hal_major_version
1224
1225   @property
1226   def hal_minor_version(self):
1227     return self._hal_minor_version
1228
1229   @property
1230   def visibility(self):
1231     return self._visibility
1232
1233   @property
1234   def applied_visibility(self):
1235     return self._visibility or 'system'
1236
1237   @property
1238   def applied_ndk_visible(self):
1239     if self._visibility in ("public", "ndk_public"):
1240       return "true"
1241     return "false"
1242
1243   @property
1244   def synthetic(self):
1245     return self._synthetic
1246
1247   @property
1248   def hwlevel(self):
1249     return self._hwlevel
1250
1251   @property
1252   def deprecated(self):
1253     return self._deprecated
1254
1255   @property
1256   def deprecation_description(self):
1257     return self._deprecation_description
1258
1259   # TODO: optional should just return hwlevel is None
1260   @property
1261   def optional(self):
1262     return self._optional
1263
1264   @property
1265   def applied_optional(self):
1266     return self._optional or False
1267
1268   @property
1269   def name_short(self):
1270     return self.get_name_minimal()
1271
1272   @property
1273   def container(self):
1274     return self._container
1275
1276   @property
1277   def container_sizes(self):
1278     if self._container_sizes is None:
1279       return None
1280     else:
1281       return (i for i in self._container_sizes)
1282
1283   @property
1284   def tuple_values(self):
1285     if self._tuple_values is None:
1286       return None
1287     else:
1288       return (i for i in self._tuple_values)
1289
1290   @property
1291   def description(self):
1292     return self._description
1293
1294   @property
1295   def range(self):
1296     return self._range
1297
1298   @property
1299   def units(self):
1300     return self._units
1301
1302   @property
1303   def details(self):
1304     return self._details
1305
1306   @property
1307   def hal_details(self):
1308     return self._hal_details
1309
1310   @property
1311   def ndk_details(self):
1312     return self._ndk_details
1313
1314   @property
1315   def applied_ndk_details(self):
1316     return (self._details or "") + (self._ndk_details or "")
1317
1318   @property
1319   def tags(self):
1320     if self._tags is None:
1321       return None
1322     else:
1323       return (i for i in self._tags)
1324
1325   @property
1326   def type_notes(self):
1327     return self._type_notes
1328
1329   @property
1330   def typedef(self):
1331     return self._typedef
1332
1333   @property
1334   def enum(self):
1335     return self._enum
1336
1337   def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
1338     if self._enum is not None:
1339       return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version)
1340     else:
1341       return False
1342
1343   def _get_children(self):
1344     if self.enum:
1345       yield self.enum
1346
1347   def sort_children(self):
1348     return None
1349
1350   def is_clone(self):
1351     """
1352     Whether or not this is a Clone instance.
1353
1354     Returns:
1355       False
1356     """
1357     return False
1358
1359   def _init_common(self, **kwargs):
1360
1361     self._parent = None # filled in by Metadata::_construct_entries
1362
1363     self._container = kwargs.get('container')
1364     self._container_sizes = kwargs.get('container_sizes')
1365
1366     hal_version = kwargs.get('hal_version')
1367     if hal_version is None:
1368       self._hal_major_version = 3
1369       self._hal_minor_version = 2
1370     else:
1371       self._hal_major_version = int(hal_version.partition('.')[0])
1372       self._hal_minor_version = int(hal_version.partition('.')[2])
1373
1374     # access these via the 'enum' prop
1375     enum_values = kwargs.get('enum_values')
1376     enum_deprecateds = kwargs.get('enum_deprecateds')
1377     enum_optionals = kwargs.get('enum_optionals')
1378     enum_hiddens = kwargs.get('enum_hiddens')
1379     enum_ndk_hiddens = kwargs.get('enum_ndk_hiddens')
1380     enum_notes = kwargs.get('enum_notes')  # { value => notes }
1381     enum_sdk_notes = kwargs.get('enum_sdk_notes')  # { value => sdk_notes }
1382     enum_ndk_notes = kwargs.get('enum_ndk_notes')  # { value => ndk_notes }
1383     enum_ids = kwargs.get('enum_ids')  # { value => notes }
1384     enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions }
1385
1386     self._tuple_values = kwargs.get('tuple_values')
1387
1388     self._description = kwargs.get('description')
1389     self._range = kwargs.get('range')
1390     self._units = kwargs.get('units')
1391     self._details = kwargs.get('details')
1392     self._hal_details = kwargs.get('hal_details')
1393     self._ndk_details = kwargs.get('ndk_details')
1394
1395     self._tag_ids = kwargs.get('tag_ids', [])
1396     self._tags = None  # Filled in by Metadata::_construct_tags
1397
1398     self._type_notes = kwargs.get('type_notes')
1399     self._type_name = kwargs.get('type_name')
1400     self._typedef = None # Filled in by Metadata::_construct_types
1401
1402     if kwargs.get('enum', False):
1403       self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1404                         enum_hiddens, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_ndk_hiddens, enum_hal_versions)
1405     else:
1406       self._enum = None
1407
1408     self._visibility = kwargs.get('visibility')
1409     self._synthetic = kwargs.get('synthetic', False)
1410     self._hwlevel = kwargs.get('hwlevel')
1411     self._deprecated = kwargs.get('deprecated', False)
1412     self._deprecation_description = kwargs.get('deprecation_description')
1413
1414     self._optional = kwargs.get('optional')
1415     self._ndk_visible = kwargs.get('ndk_visible')
1416
1417     self._property_keys = kwargs
1418
1419   def merge(self):
1420     """
1421     Copy the attributes into a new entry, merging it with the target entry
1422     if it's a clone.
1423     """
1424     return MergedEntry(self)
1425
1426   # Helpers for accessing less than the fully qualified name
1427
1428   def get_name_as_list(self):
1429     """
1430     Returns the name as a list split by a period.
1431
1432     For example:
1433       entry.name is 'android.lens.info.shading'
1434       entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1435     """
1436     return self.name.split(".")
1437
1438   def get_inner_namespace_list(self):
1439     """
1440     Returns the inner namespace part of the name as a list
1441
1442     For example:
1443       entry.name is 'android.lens.info.shading'
1444       entry.get_inner_namespace_list() == ['info']
1445     """
1446     return self.get_name_as_list()[2:-1]
1447
1448   def get_outer_namespace(self):
1449     """
1450     Returns the outer namespace as a string.
1451
1452     For example:
1453       entry.name is 'android.lens.info.shading'
1454       entry.get_outer_namespace() == 'android'
1455
1456     Remarks:
1457       Since outer namespaces are non-recursive,
1458       and each entry has one, this does not need to be a list.
1459     """
1460     return self.get_name_as_list()[0]
1461
1462   def get_section(self):
1463     """
1464     Returns the section as a string.
1465
1466     For example:
1467       entry.name is 'android.lens.info.shading'
1468       entry.get_section() == ''
1469
1470     Remarks:
1471       Since outer namespaces are non-recursive,
1472       and each entry has one, this does not need to be a list.
1473     """
1474     return self.get_name_as_list()[1]
1475
1476   def get_name_minimal(self):
1477     """
1478     Returns only the last component of the fully qualified name as a string.
1479
1480     For example:
1481       entry.name is 'android.lens.info.shading'
1482       entry.get_name_minimal() == 'shading'
1483
1484     Remarks:
1485       entry.name_short it an alias for this
1486     """
1487     return self.get_name_as_list()[-1]
1488
1489   def get_path_without_name(self):
1490     """
1491     Returns a string path to the entry, with the name component excluded.
1492
1493     For example:
1494       entry.name is 'android.lens.info.shading'
1495       entry.get_path_without_name() == 'android.lens.info'
1496     """
1497     return ".".join(self.get_name_as_list()[0:-1])
1498
1499
1500 class Clone(Entry):
1501   """
1502   A Node corresponding to a <clone> element. It has all the attributes of an
1503   <entry> element (Entry) plus the additions specified below.
1504
1505   Attributes (Read-Only):
1506     entry: an edge to an Entry object that this targets
1507     target_kind: A string describing the kind of the target entry.
1508     name: a string of the name, same as entry.name
1509     kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1510           for the <clone> element.
1511     type: always None, since a clone cannot override the type.
1512   """
1513   def __init__(self, entry=None, **kwargs):
1514     """
1515     Instantiate a new Clone node.
1516
1517     Args:
1518       name: A string with the fully qualified name, e.g. 'android.shading.mode'
1519       type: A string describing the type, e.g. 'int32'
1520       kind: A string describing the kind, e.g. 'static'
1521       target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1522       hal_version: A string for the initial HIDL HAL metadata version this entry
1523                    was added in
1524
1525     Args (if container):
1526       container: A string describing the container, e.g. 'array' or 'tuple'
1527       container_sizes: A list of string sizes if a container, or None otherwise
1528
1529     Args (if container is 'tuple'):
1530       tuple_values: A list of tuple values, e.g. ['width', 'height']
1531
1532     Args (if the 'enum' attribute is true):
1533       enum: A boolean, True if this is an enum, False otherwise
1534       enum_values: A list of value strings, e.g. ['ON', 'OFF']
1535       enum_optionals: A list of optional enum values, e.g. ['OFF']
1536       enum_notes: A dictionary of value->notes strings.
1537       enum_ids: A dictionary of value->id strings.
1538
1539     Args (optional):
1540       entry: An edge to the corresponding target Entry.
1541       description: A string with a description of the entry.
1542       range: A string with the range of the values of the entry, e.g. '>= 0'
1543       units: A string with the units of the values, e.g. 'inches'
1544       details: A string with the detailed documentation for the entry
1545       hal_details: A string with the HAL implementation details for the entry
1546       ndk_details: A string with the extra NDK documentation for the entry
1547       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1548       type_notes: A string with the notes for the type
1549
1550     Remarks:
1551       Note that type is not specified since it has to be the same as the
1552       entry.type.
1553     """
1554     self._entry = entry  # Entry object
1555     self._target_kind = kwargs['target_kind']
1556     self._name = kwargs['name']  # same as entry.name
1557     self._kind = kwargs['kind']
1558
1559     # illegal to override the type, it should be the same as the entry
1560     self._type = None
1561     # the rest of the kwargs are optional
1562     # can be used to override the regular entry data
1563     self._init_common(**kwargs)
1564
1565   @property
1566   def entry(self):
1567     return self._entry
1568
1569   @property
1570   def target_kind(self):
1571     return self._target_kind
1572
1573   def is_clone(self):
1574     """
1575     Whether or not this is a Clone instance.
1576
1577     Returns:
1578       True
1579     """
1580     return True
1581
1582 class MergedEntry(Entry):
1583   """
1584   A MergedEntry has all the attributes of a Clone and its target Entry merged
1585   together.
1586
1587   Remarks:
1588     Useful when we want to 'unfold' a clone into a real entry by copying out
1589     the target entry data. In this case we don't care about distinguishing
1590     a clone vs an entry.
1591   """
1592   def __init__(self, entry):
1593     """
1594     Create a new instance of MergedEntry.
1595
1596     Args:
1597       entry: An Entry or Clone instance
1598     """
1599     props_distinct = ['description', 'units', 'range', 'details',
1600                       'hal_details', 'ndk_details', 'tags', 'kind',
1601                       'deprecation_description']
1602
1603     for p in props_distinct:
1604       p = '_' + p
1605       if entry.is_clone():
1606         setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1607       else:
1608         setattr(self, p, getattr(entry, p))
1609
1610     props_common = ['parent', 'name', 'container',
1611                     'container_sizes', 'enum',
1612                     'tuple_values',
1613                     'type',
1614                     'type_notes',
1615                     'visibility',
1616                     'ndk_visible',
1617                     'synthetic',
1618                     'hwlevel',
1619                     'deprecated',
1620                     'optional',
1621                     'typedef',
1622                     'hal_major_version',
1623                     'hal_minor_version'
1624                    ]
1625
1626     for p in props_common:
1627       p = '_' + p
1628       if entry.is_clone():
1629         setattr(self, p, getattr(entry.entry, p))
1630       else:
1631         setattr(self, p, getattr(entry, p))