OSDN Git Service

Merge "Add missing free after read device info" am: 384bd00de2 am: e1087bb8ef
[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     range: A string range, or None.
1136     units: A string units, or None.
1137     tags: A sequence of Tag nodes associated with this Entry.
1138     type_notes: A string describing notes for the type, or None.
1139     typedef: A Typedef associated with this Entry, or None.
1140
1141   Remarks:
1142     Subclass Clone can be used interchangeable with an Entry,
1143     for when we don't care about the underlying type.
1144
1145     parent and tags edges are invalid until after Metadata#construct_graph
1146     has been invoked.
1147   """
1148   def __init__(self, **kwargs):
1149     """
1150     Instantiate a new Entry node.
1151
1152     Args:
1153       name: A string with the fully qualified name, e.g. 'android.shading.mode'
1154       type: A string describing the type, e.g. 'int32'
1155       kind: A string describing the kind, e.g. 'static'
1156       hal_version: A string for the initial HIDL HAL metadata version this entry
1157                    was added in
1158
1159     Args (if container):
1160       container: A string describing the container, e.g. 'array' or 'tuple'
1161       container_sizes: A list of string sizes if a container, or None otherwise
1162
1163     Args (if container is 'tuple'):
1164       tuple_values: A list of tuple values, e.g. ['width', 'height']
1165
1166     Args (if the 'enum' attribute is true):
1167       enum: A boolean, True if this is an enum, False otherwise
1168       enum_values: A list of value strings, e.g. ['ON', 'OFF']
1169       enum_optionals: A list of optional enum values, e.g. ['OFF']
1170       enum_notes: A dictionary of value->notes strings.
1171       enum_ids: A dictionary of value->id strings.
1172       enum_hal_versions: A dictionary of value->hal version strings
1173
1174     Args (optional):
1175       description: A string with a description of the entry.
1176       range: A string with the range of the values of the entry, e.g. '>= 0'
1177       units: A string with the units of the values, e.g. 'inches'
1178       details: A string with the detailed documentation for the entry
1179       hal_details: A string with the HAL implementation details for the entry
1180       ndk_details: A string with the extra NDK API documentation for the entry=
1181       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1182       type_notes: A string with the notes for the type
1183       visibility: A string describing the visibility, eg 'system', 'hidden',
1184                   'public'
1185       synthetic: A bool to mark whether this entry is visible only at the Java
1186                  layer (True), or at both layers (False = default).
1187       hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1188       deprecated: A bool to mark whether this is @Deprecated at the Java layer
1189                  (default = False).
1190       optional: A bool to mark whether optional for non-full hardware devices
1191       typedef: A string corresponding to a typedef's name attribute.
1192     """
1193
1194     if kwargs.get('type') is None:
1195       print >> sys.stderr, "ERROR: Missing type for entry '%s' kind  '%s'"     \
1196       %(kwargs.get('name'), kwargs.get('kind'))
1197
1198     # Attributes are Read-Only, but edges may be mutated by
1199     # Metadata, particularly during construct_graph
1200
1201     self._name = kwargs['name']
1202     self._type = kwargs['type']
1203     self._kind = kwargs['kind'] # static, dynamic, or controls
1204
1205     self._init_common(**kwargs)
1206
1207   @property
1208   def type(self):
1209     return self._type
1210
1211   @property
1212   def kind(self):
1213     return self._kind
1214
1215   @property
1216   def hal_major_version(self):
1217     return self._hal_major_version
1218
1219   @property
1220   def hal_minor_version(self):
1221     return self._hal_minor_version
1222
1223   @property
1224   def visibility(self):
1225     return self._visibility
1226
1227   @property
1228   def applied_visibility(self):
1229     return self._visibility or 'system'
1230
1231   @property
1232   def applied_ndk_visible(self):
1233     if self._visibility in ("public", "ndk_public"):
1234       return "true"
1235     return "false"
1236
1237   @property
1238   def synthetic(self):
1239     return self._synthetic
1240
1241   @property
1242   def hwlevel(self):
1243     return self._hwlevel
1244
1245   @property
1246   def deprecated(self):
1247     return self._deprecated
1248
1249   # TODO: optional should just return hwlevel is None
1250   @property
1251   def optional(self):
1252     return self._optional
1253
1254   @property
1255   def applied_optional(self):
1256     return self._optional or False
1257
1258   @property
1259   def name_short(self):
1260     return self.get_name_minimal()
1261
1262   @property
1263   def container(self):
1264     return self._container
1265
1266   @property
1267   def container_sizes(self):
1268     if self._container_sizes is None:
1269       return None
1270     else:
1271       return (i for i in self._container_sizes)
1272
1273   @property
1274   def tuple_values(self):
1275     if self._tuple_values is None:
1276       return None
1277     else:
1278       return (i for i in self._tuple_values)
1279
1280   @property
1281   def description(self):
1282     return self._description
1283
1284   @property
1285   def range(self):
1286     return self._range
1287
1288   @property
1289   def units(self):
1290     return self._units
1291
1292   @property
1293   def details(self):
1294     return self._details
1295
1296   @property
1297   def hal_details(self):
1298     return self._hal_details
1299
1300   @property
1301   def ndk_details(self):
1302     return self._ndk_details
1303
1304   @property
1305   def applied_ndk_details(self):
1306     return (self._details or "") + (self._ndk_details or "")
1307
1308   @property
1309   def tags(self):
1310     if self._tags is None:
1311       return None
1312     else:
1313       return (i for i in self._tags)
1314
1315   @property
1316   def type_notes(self):
1317     return self._type_notes
1318
1319   @property
1320   def typedef(self):
1321     return self._typedef
1322
1323   @property
1324   def enum(self):
1325     return self._enum
1326
1327   def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
1328     if self._enum is not None:
1329       return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version)
1330     else:
1331       return False
1332
1333   def _get_children(self):
1334     if self.enum:
1335       yield self.enum
1336
1337   def sort_children(self):
1338     return None
1339
1340   def is_clone(self):
1341     """
1342     Whether or not this is a Clone instance.
1343
1344     Returns:
1345       False
1346     """
1347     return False
1348
1349   def _init_common(self, **kwargs):
1350
1351     self._parent = None # filled in by Metadata::_construct_entries
1352
1353     self._container = kwargs.get('container')
1354     self._container_sizes = kwargs.get('container_sizes')
1355
1356     hal_version = kwargs.get('hal_version')
1357     if hal_version is None:
1358       self._hal_major_version = 3
1359       self._hal_minor_version = 2
1360     else:
1361       self._hal_major_version = int(hal_version.partition('.')[0])
1362       self._hal_minor_version = int(hal_version.partition('.')[2])
1363
1364     # access these via the 'enum' prop
1365     enum_values = kwargs.get('enum_values')
1366     enum_deprecateds = kwargs.get('enum_deprecateds')
1367     enum_optionals = kwargs.get('enum_optionals')
1368     enum_hiddens = kwargs.get('enum_hiddens')
1369     enum_ndk_hiddens = kwargs.get('enum_ndk_hiddens')
1370     enum_notes = kwargs.get('enum_notes')  # { value => notes }
1371     enum_sdk_notes = kwargs.get('enum_sdk_notes')  # { value => sdk_notes }
1372     enum_ndk_notes = kwargs.get('enum_ndk_notes')  # { value => ndk_notes }
1373     enum_ids = kwargs.get('enum_ids')  # { value => notes }
1374     enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions }
1375
1376     self._tuple_values = kwargs.get('tuple_values')
1377
1378     self._description = kwargs.get('description')
1379     self._range = kwargs.get('range')
1380     self._units = kwargs.get('units')
1381     self._details = kwargs.get('details')
1382     self._hal_details = kwargs.get('hal_details')
1383     self._ndk_details = kwargs.get('ndk_details')
1384
1385     self._tag_ids = kwargs.get('tag_ids', [])
1386     self._tags = None  # Filled in by Metadata::_construct_tags
1387
1388     self._type_notes = kwargs.get('type_notes')
1389     self._type_name = kwargs.get('type_name')
1390     self._typedef = None # Filled in by Metadata::_construct_types
1391
1392     if kwargs.get('enum', False):
1393       self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1394                         enum_hiddens, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_ndk_hiddens, enum_hal_versions)
1395     else:
1396       self._enum = None
1397
1398     self._visibility = kwargs.get('visibility')
1399     self._synthetic = kwargs.get('synthetic', False)
1400     self._hwlevel = kwargs.get('hwlevel')
1401     self._deprecated = kwargs.get('deprecated', False)
1402     self._optional = kwargs.get('optional')
1403     self._ndk_visible = kwargs.get('ndk_visible')
1404
1405     self._property_keys = kwargs
1406
1407   def merge(self):
1408     """
1409     Copy the attributes into a new entry, merging it with the target entry
1410     if it's a clone.
1411     """
1412     return MergedEntry(self)
1413
1414   # Helpers for accessing less than the fully qualified name
1415
1416   def get_name_as_list(self):
1417     """
1418     Returns the name as a list split by a period.
1419
1420     For example:
1421       entry.name is 'android.lens.info.shading'
1422       entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1423     """
1424     return self.name.split(".")
1425
1426   def get_inner_namespace_list(self):
1427     """
1428     Returns the inner namespace part of the name as a list
1429
1430     For example:
1431       entry.name is 'android.lens.info.shading'
1432       entry.get_inner_namespace_list() == ['info']
1433     """
1434     return self.get_name_as_list()[2:-1]
1435
1436   def get_outer_namespace(self):
1437     """
1438     Returns the outer namespace as a string.
1439
1440     For example:
1441       entry.name is 'android.lens.info.shading'
1442       entry.get_outer_namespace() == 'android'
1443
1444     Remarks:
1445       Since outer namespaces are non-recursive,
1446       and each entry has one, this does not need to be a list.
1447     """
1448     return self.get_name_as_list()[0]
1449
1450   def get_section(self):
1451     """
1452     Returns the section as a string.
1453
1454     For example:
1455       entry.name is 'android.lens.info.shading'
1456       entry.get_section() == ''
1457
1458     Remarks:
1459       Since outer namespaces are non-recursive,
1460       and each entry has one, this does not need to be a list.
1461     """
1462     return self.get_name_as_list()[1]
1463
1464   def get_name_minimal(self):
1465     """
1466     Returns only the last component of the fully qualified name as a string.
1467
1468     For example:
1469       entry.name is 'android.lens.info.shading'
1470       entry.get_name_minimal() == 'shading'
1471
1472     Remarks:
1473       entry.name_short it an alias for this
1474     """
1475     return self.get_name_as_list()[-1]
1476
1477   def get_path_without_name(self):
1478     """
1479     Returns a string path to the entry, with the name component excluded.
1480
1481     For example:
1482       entry.name is 'android.lens.info.shading'
1483       entry.get_path_without_name() == 'android.lens.info'
1484     """
1485     return ".".join(self.get_name_as_list()[0:-1])
1486
1487
1488 class Clone(Entry):
1489   """
1490   A Node corresponding to a <clone> element. It has all the attributes of an
1491   <entry> element (Entry) plus the additions specified below.
1492
1493   Attributes (Read-Only):
1494     entry: an edge to an Entry object that this targets
1495     target_kind: A string describing the kind of the target entry.
1496     name: a string of the name, same as entry.name
1497     kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1498           for the <clone> element.
1499     type: always None, since a clone cannot override the type.
1500   """
1501   def __init__(self, entry=None, **kwargs):
1502     """
1503     Instantiate a new Clone node.
1504
1505     Args:
1506       name: A string with the fully qualified name, e.g. 'android.shading.mode'
1507       type: A string describing the type, e.g. 'int32'
1508       kind: A string describing the kind, e.g. 'static'
1509       target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1510       hal_version: A string for the initial HIDL HAL metadata version this entry
1511                    was added in
1512
1513     Args (if container):
1514       container: A string describing the container, e.g. 'array' or 'tuple'
1515       container_sizes: A list of string sizes if a container, or None otherwise
1516
1517     Args (if container is 'tuple'):
1518       tuple_values: A list of tuple values, e.g. ['width', 'height']
1519
1520     Args (if the 'enum' attribute is true):
1521       enum: A boolean, True if this is an enum, False otherwise
1522       enum_values: A list of value strings, e.g. ['ON', 'OFF']
1523       enum_optionals: A list of optional enum values, e.g. ['OFF']
1524       enum_notes: A dictionary of value->notes strings.
1525       enum_ids: A dictionary of value->id strings.
1526
1527     Args (optional):
1528       entry: An edge to the corresponding target Entry.
1529       description: A string with a description of the entry.
1530       range: A string with the range of the values of the entry, e.g. '>= 0'
1531       units: A string with the units of the values, e.g. 'inches'
1532       details: A string with the detailed documentation for the entry
1533       hal_details: A string with the HAL implementation details for the entry
1534       ndk_details: A string with the extra NDK documentation for the entry
1535       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1536       type_notes: A string with the notes for the type
1537
1538     Remarks:
1539       Note that type is not specified since it has to be the same as the
1540       entry.type.
1541     """
1542     self._entry = entry  # Entry object
1543     self._target_kind = kwargs['target_kind']
1544     self._name = kwargs['name']  # same as entry.name
1545     self._kind = kwargs['kind']
1546
1547     # illegal to override the type, it should be the same as the entry
1548     self._type = None
1549     # the rest of the kwargs are optional
1550     # can be used to override the regular entry data
1551     self._init_common(**kwargs)
1552
1553   @property
1554   def entry(self):
1555     return self._entry
1556
1557   @property
1558   def target_kind(self):
1559     return self._target_kind
1560
1561   def is_clone(self):
1562     """
1563     Whether or not this is a Clone instance.
1564
1565     Returns:
1566       True
1567     """
1568     return True
1569
1570 class MergedEntry(Entry):
1571   """
1572   A MergedEntry has all the attributes of a Clone and its target Entry merged
1573   together.
1574
1575   Remarks:
1576     Useful when we want to 'unfold' a clone into a real entry by copying out
1577     the target entry data. In this case we don't care about distinguishing
1578     a clone vs an entry.
1579   """
1580   def __init__(self, entry):
1581     """
1582     Create a new instance of MergedEntry.
1583
1584     Args:
1585       entry: An Entry or Clone instance
1586     """
1587     props_distinct = ['description', 'units', 'range', 'details',
1588                       'hal_details', 'ndk_details', 'tags', 'kind']
1589
1590     for p in props_distinct:
1591       p = '_' + p
1592       if entry.is_clone():
1593         setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1594       else:
1595         setattr(self, p, getattr(entry, p))
1596
1597     props_common = ['parent', 'name', 'container',
1598                     'container_sizes', 'enum',
1599                     'tuple_values',
1600                     'type',
1601                     'type_notes',
1602                     'visibility',
1603                     'ndk_visible',
1604                     'synthetic',
1605                     'hwlevel',
1606                     'deprecated',
1607                     'optional',
1608                     'typedef',
1609                     'hal_major_version',
1610                     'hal_minor_version'
1611                    ]
1612
1613     for p in props_common:
1614       p = '_' + p
1615       if entry.is_clone():
1616         setattr(self, p, getattr(entry.entry, p))
1617       else:
1618         setattr(self, p, getattr(entry, p))