1663 lines
50 KiB
Python
1663 lines
50 KiB
Python
#!/usr/bin/python
|
|
|
|
#
|
|
# Copyright (C) 2012 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
"""
|
|
A set of classes (models) each closely representing an XML node in the
|
|
metadata_definitions.xml file.
|
|
|
|
Node: Base class for most nodes.
|
|
Entry: A node corresponding to <entry> elements.
|
|
Clone: A node corresponding to <clone> elements.
|
|
MergedEntry: A node corresponding to either <entry> or <clone> elements.
|
|
Kind: A node corresponding to <dynamic>, <static>, <controls> elements.
|
|
InnerNamespace: A node corresponding to a <namespace> nested under a <kind>.
|
|
OuterNamespace: A node corresponding to a <namespace> with <kind> children.
|
|
Section: A node corresponding to a <section> element.
|
|
Enum: A class corresponding an <enum> element within an <entry>
|
|
EnumValue: A class corresponding to a <value> element within an Enum
|
|
Metadata: Root node that also provides tree construction functionality.
|
|
Tag: A node corresponding to a top level <tag> element.
|
|
Typedef: A node corresponding to a <typedef> element under <types>.
|
|
"""
|
|
|
|
import sys
|
|
import functools
|
|
import itertools
|
|
from collections import OrderedDict
|
|
|
|
class Node(object):
|
|
"""
|
|
Base class for most nodes that are part of the Metadata graph.
|
|
|
|
Attributes (Read-Only):
|
|
parent: An edge to a parent Node.
|
|
name: A string describing the name, usually but not always the 'name'
|
|
attribute of the corresponding XML node.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._parent = None
|
|
self._name = None
|
|
|
|
@property
|
|
def parent(self):
|
|
return self._parent
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
def find_all(self, pred):
|
|
"""
|
|
Find all descendants that match the predicate.
|
|
|
|
Args:
|
|
pred: a predicate function that acts as a filter for a Node
|
|
|
|
Yields:
|
|
A sequence of all descendants for which pred(node) is true,
|
|
in a pre-order visit order.
|
|
"""
|
|
if pred(self):
|
|
yield self
|
|
|
|
if self._get_children() is None:
|
|
return
|
|
|
|
for i in self._get_children():
|
|
for j in i.find_all(pred):
|
|
yield j
|
|
|
|
def find_first(self, pred):
|
|
"""
|
|
Find the first descendant that matches the predicate.
|
|
|
|
Args:
|
|
pred: a predicate function that acts as a filter for a Node
|
|
|
|
Returns:
|
|
The first Node from find_all(pred), or None if there were no results.
|
|
"""
|
|
for i in self.find_all(pred):
|
|
return i
|
|
|
|
return None
|
|
|
|
def find_parent_first(self, pred):
|
|
"""
|
|
Find the first ancestor that matches the predicate.
|
|
|
|
Args:
|
|
pred: A predicate function that acts as a filter for a Node
|
|
|
|
Returns:
|
|
The first ancestor closest to the node for which pred(node) is true.
|
|
"""
|
|
for i in self.find_parents(pred):
|
|
return i
|
|
|
|
return None
|
|
|
|
def find_parents(self, pred):
|
|
"""
|
|
Find all ancestors that match the predicate.
|
|
|
|
Args:
|
|
pred: A predicate function that acts as a filter for a Node
|
|
|
|
Yields:
|
|
A sequence of all ancestors (closest to furthest) from the node,
|
|
where pred(node) is true.
|
|
"""
|
|
parent = self.parent
|
|
|
|
while parent is not None:
|
|
if pred(parent):
|
|
yield parent
|
|
parent = parent.parent
|
|
|
|
def sort_children(self):
|
|
"""
|
|
Sorts the immediate children in-place.
|
|
"""
|
|
self._sort_by_name(self._children)
|
|
|
|
def _sort_by_name(self, what):
|
|
what.sort(key=lambda x: x.name)
|
|
|
|
def _get_name(self):
|
|
return lambda x: x.name
|
|
|
|
# Iterate over all children nodes. None when node doesn't support children.
|
|
def _get_children(self):
|
|
return (i for i in self._children)
|
|
|
|
def _children_name_map_matching(self, match=lambda x: True):
|
|
d = {}
|
|
for i in self._get_children():
|
|
if match(i):
|
|
d[i.name] = i
|
|
return d
|
|
|
|
@staticmethod
|
|
def _dictionary_by_name(values):
|
|
d = OrderedDict()
|
|
for i in values:
|
|
d[i.name] = i
|
|
|
|
return d
|
|
|
|
def validate_tree(self):
|
|
"""
|
|
Sanity check the tree recursively, ensuring for a node n, all children's
|
|
parents are also n.
|
|
|
|
Returns:
|
|
True if validation succeeds, False otherwise.
|
|
"""
|
|
succ = True
|
|
children = self._get_children()
|
|
if children is None:
|
|
return True
|
|
|
|
for child in self._get_children():
|
|
if child.parent != self:
|
|
print("ERROR: Node '%s' doesn't match the parent (expected: %s, actual %s)" \
|
|
% (child, self, child.parent), file=sys.stderr)
|
|
succ = False
|
|
|
|
succ = child.validate_tree() and succ
|
|
|
|
return succ
|
|
|
|
def __str__(self):
|
|
return "<%s name='%s'>" %(self.__class__, self.name)
|
|
|
|
class Metadata(Node):
|
|
"""
|
|
A node corresponding to a <metadata> entry.
|
|
|
|
Attributes (Read-Only):
|
|
parent: An edge to the parent Node. This is always None for Metadata.
|
|
outer_namespaces: A sequence of immediate OuterNamespace children.
|
|
tags: A sequence of all Tag instances available in the graph.
|
|
types: An iterable of all Typedef instances available in the graph.
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""
|
|
Initialize with no children. Use insert_* functions and then
|
|
construct_graph() to build up the Metadata from some source.
|
|
"""
|
|
# Private
|
|
self._entries = []
|
|
# kind => { name => entry }
|
|
self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} }
|
|
self._entries_ordered = [] # list of ordered Entry/Clone instances
|
|
self._clones = []
|
|
|
|
# Public (Read Only)
|
|
self._name = None
|
|
self._parent = None
|
|
self._outer_namespaces = None
|
|
self._tags = []
|
|
self._types = []
|
|
|
|
@property
|
|
def outer_namespaces(self):
|
|
if self._outer_namespaces is None:
|
|
return None
|
|
else:
|
|
return (i for i in self._outer_namespaces)
|
|
|
|
@property
|
|
def tags(self):
|
|
return (i for i in self._tags)
|
|
|
|
@property
|
|
def types(self):
|
|
return (i for i in self._types)
|
|
|
|
def _get_properties(self):
|
|
|
|
for i in self._entries:
|
|
yield i
|
|
|
|
for i in self._clones:
|
|
yield i
|
|
|
|
def insert_tag(self, tag, description=""):
|
|
"""
|
|
Insert a tag into the metadata.
|
|
|
|
Args:
|
|
tag: A string identifier for a tag.
|
|
description: A string description for a tag.
|
|
|
|
Example:
|
|
metadata.insert_tag("BC", "Backwards Compatibility for old API")
|
|
|
|
Remarks:
|
|
Subsequent calls to insert_tag with the same tag are safe (they will
|
|
be ignored).
|
|
"""
|
|
tag_ids = [tg.name for tg in self.tags if tg.name == tag]
|
|
if not tag_ids:
|
|
self._tags.append(Tag(tag, self, description))
|
|
|
|
def insert_type(self, type_name, type_selector="typedef", **kwargs):
|
|
"""
|
|
Insert a type into the metadata.
|
|
|
|
Args:
|
|
type_name: A type's name
|
|
type_selector: The selector for the type, e.g. 'typedef'
|
|
|
|
Args (if type_selector == 'typedef'):
|
|
languages: A map of 'language name' -> 'fully qualified class path'
|
|
|
|
Example:
|
|
metadata.insert_type('rectangle', 'typedef',
|
|
{ 'java': 'android.graphics.Rect' })
|
|
|
|
Remarks:
|
|
Subsequent calls to insert_type with the same type name are safe (they
|
|
will be ignored)
|
|
"""
|
|
|
|
if type_selector != 'typedef':
|
|
raise ValueError("Unsupported type_selector given " + type_selector)
|
|
|
|
type_names = [tp.name for tp in self.types if tp.name == tp]
|
|
if not type_names:
|
|
self._types.append(Typedef(type_name, self, kwargs.get('languages')))
|
|
|
|
def insert_entry(self, entry):
|
|
"""
|
|
Insert an entry into the metadata.
|
|
|
|
Args:
|
|
entry: A key-value dictionary describing an entry. Refer to
|
|
Entry#__init__ for the keys required/optional.
|
|
|
|
Remarks:
|
|
Subsequent calls to insert_entry with the same entry+kind name are safe
|
|
(they will be ignored).
|
|
"""
|
|
e = Entry(**entry)
|
|
self._entries.append(e)
|
|
self._entry_map[e.kind][e.name] = e
|
|
self._entries_ordered.append(e)
|
|
|
|
def insert_clone(self, clone):
|
|
"""
|
|
Insert a clone into the metadata.
|
|
|
|
Args:
|
|
clone: A key-value dictionary describing a clone. Refer to
|
|
Clone#__init__ for the keys required/optional.
|
|
|
|
Remarks:
|
|
Subsequent calls to insert_clone with the same clone+kind name are safe
|
|
(they will be ignored). Also the target entry need not be inserted
|
|
ahead of the clone entry.
|
|
"""
|
|
# figure out corresponding entry later. allow clone insert, entry insert
|
|
entry = None
|
|
c = Clone(entry, **clone)
|
|
self._entry_map[c.kind][c.name] = c
|
|
self._clones.append(c)
|
|
self._entries_ordered.append(c)
|
|
|
|
def prune_clones(self):
|
|
"""
|
|
Remove all clones that don't point to an existing entry.
|
|
|
|
Remarks:
|
|
This should be called after all insert_entry/insert_clone calls have
|
|
finished.
|
|
"""
|
|
remove_list = []
|
|
for p in self._clones:
|
|
if p.entry is None:
|
|
remove_list.append(p)
|
|
|
|
for p in remove_list:
|
|
|
|
# remove from parent's entries list
|
|
if p.parent is not None:
|
|
p.parent._entries.remove(p)
|
|
# remove from parents' _leafs list
|
|
for ancestor in p.find_parents(lambda x: not isinstance(x, Metadata)):
|
|
ancestor._leafs.remove(p)
|
|
|
|
# remove from global list
|
|
self._clones.remove(p)
|
|
self._entry_map[p.kind].pop(p.name)
|
|
self._entries_ordered.remove(p)
|
|
|
|
def is_entry_this_kind(self, entry, kind):
|
|
"""
|
|
Check if input entry if of input kind
|
|
|
|
Args:
|
|
entry: an Entry object
|
|
kind: a string. Possible values are "static", "dynamic", "controls"
|
|
|
|
Returns:
|
|
A boolean indicating whether input entry is of input kind.
|
|
"""
|
|
if kind not in ("static", "dynamic", "controls"):
|
|
assert(False), "Unknown kind value " + kind
|
|
|
|
return entry.name in self._entry_map[kind]
|
|
|
|
# After all entries/clones are inserted,
|
|
# invoke this to generate the parent/child node graph all these objects
|
|
def construct_graph(self):
|
|
"""
|
|
Generate the graph recursively, after which all Entry nodes will be
|
|
accessible recursively by crawling through the outer_namespaces sequence.
|
|
|
|
Remarks:
|
|
This is safe to be called multiple times at any time. It should be done at
|
|
least once or there will be no graph.
|
|
"""
|
|
self.validate_tree()
|
|
self._construct_tags()
|
|
self.validate_tree()
|
|
self._construct_types()
|
|
self.validate_tree()
|
|
self._construct_clones()
|
|
self.validate_tree()
|
|
self._construct_outer_namespaces()
|
|
self.validate_tree()
|
|
|
|
def _construct_tags(self):
|
|
tag_dict = self._dictionary_by_name(self.tags)
|
|
for p in self._get_properties():
|
|
p._tags = []
|
|
for tag_id in p._tag_ids:
|
|
tag = tag_dict.get(tag_id)
|
|
|
|
if tag not in p._tags:
|
|
p._tags.append(tag)
|
|
|
|
if p not in tag.entries:
|
|
tag._entries.append(p)
|
|
|
|
def _construct_types(self):
|
|
type_dict = self._dictionary_by_name(self.types)
|
|
for p in self._get_properties():
|
|
if p._type_name:
|
|
type_node = type_dict.get(p._type_name)
|
|
p._typedef = type_node
|
|
|
|
if p not in type_node.entries:
|
|
type_node._entries.append(p)
|
|
|
|
def _construct_clones(self):
|
|
for p in self._clones:
|
|
target_kind = p.target_kind
|
|
target_entry = self._entry_map[target_kind].get(p.name)
|
|
p._entry = target_entry
|
|
if (p.hal_major_version == 0):
|
|
p._hal_major_version = target_entry._hal_major_version
|
|
p._hal_minor_version = target_entry._hal_minor_version
|
|
# should not throw if we pass validation
|
|
# but can happen when importing obsolete CSV entries
|
|
if target_entry is None:
|
|
print("WARNING: Clone entry '%s' target kind '%s' has no corresponding entry" \
|
|
% (p.name, p.target_kind), file=sys.stderr)
|
|
|
|
def _construct_outer_namespaces(self):
|
|
|
|
if self._outer_namespaces is None: #the first time this runs
|
|
self._outer_namespaces = []
|
|
|
|
root = self._dictionary_by_name(self._outer_namespaces)
|
|
for ons_name, ons in root.items():
|
|
ons._leafs = []
|
|
|
|
for p in self._entries_ordered:
|
|
ons_name = p.get_outer_namespace()
|
|
ons = root.get(ons_name, OuterNamespace(ons_name, self))
|
|
root[ons_name] = ons
|
|
|
|
if p not in ons._leafs:
|
|
ons._leafs.append(p)
|
|
|
|
for ons_name, ons in root.items():
|
|
|
|
ons.validate_tree()
|
|
|
|
self._construct_sections(ons)
|
|
|
|
if ons not in self._outer_namespaces:
|
|
self._outer_namespaces.append(ons)
|
|
|
|
ons.validate_tree()
|
|
|
|
def _construct_sections(self, outer_namespace):
|
|
|
|
sections_dict = self._dictionary_by_name(outer_namespace.sections)
|
|
for sec_name, sec in sections_dict.items():
|
|
sec._leafs = []
|
|
sec.validate_tree()
|
|
|
|
for p in outer_namespace._leafs:
|
|
does_exist = sections_dict.get(p.get_section())
|
|
|
|
sec = sections_dict.get(p.get_section(), \
|
|
Section(p.get_section(), outer_namespace))
|
|
sections_dict[p.get_section()] = sec
|
|
|
|
sec.validate_tree()
|
|
|
|
if p not in sec._leafs:
|
|
sec._leafs.append(p)
|
|
|
|
for sec_name, sec in sections_dict.items():
|
|
|
|
if not sec.validate_tree():
|
|
print("ERROR: Failed to validate tree in construct_sections (start), with section = '%s'"
|
|
% (sec), file=sys.stderr)
|
|
|
|
self._construct_kinds(sec)
|
|
|
|
if sec not in outer_namespace.sections:
|
|
outer_namespace._sections.append(sec)
|
|
|
|
if not sec.validate_tree():
|
|
print("ERROR: Failed to validate tree in construct_sections (end), with section = '%s'"
|
|
% (sec), file=sys.stderr)
|
|
|
|
# 'controls', 'static' 'dynamic'. etc
|
|
def _construct_kinds(self, section):
|
|
for kind in section.kinds:
|
|
kind._leafs = []
|
|
section.validate_tree()
|
|
|
|
group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
|
|
leaf_it = ((k, g) for k, g in group_entry_by_kind)
|
|
|
|
# allow multiple kinds with the same name. merge if adjacent
|
|
# e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
|
|
# this helps maintain ABI compatibility when adding an entry in a new kind
|
|
for idx, (kind_name, entry_it) in enumerate(leaf_it):
|
|
if idx >= len(section._kinds):
|
|
kind = Kind(kind_name, section)
|
|
section._kinds.append(kind)
|
|
section.validate_tree()
|
|
|
|
kind = section._kinds[idx]
|
|
|
|
for p in entry_it:
|
|
if p not in kind._leafs:
|
|
kind._leafs.append(p)
|
|
|
|
for kind in section._kinds:
|
|
kind.validate_tree()
|
|
self._construct_inner_namespaces(kind)
|
|
kind.validate_tree()
|
|
self._construct_entries(kind)
|
|
kind.validate_tree()
|
|
|
|
if not section.validate_tree():
|
|
print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind),
|
|
file=sys.stderr)
|
|
|
|
if not kind.validate_tree():
|
|
print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind),
|
|
file=sys.stderr)
|
|
|
|
def _construct_inner_namespaces(self, parent, depth=0):
|
|
#parent is InnerNamespace or Kind
|
|
ins_dict = self._dictionary_by_name(parent.namespaces)
|
|
for name, ins in ins_dict.items():
|
|
ins._leafs = []
|
|
|
|
for p in parent._leafs:
|
|
ins_list = p.get_inner_namespace_list()
|
|
|
|
if len(ins_list) > depth:
|
|
ins_str = ins_list[depth]
|
|
ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
|
|
ins_dict[ins_str] = ins
|
|
|
|
if p not in ins._leafs:
|
|
ins._leafs.append(p)
|
|
|
|
for name, ins in ins_dict.items():
|
|
ins.validate_tree()
|
|
# construct children INS
|
|
self._construct_inner_namespaces(ins, depth + 1)
|
|
ins.validate_tree()
|
|
# construct children entries
|
|
self._construct_entries(ins, depth + 1)
|
|
|
|
if ins not in parent.namespaces:
|
|
parent._namespaces.append(ins)
|
|
|
|
if not ins.validate_tree():
|
|
print("ERROR: Failed to validate tree in construct_inner_namespaces, with ins = '%s'"
|
|
% (ins), file=sys.stderr)
|
|
|
|
# doesnt construct the entries, so much as links them
|
|
def _construct_entries(self, parent, depth=0):
|
|
#parent is InnerNamespace or Kind
|
|
entry_dict = self._dictionary_by_name(parent.entries)
|
|
for p in parent._leafs:
|
|
ins_list = p.get_inner_namespace_list()
|
|
|
|
if len(ins_list) == depth:
|
|
entry = entry_dict.get(p.name, p)
|
|
entry_dict[p.name] = entry
|
|
|
|
for name, entry in entry_dict.items():
|
|
|
|
old_parent = entry.parent
|
|
entry._parent = parent
|
|
|
|
if entry not in parent.entries:
|
|
parent._entries.append(entry)
|
|
|
|
if old_parent is not None and old_parent != parent:
|
|
print("ERROR: Parent changed from '%s' to '%s' for entry '%s'"
|
|
% (old_parent.name, parent.name, entry.name), file = sys.stderr)
|
|
|
|
def _get_children(self):
|
|
if self.outer_namespaces is not None:
|
|
for i in self.outer_namespaces:
|
|
yield i
|
|
|
|
if self.tags is not None:
|
|
for i in self.tags:
|
|
yield i
|
|
|
|
class Tag(Node):
|
|
"""
|
|
A tag Node corresponding to a top-level <tag> element.
|
|
|
|
Attributes (Read-Only):
|
|
name: alias for id
|
|
id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
|
|
description: The description of the tag, the contents of the <tag> element.
|
|
parent: An edge to the parent, which is always the Metadata root node.
|
|
entries: A sequence of edges to entries/clones that are using this Tag.
|
|
"""
|
|
def __init__(self, name, parent, description=""):
|
|
self._name = name # 'id' attribute in XML
|
|
self._id = name
|
|
self._description = description
|
|
self._parent = parent
|
|
|
|
# all entries that have this tag, including clones
|
|
self._entries = [] # filled in by Metadata#construct_tags
|
|
|
|
@property
|
|
def id(self):
|
|
return self._id
|
|
|
|
@property
|
|
def description(self):
|
|
return self._description
|
|
|
|
@property
|
|
def entries(self):
|
|
return (i for i in self._entries)
|
|
|
|
def _get_children(self):
|
|
return None
|
|
|
|
class Typedef(Node):
|
|
"""
|
|
A typedef Node corresponding to a <typedef> element under a top-level <types>.
|
|
|
|
Attributes (Read-Only):
|
|
name: The name of this typedef as a string.
|
|
languages: A dictionary of 'language name' -> 'fully qualified class'.
|
|
parent: An edge to the parent, which is always the Metadata root node.
|
|
entries: An iterable over all entries which reference this typedef.
|
|
"""
|
|
def __init__(self, name, parent, languages=None):
|
|
self._name = name
|
|
self._parent = parent
|
|
|
|
# all entries that have this typedef
|
|
self._entries = [] # filled in by Metadata#construct_types
|
|
|
|
self._languages = languages or {}
|
|
|
|
@property
|
|
def languages(self):
|
|
return self._languages
|
|
|
|
@property
|
|
def entries(self):
|
|
return (i for i in self._entries)
|
|
|
|
def _get_children(self):
|
|
return None
|
|
|
|
class OuterNamespace(Node):
|
|
"""
|
|
A node corresponding to a <namespace> element under <metadata>
|
|
|
|
Attributes (Read-Only):
|
|
name: The name attribute of the <namespace name="foo"> element.
|
|
parent: An edge to the parent, which is always the Metadata root node.
|
|
sections: A sequence of Section children.
|
|
"""
|
|
def __init__(self, name, parent, sections=[]):
|
|
self._name = name
|
|
self._parent = parent # MetadataSet
|
|
self._sections = sections[:]
|
|
self._leafs = []
|
|
|
|
self._children = self._sections
|
|
|
|
@property
|
|
def sections(self):
|
|
return (i for i in self._sections)
|
|
|
|
class Section(Node):
|
|
"""
|
|
A node corresponding to a <section> element under <namespace>
|
|
|
|
Attributes (Read-Only):
|
|
name: The name attribute of the <section name="foo"> element.
|
|
parent: An edge to the parent, which is always an OuterNamespace instance.
|
|
description: A string description of the section, or None.
|
|
kinds: A sequence of Kind children.
|
|
merged_kinds: A sequence of virtual Kind children,
|
|
with each Kind's children merged by the kind.name
|
|
hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
|
|
"""
|
|
def __init__(self, name, parent, description=None, kinds=[]):
|
|
self._name = name
|
|
self._parent = parent
|
|
self._description = description
|
|
self._kinds = kinds[:]
|
|
|
|
self._leafs = []
|
|
|
|
@property
|
|
def description(self):
|
|
return self._description
|
|
|
|
@property
|
|
def kinds(self):
|
|
return (i for i in self._kinds)
|
|
|
|
@property
|
|
def hal_versions(self):
|
|
hal_versions = set()
|
|
for i in self._kinds:
|
|
for entry in i.entries:
|
|
hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
|
|
for namespace in i.namespaces:
|
|
hal_versions.update(namespace.hal_versions)
|
|
return hal_versions
|
|
|
|
def sort_children(self):
|
|
self.validate_tree()
|
|
# order is always controls,static,dynamic
|
|
find_child = lambda x: [i for i in self._get_children() if i.name == x]
|
|
new_lst = find_child('controls') \
|
|
+ find_child('static') \
|
|
+ find_child('dynamic')
|
|
self._kinds = new_lst
|
|
self.validate_tree()
|
|
|
|
def _get_children(self):
|
|
return (i for i in self.kinds)
|
|
|
|
@property
|
|
def merged_kinds(self):
|
|
|
|
def aggregate_by_name(acc, el):
|
|
existing = [i for i in acc if i.name == el.name]
|
|
if existing:
|
|
k = existing[0]
|
|
else:
|
|
k = Kind(el.name, el.parent)
|
|
acc.append(k)
|
|
|
|
k._namespaces.extend(el._namespaces)
|
|
k._entries.extend(el._entries)
|
|
|
|
return acc
|
|
|
|
new_kinds_lst = functools.reduce(aggregate_by_name, self.kinds, [])
|
|
|
|
for k in new_kinds_lst:
|
|
yield k
|
|
|
|
def combine_kinds_into_single_node(self):
|
|
r"""
|
|
Combines the section's Kinds into a single node.
|
|
|
|
Combines all the children (kinds) of this section into a single
|
|
virtual Kind node.
|
|
|
|
Returns:
|
|
A new Kind node that collapses all Kind siblings into one, combining
|
|
all their children together.
|
|
|
|
For example, given self.kinds == [ x, y ]
|
|
|
|
x y z
|
|
/ | | \ --> / | | \
|
|
a b c d a b c d
|
|
|
|
a new instance z is returned in this example.
|
|
|
|
Remarks:
|
|
The children of the kinds are the same references as before, that is
|
|
their parents will point to the old parents and not to the new parent.
|
|
"""
|
|
combined = Kind(name="combined", parent=self)
|
|
|
|
for k in self._get_children():
|
|
combined._namespaces.extend(k.namespaces)
|
|
combined._entries.extend(k.entries)
|
|
|
|
return combined
|
|
|
|
class Kind(Node):
|
|
"""
|
|
A node corresponding to one of: <static>,<dynamic>,<controls> under a
|
|
<section> element.
|
|
|
|
Attributes (Read-Only):
|
|
name: A string which is one of 'static', 'dynamic, or 'controls'.
|
|
parent: An edge to the parent, which is always a Section instance.
|
|
namespaces: A sequence of InnerNamespace children.
|
|
entries: A sequence of Entry/Clone children.
|
|
merged_entries: A sequence of MergedEntry virtual nodes from entries
|
|
"""
|
|
def __init__(self, name, parent):
|
|
self._name = name
|
|
self._parent = parent
|
|
self._namespaces = []
|
|
self._entries = []
|
|
|
|
self._leafs = []
|
|
|
|
@property
|
|
def namespaces(self):
|
|
return self._namespaces
|
|
|
|
@property
|
|
def entries(self):
|
|
return self._entries
|
|
|
|
@property
|
|
def merged_entries(self):
|
|
for i in self.entries:
|
|
yield i.merge()
|
|
|
|
def sort_children(self):
|
|
self._namespaces.sort(key=self._get_name())
|
|
self._entries.sort(key=self._get_name())
|
|
|
|
def _get_children(self):
|
|
for i in self.namespaces:
|
|
yield i
|
|
for i in self.entries:
|
|
yield i
|
|
|
|
def combine_children_by_name(self):
|
|
r"""
|
|
Combine multiple children with the same name into a single node.
|
|
|
|
Returns:
|
|
A new Kind where all of the children with the same name were combined.
|
|
|
|
For example:
|
|
|
|
Given a Kind k:
|
|
|
|
k
|
|
/ | \
|
|
a b c
|
|
| | |
|
|
d e f
|
|
|
|
a.name == "foo"
|
|
b.name == "foo"
|
|
c.name == "bar"
|
|
|
|
The returned Kind will look like this:
|
|
|
|
k'
|
|
/ \
|
|
a' c'
|
|
/ | |
|
|
d e f
|
|
|
|
Remarks:
|
|
This operation is not recursive. To combine the grandchildren and other
|
|
ancestors, call this method on the ancestor nodes.
|
|
"""
|
|
return Kind._combine_children_by_name(self, new_type=type(self))
|
|
|
|
# new_type is either Kind or InnerNamespace
|
|
@staticmethod
|
|
def _combine_children_by_name(self, new_type):
|
|
new_ins_dict = OrderedDict()
|
|
new_ent_dict = OrderedDict()
|
|
|
|
for ins in self.namespaces:
|
|
new_ins = new_ins_dict.setdefault(ins.name,
|
|
InnerNamespace(ins.name, parent=self))
|
|
new_ins._namespaces.extend(ins.namespaces)
|
|
new_ins._entries.extend(ins.entries)
|
|
|
|
for ent in self.entries:
|
|
new_ent = new_ent_dict.setdefault(ent.name,
|
|
ent.merge())
|
|
|
|
kind = new_type(self.name, self.parent)
|
|
kind._namespaces = new_ins_dict.values()
|
|
kind._entries = new_ent_dict.values()
|
|
|
|
return kind
|
|
|
|
class InnerNamespace(Node):
|
|
"""
|
|
A node corresponding to a <namespace> which is an ancestor of a Kind.
|
|
These namespaces may have other namespaces recursively, or entries as leafs.
|
|
|
|
Attributes (Read-Only):
|
|
name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
|
|
parent: An edge to the parent, which is an InnerNamespace or a Kind.
|
|
namespaces: A sequence of InnerNamespace children.
|
|
entries: A sequence of Entry/Clone children.
|
|
merged_entries: A sequence of MergedEntry virtual nodes from entries
|
|
hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
|
|
"""
|
|
def __init__(self, name, parent):
|
|
self._name = name
|
|
self._parent = parent
|
|
self._namespaces = []
|
|
self._entries = []
|
|
self._leafs = []
|
|
|
|
@property
|
|
def namespaces(self):
|
|
return self._namespaces
|
|
|
|
@property
|
|
def entries(self):
|
|
return self._entries
|
|
|
|
@property
|
|
def hal_versions(self):
|
|
hal_versions = set()
|
|
for entry in self.entries:
|
|
hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
|
|
for namespace in self.namespaces:
|
|
hal_versions.update(namespace.hal_versions)
|
|
return hal_versions
|
|
|
|
@property
|
|
def merged_entries(self):
|
|
for i in self.entries:
|
|
yield i.merge()
|
|
|
|
def sort_children(self):
|
|
self._namespaces.sort(key=self._get_name())
|
|
self._entries.sort(key=self._get_name())
|
|
|
|
def _get_children(self):
|
|
for i in self.namespaces:
|
|
yield i
|
|
for i in self.entries:
|
|
yield i
|
|
|
|
def combine_children_by_name(self):
|
|
r"""
|
|
Combine multiple children with the same name into a single node.
|
|
|
|
Returns:
|
|
A new InnerNamespace where all of the children with the same name were
|
|
combined.
|
|
|
|
For example:
|
|
|
|
Given an InnerNamespace i:
|
|
|
|
i
|
|
/ | \
|
|
a b c
|
|
| | |
|
|
d e f
|
|
|
|
a.name == "foo"
|
|
b.name == "foo"
|
|
c.name == "bar"
|
|
|
|
The returned InnerNamespace will look like this:
|
|
|
|
i'
|
|
/ \
|
|
a' c'
|
|
/ | |
|
|
d e f
|
|
|
|
Remarks:
|
|
This operation is not recursive. To combine the grandchildren and other
|
|
ancestors, call this method on the ancestor nodes.
|
|
"""
|
|
return Kind._combine_children_by_name(self, new_type=type(self))
|
|
|
|
class EnumValue(Node):
|
|
"""
|
|
A class corresponding to a <value> element within an <enum> within an <entry>.
|
|
|
|
Attributes (Read-Only):
|
|
name: A string, e.g. 'ON' or 'OFF'
|
|
id: An optional numeric string, e.g. '0' or '0xFF'
|
|
deprecated: A boolean, True if the enum should be deprecated.
|
|
optional: A boolean
|
|
visibility: A string, one of "system", "java_public", "ndk_public", "hidden", "public",
|
|
"fwk_java_public"
|
|
notes: A string describing the notes, or None.
|
|
sdk_notes: A string describing extra notes for public SDK only
|
|
ndk_notes: A string describing extra notes for public NDK only
|
|
parent: An edge to the parent, always an Enum instance.
|
|
hal_major_version: The major HIDL HAL version this value was first added in
|
|
hal_minor_version: The minor HIDL HAL version this value was first added in
|
|
"""
|
|
def __init__(self, name, parent,
|
|
id=None, deprecated=False, optional=False, visibility=None, notes=None, sdk_notes=None, ndk_notes=None, hal_version='3.2'):
|
|
self._name = name # str, e.g. 'ON' or 'OFF'
|
|
self._id = id # int, e.g. '0'
|
|
self._deprecated = deprecated # bool
|
|
self._optional = optional # bool
|
|
self._visibility = visibility # None or str; None is same as public
|
|
self._notes = notes # None or str
|
|
self._sdk_notes = sdk_notes # None or str
|
|
self._ndk_notes = ndk_notes # None or str
|
|
self._parent = parent
|
|
if hal_version is None:
|
|
if parent is not None and parent.parent is not None:
|
|
self._hal_major_version = parent.parent.hal_major_version
|
|
self._hal_minor_version = parent.parent.hal_minor_version
|
|
else:
|
|
self._hal_major_version = 3
|
|
self._hal_minor_version = 2
|
|
else:
|
|
self._hal_major_version = int(hal_version.partition('.')[0])
|
|
self._hal_minor_version = int(hal_version.partition('.')[2])
|
|
|
|
@property
|
|
def id(self):
|
|
return self._id
|
|
|
|
@property
|
|
def deprecated(self):
|
|
return self._deprecated
|
|
|
|
@property
|
|
def optional(self):
|
|
return self._optional
|
|
|
|
@property
|
|
def visibility(self):
|
|
return self._visibility
|
|
|
|
@property
|
|
def applied_visibility(self):
|
|
return self._visibility or 'public'
|
|
|
|
@property
|
|
def hidl_comment_string(self):
|
|
parent_enum = None
|
|
if (self.parent is not None and self.parent.parent is not None):
|
|
parent_enum = self.parent.parent
|
|
if parent_enum is not None and parent_enum.visibility in ('fwk_only', 'fwk_java_public') \
|
|
or self._visibility in ('fwk_only', 'fwk_java_public'):
|
|
return ','
|
|
return ', // HIDL v' + str(self._hal_major_version) + '.' + str(self.hal_minor_version)
|
|
|
|
@property
|
|
def hidden(self):
|
|
return self.visibility in {'hidden', 'ndk_public', 'test'}
|
|
|
|
@property
|
|
def ndk_hidden(self):
|
|
return self._visibility in {'hidden', 'java_public', 'test'}
|
|
|
|
@property
|
|
def notes(self):
|
|
return self._notes
|
|
|
|
@property
|
|
def sdk_notes(self):
|
|
return self._sdk_notes
|
|
|
|
@property
|
|
def ndk_notes(self):
|
|
return self._ndk_notes
|
|
|
|
@property
|
|
def hal_major_version(self):
|
|
return self._hal_major_version
|
|
|
|
@property
|
|
def hal_minor_version(self):
|
|
return self._hal_minor_version
|
|
|
|
def _get_children(self):
|
|
return None
|
|
|
|
class Enum(Node):
|
|
"""
|
|
A class corresponding to an <enum> element within an <entry>.
|
|
|
|
Attributes (Read-Only):
|
|
parent: An edge to the parent, always an Entry instance.
|
|
values: A sequence of EnumValue children.
|
|
has_values_with_id: A boolean representing if any of the children have a
|
|
non-empty id property.
|
|
"""
|
|
def __init__(self, parent, values, ids={}, deprecateds=[],
|
|
optionals=[], visibilities={}, notes={}, sdk_notes={}, ndk_notes={}, hal_versions={}):
|
|
self._parent = parent
|
|
self._name = None
|
|
self._values = \
|
|
[ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, visibilities.get(val), \
|
|
notes.get(val), sdk_notes.get(val), ndk_notes.get(val), hal_versions.get(val)) \
|
|
for val in values ]
|
|
|
|
@property
|
|
def values(self):
|
|
return (i for i in self._values)
|
|
|
|
@property
|
|
def has_values_with_id(self):
|
|
return bool(any(i for i in self.values if i.id))
|
|
|
|
def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
|
|
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))
|
|
|
|
def _get_children(self):
|
|
return (i for i in self._values)
|
|
|
|
class Entry(Node):
|
|
"""
|
|
A node corresponding to an <entry> element.
|
|
|
|
Attributes (Read-Only):
|
|
parent: An edge to the parent node, which is an InnerNamespace or Kind.
|
|
name: The fully qualified name string, e.g. 'android.shading.mode'
|
|
name_short: The name attribute from <entry name="mode">, e.g. mode
|
|
type: The type attribute from <entry type="bar">
|
|
kind: A string ('static', 'dynamic', 'controls') corresponding to the
|
|
ancestor Kind#name
|
|
container: The container attribute from <entry container="array">, or None.
|
|
container_sizes: A sequence of size strings or None if container is None.
|
|
enum: An Enum instance if the enum attribute is true, None otherwise.
|
|
visibility: The visibility of this entry ('system', 'hidden', 'public')
|
|
across the system. System entries are only visible in native code
|
|
headers. Hidden entries are marked @hide in managed code, while
|
|
public entries are visible in the Android SDK.
|
|
applied_visibility: As visibility, but always valid, defaulting to 'system'
|
|
if no visibility is given for an entry.
|
|
applied_ndk_visible: Always valid. Default is 'false'.
|
|
Set to 'true' when the visibility implied entry is visible
|
|
in NDK.
|
|
synthetic: The C-level visibility of this entry ('false', 'true').
|
|
Synthetic entries will not be generated into the native metadata
|
|
list of entries (in C code). In general a synthetic entry is
|
|
glued together at the Java layer from multiple visibiltity=hidden
|
|
entries.
|
|
hwlevel: The lowest hardware level at which the entry is guaranteed
|
|
to be supported by the camera device. All devices with higher
|
|
hwlevels will also include this entry. None means that the
|
|
entry is optional on any hardware level.
|
|
permission_needed: Flags whether the tag needs extra camera permission.
|
|
deprecated: Marks an entry as @Deprecated in the Java layer; if within an
|
|
unreleased version this needs to be removed altogether. If applied
|
|
to an entry from an older release, then this means the entry
|
|
should be ignored by newer code.
|
|
optional: a bool representing the optional attribute, which denotes the entry
|
|
is required for hardware level full devices, but optional for other
|
|
hardware levels. None if not present.
|
|
applied_optional: As optional but always valid, defaulting to False if no
|
|
optional attribute is present.
|
|
tuple_values: A sequence of strings describing the tuple values,
|
|
None if container is not 'tuple'.
|
|
description: A string description, or None.
|
|
deprecation_description: A string describing the reason for deprecation. Must be present
|
|
if deprecated is true, otherwise may be None.
|
|
range: A string range, or None.
|
|
units: A string units, or None.
|
|
tags: A sequence of Tag nodes associated with this Entry.
|
|
type_notes: A string describing notes for the type, or None.
|
|
typedef: A Typedef associated with this Entry, or None.
|
|
|
|
Remarks:
|
|
Subclass Clone can be used interchangeable with an Entry,
|
|
for when we don't care about the underlying type.
|
|
|
|
parent and tags edges are invalid until after Metadata#construct_graph
|
|
has been invoked.
|
|
"""
|
|
def __init__(self, **kwargs):
|
|
"""
|
|
Instantiate a new Entry node.
|
|
|
|
Args:
|
|
name: A string with the fully qualified name, e.g. 'android.shading.mode'
|
|
type: A string describing the type, e.g. 'int32'
|
|
kind: A string describing the kind, e.g. 'static'
|
|
hal_version: A string for the initial HIDL HAL metadata version this entry
|
|
was added in
|
|
|
|
Args (if container):
|
|
container: A string describing the container, e.g. 'array' or 'tuple'
|
|
container_sizes: A list of string sizes if a container, or None otherwise
|
|
|
|
Args (if container is 'tuple'):
|
|
tuple_values: A list of tuple values, e.g. ['width', 'height']
|
|
|
|
Args (if the 'enum' attribute is true):
|
|
enum: A boolean, True if this is an enum, False otherwise
|
|
enum_values: A list of value strings, e.g. ['ON', 'OFF']
|
|
enum_optionals: A list of optional enum values, e.g. ['OFF']
|
|
enum_notes: A dictionary of value->notes strings.
|
|
enum_ids: A dictionary of value->id strings.
|
|
enum_hal_versions: A dictionary of value->hal version strings
|
|
|
|
Args (if the 'deprecated' attribute is true):
|
|
deprecation_description: A string explaining the deprecation, to be added
|
|
to the Java-layer @deprecated tag
|
|
|
|
Args (optional):
|
|
description: A string with a description of the entry.
|
|
range: A string with the range of the values of the entry, e.g. '>= 0'
|
|
units: A string with the units of the values, e.g. 'inches'
|
|
details: A string with the detailed documentation for the entry
|
|
hal_details: A string with the HAL implementation details for the entry
|
|
ndk_details: A string with the extra NDK API documentation for the entry=
|
|
tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
|
|
type_notes: A string with the notes for the type
|
|
visibility: A string describing the visibility, eg 'system', 'hidden',
|
|
'public'
|
|
synthetic: A bool to mark whether this entry is visible only at the Java
|
|
layer (True), or at both layers (False = default).
|
|
hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
|
|
deprecated: A bool to mark whether this is @Deprecated at the Java layer
|
|
(default = False).
|
|
optional: A bool to mark whether optional for non-full hardware devices
|
|
typedef: A string corresponding to a typedef's name attribute.
|
|
"""
|
|
|
|
if kwargs.get('type') is None:
|
|
print("ERROR: Missing type for entry '%s' kind '%s'"
|
|
% (kwargs.get('name'), kwargs.get('kind')), file=sys.stderr)
|
|
|
|
# Attributes are Read-Only, but edges may be mutated by
|
|
# Metadata, particularly during construct_graph
|
|
|
|
self._name = kwargs['name']
|
|
self._type = kwargs['type']
|
|
self._kind = kwargs['kind'] # static, dynamic, or controls
|
|
|
|
self._init_common(**kwargs)
|
|
|
|
@property
|
|
def type(self):
|
|
return self._type
|
|
|
|
@property
|
|
def kind(self):
|
|
return self._kind
|
|
|
|
@property
|
|
def hal_major_version(self):
|
|
return self._hal_major_version
|
|
|
|
@property
|
|
def hal_minor_version(self):
|
|
return self._hal_minor_version
|
|
|
|
@property
|
|
def visibility(self):
|
|
return self._visibility
|
|
|
|
@property
|
|
def applied_visibility(self):
|
|
return self._visibility or 'system'
|
|
|
|
@property
|
|
def hidl_comment_string(self):
|
|
if self._visibility in ('fwk_only', 'fwk_java_public'):
|
|
return self._visibility
|
|
visibility_lj = str(self.applied_visibility).ljust(12)
|
|
return visibility_lj + ' | HIDL v' + str(self._hal_major_version) + '.' + str(self._hal_minor_version)
|
|
|
|
@property
|
|
def applied_ndk_visible(self):
|
|
if self._visibility in ("public", "ndk_public"):
|
|
return "true"
|
|
return "false"
|
|
|
|
@property
|
|
def synthetic(self):
|
|
return self._synthetic
|
|
|
|
@property
|
|
def hwlevel(self):
|
|
return self._hwlevel
|
|
|
|
@property
|
|
def deprecated(self):
|
|
return self._deprecated
|
|
|
|
@property
|
|
def deprecation_description(self):
|
|
return self._deprecation_description
|
|
|
|
@property
|
|
def permission_needed(self):
|
|
return self._permission_needed or "false"
|
|
|
|
# TODO: optional should just return hwlevel is None
|
|
@property
|
|
def optional(self):
|
|
return self._optional
|
|
|
|
@property
|
|
def applied_optional(self):
|
|
return self._optional or False
|
|
|
|
@property
|
|
def name_short(self):
|
|
return self.get_name_minimal()
|
|
|
|
@property
|
|
def container(self):
|
|
return self._container
|
|
|
|
@property
|
|
def container_sizes(self):
|
|
if self._container_sizes is None:
|
|
return None
|
|
else:
|
|
return (i for i in self._container_sizes)
|
|
|
|
@property
|
|
def tuple_values(self):
|
|
if self._tuple_values is None:
|
|
return None
|
|
else:
|
|
return (i for i in self._tuple_values)
|
|
|
|
@property
|
|
def description(self):
|
|
return self._description
|
|
|
|
@property
|
|
def range(self):
|
|
return self._range
|
|
|
|
@property
|
|
def units(self):
|
|
return self._units
|
|
|
|
@property
|
|
def details(self):
|
|
return self._details
|
|
|
|
@property
|
|
def hal_details(self):
|
|
return self._hal_details
|
|
|
|
@property
|
|
def ndk_details(self):
|
|
return self._ndk_details
|
|
|
|
@property
|
|
def applied_ndk_details(self):
|
|
return (self._details or "") + (self._ndk_details or "")
|
|
|
|
@property
|
|
def tags(self):
|
|
if self._tags is None:
|
|
return None
|
|
else:
|
|
return (i for i in self._tags)
|
|
|
|
@property
|
|
def type_notes(self):
|
|
return self._type_notes
|
|
|
|
@property
|
|
def typedef(self):
|
|
return self._typedef
|
|
|
|
@property
|
|
def enum(self):
|
|
return self._enum
|
|
|
|
def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
|
|
if self._enum is not None:
|
|
return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version)
|
|
else:
|
|
return False
|
|
|
|
def _get_children(self):
|
|
if self.enum:
|
|
yield self.enum
|
|
|
|
def sort_children(self):
|
|
return None
|
|
|
|
def is_clone(self):
|
|
"""
|
|
Whether or not this is a Clone instance.
|
|
|
|
Returns:
|
|
False
|
|
"""
|
|
return False
|
|
|
|
def _init_common(self, **kwargs):
|
|
|
|
self._parent = None # filled in by Metadata::_construct_entries
|
|
|
|
self._container = kwargs.get('container')
|
|
self._container_sizes = kwargs.get('container_sizes')
|
|
|
|
hal_version = kwargs.get('hal_version')
|
|
if hal_version is None:
|
|
if self.is_clone():
|
|
self._hal_major_version = 0
|
|
self._hal_minor_version = 0
|
|
else:
|
|
self._hal_major_version = 3
|
|
self._hal_minor_version = 2
|
|
else:
|
|
self._hal_major_version = int(hal_version.partition('.')[0])
|
|
self._hal_minor_version = int(hal_version.partition('.')[2])
|
|
|
|
# access these via the 'enum' prop
|
|
enum_values = kwargs.get('enum_values')
|
|
enum_deprecateds = kwargs.get('enum_deprecateds')
|
|
enum_optionals = kwargs.get('enum_optionals')
|
|
enum_visibilities = kwargs.get('enum_visibilities')
|
|
enum_notes = kwargs.get('enum_notes') # { value => notes }
|
|
enum_sdk_notes = kwargs.get('enum_sdk_notes') # { value => sdk_notes }
|
|
enum_ndk_notes = kwargs.get('enum_ndk_notes') # { value => ndk_notes }
|
|
enum_ids = kwargs.get('enum_ids') # { value => notes }
|
|
enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions }
|
|
|
|
self._tuple_values = kwargs.get('tuple_values')
|
|
|
|
self._description = kwargs.get('description')
|
|
self._range = kwargs.get('range')
|
|
self._units = kwargs.get('units')
|
|
self._details = kwargs.get('details')
|
|
self._hal_details = kwargs.get('hal_details')
|
|
self._ndk_details = kwargs.get('ndk_details')
|
|
|
|
self._tag_ids = kwargs.get('tag_ids', [])
|
|
self._tags = None # Filled in by Metadata::_construct_tags
|
|
|
|
self._type_notes = kwargs.get('type_notes')
|
|
self._type_name = kwargs.get('type_name')
|
|
self._typedef = None # Filled in by Metadata::_construct_types
|
|
|
|
if kwargs.get('enum', False):
|
|
self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
|
|
enum_visibilities, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_hal_versions)
|
|
else:
|
|
self._enum = None
|
|
|
|
self._visibility = kwargs.get('visibility')
|
|
self._synthetic = kwargs.get('synthetic', False)
|
|
self._hwlevel = kwargs.get('hwlevel')
|
|
self._deprecated = kwargs.get('deprecated', False)
|
|
self._deprecation_description = kwargs.get('deprecation_description')
|
|
|
|
self._permission_needed = kwargs.get('permission_needed')
|
|
self._optional = kwargs.get('optional')
|
|
self._ndk_visible = kwargs.get('ndk_visible')
|
|
|
|
self._property_keys = kwargs
|
|
|
|
def merge(self):
|
|
"""
|
|
Copy the attributes into a new entry, merging it with the target entry
|
|
if it's a clone.
|
|
"""
|
|
return MergedEntry(self)
|
|
|
|
# Helpers for accessing less than the fully qualified name
|
|
|
|
def get_name_as_list(self):
|
|
"""
|
|
Returns the name as a list split by a period.
|
|
|
|
For example:
|
|
entry.name is 'android.lens.info.shading'
|
|
entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
|
|
"""
|
|
return self.name.split(".")
|
|
|
|
def get_inner_namespace_list(self):
|
|
"""
|
|
Returns the inner namespace part of the name as a list
|
|
|
|
For example:
|
|
entry.name is 'android.lens.info.shading'
|
|
entry.get_inner_namespace_list() == ['info']
|
|
"""
|
|
return self.get_name_as_list()[2:-1]
|
|
|
|
def get_outer_namespace(self):
|
|
"""
|
|
Returns the outer namespace as a string.
|
|
|
|
For example:
|
|
entry.name is 'android.lens.info.shading'
|
|
entry.get_outer_namespace() == 'android'
|
|
|
|
Remarks:
|
|
Since outer namespaces are non-recursive,
|
|
and each entry has one, this does not need to be a list.
|
|
"""
|
|
return self.get_name_as_list()[0]
|
|
|
|
def get_section(self):
|
|
"""
|
|
Returns the section as a string.
|
|
|
|
For example:
|
|
entry.name is 'android.lens.info.shading'
|
|
entry.get_section() == ''
|
|
|
|
Remarks:
|
|
Since outer namespaces are non-recursive,
|
|
and each entry has one, this does not need to be a list.
|
|
"""
|
|
return self.get_name_as_list()[1]
|
|
|
|
def get_name_minimal(self):
|
|
"""
|
|
Returns only the last component of the fully qualified name as a string.
|
|
|
|
For example:
|
|
entry.name is 'android.lens.info.shading'
|
|
entry.get_name_minimal() == 'shading'
|
|
|
|
Remarks:
|
|
entry.name_short it an alias for this
|
|
"""
|
|
return self.get_name_as_list()[-1]
|
|
|
|
def get_path_without_name(self):
|
|
"""
|
|
Returns a string path to the entry, with the name component excluded.
|
|
|
|
For example:
|
|
entry.name is 'android.lens.info.shading'
|
|
entry.get_path_without_name() == 'android.lens.info'
|
|
"""
|
|
return ".".join(self.get_name_as_list()[0:-1])
|
|
|
|
|
|
class Clone(Entry):
|
|
"""
|
|
A Node corresponding to a <clone> element. It has all the attributes of an
|
|
<entry> element (Entry) plus the additions specified below.
|
|
|
|
Attributes (Read-Only):
|
|
entry: an edge to an Entry object that this targets
|
|
target_kind: A string describing the kind of the target entry.
|
|
name: a string of the name, same as entry.name
|
|
kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
|
|
for the <clone> element.
|
|
type: always None, since a clone cannot override the type.
|
|
"""
|
|
def __init__(self, entry=None, **kwargs):
|
|
"""
|
|
Instantiate a new Clone node.
|
|
|
|
Args:
|
|
name: A string with the fully qualified name, e.g. 'android.shading.mode'
|
|
type: A string describing the type, e.g. 'int32'
|
|
kind: A string describing the kind, e.g. 'static'
|
|
target_kind: A string for the kind of the target entry, e.g. 'dynamic'
|
|
hal_version: A string for the initial HIDL HAL metadata version this entry
|
|
was added in
|
|
|
|
Args (if container):
|
|
container: A string describing the container, e.g. 'array' or 'tuple'
|
|
container_sizes: A list of string sizes if a container, or None otherwise
|
|
|
|
Args (if container is 'tuple'):
|
|
tuple_values: A list of tuple values, e.g. ['width', 'height']
|
|
|
|
Args (if the 'enum' attribute is true):
|
|
enum: A boolean, True if this is an enum, False otherwise
|
|
enum_values: A list of value strings, e.g. ['ON', 'OFF']
|
|
enum_optionals: A list of optional enum values, e.g. ['OFF']
|
|
enum_notes: A dictionary of value->notes strings.
|
|
enum_ids: A dictionary of value->id strings.
|
|
|
|
Args (optional):
|
|
entry: An edge to the corresponding target Entry.
|
|
description: A string with a description of the entry.
|
|
range: A string with the range of the values of the entry, e.g. '>= 0'
|
|
units: A string with the units of the values, e.g. 'inches'
|
|
details: A string with the detailed documentation for the entry
|
|
hal_details: A string with the HAL implementation details for the entry
|
|
ndk_details: A string with the extra NDK documentation for the entry
|
|
tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
|
|
type_notes: A string with the notes for the type
|
|
|
|
Remarks:
|
|
Note that type is not specified since it has to be the same as the
|
|
entry.type.
|
|
"""
|
|
self._entry = entry # Entry object
|
|
self._target_kind = kwargs['target_kind']
|
|
self._name = kwargs['name'] # same as entry.name
|
|
self._kind = kwargs['kind']
|
|
|
|
# illegal to override the type, it should be the same as the entry
|
|
self._type = None
|
|
# the rest of the kwargs are optional
|
|
# can be used to override the regular entry data
|
|
self._init_common(**kwargs)
|
|
|
|
@property
|
|
def entry(self):
|
|
return self._entry
|
|
|
|
@property
|
|
def target_kind(self):
|
|
return self._target_kind
|
|
|
|
def is_clone(self):
|
|
"""
|
|
Whether or not this is a Clone instance.
|
|
|
|
Returns:
|
|
True
|
|
"""
|
|
return True
|
|
|
|
class MergedEntry(Entry):
|
|
"""
|
|
A MergedEntry has all the attributes of a Clone and its target Entry merged
|
|
together.
|
|
|
|
Remarks:
|
|
Useful when we want to 'unfold' a clone into a real entry by copying out
|
|
the target entry data. In this case we don't care about distinguishing
|
|
a clone vs an entry.
|
|
"""
|
|
def __init__(self, entry):
|
|
"""
|
|
Create a new instance of MergedEntry.
|
|
|
|
Args:
|
|
entry: An Entry or Clone instance
|
|
"""
|
|
props_distinct = ['description', 'units', 'range', 'details',
|
|
'hal_details', 'ndk_details', 'tags', 'kind',
|
|
'deprecation_description']
|
|
|
|
for p in props_distinct:
|
|
p = '_' + p
|
|
if entry.is_clone():
|
|
setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
|
|
else:
|
|
setattr(self, p, getattr(entry, p))
|
|
|
|
props_common = ['parent', 'name', 'container',
|
|
'container_sizes', 'enum',
|
|
'tuple_values',
|
|
'type',
|
|
'type_notes',
|
|
'visibility',
|
|
'ndk_visible',
|
|
'synthetic',
|
|
'hwlevel',
|
|
'deprecated',
|
|
'optional',
|
|
'typedef',
|
|
'hal_major_version',
|
|
'hal_minor_version',
|
|
'permission_needed'
|
|
]
|
|
|
|
for p in props_common:
|
|
p = '_' + p
|
|
if entry.is_clone():
|
|
setattr(self, p, getattr(entry.entry, p))
|
|
else:
|
|
setattr(self, p, getattr(entry, p))
|